Recently I updated my compiler toolset from gcc to a clang based frontend. In this switch, the -Wno-pmf-conversion option disappeared. Searching the net for easy ways to recreate the functionality proved to be in vane. So after a bit (well a lot) of hacking, I developed the following code that does the trick. Since obtaining a C style pointer to a function and passing it arguments are useful things to have, I thought this approach would be useful for others as well. Note that I have no idea how portable this is. It could very well only work for my particular version of clang/LLVM. If any C++ gurus out there can tell me that, I’d like to know.
What’s the problem?
The following bit of code used to work with gcc and the Wno-pmf-conversions flag, but is not actually standard C++ and does not compile with clang:
// does not work :( template<typename Func> void *obtain_ptr(Func f) { return (void*) &Func::operator(); }
An error like the following occurrs:
error: cannot cast from type 'auto ((lambda at ...)() const -> void' to pointer type 'void *'
Looking around there are some tantalizing hints about how to achieve the same functionality. Namely, this from the C++ standard:
This user-defined conversion function is only defined if the capture list of the lambda-expression is empty. It is a public, constexpr (since C++17) non-virtual, non-explicit, const noexcept (since C++14) member function of the closure object.
Unfortunately, for my requirements I needed to support lamdas that have a capture list.
Enter the hack
The following template function will splice apart a lamda into a function pointer and an argument pointer:
template<typename Func> size_t function_from_lambda( void (**func_ptr)(void *), void *arg_ptr, size_t max_args, Func f) { class _dummy { public: static void function_pointer(Func *local_f) { (*local_f)(); } } dummy; *func_ptr = (void (*)(void *)) &dummy.function_pointer; assert(max_args >= sizeof(f)); memcpy(arg_ptr, &f, sizeof(f)); return sizeof(f); }
Here are some examples that use it:
int main() { void (*f)(void *); void *args = malloc(16); size_t arg_size; unsigned long long var = 1; printf("main %llx %llx\n", var, &var); arg_size = function_from_lambda(&f, args, 16, [=] { printf("Hello! %llx %llx\n", var, &var); }); printf("We're actually going to invoke it here!\n"); f(args); auto foo = [=] { printf("It even works with stored lamdas! %llx %llx\n", var, &var); }; arg_size = function_from_lambda(&f, args, 16, foo); f(args); return 0; }
Now your milage may vary, but so far, it’s a useful hack for me. Note that with this trick you can also play some nasty games:
// deep dark voodoo... don't try this at home! unsigned long long *iptr = (unsigned long long *) args; iptr[0] = 2; f(args);
Namely, you can alter the arguments after the fact if you know where they are in the class struct created for the lambda.