Создание структуры C++, полученной из класса в cython

Я пытаюсь связать Python с библиотекой Bullet Physics C++, используя Cython. Я хотел бы сделать это на скорости C или близкой к скорости C с минимальной упаковкой и распаковкой переменных из Python. Я смог использовать этот подход для создания интерфейса Cython для OpenGL (хотя OpenGL имеет гораздо более простой, не объектно-ориентированный C API, по сравнению с Bullet Physics). Мои интерфейсы OpenGL работают хорошо, но работа над взаимодействием с Bullet Physics идет медленно.

Я прочитал учебник Cython C++ и другие связанные сообщения здесь, но пока не повезло.

Организация Bullet Physics немного сложна, но для этого примера, надеюсь, будет достаточно знать, что btDbvtBroadphase — это структура, производная от класса btBroadphaseInterface. Как вы, наверное, знаете, структуры могут быть производными от классов (наследоваться от них), и между ними не так уж много различий — по крайней мере, в C++. Однако Cython, похоже, позволяет делать «новые» только в классах, а не в структурах. Я думаю, что это приводит к моей первой проблеме.

Объявления в файлах Bullet Physics .h выглядят следующим образом:

class btBroadphaseInterface
{
    ...

а также

struct  btDbvtBroadphase : btBroadphaseInterface
{
    ...

Вот очень простая строка кода C++, которую я пытаюсь эффективно воспроизвести в Cython:

btBroadphaseInterface* broadphase = new btDbvtBroadphase();

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

Я видел несколько упоминаний об ограничениях наследования в Cython.

Очевидно, я могу использовать функцию malloc() для выделения пространства для структуры, но, думаю, это мне мало поможет. Я не вижу, как какой-либо кастинг может помочь.

У меня есть материал Cython ".pxd", показывающий, как я делаю extern cdef из файла .h, но я не думаю, что это поможет прояснить этот вопрос, поэтому я пропущу это, если кто-то не думает, что это может быть полезно.

Я пробовал много вариантов cdefs без везения. Вот как это выглядит сейчас:

cdef extern from "bullet/btBulletDynamicsCommon.h":
    cdef cppclass btBroadphaseInterface:
        pass

    cdef cppclass btDbvtBroadphase(btBroadphaseInterface):
        pass

Когда я пытаюсь написать конструктор, который будет делать "новое" в этой структуре, полученной из класса, например:

cdef class PbtDbvtBroadphase:
    cdef btBroadphaseInterface *bp

    def __cinit__(self):
        self.bp=<btBroadphaseInterface *> new btDbvtBroadphase()

Компилятор терпит неудачу с:

Ошибка при компиляции файла Cython: ------------------------------------------------------------ --------------- ...

класс cdef PbtDbvtBroadphase: cdef btBroadphaseInterface *bp

def __cinit__(self):
    self.bp=<btBroadphaseInterface *> new btDbvtBroadphase()

^

fun4.pyx:173:46: новый оператор может применяться только к классу C++.

Изменить: улучшенный заголовок, который я изначально ввел как строку ключевых слов.

Edit2: добавлены cdefs и вывод компилятора

Я принимаю очень терпеливый ответ @DavidW, поскольку он продемонстрировал, что все должно работать. Я думаю, что произошло то, что у меня был бродячий файл .pxd с тем же базовым именем, что и у моего файла .pyx, и он был интегрирован в сборку, хотя я использовал не distutils, а просто cython из команды строка. .pxd остался от более раннего теста, когда я подумал, что мой метод сборки может не давать таких же результатов, как подход distutils/setup.py. Я убедился, что мой метод сборки не связан с проблемой, и вернулся к моему предпочтительному подходу (помещение cdefs в файл .pyx и просто компиляция и компоновка с помощью простого сценария оболочки, как показано в одном из моих комментариев ниже), но файл . pxd все еще был рядом, и компилятор cython без моего ведома все еще загружал его. Я обнаружил это, когда заметил, что при компиляции получаю "переобъявленные" ошибки. Я пропустил «повторно объявленные», которые прокручивались, и видел ошибки только при попытках нового конструктора.


person rpdrewes    schedule 30.03.2017    source источник


Ответы (1)


Самый простой вариант — просто сказать Cython, что это класс:

cdef cppclass btDvbtBroadphase(btBroadphaseInterface):
    #etc

Разница между структурой и классом в C++ заключается только в видимости членов по умолчанию. С точки зрения Cython он ведет себя одинаково, поэтому не имеет значения, что то, что вы говорите Cython, не соответствует коду C++.

(Возможно, также стоит не сообщать Cython о наследовании. Если вы не используете наследование в Cython, то ему не нужно знать. Часто проще не воспроизводить полную иерархию, а только те биты, которые вам нужны)


Изменить: у меня работает следующий код:

заголовок.hpp:

class btBroadphaseInterface {

};

struct btDbvtBroadphase : btBroadphaseInterface {

};

pymod.pyx:

cdef extern from "header.hpp":
    cppclass btBroadphaseInterface:
        pass

