Search code examples
c++11lambdacallbackgcc5.2

C++ lambda callbacks


I am trying to make an HTTP class, and I want to use C++11 (not C++14 yet) callbacks via lambdas. I have 2 mockups available, the first one works... but looks ugly. The second one I am aiming, is not compiling (error at the end).

I cannot use std::function, as this is an embedded project, and that template generates a lot of code.

#include <cstring>

class HTTP
{
public:
    void get1(const char* url, void* context, void (*callback)(void*, const char*) )
    {
        callback(context, "");
    }

    void get2(const char* url, void (*callback)(const char*) )
    {
        callback("");
    }
};

void test()
{
    int k;
    HTTP http;
    http.get1( "http://google.com", &k, [](void* context, const char* s){
        int *k = (int*) context;
        *k = strlen(s);
    });

    // this does not compile, looking for other alternatives
    http.get2( "http://google.com", [&k](const char* s){
        k = strlen(s);
    });
}

Error from gcc (xtensa-esp32-elf-g++ (crosstool-NG crosstool-ng-1.22.0-80-g6c4433a) 5.2.0)

HttpRequests.cpp: In function 'void test()':
HttpRequests.cpp:29:6: error: no matching function for call to 'HTTP::get2(const char [18], test()::<lambda(const char*)>)'
     });
      ^
HttpRequests.cpp:11:10: note: candidate: void HTTP::get2(const char*, void (*)(const char*))
     void get2(const char* url, void (*callback)(const char*) )
          ^
HttpRequests.cpp:11:10: note:   no known conversion for argument 2 from 'test()::<lambda(const char*)>' to 'void (*)(const char*)'

Solution

  • Lambdas without a capture list are compatible with function pointers, so your first lambda can be passed as an argument to get1(). However, lambdas with a capture list are not convertible to function pointers so it can not be passed to get2().

    Lambdas with captures have state but functions can not have state, which is why such lambdas are not convertible to function pointers.

    The most common way to have a function accept any lambda (or any callable object) is to use function templates:

    class HTTP {
    
        // ...
    
        template <typename Callable>
        void get1(const char* url, void* context, Callable callback)
        {
            callback(context, "");
        }
    
        template <typename Callable>
        void get2(const char* url, Callable callback)
        {
            callback("");
        }
    }
    

    Being function templates, code size might become an issue. If that's not acceptable, then keep your current functions and restrict yourself to never passing lambdas that use captures.