如何使用语言包装器构建C++库?

How to structure a C++ library with language wrappers?

本文关键字:C++ 构建 何使用 语言 包装      更新时间:2023-10-16

我有一个关于编写包含其他语言(如Python和Matlab(包装器的C++库的良好实践的问题。这可能是一个简单的问题或重复的问题,但我还没有找到一个好的资源或其他答案来帮助解释如何做到这一点。

作为背景,我正在开发一个C++库项目,该项目专门包含 C 包装器以与其他语言兼容。该库是一个用C++编写的科学计算库,我已经为要用作共享库一部分的函数和类编写了 C 包装器。

我的问题是如何将C包装器合并到其他语言的模块中,例如Python和Matlab的包装库。我不是在询问如何为这些其他语言实现代码的细节,因为这完全是另一个问题,我已经对如何编写可以为每种语言构建和加载的代码有基本的了解作为自己的库。我的问题主要是关于将 C 包装器包含在这些其他构建中。

例如,我有以下目录结构,其中每个文件夹都包含与不同语言/模块相关的代码。

top
├─ matlab
│  ├─ CMakeLists.txt
│  ├─ matlab_wrapper.hpp
│  └─ matlab_wrapper.cc
├─ python
│  ├─ CMakeLists.txt
│  ├─ python_wrapper.hpp
│  └─ python_wrapper.cc
├─ src
│  ├─ CMakeLists.txt
│  ├─ c_wrapper.hpp
│  ├─ c_wrapper.cc
│  └─ other code...
└─ CMakeLists.txt

我知道每个文件夹都应该是自给自足的,并且可以自行构建,但是我无法弄清楚如何分发代码以使其包含 C 包装器。基本上,我在c_wrapper文件中有 C 实现,我想在 matlab 和 python 库中使用它。

通常,我会在构建步骤中包含其他源文件并完成它(这可能只是答案(,但src文件夹中的所有内容都作为自己的共享库构建和分发。重写matlab_wrapperpython_wrapper文件中的 C 包装器(基本上创建同一函数的 3 个版本(或将c_wrapper文件包含在其他库的构建步骤中似乎是多余的,因为它们已经包含在主C++库中。

我不是打包共享库的专家,那么最好的方法是什么?我应该只包含c_wrapper源文件作为构建步骤的一部分,重写每个模块中的C++包装器,还是有另一种方法可以将代码包含在包装库中的c_wrapper中?理想情况下,每个模块将共享相同(或非常相似(的 API,因此如果有一种方法可以包含此代码而无需在每次添加新的 C 包装器时重写相同的函数,那就太好了。任何帮助或建议将不胜感激。

你可以为所有目的创建一个共享库:毕竟,Matlab会忽略Python寻找的PyInit_foo。 但是您可能仍然不想更改主库以支持此类用途:也许它有其他不需要包装器的编译客户端,或者它应该与特殊包装器分开安装,或者它需要在没有安装主机的机器上使用。

另一种选择是为每个最终客户端创建一个共享库。 将相同的对象文件链接到每个目标文件会破坏共享库的某些目的,例如在 Matlab 进程和每个使用您的库的并发 Python 进程之间共享内存。 它可能(我不确定(最终也会多次运行全局构造函数,但无论如何最好避免这些。 这些问题可能与您的用例无关紧要,但与以前相同的实际C++库构建问题适用。

否则,您将在一个进程中有多个共享库(一个用于"真正的"C++库,另一个用于主语言模块(。 这里有 ABI 问题;避免它们的一种方法是,如您所建议的那样,在核心库中提供 C API。 当然,还有其他方法可以处理这些问题,尤其是当您控制所有编译时。

还有更多的方法:主库可以包含一种宿主语言的接口(特别是如果它也可以用作C API(,但不能包含另一种,或者一个共享库可以在不包含C++核心的情况下提供多种语言。 给定C API,您可以使用来自主机语言FFI(ctypes用于Python(,而不是专门为主机编写(更多(C。

无论您选择哪种链接策略,编译始终是相同的:只需#include相关的任何标头(如果适用,则为 C API 的标头(以及主机语言所需的任何标头(如果有的话(即可。