Passing capturing lambda as function pointer

前端 未结 9 2227
-上瘾入骨i
-上瘾入骨i 2020-11-21 06:58

Is it possible to pass a lambda function as a function pointer? If so, I must be doing something incorrectly because I am getting a compile error.

Consider the follo

相关标签:
9条回答
  • 2020-11-21 07:21

    Lambda expressions, even captured ones, can be handled as a function pointer (pointer to member function).

    It is tricky because an lambda expression is not a simple function. It is actually an object with an operator().

    When you are creative, you can use this! Think of an "function" class in style of std::function. If you save the object you also can use the function pointer.

    To use the function pointer, you can use the following:

    int first = 5;
    auto lambda = [=](int x, int z) {
        return x + z + first;
    };
    int(decltype(lambda)::*ptr)(int, int)const = &decltype(lambda)::operator();
    std::cout << "test = " << (lambda.*ptr)(2, 3) << std::endl;
    

    To build a class that can start working like a "std::function", first you need a class/struct than can store object and function pointer. Also you need an operator() to execute it:

    // OT => Object Type
    // RT => Return Type
    // A ... => Arguments
    template<typename OT, typename RT, typename ... A>
    struct lambda_expression {
        OT _object;
        RT(OT::*_function)(A...)const;
    
        lambda_expression(const OT & object)
            : _object(object), _function(&decltype(_object)::operator()) {}
    
        RT operator() (A ... args) const {
            return (_object.*_function)(args...);
        }
    };
    

    With this you can now run captured, non-captured lambdas, just like you are using the original:

    auto capture_lambda() {
        int first = 5;
        auto lambda = [=](int x, int z) {
            return x + z + first;
        };
        return lambda_expression<decltype(lambda), int, int, int>(lambda);
    }
    
    auto noncapture_lambda() {
        auto lambda = [](int x, int z) {
            return x + z;
        };
        return lambda_expression<decltype(lambda), int, int, int>(lambda);
    }
    
    void refcapture_lambda() {
        int test;
        auto lambda = [&](int x, int z) {
            test = x + z;
        };
        lambda_expression<decltype(lambda), void, int, int>f(lambda);
        f(2, 3);
    
        std::cout << "test value = " << test << std::endl;
    }
    
    int main(int argc, char **argv) {
        auto f_capture = capture_lambda();
        auto f_noncapture = noncapture_lambda();
    
        std::cout << "main test = " << f_capture(2, 3) << std::endl;
        std::cout << "main test = " << f_noncapture(2, 3) << std::endl;
    
        refcapture_lambda();
    
        system("PAUSE");
        return 0;
    }
    

    This code works with VS2015

    Update 04.07.17:

    template <typename CT, typename ... A> struct function
    : public function<decltype(&CT::operator())(A...)> {};
    
    template <typename C> struct function<C> {
    private:
        C mObject;
    
    public:
        function(const C & obj)
            : mObject(obj) {}
    
        template<typename... Args> typename 
        std::result_of<C(Args...)>::type operator()(Args... a) {
            return this->mObject.operator()(a...);
        }
    
        template<typename... Args> typename 
        std::result_of<const C(Args...)>::type operator()(Args... a) const {
            return this->mObject.operator()(a...);
        }
    };
    
    namespace make {
        template<typename C> auto function(const C & obj) {
            return ::function<C>(obj);
        }
    }
    
    int main(int argc, char ** argv) {
       auto func = make::function([](int y, int x) { return x*y; });
       std::cout << func(2, 4) << std::endl;
       system("PAUSE");
       return 0;
    }
    
    0 讨论(0)
  • 2020-11-21 07:25

    A simular answer but i made it so you don't have to specify the type of returned pointer (note that the generic version requires C++20):

    #include <iostream>
    
    
    template<typename Function>
    struct function_traits;
    
    template <typename Ret, typename... Args>
    struct function_traits<Ret(Args...)> {
        typedef Ret(*ptr)(Args...);
    };
    
    template <typename Ret, typename... Args>
    struct function_traits<Ret(*const)(Args...)> : function_traits<Ret(Args...)> {};
    
    template <typename Cls, typename Ret, typename... Args>
    struct function_traits<Ret(Cls::*)(Args...) const> : function_traits<Ret(Args...)> {};
    
    using voidfun = void(*)();
    
    template <typename F>
    voidfun lambda_to_void_function(F lambda) {
        static auto lambda_copy = lambda;
    
        return []() {
            lambda_copy();
        };
    }
    
    // requires C++20
    template <typename F>
    auto lambda_to_pointer(F lambda) -> typename function_traits<decltype(&F::operator())>::ptr {
        static auto lambda_copy = lambda;
        
        return []<typename... Args>(Args... args) {
            return lambda_copy(args...);
        };
    }
    
    
    
    int main() {
        int num;
    
        void(*foo)() = lambda_to_void_function([&num]() {
            num = 1234;
        });
        foo();
        std::cout << num << std::endl; // 1234
    
        int(*bar)(int) = lambda_to_pointer([&](int a) -> int {
            num = a;
            return a;
        });
        std::cout << bar(4321) << std::endl; // 4321
        std::cout << num << std::endl; // 4321
    }
    
    0 讨论(0)
  • 2020-11-21 07:25

    As it was mentioned by the others you can substitute Lambda function instead of function pointer. I am using this method in my C++ interface to F77 ODE solver RKSUITE.

    //C interface to Fortran subroutine UT
    extern "C"  void UT(void(*)(double*,double*,double*),double*,double*,double*,
    double*,double*,double*,int*);
    
    // C++ wrapper which calls extern "C" void UT routine
    static  void   rk_ut(void(*)(double*,double*,double*),double*,double*,double*,
    double*,double*,double*,int*);
    
    //  Call of rk_ut with lambda passed instead of function pointer to derivative
    //  routine
    mathlib::RungeKuttaSolver::rk_ut([](double* T,double* Y,double* YP)->void{YP[0]=Y[1]; YP[1]= -Y[0];}, TWANT,T,Y,YP,YMAX,WORK,UFLAG);
    
    0 讨论(0)
  • 2020-11-21 07:26

    Not a direct answer, but a slight variation to use the "functor" template pattern to hide away the specifics of the lambda type and keeps the code nice and simple.

    I was not sure how you wanted to use the decide class so I had to extend the class with a function that uses it. See full example here: https://godbolt.org/z/jtByqE

    The basic form of your class might look like this:

    template <typename Functor>
    class Decide
    {
    public:
        Decide(Functor dec) : _dec{dec} {}
    private:
        Functor _dec;
    };
    

    Where you pass the type of the function in as part of the class type used like:

    auto decide_fc = [](int x){ return x > 3; };
    Decide<decltype(decide_fc)> greaterThanThree{decide_fc};
    

    Again, I was not sure why you are capturing x it made more sense (to me) to have a parameter that you pass in to the lambda) so you can use like:

    int result = _dec(5); // or whatever value
    

    See the link for a complete example

    0 讨论(0)
  • 2020-11-21 07:30

    Capturing lambdas cannot be converted to function pointers, as this answer pointed out.

    However, it is often quite a pain to supply a function pointer to an API that only accepts one. The most often cited method to do so is to provide a function and call a static object with it.

    static Callable callable;
    static bool wrapper()
    {
        return callable();
    }
    

    This is tedious. We take this idea further and automate the process of creating wrapper and make life much easier.

    #include<type_traits>
    #include<utility>
    
    template<typename Callable>
    union storage
    {
        storage() {}
        std::decay_t<Callable> callable;
    };
    
    template<int, typename Callable, typename Ret, typename... Args>
    auto fnptr_(Callable&& c, Ret (*)(Args...))
    {
        static bool used = false;
        static storage<Callable> s;
        using type = decltype(s.callable);
    
        if(used)
            s.callable.~type();
        new (&s.callable) type(std::forward<Callable>(c));
        used = true;
    
        return [](Args... args) -> Ret {
            return Ret(s.callable(std::forward<Args>(args)...));
        };
    }
    
    template<typename Fn, int N = 0, typename Callable>
    Fn* fnptr(Callable&& c)
    {
        return fnptr_<N>(std::forward<Callable>(c), (Fn*)nullptr);
    }
    

    And use it as

    void foo(void (*fn)())
    {
        fn();   
    }
    
    int main()
    {
        int i = 42;
        auto fn = fnptr<void()>([i]{std::cout << i;});
        foo(fn);  // compiles!
    }
    

    Live

    This is essentially declaring an anonymous function at each occurrence of fnptr.

    Note that invocations of fnptr overwrite the previously written callable given callables of the same type. We remedy this, to a certain degree, with the int parameter N.

    std::function<void()> func1, func2;
    auto fn1 = fnptr<void(), 1>(func1);
    auto fn2 = fnptr<void(), 2>(func2);  // different function
    
    0 讨论(0)
  • 2020-11-21 07:30

    A shortcut for using a lambda with as a C function pointer is this:

    "auto fun = +[](){}"
    

    Using Curl as exmample (curl debug info)

    auto callback = +[](CURL* handle, curl_infotype type, char* data, size_t size, void*){ //add code here :-) };
    curl_easy_setopt(curlHande, CURLOPT_VERBOSE, 1L);
    curl_easy_setopt(curlHande,CURLOPT_DEBUGFUNCTION,callback);
    
    0 讨论(0)
提交回复
热议问题