Подтвердить что ты не робот

Как связать класс С++ с Cython?

У меня есть класс С++. Он состоит из одного файла .ccp и одного файла .h. Он компилирует (я могу написать основной метод, который успешно использует его в С++). Как я могу связать этот класс с Cython, чтобы сделать его доступным в Python?

Я читал документы и не слежу. Они говорят о создании файла cpp. Когда я пытался следить за документами, мой уже существующий cpp сбрасывается...

Что я должен поставить в файл pyx? Мне сказали определение класса, но сколько его? Просто общественные методы?

Нужен ли мне файл .pxd? Я не понимаю, когда этот файл нужен или не требуется.

Я пробовал задавать этот вопрос на #python IRC-канале и не могу получить ответ.

4b9b3361

Ответ 1

Итак, после многих попыток, проб и ошибок, крича и вырвав мои волосы, я наконец получил это, чтобы работать. Во-первых, мне пришлось переписать мой С++ в C, который для меня действительно просто включал преобразование всех моих переменных std::string в char* и отслеживание некоторых длин.

После этого у меня были файлы .h и .c. Я хотел сделать одну функцию из C-кода, доступного в Python. Оказывается, Cython может скомпилировать ваши C файлы в расширение для вас и связать любые библиотеки за один раз, поэтому, начиная с моей setup.py, это выглядело так:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_modules=[
  Extension("myext",
    ["myext.pyx", "../stuff.c"],
    libraries=["ssl", "crypto"]
  )
]

setup(
  name = "myext",
  cmdclass = {"build_ext": build_ext},
  ext_modules = ext_modules
)

Как вы можете видеть, второй аргумент Extension просто перечисляет все файлы, которые необходимо скомпилировать, Cython разрабатывает, как их компилировать, в зависимости от их расширения файла, насколько я могу судить. Массив библиотек сообщает компилятору Cython, с чем нужно связать (в этом случае я обертывал некоторые криптографические материалы, которые я не мог бы имитировать непосредственно через существующие библиотеки Python).

Чтобы сделать мою C-функцию доступной в файле .pyx, вы пишете небольшую обертку в .pxd. Мой myext.pxd выглядел следующим образом:

cdef extern from "../stuff.h":
    char* myfunc(char* arg1, char* arg2, char* arg3)

В .pyx вы затем используете объявление cimport для импорта этой функции, которая затем доступна для использования, как если бы это была любая другая функция Python:

cimport myext

def my_python_func(arg1, arg2, arg3):
    result = myext.myfunc(arg1, arg2, arg3)
    return result

Когда вы создаете это (по крайней мере на Mac), вы получаете .so, что вы можете импортировать в python и запускать функции из .pyx. Там может быть лучший, более правильный способ заставить все это работать, но это исходит из опыта, и это стало первой встречей, с которой мне удалось разобраться. Я был бы очень заинтересован в указателях, где я, возможно, ошибся.

Update:

После дальнейшего использования Cython я обнаружил, что было супер просто интегрировать его с С++, как только вы знаете, что делаете. Создание С++ string доступно так же просто, как from libcpp.string cimport string в вашем pyx/pyd. Объявление класса С++ аналогично просто:

cdef extern from "MyCPPClass.h":
    cdef cppclass MyCPPClass:
        int foo;
        string bar;

Уверен, что вам нужно в основном переопределить .h определение вашего класса в формате Pythonic, но это небольшая цена, чтобы заплатить за получение доступа к уже написанным функциям С++.

Ответ 2

Даже Cython обычно используется с C, он также может генерировать код на С++. При компиляции вы добавляете флаг --cplus.

Теперь создание оболочки для класса прост и не сильно отличается от обертывания структуры. Это в основном отличается от объявления extern, но это не так много.

Предположим, что у вас есть класс MyCppClass в mycppclass.h.

cdef extern from "mycppclass.h":
    cppclass MyCppClass:
        int some_var

        MyCppClass(int, char*)
        void doStuff(void*)
        char* getStuff(int)

cdef class MyClass:

    # the public-modifier will make the attribute public for cython,
    # not for python. Maybe you need to access the internal C++ object from
    # outside of the class. If not, you better declare it as private by just
    # leaving out the `private` modifier.
    # ---- EDIT ------
    # Sorry, this statement is wrong. The `private` modifier would make it available to Python,
    # so the following line would cause an error es the Pointer to MyCppClass
    # couldn't be converted to a Python object.
    #>> cdef public MyCppClass* cobj
    # correct is:
    cdef MyCppClass* obj

    def __init__(self, int some_var, char* some_string):
        self.cobj = new MyCppClass(some_var, some_string)
        if self.cobj == NULL:
            raise MemoryError('Not enough memory.')

    def __del__(self):
        del self.cobj

    property some_var:
        def __get__(self):
            return self.cobj.some_var
        def __set__(self, int var):
            self.cobj.some_var = var

Обратите внимание, что ключевое слово new доступно только тогда, когда установлен флаг --cplus, в противном случае используйте malloc из <stdlib.h> путем его externing.

Также обратите внимание, что вам не нужно разыменовывать указатель (->), чтобы вызвать метод. Cython отслеживает тип объекта и применяет то, что подходит.

.pxd файлы предназначены для разделения объявлений от реализации или для предотвращения столкновения пространства имен. Представьте, что вы хотите назвать Python-оберткой, как класс С++. Просто добавьте в свой .pxd файл объявления extern и cimport файл pxd в .pyx.

cimport my_pxd
cdef my_pxd.SomeExternedType obj

Обратите внимание, что вы не можете записывать реализации в файл .pxd.

Ответ 3

Cython в основном для разработки C, для интеграции С++ с Python я бы рекомендовал Boost.Python. Их отличная документация должна начать работать довольно быстро.