В одной кавычки строка не содержит переменных внутри нее. Строка с двойными кавычками.
Кроме того, строка с двумя кавычками может содержать апострофы без обратных косых черт, в то время как строка с одним кавычком может содержать неэкранированные кавычки.
Одиночный кавычек строки во время выполнения быстрее, потому что их не нужно анализировать.
Лямбда, которая захватывает переменные контекста, не может быть преобразована в указатель на голой функции, поскольку это может привести к невозможности переноса захваченного состояния. То, что вы показали в примере, - это правильный способ решить проблему вызова функций-членов C ++ с помощью обратного вызова C.
Вы можете заменить c_callback_wrapper
на захват меньше лямбда, если вы обнаружите, что более привлекательным.
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
[](char *ptr, size_t size, size_t nmemb, void *userdata) {
// invoke the member function via userdata
auto p = static_cast<MyClass *>(userdata);
return p->actualCallback(ptr, size, nmemb, userdata);
});
Обратите внимание, что вам, вероятно, следует избавиться от последнего параметра actualCallback()
, так как это только указатель this
, который не нужно передавать нестатической функции-члена явно.
Вот что я хотел бы сделать (, пожалуйста, обратите внимание, что я достаточно знаю о C ++, чтобы сдуть случайную ногу ):
#include <iostream>
#include <functional>
void external_c_function(void cb(void *), void *userdata)
{
cb(userdata);
}
void c_callback_wrapper(void *userdata)
{
auto &lambda = *static_cast< std::function<void(void)>* >(userdata);
std::cout << "calling lambda" << std::endl;
lambda();
}
int main(void)
{
int foo = 42;
std::function<void(void)> lambda = [&] { std::cout << foo << std::endl; };
external_c_function(c_callback_wrapper, &lambda);
return 0;
}
template<class T>using type=T; // makes some declarations easier
template<class F>
struct callback_t;
template<class F, class Sig>
struct cast_helper_pvoid_last;
template<class F, class R, class... Args>
struct cast_helper_pvoid_last<F, R(Args...)> {
type<R(*)(Args..., void*)> operator()() const {
return [](Args... args, void* pvoid)->R {
auto* callback = static_cast<callback_t<F>*>(pvoid);
return callback->f( std::forward<Args>(args)... );
};
}
};
template<class F>
struct callback_t {
F f;
void* pvoid() { return this; }
template<class Sig>
auto pvoid_at_end()->decltype( cast_helper_pvoid_last<F, Sig>{}() ) {
return cast_helper_pvoid_last<F,Sig>{}();
}
};
template<class T>using decay_t=typename std::decay<T>::type;
template<class F>
callback_t<decay_t<F>> make_callback( F&& f ) { return {std::forward<F>(f)}; }
Пример использования:
int x = 3;
auto callback = make_callback( [&]( int y ) { return x+y; } );
int (*func)(int, void*) = callback.pvoid_at_end<int(int)>();
std::cout << func( 1, callback.pvoid() ) << "\n";
должен печатать 4. (живой пример)
Время жизни вашего callback_t
должно превышать pvoid
, который он производит.
Я мог бы автоматически выводить подпись указателя функции вместо того, чтобы потребовать вас передать <int(int)>
(подпись без void*
), но опять же, что делает код намного легче.
и добавьте это, если вы хотите, чтобы void*
был первым:
template<class F, class Sig>
struct cast_helper_pvoid_first;
template<class F, class R, class... Args>
struct cast_helper_pvoid_first<class F, R(Args...)> {
type<R(*)(void*, Args...)> operator()() const {
return [](void* pvoid, Args... args)->R {
auto* callback = static_cast<callback<F>*>(pvoid);
return callback->f( std::forward<Args>(args)... );
};
}
};
// inside the callback_t<?> template:
template<class Sig>
auto pvoid_at_start()->decltype( cast_helper_pvoid_first<F, Sig>{}() ) {
return cast_helper_pvoid_first<F,Sig>{}();
}
, делающий void*
в середине, становится более сложным.
Если у вас есть проблемы с вызовами соглашения, нам нужно использовать вспомогательный объект
. Имейте pvoid_at_end
, возвратите cast_helper_pvoid_last
вместо вызова ()
на нем.
Затем , добавьте operator
перегрузки к указателю на функцию для каждого вызывающего соглашения, которое необходимо поддерживать. Тело идентично operator()
, поскольку лямбда должна поддерживать любой из них.
В качестве альтернативы, с некоторой поддержкой C ++ 14 вы можете изменить тип возврата operator()
на auto
, и оставьте код в противном случае неповрежденным, и полагайтесь на прямой cast-from-lambda, чтобы получить право называть право.