Search code examples
c++cpointerslambdaruntime

Converting lambda (cpp) to function pointer (for c)


I am trying to implement a parallel runtime using argobots api.

In the main.cpp I am using a lambda function which is argument to the "kernel" function in lib.cpp. I need to convert the lambda received in lib.hpp to a function pointer and call "lib_kernel" in lib.c. I have read many answers and came to know that converting lambdas (capture by reference) to function pointer is not possible. Is there any alternative?. I also don't have any idea how deal with the template parameter T in the "kernel" function. Please help.

// main.cpp

#include "lib.hpp"

int main(int argc, char **argv) {
    int result;
    lib::kernel([&]() {
        result = fib(10); 
    }); 
    cout << "Fib(10) = " << result << "\n";
    // fib is parallel implementation of fibonacci
    // similar to passing function pointers to threads
}

// lib.hpp

namespace lib {

    void kernel(T &&lambda) {
        // T is template argument
        // Need to convert lambda to function pointer
        // ie. lib_kernel(fptr, args)
        // Here fptr and args are received from lambda
        // ie. fptr will be fib function 
        // args is value 10.
    }
}

// lib.c

typedef void (*fork_t)(void* args);

void lib_kernel(fork_t fptr, void* args) {
    fptr(args);
}

Solution

  • You can convert a lambda to a function pointer with +, i.e.:

    typedef void (*func)(void* args);
    
    void kernel(func fn, void* args){ 
        fn(args);
    }
    
    void CallKernelWithLambda() {
        func f = +[](void*) { };
        kernel(f, nullptr);
    }
    

    However, lambdas that capture cannot be converted to a function pointer -- the following fails to compile:

    typedef void (*func)(void* args);
    
    void kernel(func fn, void* args){ 
        fn(args);
    }
    
    void CallKernelWithLambda() {
        int value = 0;
        func f = +[&](void*) { ++value; };
        kernel(f, nullptr);
    }
    

    You need to put the value that you want to capture in static storage or global scope, e.g.:

    typedef void (*func)(void* args);
    
    void kernel(func fn, void* args){ 
        fn(args);
    }
    
    int value = 0;
    void CallKernelWithLambda() {
        func f = +[](void*) { ++value; };
        kernel(f, nullptr);
    }
    

    Putting the call to kernel inside of a templated function doesn't make any difference, i.e.:

    typedef void (*func)(void* args);
    
    void kernel(func fn, void* args){ 
        fn(args);
    }
    
    template <typename T>
    void CallKernelWithTemplateArgumentLambda(T&& lambda) {
        kernel(+lambda, nullptr);
    }
    int value = 0;
    void CallKernelWithLambda() {
        CallKernelWithTemplateArgumentLambda([](void*) { ++value; });
    }
    

    behaves the same way as the previous snippet.