关于包装第三方库的建议

时间:2009-06-15 00:44:04

标签: c++ c api

我作为一家公司的计算机视觉部门的软件开发人员已经工作了一年。我的主要工作是将第三方软件集成到一个框架中,所以我通常最终编写包装库,因为很多这个第三方软件不能按我们希望的方式工作(不是线程安全,a *中的痛苦) *使用等。)。

通常我只是包装整个库并保护对具有互斥的库的调用(线程安全在某种程度上是大多数外部库的主要问题)。我非常喜欢这样做,因为它会让你进入很多有趣的场景,你会看到很多有趣的代码。但是,我经常认为我没有正确地做到这一点,或者我的实施并不是很好。我觉得我缺乏某种关于如何正确地做这样的事情的设计知识。

基本上我想知道是否有任何关于设计正确的'API ontop of broken API'的良好指导或提示,或者如果这总是一定非常hackish和丑陋。

2 个答案:

答案 0 :(得分:5)

我将在前几天引用另一个问题的答案

  1. 您当前的方法是否通过测试?
  2. 足够快吗?
  3. 如果是,请继续做你正在做的事情。

    作为替代

    确保您的新API包含预期功能以及原始版本的常规意外功能。同时确保它呈现“适合目的”的重新呈现。看看FOSS项目中C库的C ++包装,例如GTK / GTK for C ++(它只包装前者)。

    如果API被破坏,修复它并提交补丁......加入第三方(我假设有权访问来源意味着他们不介意这一点)...你可以重写他们的一些API是'包装友好',并建议他们合并一些变化。如果有问题,请修理它。

    对此并不多,只需用A包裹A并确保B执行A应该使用的或用于。

答案 1 :(得分:2)

我唯一能添加到Aiden's response的是你还应该寻找替换需要使用RAII技术进行显式初始化和终止的代码。当我面对提供API的外观时,我似乎总是遇到一个看起来像这样的类:

struct ADVERTISER {
    /* a bunch of members here */
};

void adv_Initialize(ADVERTISER *adv, /* a bunch of arguments */);
void adv_DoStuff(ADVERTISER *adv);
void adv_Terminate(ADVERTISER *adv);

我已经看到它以下列方式包装在C ++类中:

namespace wrapper {
  class Advertiser {
  public:
      Advertiser(): inited_(false) {}
      void initialize(/* a bunch of arguments */) {
        terminate();
        adv_Initialize(&adv_, ...);
        inited_ = true;
      }
      void doStuff() {
        validate();
        adv_DoStuff(&adv_);
      }
      void terminate() {
        if (inited_) {
            adv_Terminate(&adv_);
            inited_ = false;
        }
      }
  protected:
      void validate() {
        if (!inited_) {
            throw std::runtime_error("instance is not valid");
        }
      }
  private:
      ADVERTISER adv_;
      bool inited_;
  };
}

问题是Advertiser类并没有真正使API更容易使用,甚至更清洁恕我直言。如果遇到这样的情况,那么:

  1. 使用完全参数化的构造函数来确保不存在无效实例
  2. 清理析构函数中的所有资源
  3. 编写一个复制构造函数和赋值运算符,如果它们有意义将它们设为私有而不实现它们。
  4. 我的目标是确保我呈现/创建/包装的任何API都适用于我们现有的编码风格。我也试图将API变成比现在更多的OO样式。我已经看到了一些我称之为面向对象的C 的内容,就像我上面提到的那样。如果你想让它们真正适合C ++,那么那就真正面向对象并利用C ++给你的东西:

    • 小心管理任何状态变量。
    • 如果像复制这样的行为没有意义,那就隐藏它们。
    • 如果有可能泄漏资源,那么找一些方法来防止它发生(通常使用RAII帮助)。
    • 使用构造函数限制实例的创建,以消除无效实例和其他边缘情况。