我有一个工作的Cython程序,它包含了一些C库和自定义C代码。最近,我不得不将项目切换到C ++,因此我将所有C代码重命名为*.cpp
。 Cython编译正常并生成.so
文件。但是,当我尝试在Python中导入我的库时,出现以下错误。
File "example.py", line 1, in <module>
from tag36h11_detector import detect
ImportError: dlopen(/Users/ajay/Documents/Code/calibration/apriltag_detector/examples/tag36h11_detector.cpython-36m-darwin.so, 2): Symbol not found: _free_detection_payload
Referenced from: /Users/ajay/Documents/Code/calibration/apriltag_detector/examples/tag36h11_detector.cpython-36m-darwin.so
Expected in: flat namespace
in /Users/ajay/Documents/Code/calibration/apriltag_detector/examples/tag36h11_detector.cpython-36m-darwin.so
因为我不确定错误的来源,所以我不确定要提供哪些相关信息。
这是我的setup.py
from distutils.core import setup, Extension
from Cython.Build import cythonize
import numpy
setup(ext_modules=cythonize(Extension(
name='tag36h11_detector',
sources=["tag36h11_detector.pyx",
"tag36h11_detector/tag36h11_detector.cpp"],
include_dirs=["/usr/local/include/apriltag", numpy.get_include()],
libraries=["apriltag"])))
我用python setup.py build_ext --inplace
感谢您的帮助!
答案 0 :(得分:2)
将language=c++
添加到您的设置中:
setup(ext_modules=cythonize(Extension(
name='XXX',
....
language="c++",
)))
您可能使用gcc。 gcc(和许多其他编译器)的前端决定文件是编译为C(使用cc1)还是C ++(使用cc1plus),具体取决于其扩展名:“。c”是C,“。cpp”是C ++。 / p>
如果在设置中使用extra_compile_args=["-v"],
,则可以确切地看到使用了哪个编译器:
C和C ++之间的区别之一是name mangling:C期望目标文件中符号的名称不会被破坏,但C ++会破坏它。
例如,对于具有签名int test(int)
的函数,C告诉链接器搜索名为test
的符号,但C ++会创建一个名为_Z4testi
的符号,因此它不能找到联动步骤。
现在,联系期间会发生什么?在Linux上,链接共享对象的默认行为是,我们可以使用未定义的符号。隐含地假设,在加载共享库的运行时,这些符号将是有效的。这意味着只有在加载共享对象并且找不到符号时,即导入模块时,程序才会失败。
如果存在未定义的符号,则可以添加extra_link_args=["-Wl,--no-undefined"]
以确保编译失败,以便在运行时没有任何意外。
解决问题的一种方法可能是C ++ - 编译器在代码中使用extern "C"
发出未编码的名称,如评论中所指出的那样。
一种较少侵入性的方法是向编译器说明,通过将language="c++"
添加到设置来使用C ++ - 约定。
使用language="c++"
,Cython从“XXX.pyx”创建“XXX.cpp”(而不是“XXX.c”),因此gcc为cythonized文件选择C ++编译器,该文件知道正确的名字 - 错误。