如何使用语言包装器构建 C++ 库?
How to structure a C++ library with language wrappers?
我对编写包含其他语言(例如 Python 和 Matlab)包装器的 C++ 库的最佳做法有疑问。这可能是一个简单的问题或重复的问题,但我还没有找到好的资源或其他答案来帮助解释如何做到这一点。
作为背景,我正在开发一个 C++ 库项目,其中包含专门用于与其他语言兼容的 C 包装器。该库是一个用 C++ 编写的科学计算库,我已经为函数编写了 C 包装器,类 用作共享库的一部分。
我的问题是如何将 C 包装器合并到其他语言的模块中,例如 Python 和 Matlab 的包装器库。我不是在询问如何为这些其他语言实现代码的具体细节,因为这完全是另一个问题,而且我已经对如何编写可以为每种语言构建和加载的代码有了基本的了解图书馆。我的问题主要是关于将 C 包装器包含到这些其他构建中。
例如,我有以下目录结构,其中每个文件夹包含与不同 language/module 相关的代码。
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_wrapper
和 python_wrapper
文件中的 C 包装器(基本上创建同一函数的 3 个版本)或在另一个的构建步骤中包含 c_wrapper
文件似乎是多余的库,因为它们已经包含在主 C++ 库中。
我不是打包共享库方面的专家,那么最好的方法是什么?我应该只将 c_wrapper
源文件作为构建步骤的一部分,重写每个模块中的 C++ 包装器,还是有另一种方法将 c_wrapper
中的代码包含在包装器库中?理想情况下,每个模块都将共享相同的(或非常相似的)API,因此如果有一种方法可以包含此代码而无需在每次添加新的 C 包装器时都重写相同的函数,那就太好了。非常感谢任何帮助或建议。
您也许可以为所有目的创建一个共享库:毕竟 Python looks for 的 PyInit_foo
将被 Matlab 简单地忽略。但是你可能仍然不想改变你的主库来支持这样的用途:也许它有其他不需要包装器的编译客户端,或者它应该与特殊包装器分开安装,或者它可能需要在没有包装器的机器上使用安装的主机。
另一种选择是为每个最终客户创建一个共享库。将相同的 object 文件链接到每个文件会破坏共享库的某些目的,例如在 Matlab 进程和并发 Python 进程之间共享内存,每个进程都使用您的库。它可能(我不确定)也不止一次以 运行 全局构造函数结束,但无论如何最好避免这些。这些问题对于您的用例可能无关紧要,但对于真正的 C++ 库,存在与之前相同的构建问题。
否则,您将在一个进程中拥有多个共享库(一个用于“真正的”C++ 库,另一个用于宿主语言模块)。这里有ABI issues;正如您所建议的那样,避免它们的一种方法是在核心库中提供 C API 。当然,还有其他方法可以解决这些问题,尤其是当您控制所有编译时。
还有更多的方法:主库可以包括一种宿主语言的接口(特别是如果它也可以作为 C API)而不是另一种,或者一个共享库可以为多种语言提供服务不包括 C++ 核心。鉴于 C API,您可以使用 FFI 来自 宿主语言(ctypes
for Python)而不是专门为主机编写(更多)C。
无论您选择什么 link 策略,编译 总是相同的:只有 #include
您的 headers 相关(那些用于C API 如果合适的话)和宿主语言所需的任何东西(如果有的话)然后去。
我对编写包含其他语言(例如 Python 和 Matlab)包装器的 C++ 库的最佳做法有疑问。这可能是一个简单的问题或重复的问题,但我还没有找到好的资源或其他答案来帮助解释如何做到这一点。
作为背景,我正在开发一个 C++ 库项目,其中包含专门用于与其他语言兼容的 C 包装器。该库是一个用 C++ 编写的科学计算库,我已经为函数编写了 C 包装器,类 用作共享库的一部分。
我的问题是如何将 C 包装器合并到其他语言的模块中,例如 Python 和 Matlab 的包装器库。我不是在询问如何为这些其他语言实现代码的具体细节,因为这完全是另一个问题,而且我已经对如何编写可以为每种语言构建和加载的代码有了基本的了解图书馆。我的问题主要是关于将 C 包装器包含到这些其他构建中。
例如,我有以下目录结构,其中每个文件夹包含与不同 language/module 相关的代码。
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_wrapper
和 python_wrapper
文件中的 C 包装器(基本上创建同一函数的 3 个版本)或在另一个的构建步骤中包含 c_wrapper
文件似乎是多余的库,因为它们已经包含在主 C++ 库中。
我不是打包共享库方面的专家,那么最好的方法是什么?我应该只将 c_wrapper
源文件作为构建步骤的一部分,重写每个模块中的 C++ 包装器,还是有另一种方法将 c_wrapper
中的代码包含在包装器库中?理想情况下,每个模块都将共享相同的(或非常相似的)API,因此如果有一种方法可以包含此代码而无需在每次添加新的 C 包装器时都重写相同的函数,那就太好了。非常感谢任何帮助或建议。
您也许可以为所有目的创建一个共享库:毕竟 Python looks for 的 PyInit_foo
将被 Matlab 简单地忽略。但是你可能仍然不想改变你的主库来支持这样的用途:也许它有其他不需要包装器的编译客户端,或者它应该与特殊包装器分开安装,或者它可能需要在没有包装器的机器上使用安装的主机。
另一种选择是为每个最终客户创建一个共享库。将相同的 object 文件链接到每个文件会破坏共享库的某些目的,例如在 Matlab 进程和并发 Python 进程之间共享内存,每个进程都使用您的库。它可能(我不确定)也不止一次以 运行 全局构造函数结束,但无论如何最好避免这些。这些问题对于您的用例可能无关紧要,但对于真正的 C++ 库,存在与之前相同的构建问题。
否则,您将在一个进程中拥有多个共享库(一个用于“真正的”C++ 库,另一个用于宿主语言模块)。这里有ABI issues;正如您所建议的那样,避免它们的一种方法是在核心库中提供 C API 。当然,还有其他方法可以解决这些问题,尤其是当您控制所有编译时。
还有更多的方法:主库可以包括一种宿主语言的接口(特别是如果它也可以作为 C API)而不是另一种,或者一个共享库可以为多种语言提供服务不包括 C++ 核心。鉴于 C API,您可以使用 FFI 来自 宿主语言(ctypes
for Python)而不是专门为主机编写(更多)C。
无论您选择什么 link 策略,编译 总是相同的:只有 #include
您的 headers 相关(那些用于C API 如果合适的话)和宿主语言所需的任何东西(如果有的话)然后去。