Как обернуть класс C ++ с помощью Cython? - PullRequest
20 голосов
/ 20 января 2012

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

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

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

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

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

Ответы [ 3 ]

16 голосов
/ 20 января 2012

Даже Cython обычно используется с C , он также может генерировать код 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>, расширив его.

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

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

cimport my_pxd
cdef my_pxd.SomeExternedType obj

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

5 голосов
/ 07 февраля 2012

Итак, после множества попыток, проб и ошибок, криков и рвущихся волос я наконец-то начал работать.Сначала я должен был переписать свой C ++ в 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
)

По мере того как выКак видите, второй аргумент в расширении просто перечисляет все файлы, которые должны быть скомпилированы, Cython решает, как их скомпилировать в зависимости от их расширения, насколько я могу судить.Массив library сообщает компилятору 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.Возможно, есть лучший, более правильный способ заставить все это работать, но это происходит из опыта, и это была первая встреча, которую мне удалось выработать.Я был бы очень заинтересован в указателях, где я мог ошибиться.

Обновление:

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

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

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

3 голосов
/ 20 января 2012

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

...