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