2

我需要将 Lambda 作为回调传递(特别是对于 WinAPI)。思路如下:

  1. 将 lambda 存储在一个单例类中(每个 Lambda,也有两个相同的,具有不同的类型),所以它应该是安全的

    LambdaSingleton<Lambda_Type>::instance = l;

  2. 将调用 lambda 实例的静态方法的地址作为回调传递。

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

我已经有一个工作类,用于在编译时提取有关函数的信息:

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

由于我只需要让编译器生成一个回调类专业化(而不是实际调用它的方法),我如何应用本网站其他问题中提出的迭代解包技术?

谢谢

4

1 回答 1

3

作为对您的问题(如何进行元组解包)的一般回答,参数包只能在模板参数类型推导的上下文中隐式生成,因此如果您想将类型“解包”为一系列类型,则必须实例化该元组并将该实例作为输入提供给某个函数模板:tuple<T1, ..., Tn>T1, ..., Tn

template<typename... Ts>
void unpack(tuple<Ts...> const&) // Now you have an argument pack...

但是,考虑到您想要实现的目标(从 lambda 获取 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;
}
于 2013-01-15T18:43:43.670 回答