2

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*)'
elcuco
  • 8,948
  • 9
  • 47
  • 69
  • Why not make `get2` as function template which could accept any callable object? : `template void get2(const char* url, F f) { f(""); }`. – rafix07 Mar 11 '20 at 14:00

1 Answers1

2

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.

Nikos C.
  • 50,738
  • 9
  • 71
  • 96
  • Nice.. but, how can I notify the user of the API about the required arguments to use? With this (working) implementation it's up to his job to guess what I need. – elcuco Mar 11 '20 at 14:16
  • 1
    @elcuco C++20 concepts might help, but I guess that's way too recent to be an option in your case. So you'll have to just document the expected signature. – Nikos C. Mar 11 '20 at 14:23