最有效的方法是直接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* 由带有特殊删除器的 unique_ptr 拥有,因此其资源被正确清理。
与std::function 解决方案相比,它的优势在于效率——我消除了一级运行时间接。回调有效的生命周期也很清楚,因为它在voidify 返回的std::unique_ptr<void, void(*)(void*)> 中。
如果您想要更复杂的生命周期,unique_ptr<T,D>s 可以将 moved 转换为 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 没有分配。只需在回调的生命周期内存储您的 voidify,将指向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";
}