【问题标题】:Creating C++ API Library创建 C++ API 库
【发布时间】:2019-11-07 02:57:36
【问题描述】:

我试图了解正确的方法或正确的方法,以便为非开源项目提供相当大的 C++ API。我不想提供“仅标头”库,因为代码库相当大并且是封闭源代码。目标如下:

  • 提供本机 C++ API,用户可以在 C++ 中实例化 C++ 类、传递数据,而无需仅使用 C 语言的包装器
  • 允许方法作为参数并返回 C++ 对象,尤其是 STL 类型(std::string、std::vector 等)
  • 没有自定义分配器
  • 如果存在这样的标准,大多数行业标准/规范方法都是可行的
  • 不能重新创建 COM 或使用 MS COM
  • 假设所有 C++ 编译器至少“兼容”C++11

我的目标是 Windows 以及其他平台 (Linux)。

由于 DLL 边界问题,我的理解是创建 DLL 或共享库是不可能的。为了处理 DLL 边界问题,DLL 和调用代码必须使用动态链接运行时(以及正确的版本,Windows 上的多线程/调试/等)编译,然后所有编译器设置都需要与调试符号匹配(迭代器调试设置等)。我的一个问题是,如果在 Windows 上,我们是否使用 Visual Studio 中的默认“Debug”和“Releas”设置确保编译器设置在 /MD 方面匹配,我们是否真的可以“安全”使用DLL 以这种方式(即来回传递 STL 对象以及如果不匹配肯定会很危险/失败的各种事情)? gcc下Linux中的共享对象、*.so有同样的问题吗?

使用静态库能解决这个问题吗?静态库和与其链接的调用代码之间的编译器设置需要匹配多少?它与 DLL (在 Windows 上)几乎相同的问题吗?

我试图在网上找到图书馆的例子,但找不到太多关于这方面的指导。许多资源讨论了开源解决方案,这似乎是将头文件和实现文件复制到代码库中(对于非头文件),这对于封闭源代码不起作用。

这样做的正确方法是什么?似乎这应该是一个普遍的问题;虽然我想知道大多数商业供应商是否只使用 C 接口。

如果可以解决问题,我可以使用静态库。我也可以接受这样的想法:拥有一组具有 Y 种设置变体的 X 编译器(其中 X 和 Y 是要支持的预先确定的选项列表)并拥有一个生成 X * Y 共享二进制库的构建系统,如果那样的话是“安全的”。

答案真的只是要么做 C 接口,要么用工厂创建纯抽象接口? (如果是这样,是否有一本规范的书或指南来编写这篇文章,而不是实现 Microsoft COM?)。

我知道 Stefanus DuToit 的沙漏模式: https://www.youtube.com/watch?v=PVYdHDm0q6Y

我担心这是很多代码重复。

我不是在抱怨事情的状态,我只是想了解“正确”的方式,希望这对处于类似位置的其他人来说是一个很好的问题。

我查看了这些 Stackoverflow 参考资料:

When to use dynamic vs. static libraries

How do I safely pass objects, especially STL objects, to and from a DLL?

Distributing Windows C++ library: how to decide whether to create static or dynamic library?

Static library API question (std::string vs. char*)

Easy way to guarantee binary compatibility for C++ library, C linkage?

也已审核:

https://www.acodersjourney.com/cplusplus-static-vs-dynamic-libraries/

https://blogs.msmvps.com/gdicanio/2016/07/11/the-perils-of-c-interface-dlls/

【问题讨论】:

  • DLL 是一种避免静态链接问题的方法。

标签: c++ api-design static-linking dynamic-linking


【解决方案1】:

在 linux 上,gcc 使用 libstdc++ 作为 c++ 标准库,而 clang 可以使用 libc++ 或 libstdc++。如果您的用户使用 clang 和 libc++ 构建,如果您仅使用 gcc 和 libstdc++ 构建(libc++ 和 libstdc++ 不兼容二进制),他们将无法链接。因此,如果您不想同时获得目标,则需要两个版本的库,一个用于 libstdc++,另一个用于 libc++。

此外,Linux 上的二进制文件(可执行文件、静态库、动态库)在发行版之间不兼容。它可能适用于您的发行版,而不适用于其他发行版甚至您的发行版的不同版本。非常小心地测试它是否适用于您想要定位的任何发行版。 Holy Build Box 可以帮助您生成交叉分发的二进制文件。听说不错,只是没试过。在最坏的情况下,您可能需要在您想要支持的所有 linux 发行版上构建。

