gpt4 book ai didi

c++ - 作为回调传递静态方法的地址,其签名来自元组解包

转载 作者:搜寻专家 更新时间:2023-10-31 01:51:01 25 4
gpt4 key购买 nike

我需要传递一个 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);
    }
    };

我已经有一个工作类用于在编译时提取有关函数的信息 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...

但是,考虑到您想要实现的目标(从 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;
}

关于c++ - 作为回调传递静态方法的地址,其签名来自元组解包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14343428/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com