Как я могу вручную скомпилировать код Cython, который использует C++?

я точно скопировал код примера, приведенный в документация Cython для упаковки классов C++. Я могу успешно построить и импортировать rect.so расширение с помощью distutils и cythonize() метод, т. е.:

  1. поставив следующие директивы сверху rect.pyx:

    # distutils: language = c++
    # distutils: sources = Rectangle.cpp
    
  2. пишем , который содержит это:

    from distutils.core import setup
    from Cython.Build import cythonize
    
    setup(
        name = "rectangleapp",
        ext_modules = cythonize('*.pyx'),
    )
    
  3. вызов

    $ python setup.py build_ext --inplace
    

однако, когда я обертываю код C в Cython, мне часто удобнее компилировать отдельные расширения вручную из командной строки, т. е.:

  1. создать .c код с помощью командной строки Cython compiler

    $ cython foo.pyx
    
  2. вручную скомпилировать его с помощью gcc:

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

я попытался применить тот же процесс для создания rect.so пример выше:

$ 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

оба шага компиляции Cython и g++ кажутся успешными - я не получаю вывода командной строки, и в конце у меня есть rect.so причине. Однако, когда я пытаюсь импортировать модуль я получаю undefined symbol ошибка:

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

какова правильная процедура для ручной компиляции кода Cython, который обертывает классы C++?

1 ответов


проблема здесь в том, что вы сказали, что где-то вы предоставите определение класса под названием Rectangle -- где в примере кода говорится

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

однако, когда вы скомпилировали библиотеку, вы не предоставили код для прямоугольника или библиотеки, которая его содержала, поэтому rect.so понятия не имеет, где найти этот класс прямоугольника.

для запуска кода необходимо сначала создать файл объекта Rectangle.

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

теперь вы можете либо создать библиотека для динамической ссылки или статической ссылки на объектный файл в rect.so. Я статически крышка связав сначала как это простое.

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

обратите внимание, что я не включил библиотеку для python. Это связано с тем, что вы ожидаете, что ваша библиотека будет загружена интерпретатором python, поэтому библиотеки python уже будут загружены процессом при загрузке вашей библиотеки. В дополнение к предоставлению rect.cpp в качестве источника я также предоставляю Rectangle.o. Итак, давайте попробуем запустить программу используя ваш модуль.

run.py

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

к сожалению, это приводит к другой ошибке:

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

это потому, что cython нуждается в стандартной библиотеке C++, но python не загрузил ее. Вы можете исправить это, добавив стандартную библиотеку c++ в необходимые библиотеки для rect.so

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

выполнить run.py снова и все должно работать. Однако код для rect.so больше, чем это должно быть, особенно если вы производите несколько библиотек, зависящих от одного кода. Вы можете динамически связать код прямоугольника, сделав его библиотекой.

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

мы компилируем код прямоугольника в общую библиотеку в текущем каталоге и предоставляем -L. таким образом, gcc знает, чтобы искать библиотеки в текущем каталоге и -lRectangle поэтому gcc знает, что нужно искать библиотеку прямоугольников. Наконец, чтобы иметь возможность запускать код, вы должны сообщить python, где живет библиотека прямоугольников. Перед запуском питона enter

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

вы можете использовать shell-скрипт, чтобы убедиться, что это делается каждый раз перед запуском программы, но это делает вещи сложнее. Лучше всего просто придерживаться статически связывающего прямоугольника.