假设这是一个需要被封装的C函数:
void foo(int(__stdcall *callback)());
C函数指针回调的两个主要问题是:
- 无法存储绑定表达式
- 无法存储捕获的Lambda表达式
我想知道如何包装这些函数以实现这样的功能。第一个问题尤其适用于成员函数回调,第二个问题适用于使用周围变量的内联定义,但这些并不是唯一的用途。
这些特定函数指针的另一个属性是它们需要使用__stdcall
调用约定。据我所知,这完全排除了Lambda表达式作为选项,并且在其他情况下有点麻烦。我希望至少允许 __cdecl
作为选项。
这是我能想到的最好的方法,而不必依靠函数指针不支持的支持。通常会放在头文件中。以下是在Coliru上的示例代码。
#include <functional>
//C function in another header I have no control over
extern "C" void foo(int(__stdcall *callback)()) {
callback();
}
namespace detail {
std::function<int()> callback; //pretend extern and defined in cpp
//compatible with the API, but passes work to above variable
extern "C" int __stdcall proxyCallback() { //pretend defined in cpp
//possible additional processing
return callback();
}
}
template<typename F> //takes anything
void wrappedFoo(F f) {
detail::callback = f;
foo(detail::proxyCallback); //call C function with proxy
}
int main() {
wrappedFoo([&]() -> int {
return 5;
});
}
然而,有一个主要的缺陷,这是不可重入的。如果在变量被使用之前重新分配变量,则旧函数将永远不会被调用(不考虑多线程问题)。
我尝试过的一件事情是将std::function
存储为数据成员并使用对象,使每个对象操作不同的变量,但没有办法将对象传递给代理。将对象作为参数会导致签名不匹配,并且绑定它将不能将结果存储为函数指针。
我想到的一个想法,但还没有玩过的是std::function
的向量。但是,我认为仅在没有任何人使用它时清除它时,才是真正安全的时间。但是,每个条目首先在wrappedFoo
中添加,然后在proxyCallback
中使用。我想知道在前者中增加,在后者中减少,然后在清除向量之前检查零的计数器是否起作用,但听起来比必要的更为复杂。
有没有一种方法可以使用函数指针回调包装C函数,以便C++的封装版本:
- 允许任何函数对象
- 允许不止是C回调的调用约定(如果这是至关重要的,用户可以传递具有正确调用约定的内容)
- 线程安全/可重入
注意:显而易见的解决方案是利用应该存在的void *
参数,如Mikael Persson的答案所述。然而,由于无能力,这很遗憾不是一个万无一失的选择。对于那些没有此选项的函数,存在什么可能性才是有趣的地方,并且是获得非常有用的答案的主要途径。
CreateWindow
,据我所知,它应该可以同时从两个线程中调用,并且还有一个(更隐蔽的)void *
选项。犹豫的原因是我希望这个问题比那个具体一点。不过,这确实是一个值得思考的好点子。 - chrislpParam
)。任何具有此类存储的回调函数都将正常工作。唯一需要诉诸全局手段的时候是那些不接受这种参数的非常恼人的回调函数。 - Nicol Bolas