如何将C ++ lambda传递给需要函数指针和上下文的C-callback?

我正在尝试在使用标准函数指针+上下文范例的C-API中注册回调。 这是api的样子:

void register_callback(void(*callback)(void *), void * context); 

我真正想做的是能够注册一个C ++ lambda作为回调。 另外,我希望lambda是一个捕获变量的(即无法转换为直接无状态std::function

我需要写什么样的适配器代码才能将lambda注册为回调?

简单的aporoach是将lambda粘贴到std::function ,它保存在某个地方。 它可能在堆上分配,仅由在采用回调的实体注册的void*引用。 那么回调就是这样的函数:

 extern "C" void invoke_function(void* ptr) { (*static_cast*>(ptr))(); } 

请注意, std::function可以保存具有状态的函数对象,例如具有非空捕获的lambda函数。 您可以注册这样的回调:

 register_callback(&invoke_function, new std::function([=](){ ... })); 

最有效的方法是直接使lambda voidify

 #include  #include  #include  template std::pair< void(*)(void*, Args...), std::unique_ptr > voidify( Lambda&& l ) { typedef typename std::decay::type Func; std::unique_ptr data( new Func(std::forward(l)), +[](void* ptr){ delete (Func*)ptr; } ); return { +[](void* v, Args... args)->void { Func* f = static_cast< Func* >(v); (*f)(std::forward(args)...); }, std::move(data) }; } void register_callback( void(*function)(void*), void * p ) { function(p); // to test } void test() { int x = 0; auto closure = [&]()->void { ++x; }; auto voidified = voidify(closure); register_callback( voidified.first, voidified.second.get() ); register_callback( voidified.first, voidified.second.get() ); std::cout << x << "\n"; } int main() { test(); } 

这里voidify接受一个lambda和(可选)一个参数列表,并生成一个传统的C风格的callback- void*对。 void*由带有特殊删除器的unique_ptr拥有,因此可以正确清理其资源。

这比std::function解决方案的优点是效率 - 我消除了一个级别的运行时间接。 回调有效的生命周期也很明确,因为它位于voidify返回的std::unique_ptr

如果你想要更复杂的生命周期unique_ptr s可以moveshared_ptr


以上内容将数据与生命周期混合在一起,并使用实用程序进行类型擦除。 我们可以分开它:

 template std::pair< void(*)(void*, Args...), std::decay_t > voidify( Lambda&& l ) { typedef typename std::decay::type Func; return { +[](void* v, Args... args)->void { Func* f = static_cast< Func* >(v); (*f)(std::forward(args)...); }, std::forward(l) }; } 

现在voidify不分配。 只需将voidify存储在回调的生命周期中,将指针传递给second函数指针旁边的void*

如果您需要将此构造存储在堆栈之外,将lambda转换为std::function可能会有所帮助。 或者使用上面的第一个变体。

lambda函数与C回调函数兼容,只要它没有捕获变量即可。
用新的方式强制给旧的东西添加新东西是没有意义的。
如何遵循老式的方式?

 typedef struct { int cap_num; } Context_t; int cap_num = 7; Context_t* param = new Context_t; param->cap_num = cap_num; // pass capture variable register_callback([](void* context) -> void { Context_t* param = (Context_t*)context; std::cout << "cap_num=" << param->cap_num << std::endl; }, param);