Function pointers from lambdas

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.