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.