如何将C++lambda传递给需要函数指针和上下文的C回调
How can I pass a C++ lambda to a C-callback that expects a function pointer and a context?
我正在尝试在使用标准函数-接口+上下文范例的C-API中注册回调。以下是api的样子:
void register_callback(void(*callback)(void *), void * context);
我真正想做的是能够注册一个C++lambda作为回调。此外,我希望lambda是一个具有捕获变量(即不能转换为直接的无状态std::function
)的lambda
我需要编写什么样的适配器代码才能将lambda注册为回调?
简单的方法是将lambda粘贴到保存在某个地方的std::function<void()>
中。它可能是在堆上分配的,并且仅由向接受回调的实体注册的void*
引用。回调将只是一个这样的函数:
extern "C" void invoke_function(void* ptr) {
(*static_cast<std::function<void()>*>(ptr))();
}
注意,std::function<S>
可以保存具有状态的函数对象,例如,具有非空捕获的lambda函数。你可以注册这样的回调:
register_callback(&invoke_function,
new std::function<void()>([=](){ ... }));
最有效的方法是直接voidify
lambda。
#include <iostream>
#include <tuple>
#include <memory>
template<class...Args>
struct callback {
void(*function)(void*, Args...)=nullptr;
std::unique_ptr<void, void(*)(void*)> state;
};
template<typename... Args, typename Lambda>
callback<Args...> voidify( Lambda&& l ) {
using Func = typename std::decay<Lambda>::type;
std::unique_ptr<void, void(*)(void*)> data(
new Func(std::forward<Lambda>(l)),
+[](void* ptr){ delete (Func*)ptr; }
);
return {
+[](void* v, Args... args)->void {
Func* f = static_cast< Func* >(v);
(*f)(std::forward<Args>(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.function, voidified.state.get() );
register_callback( voidified.function, voidified.state.get() );
std::cout << x << "n";
}
int main() {
test();
}
这里,voidify
接受一个lambda和(可选)一个参数列表,并生成一个传统的C风格回调-void*
对。void*
由具有特殊deleter的unique_ptr
所有,因此其资源被正确清理。
与std::function
解决方案相比,它的优势在于效率——我消除了一个级别的运行时间接性。回调有效的生存期也很清楚,因为它在voidify
返回的std::unique_ptr<void, void(*)(void*)>
中。
如果您想要更复杂的生存期,unique_ptr<T,D>
s可以是move
d到shared_ptr<T>
。
以上内容将寿命与数据相结合,将类型擦除与实用性相结合。我们可以拆分:
template<class Lambda, class...Args>
struct callback {
void(*function)(void*, Args...)=nullptr;
Lambda state;
};
template<typename... Args, typename Lambda>
callback<typename std::decay<Lambda>::type, Args...> voidify( Lambda&& l ) {
using Func = typename std::decay<Lambda>::type;
return {
+[](void* v, Args... args)->void {
Func* f = static_cast< Func* >(v);
(*f)(std::forward<Args>(args)...);
},
std::forward<Lambda>(l)
};
}
现在voidify
不分配。只需在回调的生存期内存储您的void,将一个指向-second
的指针作为void*
,并与first
函数指针一起传递。
如果您需要在堆栈外存储此构造,将lambda转换为std::function
可能会有所帮助。或者使用上面的第一个变体。
void test() {
int x = 0;
auto closure = [&]()->void { ++x; };
auto voidified = voidify(closure);
register_callback( voidified.function, &voidified.state );
register_callback( voidified.function, &voidified.state );
std::cout << x << "n";
}
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);
将lamda函数用作C函数指针的一种非常简单的方法是:
auto fun=+[](){ //add code here }
请参阅此答案作为示例。https://stackoverflow.com/a/56145419/513481
- 如何正确编写指针函数声明?
- C++常规指针函数或模板
- 如何重新定义 C++ 指针函数?
- C++ 指向其他类函数的指针函数
- 指针到指针函数参数
- 将指向成员的指针函数传递到模板中
- C++ 在 none 常量指针函数中返回一个常量指针
- 如何使用指针函数编写/读取数组
- 是C 中的函数指针函数对象
- 如何从另一个类调用指向成员的指针函数
- 如何声明采用指向成员的指针函数的函数
- 如何构造一个以可变参数指针函数作为成员的类?
- c 通过值或指针函数语法
- 将typedef方法作为指针函数传递
- 从 Main 中的双指针函数打印出指针数组
- strcpy 对指针函数的引用
- 这是否仍然声明一种指针函数的别名?
- 将指向成员的指针函数与 std::shared_ptr 结合使用
- 'Incomplete type' 为标准::函数声明指向成员的指针函数模板参数时出错
- 从注入进程的 DLL 调用函数并更改指针函数的地址