围绕共享库边界的c++接口设计

C++ interface design around shared library boundaries

本文关键字:c++ 接口 边界 共享      更新时间:2023-10-16

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

我的应用程序使用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)或类似的操作。

在这种情况下,什么设计最有效?每种方法的优缺点是什么?

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

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

结论(偏向于我在这件事上的经验):如果你不需要C,那就用stl。

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

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

许多api和共享库使用"不透明"或泛型指针作为函数的参数,以避免版本之间的差异。

// 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);

有时,如果共享库函数有多个参数,则将其替换为指针,该指针可能是指向数组或结构体甚至类的指针。

这取决于你打算如何使用你的库,因为许多库都像普通C一样使用,即使你正在使用c++。