Лямбда-обратные вызовы C++

Я пытаюсь создать класс HTTP и хочу использовать обратные вызовы C++11 (еще не C++14) через лямбды. У меня есть 2 мокапа, первый работает... но выглядит некрасиво. Второй, к которому я стремлюсь, не компилируется (ошибка в конце).

Я не могу использовать std::function, поскольку это встроенный проект, и этот шаблон генерирует большой объем кода.

#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);
    });
}

Ошибка от gcc (xtensa-esp32-elf-g++ (crossstool-NG crossstool-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*)'

1 ответ

Решение

Лямбда без списка захвата совместимы с указателями на функции, поэтому ваша первая лямбда может быть передана в качестве аргумента в get1(). Однако лямбда-выражения со списком захвата не могут быть преобразованы в указатели функций, поэтому их нельзя передать вget2().

Лямбда-выражения с захватами имеют состояние, но функции не могут иметь состояния, поэтому такие лямбда-выражения не могут быть преобразованы в указатели на функции.

Самый распространенный способ заставить функцию принимать любую лямбду (или любой вызываемый объект) - использовать шаблоны функций:

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("");
    }
}

Поскольку это шаблоны функций, размер кода может стать проблемой. Если это неприемлемо, оставьте свои текущие функции и ограничьте себя, никогда не передавая лямбды, использующие захват.

Другие вопросы по тегам