передать в качестве обратного вызова адрес статического метода, подпись которого пришла при распаковке кортежа

Мне нужно передать лямбду в качестве обратного вызова (в частности, для WinAPI). Идея заключается в следующем:

  1. Храните лямбду в синглтон-классе (каждая лямбда, также две идентичные, имеют разные типы), поэтому она должна быть безопасной

    LambdaSingleton<Lambda_Type>::instance = l;

  2. Передайте в качестве обратного вызова адрес статического метода, который вызывает лямбда-экземпляр.

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

Поскольку мне нужно только позволить компилятору генерировать специализацию класса обратного вызова (а не вызывать его метод) Как я могу применить метод итеративной распаковки, предложенный в других вопросах на этом сайте?

Спасибо

2

Решение

Как общий ответ на ваш вопрос (как распаковать кортежи), пакеты параметров могут быть сгенерированы только косвенным образом в контексте вывод типа аргумента шаблона, так что если вы хотите «распаковать» тип 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;
}
3

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]