【问题标题】:Implementing callback (for C library) as pure virtual in C++ abstract class在 C++ 抽象类中将回调(用于 C 库)实现为纯虚拟
【发布时间】:2014-01-31 12:54:15
【问题描述】:

我在使用 C 音频库时遇到了一个问题(使用 ASIO SDK 构建的 PortAudio,但这与这个问题并不真正相关;技术细节只会妨碍我提出这个问题) .

简单地说,该库中的某个 C 函数需要我给它一个回调函数,该函数返回某个值并接受某些参数;为了清楚起见,假设回调应该如下所示:

int callback(int arg)

对库函数的调用如下所示:

theCLibraryFuntion(foo, bar, callback);

使用这样的库(即以过程 C 风格的方式)工作正常,但我想将与我相关的库功能包装在结构良好的 C++ 类中。我需要这个库的项目是迄今为止我做过的最大的项目,所以 OO 设计对我来说是必须的。 我想在 C++ 中创建一个抽象类,它为派生类提供一个纯虚函数来实现,然后让知道该库的类将这个实现用于回调。 换句话说,我需要实际的多态性而不是讨厌的函数指针。

到目前为止,我还无法编写这样的课程。编译器不允许我将任何类的非静态成员函数作为回调传递给 C 库函数。我明白为什么,但我想知道一些解决方法。只要该解决方法(如果存在)为我提供前端类的多态行为,我不在乎后端代码变得多么邪恶。如果不存在这样的解决方法,那就这样吧。我非常了解 C 在后台是如何工作的,但我对 C++ 中 OO 功能的技术细节知之甚少,所以我假设有办法。

提前致谢!

注意:会增加大量开销的解决方法对我来说可能毫无价值。我不得不使用这个 C portaudio 库,因为它是唯一可以为我的项目所需的音频流提供极低延迟的 API,因此大幅增加开销并不是一个真正的选择。

【问题讨论】:

  • 没有void*参数的回调。啊啊啊……
  • 他们似乎有详细的在线文档。你能指定你需要实现哪个回调吗?
  • 这只是一个简化的例子,我向你保证,实际的回调有多个 void* 参数。 @sharptooth:这是标准的流回调。但总的来说,我只需要一个回调的解决方法......如果这有意义的话。
  • @DiscobarMolokai:它有“void* userData”吗?
  • @sharptooth:是的,确实如此。

标签: c++ c callback polymorphism


【解决方案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。除非您有多重继承,否则您将不需要它,无论如何它仍然存在。

【讨论】:

  • 一个优雅的解决方案!我认为这会很好。非常感谢!
  • 我应该补充一点,我刚刚尝试在我的项目中使用它,它确实在我需要它工作的上下文中工作。不过,我没想到会这么简单...... ;)
【解决方案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;
};

【讨论】:

  • 把它设为virtual void callback() = 0,它是一个抽象类
  • @DieterLücking:哦,是的,我没有注意到这个要求。
猜你喜欢
  • 1970-01-01
  • 2011-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-09
  • 2012-10-01
  • 2013-12-08
  • 1970-01-01
相关资源
最近更新 更多