【问题标题】:C++ interface design around shared library boundaries围绕共享库边界的 C++ 接口设计
【发布时间】:2011-08-03 14:51:17
【问题描述】:

假设我有两个项目。一个是应用程序,另一个是共享库,其中包含通用的、可重用的代码,这些代码不仅仅可供该应用程序使用。

我的应用程序使用 STL,而我的共享库也使用 STL。这里的第一个问题是我的共享库使用的是 STL。如果我在我的应用程序中构建了新版本的 STL,但因为没有必要而没有重新构建共享库,那么我们将立即遇到兼容性问题。

我解决这个问题的第一个想法是在共享库类的接口中根本不使用 STL。假设我们的库中有一个函数,它接受一个字符串并用它做一些事情。我会让函数原型看起来像:

void DoStuffWithStrings( char const* str );

代替:

void DoStuffWithStrings( std::string const& str );

对于字符串,这在不同版本的 STL 之间可能没问题,但缺点是我们要从 std::stringchar*,再回到 std::string,这似乎会导致性能问题。

是否推荐将原始类型装箱/拆箱到对应的 STL 类型?当我们尝试对std::list 执行此操作时,情况会变得更糟,因为确实没有“原始类型”,我知道我们可以轻松地传递它,而无需执行某种 O(n) 或类似操作。

什么设计最适合这种情况?各有什么优缺点?

【问题讨论】:

    标签: c++ design-patterns stl shared-libraries


    【解决方案1】:

    const char* 方法的优点之一是您的库也可以从 C 调用,因此也可以从许多与 C 接口的其他语言调用(几乎所有东西都在那里)。这本身就是一个非常有趣的卖点。

    但是,如果您编写和维护这两个库,并且它们将仅在 C++ 中使用(比如未来 5 年),我就不会经历转换所有内容的麻烦。 std::string 是一回事,std::vectorstd::map 不会很好地转换。除此之外,您有多少次迁移到另一个 STL 实现?在这些情况下,你真的会“忘记”重建你的共享库吗?此外,如果确实需要,您仍然可以在之后编写/生成 C 样式包装器。

    结论(偏向于我对这件事的经验):如果您不需要 C,请使用 stl。

    【讨论】:

    • 与 C 的兼容性并不是唯一的问题,恕我直言,这不是一个很好的经验法则。正如您所说,如果要求不能保证共享库将始终与应用程序一起构建,但根本不被 C 使用,那么您仍然需要考虑兼容性问题。 C++ 对共享库的支持非常弱(与 .NET 和 C# 之类的东西相比),所以无论要求如何,我总是努力保证兼容性永远不会成为问题。对于std::vectorstd::map,我认为我们能做的最好的事情就是以某种方式将它们映射到用户类型。
    • 是的,条件应该更像“如果您不需要 C 并且可以控制所有库”..
    【解决方案2】:

    当然,标准 C++ 库应该被视为 C++ ABI 的一部分,就像虚拟表布局或名称修改方案一样。此外,ABI 中任何不兼容的更改更有可能影响模糊的极端情况,而不是 std::vector 的布局。换句话说:如果您要创建 C++ 库,请随意使用标准 C++ 类。

    【讨论】:

    • 在什么例子下 vtables 和 name mangling 方案会改变?这些似乎不会像 STL 那样发生变化。
    • @Robert 如果旧编译器版本中存在错误,或者新编译器版本支持新版本标准的新语言功能,则需要进行更改。基本上相同的条件适用于标准库 ABI 的更改。没有很好的理由,没有人会以不兼容的方式更改标准库。
    • 那么例如如果我从 VS8 升级到 VS9,会导致不兼容?
    • 是的,微软只保证在同一主要版本内的兼容性。
    【解决方案3】:

    如果库使用与应用程序不同的堆,则会出现另一个问题。如果是这种情况或可能是这种情况,请确保库拥有/管理自己的内存。当库使用不同的 c 库并因此使用不同的 malloc/free 并因此使用不同的堆时,多个堆可能会成为问题。 http://msdn.microsoft.com/en-us/library/ms810466.aspx

    【讨论】:

      【解决方案4】:

      许多 A.P.I.和共享库使用“不透明”或通用指针作为函数中的参数,以避免版本之间的差异。

      // this:
      int MyFunc(char* Param1, int Param2, bool Param3);
      
      // into this:
      struct MyParams
      {
        char* Param1;
        int Param2;
        bool Param3;
      }
      
      // "Params" its really "struct MyParams*"
      int MyFunc(void* Params);
      

      有时如果共享库函数有多个参数,它们会被替换为指针,taht 应该是指向数组或结构甚至类的指针。

      这取决于您将如何使用您的库,因为许多库都像普通 C 一样使用,即使您使用的是 C++。

      【讨论】:

        猜你喜欢
        • 2021-09-24
        • 2015-09-23
        • 1970-01-01
        • 1970-01-01
        • 2018-01-02
        • 2022-06-12
        • 1970-01-01
        • 1970-01-01
        • 2011-01-05
        相关资源
        最近更新 更多