协调类,继承和C回调

时间:2009-02-17 07:13:51

标签: c++ c oop

在我的C ++项目中,我选择使用C库。我热衷于拥有一个精心设计和简单的设计,我最终做了一点点努力。我的设计要求的一部分是我可以轻松支持给定任务的多个API和库(主要是因为我对跨平台支持的要求)。因此,我选择创建一个抽象基类,它将统一处理给定的库选择。

考虑我的设计简化:

class BaseClass
{
public:
    BaseClass() {}
    ~BaseClass() {}

    bool init() { return doInit(); }
    bool run() { return doWork(); }
    void shutdown() { destroy(); }
private:
    virtual bool doInit() = 0;
    virtual bool doWork() = 0;
    virtual void destroy() = 0;
};

一个继承自它的类:

class LibrarySupportClass : public BaseClass
{
public:
    LibrarySupportClass()
        : BaseClass(), state_manager(new SomeOtherClass()) {}

    int callbackA(int a, int b);
private:
    virtual bool doInit();
    virtual bool doWork();
    virtual void destroy();

    SomeOtherClass* state_manager;
};

// LSC.cpp:

bool LibrarySupportClass::doInit()
{
    if (!libraryInit()) return false;

    // the issue is that I can't do this:
    libraryCallbackA(&LibrarySupportClass::callbackA);

    return true;
}
// ... and so on

我遇到的问题是因为这是一个C库,我需要提供int (*)(int, int)形式的C兼容回调,但该库不支持额外的userdata指针对于这些回调。我会更喜欢在类中执行所有这些回调,因为该类带有一个状态对象。

我最终做的是......

static LibrarySupportClass* _inst_ptr = NULL;
static int callbackADispatch(int a, int b)
{
    _inst_ptr->callbackA(a, b);
}

bool LibrarySupportClass::doInit()
{
    _inst_ptr = this;

    if (!libraryInit()) return false;

    // the issue is that I can't do this:
    libraryCallbackA(&callbackADispatch);

    return true;
}

如果LibrarySupportClass被多次实例化,这显然会做坏事(TM),所以我考虑使用单例设计,但由于这个原因,我无法证明这一选择。

有更好的方法吗?

6 个答案:

答案 0 :(得分:3)

你可以证明这个选择:你的理由是C库只支持一个回调实例。

  单身人士吓唬我:目前尚不清楚如何正确摧毁一个单身人士,而继承只会使问题复杂化。我将再看看这种方法。

我是这样做的。

<强> LibrarySupportClass.h

class LibrarySupportClass : public BaseClass
{
public:
    LibrarySupportClass();
    ~LibrarySupportClass();
    static int static_callbackA(int a, int b);
    int callbackA(int a, int b);
private:
    //copy and assignment are rivate and not implemented
    LibrarySupportClass(const LibrarySupportClass&);
    LibrarySupportClass& operator=(const LibrarySupportClass&);
private:
    static LibrarySupportClass* singleton_instance;
};

<强> LibrarySupportClass.cpp

LibrarySupportClass* LibrarySupportClass::singleton_instance = 0;


int LibrarySupportClass::static_callbackA(int a, int b)
{
  if (!singleton_instance)
  {
    WHAT? unexpected callback while no instance exists
  }
  else
  {
    return singleton_instance->callback(a, b);
  }
}

LibrarySupportClass::LibrarySupportClass()
{
  if (singleton_instance)
  {
    WHAT? unexpected creation of a second concurrent instance
    throw some kind of exception here
  }
  singleton_instance = this;
}

LibrarySupportClass::~LibrarySupportClass()
{
  singleton_instance = 0;
}

我的观点是,你不需要给它一个规范的“单身”的外部界面(例如,它很难被破坏)。

相反,它只有一个的事实可以是私有实现细节,并由私有实现细节强制执行(例如通过构造函数中的throw语句)...假设应用程序代码已经是这样的它不会尝试创建这个类的多个实例。

拥有这样的API(而不是更规范的'singleton'API)意味着您可以在堆栈中创建此类的实例(如果您不想尝试创建多个)它)。

答案 1 :(得分:3)

c库的外部约束规定,当你的回调被调用时,你没有回调的“拥有”实例的标识。因此,我认为你的方法是正确的。

我建议将callbackDispatch方法声明为类的静态成员,并使类本身成为单例(有很多关于如何实现单例的示例)。这将允许您为其他库实现类似的类。

答案 2 :(得分:1)

Dani打败了我的答案,但另一个想法是你可以有一个消息系统,其中回调函数将结果发送给你班级的全部或部分实例。如果没有一个干净的方法来确定哪个实例应该得到结果,那么就让那些不需要它的实例忽略结果。

当然,如果你有很多实例,那么就会出现性能问题,你必须遍历整个列表。

答案 3 :(得分:1)

我看到的问题是因为你的方法不是静态的,你很容易在一个不应该有一个的函数中最终得到一个内部状态,因为在它上面有一个实例文件的顶部,可以在调用之间进行,这是一个非常糟糕的事情(tm)。至少,正如Dani在上面所建议的那样,你在C回调中调用的任何方法都必须是静态的,这样你就可以保证调用回调不会留下剩余状态。

以上假设您已在最顶层声明static LibrarySupportClass* _inst_ptr。作为替代方案,请考虑使用工厂功能,该功能将根据池中的LibrarySupportClass按需创建工作副本。这些副本可以在完成后再返回池中并进行回收,这样每次需要该功能时都不会创建实例。

通过这种方式,您可以让对象在单个回调调用期间保持状态,因为您的实例将被清除,并且可以重新使用绿灯。对于多线程环境,您也处于更好的位置,在这种情况下,每个线程都有自己的LibrarySupportClass实例。

答案 4 :(得分:0)

  

我遇到的问题是因为这是一个C库,我需要提供形式为int(*)(int,int)的C兼容回调,但是库不支持这些回调的额外userdata指针

你能详细说明吗?选择基于userdata的回调类型是一个问题吗?

答案 5 :(得分:0)

您的回调可以选择基于a和/或b的实例吗?如果是这样,那么在全局/静态地图中注册您的库支持类,然后让callbackADispatch()在地图中查找正确的实例。

使用互斥锁序列化对地图的访问是一种使这种线程安全的合理方法,但要注意:如果库在调用你的回调时持有任何锁,那么你可能需要做一些更聪明的事情来避免死锁,取决于您的锁定层次结构。