Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top