c++ - 创建 C++ API 库

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

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

  • 提供本地 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 上,我们是否确保编译器设置在/MD 方面使用 Visual Studio 中的默认“调试”和“发布”设置匹配,我们是否真的可以“安全”使用DLL 以这种方式(即来回传递 STL 对象以及各种在不匹配的情况下肯定会危险/失败的事情)? Linux下gcc下的shared object, *.so有同样的问题吗?

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

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

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

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

    答案真的只是做 C 接口(interface)或使用工厂创建纯抽象接口(interface)吗? (如果是这样,是否有规范的书籍或指南用于编写此代码,而不是实现 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/

    最佳答案

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

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

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

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

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

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

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

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

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

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

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

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

    关于c++ - 创建 C++ API 库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56751186/

    相关文章:

    http - ReST API 是否应该默默地忽略更改不存在字段的请求?

    c++ - 链接静态库会导致链接库出错

    c++ - 跨编译单元的单例 : linking library vs linking objects

    C++ 输出被打印两次

    c++ - 在opencv中聚类图像片段

    authentication - PWA 的 API 身份验证

    c++ - 如何在 C/C++ 中使用带有 OpenSSL 的静态链接

    c++ - 我可以将 C++ 与 UDK 一起使用吗?

    c++ - 如何将静态 Stasm 库链接到我的程序?

    python - 直接在函数中更改对象在Python中是反模式吗?