tl;dr:我正在尝试编译一个引用另一个已编译 API(.so + .h 文件)的 .cpp 文件,但在尝试加载它时我得到了一个丢失的符号。下面是一个简单的示例,说明我如何创建模块,以及我在此过程中执行的所有步骤。帮助将不胜感激。
注意: 在你决定跳过这个问题之前,因为你可能对 Cython 一无所知,这更像是一个链接器/g++ 问题而不是一个 cython 问题,所以请读到最后。
我正在尝试创建一个包含预编译 C++ API 库的 Cython 模块,但在导入到 Python 时出现奇怪的错误。所以我做了一个简单的例子,期间:
首先我有以下两个文件:
// circle.h
#ifndef CIRCLE_H
#define CIRCLE_H
class Circle {
public:
Circle(float radius);
float getDiameter() const;
private:
float m_radius;
};
#endif // CIRCLE_H
//circle.cpp
#include "circle.h"
Circle::Circle(float radius) : m_radius(radius) {}
float Circle::getDiameter() const {
return m_radius * 2.0f;
}
我使用以下命令将这些编译成
build/Circle.so
:
mkdir -p build
g++ -O2 -fPIC -c circle.cpp -o build/Circle.o
g++ -shared build/Circle.o -o build/Circle.so
cp circle.h build/Circle.h
现在我转而创建我的 Cython 模块,方法是创建以下名为
example.pyx
的文件:
# distutils: language = c++
cdef extern from "build/Circle.h":
cdef cppclass Circle:
Circle(float radius)
float getDiameter()
cdef class PyCircle:
cdef Circle* c_circle
def __cinit__(self, float radius):
self.c_circle = new Circle(radius)
def __dealloc__(self):
del self.c_circle
def getDiameter(self):
return self.c_circle.getDiameter()
然后我运行
cythonize -i example.pyx
并在根目录中得到两个文件:example.cpp
(Cython生成的源文件)和example.cpython-39-x86_64-linux-gnu.so
。我将后者重命名为 example.so
并尝试从 python (import example
) 导入它,但出现以下错误:
导入错误:example.cpython-39-x86_64-linux-gnu.so:未定义符号:_ZNK6Circle11getDiameterEv
所以我使用命令
Circle.so
查看了预先编译的(几乎是一个词)模块nm -D build/Circle.so | grep Diameter
,实际上我看到丢失的符号位于该模块中:
0000000000001110 T _ZNK6Circle11getDiameterEv
所以我想“链接器一定有问题。我知道!让我们编译自动生成的 .cpp 源代码并链接到那个库!”。所以我做了以下事情:
g++ -O2 -fPIC -c example.cpp -I /usr/include/python3.9 -I build/Circle.h
build/Circle.so
为libCircle.so
g++ -shared example.o -Lbuild -lCircle -o example.so
import example
,我得到了同样的错误。我做错了什么?为什么那个符号没有加载?