0

I have the following code:

#include <functional>

//...

typedef int (*Myfun)(int);
std::function<int (int)> fn0([](int a)->int {
    return -a;
});
std::cout << "val == " << fn0(3) << std::endl; //"val == -3"
Myfun *fptr = fn0.target<Myfun>(); //fptr is NULL!!
std::cout << "val == " << (*fptr)(3) << std::endl; //Access violation error

Actually, this code is a code from MSDN with a slight change: using lambda instead of a plain function.

Why does the call fn0.target<Myfun>() returns NULL?

When I declare a regular function

int neg(int val) { 
    return (-val); 
}

and write std::function<int (int)> fn0(neg);, everything seems to work, but lambda is not been handled correctly.

Cruel_Crow
  • 359
  • 3
  • 15

2 Answers2

5

The type Myfun from typedef int (*Myfun)(int); is not related to the type of the function's target, which is the unique, unnamed type of the closure object produced by executing the expression [](int a)->int { return -a; }

Try executing std::cout << fn0.target_type().name() << '\n'; to see for yourself.

When you declare a function with int neg(int val) { return (-val); }, the type of neg is exactly Myfun (after function-to-pointer conversion, which happens in std::function<int(int)> fn0(neg)), which is why std::function::target() is able to return a pointer to it.

Cubbi
  • 46,567
  • 13
  • 103
  • 169
  • It seems natural to me, after experience with function languages, that lambda functions are almost the same as regular functions, with the same signature. Is there any way to perform what I'm trying to do here? My final task is to convert a **capturing** lambda (with [this]) to a row function pointer. – Cruel_Crow May 02 '14 at 18:48
  • @Cruel_Crow It's unclear what you're trying to do. What do you need a raw function pointer for? Passing as callback to a C API? – Cubbi May 02 '14 at 18:54
  • 2
    @Cruel_Crow there is no storage to hold a captured parameter in a C function, you'll need a wrapper of some kind. There are plenty of Q&As on that, e.g. http://stackoverflow.com/questions/18169180/whats-the-best-way-to-wrap-a-c-callback-with-a-c11-interface – Cubbi May 02 '14 at 19:07
  • Actually, I came to a relatively "unpainful" solution: I declare a temporary static variable with a value I want to pass before the lambda. The certainly has it's limitations, but it is something. – Cruel_Crow May 02 '14 at 19:13
1

Cubbi explained why your code doesn't work -- a lambda is not a function pointer.

Now, trivial lambdas can be converted to function pointers. So supposed you really want to force that conversion?

template<typename F>
struct as_pointer_t {
  F f;
  template<typename R, typename... Args>
  operator type<R(*)(Args...)>() const { return {f}; }
  template<typename R, typename... Args>
  operator std::function<R(Args...)>() const { return (R(*)(Args...))f; }
};
template<typename F>
as_pointer_t<F> as_pointer( F&& f ) { return {std::forward<F>(f)}; }

now we can do this:

int main() {
  typedef int (*Myfun)(int);
  std::function<int (int)> fn0(as_pointer([](int a)->int {
    return -a;
  }));
  std::cout << "val == " << fn0(3) << std::endl; //"val == -3"
  Myfun *fptr = fn0.target<Myfun>(); //fptr is no longer NULL!!
  std::cout << "val == " << (*fptr)(3) << std::endl;
}

and your code works as expected. However, the above only compiles if your lambda captures nothing.

If your goal is to convert a capturing lambda to a function pointer, you cannot. You can store the state in a global variable, and use it in a non-capturing lambda. You can also convert a capturing lambda into a function-pointer and void* pair.

I have written code that takes a compile-time index to inject the void* into the list (and an optional type to use instead of void*), and produces said pair of void* and function pointer. The general case is tricky -- the specific case (say, the first argument) is far easier.

template<typename T> using type=T;

template<typename F, typename X=void*>
struct callback_t {
  F f;
  operator X() { return X(&f); }
  template<typename R, typename...Args>
  operator type<R(*)(X, Args...)>() const {
    return []( X x, Args... args )->R {
      F* f = (F*)(x);
      return (*f)(std::forward<Args>(args)...);
    };
  }
};
template<typename X=void*, typename F>
callback_t<F,X> make_callback( F f ) {
  return {std::forward<F>(f)};
}

use:

typedef void(*pfun)(void*, int);
void call_pfun( pfun f, void* p) {
  for (int i = 0; i < 3; ++i)
    f( p, i );
}
int main()
{
  int y = 7;
  auto callback = make_callback([y]( int x ) { std::cout << x+y << "\n"; });
  call_pfun( callback, callback );
}

live example.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Thanks, this example is awesome! However, I don't think I'll use this example in a real program. Instead, I prefer to wait until C++14 - maybe there it will be possible natively, who knows. – Cruel_Crow May 02 '14 at 23:05