0

I am trying to cast a lambda function to a function pointer. However, the cast fails when the lambda has a non-void return type. Details see the code snippet.

Is it possible to cast fun2 into a generic function pointer which I can save for later usage?

Update: My intention is to cast all kinds of functions to a "generic function pointer" which can be saved in a std::map. Upon usage, I will cast it back to its original function type.

#include <iostream>

int fun3() { return 1; }

int main(int argc, char *argv[]) {

  typedef void (*voidFunctionType)(void);

  // Case 1: lambda, return type void 
  auto fun1 = []() { std::cout << "hello" << std::endl; };
  // -> compiles
  auto casted_fun1 = (voidFunctionType)fun1;

  // Case 2: lambda, return type int
  auto fun2 = []() { std::cout << "world" << std::endl; return -1;};
  // -> error: invalid cast from type ‘main(int, char**)::<lambda()>’ to type ‘voidFunctionType {aka void (*)()}’
  auto casted_fun2 = (voidFunctionType)fun2;

  // Case 3: free function, return type int -> compiles
  auto casted_fun3 = (voidFunctionType)fun3;

  return 0;
}
Zheng Qu
  • 587
  • 5
  • 17

1 Answers1

1

The problem is that you are using C-style explicit casts. These are notoriously dangerous.

Here in this case the problem is that fun3 (in contrast to fun2) already decays to a function pointer of type int(*)().

You then cast it to void(*)(). This works because the C-style cast will try to do different C++ cast expressions until one works. In particular it will also try a reinterpret_cast.

reinterpret_cast<voidFunctionType>(fun3)

works, because reinterpret_cast can cast any function pointer to any other function pointer.

However, you are not allowed to call the function through the obtained pointer. Doing so causes your program to have undefined behavior. As you can see this cast is of very limited use and dangerous if you are not aware of it.

Don't use C-style casts, use static_cast<voidFunctionType>(fun3) instead and you will get the appropriate compile-time error in both cases.

You cannot use a function (whether free function or lambda) that returns one type as if it returned another (or no) type. Casting the lambda that returns int to void(*)() therefore doesn't make sense.


If you really want to save arbitrary function pointers you can make the lambda cast work by first converting it to a function pointer and then casting it to the destination function pointer type with a reinterpret_cast. I would still not use C style casts, because the reinterpret_cast will at least make it clear what kind of cast you are intending to do:

auto casted_fun2 = reinterpret_cast<voidFunctionType>(+fun2);

The unary + is a common trick to force lambda to function pointer conversion. Note however also that only lambdas without capture can be converted to function pointers.

As I explained above though, you must cast the pointer back to its original type before calling it, so you need to store the type information somewhere. I am not sure how you intend to do that, but you probably need to implement some extended version of std::function, see e.g. this question for how std::function does it.

walnut
  • 20,566
  • 4
  • 18
  • 54
  • See [this](https://stackoverflow.com/a/18889029/6018272) for more about the `+` operator on lambda. – Zheng Qu Jan 26 '20 at 14:38