在没有实例的情况下调用无状态 lambda(仅类型)
Calling a stateless lambda without an instance (only type)
我正在尝试为 C 库中的 "register callback" type of interface 编写包装器。这个问题相当复杂,因为该库允许您通过接受参数定义列表来注册 "variadic" 函数。然后在回调时,该函数应从类型擦除的参数列表中提取其参数。好老C...
我试图创建的接口是接受任何函数,甚至是 lambda,并自动生成所有机制以正确注册此函数,同时注册参数并在回调期间提取它们。所以用户只需要输入:
register_callback("Callback Name", [](int i){return i+0.5;});
现在这在理论上是可行的,但在某一时刻,我需要能够在没有任何实例的情况下调用 lambda,但只能访问其类型。我不明白为什么,但是无状态(非捕获)lambda 的默认构造函数被删除了,所以我不能简单地从它的类型进行默认构造。我打算做的是看起来很脏,但应该可以解决这个问题的方法:
template <class Func, class RET, class ...ARGS>
struct from_stateless {
static RET to_function(ARGS...args) {
RET (*f)(ARGS...) = *(Func *)(0);
return f(args...);
}
};
所以 from_stateless<LAMBDA_TYPE, RET, ARGS...>::to_function
实际上是一个函数指针,我可以在没有参数的情况下调用它,更重要的是,我可以将它作为模板参数传递。
在正常情况下,RET (*f)(ARGS...) = *(Func *)(0);
行会自杀,但在这个用例中应该是安全的,不是吗?毕竟,转换后得到的函数指针在任何宇宙中都不可能依赖于lambda实例。
所以,问题是,只要我确定类型确实是无状态 lambda,这样做是否安全?或者我错过了什么?请注意,RET (*f)(ARGS...) = *(Func *)(0);
会在闭包意外漏掉时触发编译器错误。
澄清:我不能只将 lambda 衰减为函数指针并注册它,因为 lambda 的签名与 "Register" 方法不兼容。 register 方法需要一个带有签名的函数:void (*)(int number_of_args, TypeErasedValue * arguments, TypeErasedValue * result)
所以你看,我需要(并且已经做了),通过模板元编程,生成一个以 lambda 类型为模板的自由函数,作为预期之间的适配器以及实际的签名。
假设你有东西注册正常功能
template <typename Ret, typename... Args>
void register_callback(const std::string& s, Ret(*)(Args...))
{
std::cout << "register function " << s << std::endl;
// Implementation
}
您可以简单地在函数指针中显式转换您的非捕获 lambda:
template <typename Lambda>
void register_callback(const std::string& s, const Lambda&f)
{
// convert non capturing lambda into function pointer
Register(s, +f);
}
或将您的呼叫站点更改为:
register_callback("Callback Name", +[](int i){return i + 0.5;});
注意:您可以使用 *
而不是 +
进行衰减。
想要定义的行为?
添加:
static Func state(Func* op=0){
static Func f=*op;
return f;
}
到您的模板 class。第一次调用它时,将 ptr 传递给 lambda。第二次,免费提取价值
static Func f
是在第一次调用函数时构造的:只要那次指针不为空,我们就可以了。每次调用它时,指针可以为 null 并且它不使用它,因为 static
局部变量只构造一次。
使用指向 lambda 的指针将其调用到 'register'。在其他地方(在回调时),调用它来获取副本。 Lambda 是无状态的,所以复制是免费的。
如果您利用 lambda 可以看到其封闭范围的 static
局部变量而不需要捕获它们这一事实,它会比 @Yakk 的答案更容易。使用这个事实,您可以将用户提供的无状态 lambda 包装在您自己的具有所需签名的无状态 lambda 中,并将后者转换为函数指针。
using callback = void (*)(int, TypeErasedValue*, TypeErasedValue*);
template <typename F>
callback stateless_to_callback(F f) {
static F static_f = f;
return [](int argc, TypeErasedValue* argv, TypeErasedValue* result) {
// extract arguments from argv
auto r = static_f( /* arguments... */ );
// store r in result
};
}
我正在尝试为 C 库中的 "register callback" type of interface 编写包装器。这个问题相当复杂,因为该库允许您通过接受参数定义列表来注册 "variadic" 函数。然后在回调时,该函数应从类型擦除的参数列表中提取其参数。好老C...
我试图创建的接口是接受任何函数,甚至是 lambda,并自动生成所有机制以正确注册此函数,同时注册参数并在回调期间提取它们。所以用户只需要输入:
register_callback("Callback Name", [](int i){return i+0.5;});
现在这在理论上是可行的,但在某一时刻,我需要能够在没有任何实例的情况下调用 lambda,但只能访问其类型。我不明白为什么,但是无状态(非捕获)lambda 的默认构造函数被删除了,所以我不能简单地从它的类型进行默认构造。我打算做的是看起来很脏,但应该可以解决这个问题的方法:
template <class Func, class RET, class ...ARGS>
struct from_stateless {
static RET to_function(ARGS...args) {
RET (*f)(ARGS...) = *(Func *)(0);
return f(args...);
}
};
所以 from_stateless<LAMBDA_TYPE, RET, ARGS...>::to_function
实际上是一个函数指针,我可以在没有参数的情况下调用它,更重要的是,我可以将它作为模板参数传递。
在正常情况下,RET (*f)(ARGS...) = *(Func *)(0);
行会自杀,但在这个用例中应该是安全的,不是吗?毕竟,转换后得到的函数指针在任何宇宙中都不可能依赖于lambda实例。
所以,问题是,只要我确定类型确实是无状态 lambda,这样做是否安全?或者我错过了什么?请注意,RET (*f)(ARGS...) = *(Func *)(0);
会在闭包意外漏掉时触发编译器错误。
澄清:我不能只将 lambda 衰减为函数指针并注册它,因为 lambda 的签名与 "Register" 方法不兼容。 register 方法需要一个带有签名的函数:void (*)(int number_of_args, TypeErasedValue * arguments, TypeErasedValue * result)
所以你看,我需要(并且已经做了),通过模板元编程,生成一个以 lambda 类型为模板的自由函数,作为预期之间的适配器以及实际的签名。
假设你有东西注册正常功能
template <typename Ret, typename... Args>
void register_callback(const std::string& s, Ret(*)(Args...))
{
std::cout << "register function " << s << std::endl;
// Implementation
}
您可以简单地在函数指针中显式转换您的非捕获 lambda:
template <typename Lambda>
void register_callback(const std::string& s, const Lambda&f)
{
// convert non capturing lambda into function pointer
Register(s, +f);
}
或将您的呼叫站点更改为:
register_callback("Callback Name", +[](int i){return i + 0.5;});
注意:您可以使用 *
而不是 +
进行衰减。
想要定义的行为?
添加:
static Func state(Func* op=0){
static Func f=*op;
return f;
}
到您的模板 class。第一次调用它时,将 ptr 传递给 lambda。第二次,免费提取价值
static Func f
是在第一次调用函数时构造的:只要那次指针不为空,我们就可以了。每次调用它时,指针可以为 null 并且它不使用它,因为 static
局部变量只构造一次。
使用指向 lambda 的指针将其调用到 'register'。在其他地方(在回调时),调用它来获取副本。 Lambda 是无状态的,所以复制是免费的。
如果您利用 lambda 可以看到其封闭范围的 static
局部变量而不需要捕获它们这一事实,它会比 @Yakk 的答案更容易。使用这个事实,您可以将用户提供的无状态 lambda 包装在您自己的具有所需签名的无状态 lambda 中,并将后者转换为函数指针。
using callback = void (*)(int, TypeErasedValue*, TypeErasedValue*);
template <typename F>
callback stateless_to_callback(F f) {
static F static_f = f;
return [](int argc, TypeErasedValue* argv, TypeErasedValue* result) {
// extract arguments from argv
auto r = static_f( /* arguments... */ );
// store r in result
};
}