我正在使用图书馆:
// External Library:
typedef void (*callback_type)(void *arg);
void external_register_callback(callback_type callback) {
// ...
}
它允许注册回调函数:
void example_callback_function(void *arg) {
// do something with *arg.
}
external_register_callback(example_callback_function);
我需要为库编写一个包装层,它也允许注册回调函数,但它不是带有
void*
参数的函数,而是应该采用不带参数的 std::function
。
这就是我的包装纸的样子:
void my_register_callback(std::function<void()> callback) {
external_register_callback(... callback ...); // how to "convert" callback to callback_type?
}
应该这样使用:
void my_callback_function() {
// do something
}
void my_register_callback(my_callback_function);
TL;博士:
如何将我的
std::function<void()>
转换为接受 void*
参数但忽略它的函数指针?
备注:
我正在使用c++20。
函数的“转换”应该(如果可能的话)在没有运行时开销的情况下完成。需要明确的是:我不关心
register_callback()
的运行时间,但是当外部库实际调用回调时,那应该很快。
您可以创建一个代理函数并存储它调用的
std::function<void()>
的列表。示例:
#include <functional>
#include <vector>
#include <iostream>
std::vector<std::function<void()>> proxy1_callbacks;
extern "C" // probably
void proxy1(void*) {
for(auto&& func : proxy1_callbacks) func();
}
void my_register_callback(std::function<void()> callback) {
proxy1_callbacks.emplace_back(std::move(callback));
external_register_callback(proxy1);
}
当您想要将任何回调类型发送到 C 库时,有一个常见的模式。这不是删除参数,而是将您自己的回调放入上下文中。
为了简单起见,让我们想象一下我们不需要担心生命周期。
该模式包括将回调视为具有工作调用语法的对象。它可以是函数指针、lambda 或
std::function
。您将 void*
设置为指向它,然后发送一个函数指针,该指针获取上下文并将其转换回函数对象以调用它。
事情是这样的:
template<std::invocable<> F>
void my_register_callback(F& callback) {
set_context(&callback); // sets the void*
external_register_callback([](void* context) {
// Retrieve the callback
F* callback = static_cast<F*>(context);
// Call it!
(*callback)();
});
}
这允许设置对 c 库的任何回调,包括函数对象、函数指针、lambda 或其他多态包装器,如
std::function
。
这种模式之所以有效,是因为没有捕获的 lambda 可以转换为函数指针。