tl;dr: I'm trying to compile a .cpp file that references another compiled API (.so + .h files), and I get a missing symbol when I try to load it. Below is a simple example of how I created the modules, and all the steps that I made along the way. Help would be very appreciated.
NOTE: Before you decide to skip this question because you might know nothing about Cython, this is more of a linker/g++ question than a cython question, so please read to the end.
I'm trying to create a Cython module that wraps a precompiled C++ API library, and I get strange errors on importation to python. So I have made a simple example during which:
- Compile a simple C++ library (.so + .h files)
- Compile a simple Cython module that references it.
- Load that module in Python to access the underlying C++ library.
First I have the following two files:
// 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;
}
I compile these into build/Circle.so
using the following commands:
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
Now I turned to create my Cython module by creating the following file named 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()
Then I ran cythonize -i example.pyx
and got two files in the root directory: example.cpp
(a Cython-generated source file) & example.cpython-39-x86_64-linux-gnu.so
. I renamed the latter to example.so
and tried to import it from a python (import example
), and I get the following error:
ImportError: example.cpython-39-x86_64-linux-gnu.so: undefined symbol: _ZNK6Circle11getDiameterEv
So I looked into the aforecompiled (that's almost a word) module Circle.so
using the command nm -D build/Circle.so | grep Diameter
, and indeed I saw that the missing symbol resides inside that module:
0000000000001110 T _ZNK6Circle11getDiameterEv
So I figured "there must be some problem with the linker. I know! Let's compile that auto-generated .cpp source code and link to that library!". So I did the following:
- Ran
g++ -O2 -fPIC -c example.cpp -I /usr/include/python3.9 -I build/Circle.h
- Renamed
build/Circle.so
tolibCircle.so
- Ran
g++ -shared example.o -Lbuild -lCircle -o example.so
- Ran
import example
in the python console and I got the same error.
What am I doing wrong? How come that symbol is not loaded?