使用Boost Python 3.10封装C++类导入报未定义符号错误
问题产生原因
报错里的未定义符号_ZN4TestD1Ev经C++符号修饰解析,对应Test类的析构函数Test::~Test(),问题根源是代码中只在类内声明了构造函数Test(int x)和析构函数~Test(),没有提供任何实现。
C++编译生成共享库时,默认不会强制检查所有声明过的函数是否有实现,只有代码中实际执行路径调用到对应函数时,动态库加载阶段才会去查找符号地址,找不到就抛出ImportError:
- 当在栈上创建
Test test(10)局部对象时,函数退出时会自动触发析构函数调用,必须查找析构函数的实现地址,因此导入时直接报错。 - 当用
new Test(10)创建堆对象且不调用delete时,Release模式下编译器检测到该类无任何成员变量、构造/析构无实际可观测副作用,会直接把构造函数调用优化掉,不会生成对应的符号引用,因此暂时不报错;但只要加上delete触发析构调用,就会再次触发符号找不到的错误。
另外代码还有两个隐藏问题:
BOOST_PYTHON_MODULE块内不需要手动调用Py_Initialize(),Python加载C扩展时解释器已经完成初始化,重复调用会引发异常。- CMake生成的动态库名和
BOOST_PYTHON_MODULE声明的模块名不匹配,后续即使修复了符号问题,Python也会因为找不到模块入口函数导入失败。
正确解决方法
- 为
Test类的构造、析构函数提供实现,无自定义逻辑时可直接用编译器默认生成的版本:
class Test { public: Test(int x) { // 填充构造逻辑,例如初始化成员变量 } // 无自定义析构逻辑时直接使用默认实现即可 ~Test() = default; };
修复后无论栈上创建对象、还是new/delete堆对象,都能找到对应的函数符号,不会再报未定义错误。
- 删除模块定义中多余的
Py_Initialize()调用,正确的模块导出写法如下:
BOOST_PYTHON_MODULE(libTestName) { using namespace boost::python; def("greet", greet); }
- 调整CMake配置,让生成的动态库名和模块名一致,符合Python扩展的命名规则。在CMake的Boost配置块后添加如下配置,去掉动态库默认的
lib前缀,将输出文件名和BOOST_PYTHON_MODULE声明的模块名对齐:
# 若修改BOOST_PYTHON_MODULE的参数,需要同步修改OUTPUT_NAME的值,保证二者完全一致 set_target_properties(TestLib PROPERTIES PREFIX "" OUTPUT_NAME "libTestName" )
内容的提问来源于stack exchange,提问作者kaiserm99
相关产品推荐
相关产品推荐

