How can I manually compile Cython code that uses C ++?

I have precisely copied the sample code provided in the Cython documentation for packaging C ++ classes . I can successfully build and import the rect.so extension using distutils and the cythonize() method, that is:

  • Put the following directives at the top of rect.pyx :

     # distutils: language = c++ # distutils: sources = Rectangle.cpp 
  • Writing a setup.py that contains this:

     from distutils.core import setup from Cython.Build import cythonize setup( name = "rectangleapp", ext_modules = cythonize('*.pyx'), ) 
  • Call

     $ python setup.py build_ext --inplace 

However, when I wrap C code in Cython, it is often more convenient for me to compile individual extensions manually from the command line, i.e.:

  • Generate .c code using the Cython compiler command line

     $ cython foo.pyx 
  • Manually compile it with gcc :

     $ gcc -shared -fPIC -O3 -I /usr/lib/python2.7 -L /usr/lib/python2.7 \ foo.c -lpython2.7 -o foo.so 

I tried applying the same process to build the rect.so example above:

 $ cython --cplus rect.pyx $ g++ -shared -fPIC -O3 -I /usr/lib/python2.7 -L /usr/lib/python2.7 \ rect.cpp -lpython2.7 -o rect.so 

Both compilation steps of Cython and g ++ seem to succeed - I don't get any command line output, and in the end I have rect.so However, when I try to import a module, I get an undefined symbol error:

 In [1]: import rect --------------------------------------------------------------------------- ImportError Traceback (most recent call last) <ipython-input-1-ba16f97c2145> in <module>() ----> 1 import rect ImportError: ./rect.so: undefined symbol: _ZN6shapes9Rectangle9getLengthEv 

What is the correct procedure for manually compiling Cython code that wraps C ++ classes?

+6
source share
1 answer

The problem here is that you said that somewhere you will provide a definition of the Rectangle class - where the sample code contains

 cdef extern from "Rectangle.h" namespace "shapes": cdef cppclass Rectangle: ... 

However, when you compiled the library, you did not provide code for the Rectangle or the library that contained it, so rect.so has no idea where to find this Rectangle class.

To run the code, you must first create a Rectangle object file.

 gcc -c Rectangle.cpp # creates a file called Rectangle.o 

Now you can either create a library for dynamic linking or statically link the object file with rect.so First I will talk about static binding as the simplest.

 gcc -shared -fPIC -I /usr/include/python2.7 rect.cpp Rectangle.o -o rect.so 

Please note that I did not include the library for python. This is because you expect your library to be loaded by the python interpreter, so the python libraries will already be loaded by the process when you load your library. Besides providing rect.cpp as a source, I also provide Rectangle.o . Therefore, try to run the program using your module.

run.py

 import rect print(rect.PyRectangle(0, 0, 1, 2).getLength()) 

Unfortunately, this causes another error:

 ImportError: /home/user/rectangle/rect.so undefined symbol: _ZTINSt8ios_base7failureE 

This is because cython needs a standard C ++ library, but python did not load it. You can fix this by adding the standard C ++ library to the necessary libraries for rect.so

 gcc -shared -fPIC -I/usr/include/python2.7 rect.cpp Rectangle.o -lstdc++ \ -o rect.so 

Run run.py again and everything should work. However, the code for rect.so more than it should be, especially if you are creating several libraries that depend on the same code. You can dynamically link the Rectangle code, making it also a library.

 gcc -shared -fPIC Rectangle.o -o libRectangle.so gcc -shared -fPIC -I/usr/include/python2.7 -L. rect.cpp -lRectangle -lstdc++ \ -o rect.so 

We compile the Rectangle code into a shared library in the current directory and provide -L. , so gcc knows how to search libraries in the current directory and -lRectangle , so gcc knows how to search the Rectangle library. Finally, in order to be able to run your code, you must tell python where the Rectangle library is. Before starting python, enter

 export LD_LIBRARY_PATH="/home/user/rectangle" # where libRectangle.so lives 

You can use a shell script to make sure it is done every time before running your program, but it makes things more messy. It’s best to stick with a Rectangle static binding.

+6
source

Source: https://habr.com/ru/post/982093/


All Articles