    cppclass btDbvtBroadphase(btBroadphaseInterface):
        pass

cdef class PyBroadphase:
    cdef btBroadphaseInterface* pt
    def __cinit__(self):
        self.pt = new btDbvtBroadphase()

    def __dealloc__(self):
        del self.pt

setup.py

from distutils.core import setup, Extension
from Cython.Build import cythonize

setup(ext_modules = cythonize(Extension(
           "pymod",                                # the extension name
           sources=["pymod.pyx",], # the Cython source and
                                                  # additional C++ source files
           language="c++",                        # generate and compile C++ code
      )))
person DavidW    schedule 31.03.2017
comment
Что касается вашего последнего комментария, я действительно не могу изменить структуру наследования, потому что библиотека создается другими, и я просто пытаюсь подключиться к ней. Я мог бы внести в него изменения, но это стало бы головной болью при сопровождении. Что касается вашего первого предложения, оно отличается от того, что я делал, тем, что в скобках есть btBroadphaseInterface, что предположительно указывает на наследование. Я тестирую это сейчас и варианты, но, похоже, это не помогает. Я отвечу еще раз, как только я исчерпал возможности по этому поводу. Спасибо. - person rpdrewes; 31.03.2017
comment
Последний комментарий был: не говорите Сайтону о наследовании, если вам это не нужно. Он не рекомендовал изменять код C++ - person DavidW; 31.03.2017
comment
Начнем с одной вещи, я думаю, вы имели в виду: cdef cppclass btDbvtBroadphase(btBroadphaseInterface): с Dbvt, фактическим именем структуры. - person rpdrewes; 31.03.2017
comment
Что касается пропуска наследства, я не понимаю, как я мог это сделать. Я хочу создать экземпляр struct, производного от class. Я не уверен, как создать экземпляр производной структуры (с методами и т. д.) в Cython. - person rpdrewes; 31.03.2017
comment
У меня есть следующее: cdef extern from "bullet/btBulletDynamicsCommon.h": cdef cppclass btDbvtBroadphase(btBroadphaseInterface): pass (извините, я не могу правильно перевести строки в комментарии) Когда я пытаюсь скомпилировать cdef class PbtDbvtBroadphase: cdef btBroadphaseInterface *bp def __cinit__(self): self.bp=<btBroadphaseInterface *> new btDbvtBroadphase() , я получаю сообщение об ошибке fun4.pyx:172:46: новый оператор может применяться только к классу C++ - person rpdrewes; 31.03.2017
comment
Вы правы насчет имени. Суть наследования в том, что Cython не нужно знать, от чего наследуется структура, если она сама не использует эту информацию. Это - person DavidW; 31.03.2017
comment
У вас тоже есть cdef cppclass btBroadphaseInterface? - person DavidW; 31.03.2017
comment
Да, у меня тоже есть cdef cppclass btBroadphaseInterface. Я также попытался просто сделать его cdef class btBroadphaseInterface. Нет радости. Несмотря на мой cdef, Cython, похоже, знает, что btDbvtBroadphase - это структура, а не класс, и отказывается разрешать использование в ней новых, хотя на самом деле они примерно такие же, как я указал в своем вопросе, и вы также указали . - person rpdrewes; 31.03.2017
comment
См. редактирование. Я разместил полный код, который компилируется и запускается для меня. - person DavidW; 31.03.2017
comment
Убедитесь, что вы компилируете файл в режиме C++. - person DavidW; 31.03.2017
comment
Ваш пример работает для меня. Я думаю, что в файлах пули .h должно быть что-то более сложное, что усложняет ситуацию, но я не могу понять, что это может быть. Я смотрю на это сейчас. - person rpdrewes; 31.03.2017
comment
Да, я компилирую файл в режиме С++. Я не использую setup.py, так как нахожу такую ​​неуклюжесть и хочу создать исполняемый файл. Я использую простую компиляцию следующим образом, и она работает на вашем примере: cython --cplus --embed pyxmod.pyx && g++ -O3 -I/usr/include/bullet -I/usr/include/python2.7 -o pyxmod4 pyxmod.cpp -lLinearMath -lBulletSoftBody -lBulletCollision -lBulletDynamics -lpython2.7 -lm -lutil -ldl -l GL -l GLU -l перенасыщение - person rpdrewes; 31.03.2017