在C ++抽象类中实现回调(对于C库)为纯虚拟

时间:2014-01-31 12:54:16

标签: c++ c callback polymorphism

我在使用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,所以开销不会大幅提高。

2 个答案:

答案 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。你不需要它,除非你有多重继承,它仍然存在。