我在使用C音频库时遇到了问题(使用ASIO SDK构建的PortAudio,但这与此问题无关;技术细节只会阻碍我提出问题)
简单地说,来自该库的某个C函数需要我给它一个回调函数,该函数返回一个值并接受某些参数;为了清楚起见,让我们说回调应该是这样的:
int callback(int arg)
并且对库函数的调用如下所示:
theCLibraryFuntion(foo, bar, callback);
使用这样的库(即以程序C风格的方式)工作正常,但我想在结构良好的C ++类中包装与我相关的库函数。我需要图书馆的项目是迄今为止我所做过的最大的项目,所以OO-design对我来说是必须的。 我想在C ++中创建一个抽象类,为派生类提供一个纯虚函数来实现,然后让这个实现用于知道库的类的回调。换句话说,我需要实际的多态性而不是讨厌的函数指针。
到目前为止,我还没能写出这样的课程。编译器不允许我将任何类的非静态成员函数作为回调传递给C库函数。我理解为什么,但我想了解一些解决方法。只要该解决方法,如果它存在,给我前端类的多态行为,我不关心后端代码变得多么邪恶。如果不存在这样的解决方法,那就这样吧。我很清楚C在后台如何工作,但我对C ++中OO功能的技术性知之甚少,所以我假设有一种方法。
提前致谢!
注意:一个会增加大量开销的解决方法对我来说可能毫无价值。我被迫使用这个C portaudio库,因为它是唯一能够让我的项目所需的音频流延迟极低的API,所以开销不会大幅提高。
答案 0 :(得分:2)
据我所知,PortAudio中的回调(例如this one)采用void*
参数将用户数据传递给它们。您可以使用它来通过将类指针作为用户数据传递来调用成员函数,并编写一个小的静态或非成员函数来注册为C兼容的回调。例如:
// C library function
typedef void (*c_library_callback)(void * user_data);
void c_library_start_stuff(c_library_callback, void * user_data);
// C++ class using it
class audio_thing {
public:
virtual ~audio_thing() {} // Better have one of these in an abstract class
void start_stuff() {
c_library_start_stuff(&audio_thing::static_callback, this);
}
private:
// C-compatible callback forwards to the implementation
static void static_callback(void * thing) {
static_cast<audio_thing*>(thing)->callback();
}
// Derived classes implement the callback behaviour
virtual void callback() = 0;
};
答案 1 :(得分:1)
您的代码过于简单了。在现实世界中,“库函数”和回调都将具有void*
“用户数据”参数。您可以将指向类实例的指针传递到“库函数”的“用户数据”中,它将被转发到回调中,您可以使用该指针访问该对象。
class CallbackImplBase {
public:
virtual void CallbackImpl() = 0;
static void CallbackToPass( void* userData )
{
static_cast<CallbackImplBase*>(userData)->CallbackImpl();
}
};
class ActualCallbackImpl : public CallbackImplBase {
void CallbackImpl() { //implemented here }
};
ActualCallbackImpl callback;
callLibraryFunction( params,
&CallbackImplBase::CallbackToPass, static_cast<CallbackImplBase*>( &callback ) );
请注意“库函数”调用的最后一个参数附近的static_cast
。你不需要它,除非你有多重继承,它仍然存在。