将签名来自元组解包的静态方法的地址作为回调传递



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

  1. 将lambda存储在单例类中(每个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);

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

谢谢

作为你的问题(如何做元组解包)的一般答案,参数包只能在模板参数类型演绎的上下文中隐式地生成,所以如果你想将类型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;
}

最新更新