Лямбда-обратные вызовы 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("");
}
}
Поскольку это шаблоны функций, размер кода может стать проблемой. Если это неприемлемо, оставьте свои текущие функции и ограничьте себя, никогда не передавая лямбды, использующие захват.