在没有实例的情况下调用无状态 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);
}

Live demo

或将您的呼叫站点更改为:

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