Мне нужно передать лямбду в качестве обратного вызова (в частности, для WinAPI). Идея заключается в следующем:
Храните лямбду в синглтон-классе (каждая лямбда, также две идентичные, имеют разные типы), поэтому она должна быть безопасной
LambdaSingleton<Lambda_Type>::instance = l;
Передайте в качестве обратного вызова адрес статического метода, который вызывает лямбда-экземпляр.
template <
typename Lambda,
typename Callback_Signature_R,
typename... Callback_Signature_Args>
struct LambdaCallbackSupport{
/**
* Callback method
*
* @param args
* The parameters to feed to the lambda
* @return
* The return value of the execution of the lambda
*/
static Callback_Signature_R __stdcall callback(Callback_Signature_Args... args){
return LambdaSingleton<Lambda>::instance(args);
}
};
У меня уже есть рабочий класс для извлечения информации о функциях во время компиляции es:
template<
typename C,
typename R,
typename... Args>
struct Traits<R(__stdcall *)(Args...) const>{
//various typedefs for R, tuple of args, arity etc..
};
Так что я бы получил что-то вроде этого:
//Example lambda
int toBeCaptured = 8;
auto lambda =
[&](std::string& str) -> size_t{
return toBeCaptured + str.length();
};
typedef decltype(lambda) Lambda;
//Expected callback signature
typedef size_t(__stdcall *CallbackSignature)(std::string&);
//Configure a callback support and pass its method
typedef Traits<CallbackSignature> callbackTraits;
typedef LambdaCallbackSupport<
Lambda,
callbackTraits::Result_Type,
callbackTraits::Args_Tuple_Pack> CallbackSupportType;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //How to unpack the tuple without actually have the arguments??
//Store the lambda instance statically
Singleton<Lambda>::instance = lambda;
//Pass the callback
void* pFunc = &CallbackSupportType::callback;
//Simulate invocation of callback
std::string str("may work?");
size_t ret = (*pFunc)(str);
Поскольку мне нужно только позволить компилятору генерировать специализацию класса обратного вызова (а не вызывать его метод) Как я могу применить метод итеративной распаковки, предложенный в других вопросах на этом сайте?
Спасибо
Как общий ответ на ваш вопрос (как распаковать кортежи), пакеты параметров могут быть сгенерированы только косвенным образом в контексте вывод типа аргумента шаблона, так что если вы хотите «распаковать» тип tuple<T1, ..., Tn>
в последовательность типов T1, ..., Tn
Вы должны создать экземпляр этого кортежа и предоставить этот экземпляр для ввода в некоторый шаблон функции:
template<typename... Ts>
void unpack(tuple<Ts...> const&) // Now you have an argument pack...
Однако, учитывая, чего вы хотите достичь (получить обратный вызов WinAPI от лямбды), я бы не стал полагаться на кортежи, а скорее использовал бы бесплатный шаблон функций. Это можно сделать, не вводя много уровней косвенных указателей и оболочек. Вот возможное простое решение:
#include <type_traits>
#include <memory>
template<typename F>
struct singleton
{
static void set_instance(F f) { instance.reset(new F(f)); }
static std::unique_ptr<F> instance;
};
template<typename F>
std::unique_ptr<F> singleton<F>::instance;
template<typename F, typename... Ts>
typename std::result_of<F(Ts...)>::type __stdcall lambda_caller(Ts... args)
{
if (singleton<F>::instance == nullptr)
{
// throw some exception...
}
else
{
return (*(singleton<F>::instance))(args...);
}
}
Это рамки. И вот как вы бы это использовали:
#include <iostream>
int main()
{
//Example lambda
int toBeCaptured = 8;
auto lambda =
[&](std::string& str) -> size_t{
return toBeCaptured + str.length();
};
singleton<decltype(lambda)>::set_instance(lambda);
size_t (__stdcall *pfn)(std::string&) = &lambda_caller<decltype(lambda)>;
std::string str = "hello";
int out = pfn(str);
std::cout << out;
return 0;
}
Если вы не возражаете против макросов и хотите еще больше упростить это для некоторых шаблонов использования (например, приведенных выше), вы можете добавить макрос следующим образом:
#define get_api_callback(lambda) \
&lambda_caller<decltype(lambda)>; singleton<decltype(lambda)>::set_instance(lambda);
Это изменит ваш main()
функция в следующем:
#include <iostream>
int main()
{
//Example lambda
int toBeCaptured = 8;
auto lambda =
[&](std::string& str) -> size_t{
return toBeCaptured + str.length();
};
// As simple as that...
size_t (__stdcall *pfn)(std::string&) = get_api_callback(lambda);
std::string str = "hello";
int out = pfn(str);
std::cout << out;
return 0;
}
Других решений пока нет …