如何通过Boost Python与CMake实现条件式合并Python模块至单一库
如何用Boost.Python + CMake构建单库多模块的条件式Python扩展
我刚好折腾过类似的需求,完全可以把原来分散的多个.so合并成一个统一的MyLib.so,同时根据CMake编译选项决定哪些模块会被包含进去。下面分步骤给你拆解实现方法:
第一步:CMake配置——条件式添加源文件
首先要在CMake里定义开关选项,控制是否编译B、C模块的代码,然后把需要的源文件统一打包到MyLib库中:
cmake_minimum_required(VERSION 3.14) project(MyLib) # 找到Boost.Python和Python开发依赖 find_package(Boost REQUIRED COMPONENTS python3) find_package(Python3 REQUIRED COMPONENTS Development) # 定义编译开关,默认关闭B、C模块 option(BUILD_MODULE_B "把B模块打包进MyLib" OFF) option(BUILD_MODULE_C "把C模块打包进MyLib" OFF) # 主库的核心源文件——A模块始终编译 set(MYLIB_SOURCES src/A.cpp ) # 根据开关添加B、C的源文件 if(BUILD_MODULE_B) list(APPEND MYLIB_SOURCES src/B.cpp) endif() if(BUILD_MODULE_C) list(APPEND MYLIB_SOURCES src/C.cpp) endif() # 构建Python扩展库,注意要设为SHARED,并且去掉默认的lib前缀 add_library(MyLib SHARED ${MYLIB_SOURCES}) # 链接Boost和Python依赖 target_link_libraries(MyLib PRIVATE Boost::python3 Python3::Python ) # 让生成的库直接叫MyLib.so,而不是libMyLib.so set_target_properties(MyLib PROPERTIES PREFIX "")
第二步:C++代码结构——向同一个模块注册内容
核心技巧是用BOOST_PYTHON_MODULE_INIT宏,让每个模块的代码都向同一个MyLib命名空间注册内容,而不是各自创建独立模块:
1. 主模块初始化(比如放在A.cpp里)
这是整个MyLib的入口,负责创建模块本身,同时注册A的内容:
#include <boost/python.hpp> #include "A.h" namespace bp = boost::python; // Boost.Python的模块入口函数,创建MyLib模块 BOOST_PYTHON_MODULE(MyLib) { bp::object module = bp::scope(); // 注册A类到MyLib模块下 bp::class_<A>("A") .def("some_method", &A::some_method); // 给模块加个文档说明(可选) module.attr("__doc__") = "统一的MyLib模块,包含条件编译的子模块"; }
2. B模块的代码(B.cpp,仅当BUILD_MODULE_B开启时编译)
用BOOST_PYTHON_MODULE_INIT获取已创建的MyLib模块,然后注册B的内容:
#include <boost/python.hpp> #include "B.h" namespace bp = boost::python; // 模块初始化时追加注册B的内容 BOOST_PYTHON_MODULE_INIT(MyLib) { bp::object module = bp::scope(); // 把B类注册到MyLib模块下 bp::class_<B>("B") .def("another_method", &B::another_method); }
3. C模块的代码(C.cpp,仅当BUILD_MODULE_C开启时编译)
和B模块完全同理:
#include <boost/python.hpp> #include "C.h" namespace bp = boost::python; BOOST_PYTHON_MODULE_INIT(MyLib) { bp::object module = bp::scope(); bp::class_<C>("C") .def("third_method", &C::third_method); }
第三步:验证效果
编译时根据需要加开关就行:
- 只编译A模块:
cmake .. && make - 编译A+B:
cmake .. -DBUILD_MODULE_B=ON && make - 编译A+B+C:
cmake .. -DBUILD_MODULE_B=ON -DBUILD_MODULE_C=ON && make
然后在Python里测试:
import MyLib # A始终可用 a = MyLib.A() a.some_method() # B仅当编译时开启才存在 try: b = MyLib.B() b.another_method() except AttributeError: print("B模块未启用") # C仅当编译时开启才存在 try: c = MyLib.C() c.third_method() except AttributeError: print("C模块未启用")
几个注意点
- 所有源文件里的
BOOST_PYTHON_MODULE_INIT必须用同一个模块名MyLib,不然会注册到不同模块里。 - 如果你要注册模块级的函数(不是类成员),可以用
module.attr("my_func") = bp::make_function(&my_func)的方式添加到MyLib下。 - 确保CMake找到的Boost.Python版本和你用的Python版本匹配,避免兼容性问题。
内容的提问来源于stack exchange,提问作者Svalorzen
相关产品推荐
相关产品推荐