https://phusion.github.io/holy-build-box/

【讨论】:

    【解决方案2】:
    • 提供本机 C++ API,用户可以在 C++ 中实例化 C++ 类、传递数据,而无需仅使用 C 语言的包装器

    这不包括 COM。

    • 允许方法作为参数并返回 C++ 对象,尤其是 STL 类型(std::string、std::vector 等)

    这不包括 DLL

    • 如果存在这样的标准,大多数行业标准/规范方法都是可行的

    不是“标准”的东西,但有一些常见的做法。例如,在 DLL 中,仅传递原始 C 内容。

    • 不能重新创建 COM 或使用 MS COM

    这需要 DLL/COM 服务器

    • 假设所有 C++ 编译器至少“兼容”C++11

    也许吧。通常是的。

    通常:如果源可用,则仅使用标题(如果是模板)或 h +cpp。 如果没有源码,最好是DLL。静态库 - 您必须为许多编译器构建,并且必须在任何地方都使用您的库并链接到它。

    【讨论】:

      【解决方案3】:

      根据您的要求,您需要在构建阶段链接到您的程序的静态库(例如 windows 下的 .lib)。

      该库的接口需要位于声明类型和函数的头文件中。

      您可以选择作为一组库和头文件分发,如果它们可以干净地分成单独的部分 - 如果您管理这些部分之间的依赖关系。这是可选的。

      您将无法定义自己的模板化函数或类,因为(对于大多数编译器)需要分发这些函数或类的源代码。或者,如果这样做,您将无法将它们用作库接口的一部分(例如,您可以在库内部使用模板化函数/类,但不能在库头文件中将它们公开给图书馆)。

      主要的缺点是您需要为每个编译器和主机系统(结合您支持的组合)构建和分发库的一个版本。 C++ 标准特别鼓励编译器之间的各种类型的不兼容性(例如,不同的名称修饰),因此,一般来说,用一个编译器构建的代码不会与用另一个 C++ 编译器构建的代码互操作。实际上,您的库不能与构建它的编译器以外的编译器一起使用,除非这些编译器是专门实现的(例如,通过双方供应商的协议)以兼容。如果您支持的编译器支持版本之间的不同 ABI(例如 g++),那么您需要分发使用每个 ABI 版本构建的库版本(例如支持每个 ABI 的最新版本的编译器)

      一个好处——为你支持的每个编译器和主机构建你的库——是使用标准库没有问题,包括标准库中的模板化类型或函数。传递参数将起作用。标准库类型可以是您的类的成员。这仅仅是因为库和使用它的程序将使用相同的编译器构建。

      您需要严格正确地包含标准头文件(例如,不要依赖一个标准头文件包括另一个,除非标准说明它确实如此 - 一些库供应商对此不够严格,这可能会导致您的代码中断当使用不同的编译器及其标准库构建时)。

      您的库几乎不需要“调试”和“发布”版本(或具有其他优化设置的版本)。一般而言,链接程序的某些部分使用不同的优化设置编译是没有问题的。 (如果您 - 或在他们的程序中使用您的库的程序员 - 使用构建选项的奇异组合,则可能导致此类事情中断,因此请将这些组合保持在最低限度)。分发您的库的“调试”版本将允许使用调试器单步执行您的库,这似乎与您的意愿背道而驰。

      以上都不会阻止您使用自定义分配器,但也不需要它。

      除非你真的想要,否则你不需要重新创建 COM。实际上,您应该致力于确保您的代码尽可能标准 - 尽量减少使用特定于编译器的功能,不要对类型的大小或类型的布局等做出特定假设。使用供应商特定的功能(如 COM)是不行的- 不,除非您支持的所有目标编译器和系统都统一支持这些功能。哪个 COM 不是。

      我不知道是否有这样做的“标准/规范”方式。编写适用于多个编译器和主机系统的代码并非易事,因为不同编译器供应商对标准的解释或支持方式之间存在差异。保持代码简单是最好的 - 您使用的语言或标准库功能越新奇或越新,您在某些编译器中遇到错误的可能性就越大。

      另外,请花时间为您的库设置测试套件,并定期维护它,使其尽可能完整。在您支持的每种编译器/系统组合上使用该套件测试您的库。

      【讨论】:

        猜你喜欢
        • 2018-03-22
        • 2013-07-04
        • 1970-01-01
        • 1970-01-01
        • 2012-12-08
        • 2020-06-06
        • 1970-01-01
        • 1970-01-01
        • 2020-06-16
        相关资源
        最近更新 更多