Передача указателя С++ в качестве аргумента в функцию Cython

cdef extern from "Foo.h":
    cdef cppclass Bar:
        pass

cdef class PyClass:
    cdef Bar *bar

    def __cinit__(self, Bar *b)
        bar = b

Это всегда даст мне что-то вроде:
  Cannot convert Python object argument to type 'Bar *'

Есть ли способ выполнить это, или мне нужно извлечь все из объекта Bar, создать эквивалент Python, передать его, а затем восстановить его в PyClass?

Ответы

Ответ 1

Я столкнулся с этой проблемой, пытаясь обернуть C-код с помощью структур как классов python. Проблема заключается в том, что "специальная" функция, включая __init__ и __cinit__, должна быть объявлена ​​как def, а не cdef. Это означает, что их можно вызывать из обычного питона, поэтому параметры типа эффективно игнорируются, и все рассматривается как объект.

В J.F. Sebastian ответ исправить не обертывание - двойной является основным числовым типом, и поэтому происходит преобразование по умолчанию между типом C/С++ и объектом Python. Ответ Czarek в основном правильный - вам нужно использовать фальшивую конструкторскую идиому, используя глобальную функцию. Невозможно использовать декоратор @staticmethod, поскольку они не могут применяться к функциям cdef. Ответ выглядит проще в исходном примере.

cdef extern from "Foo.h":
    cdef cppclass Bar:
        pass

cdef class PyClass:
    cdef Bar *bar

cdef PyClass_Init(Bar *b):
    result = PyClass()
    result.bar = b
    return result

Ответ 2

Для каждого класса cdef создайте глобальную функцию cdef, которая действует как конструктор, CefResponse - это объект С++, PyResponse - эквивалент python объекта С++:

cdef object CreatePyResponse(CefRefPtr[CefResponse] cefResponse):

    pyResponse = PyResponse()
    pyResponse.cefResponse = cefResponse
    return pyResponse

cdef class PyResponse:

    cdef CefRefPtr[CefResponse] cefResponse

    def GetStatus(self):

        return (<CefResponse*>(self.cefResponse.get())).GetStatus()

Итак, вместо resp = PyResponse(cppObject) вызов resp = CreatePyResponse(cppObject).

Пример, взятый из CEF Python: https://code.google.com/p/cefpython/source/browse/cefpython/response.pyx?r=0250b65e046a

Ответ 3

Начиная с Cython 0.21 было объявлено о методах cdef с помощью декоратора @staticmethod. Это позволяет статическим методам создания принимать аргументы, отличные от Python:

cdef extern from "Foo.h":
    cdef cppclass Bar:
        pass

cdef class PyClass:
    cdef Bar *bar

    @staticmethod
    cdef create(Bar *bar):
        cdef PyClass pc = PyClass()
        pc.bar = bar
        return pc

Ответ 4

Класс Python принимает аргументы Python. Чтобы передать аргумент С++, вам необходимо обернуть его:

# distutils: language = c++

cdef extern from "Foo.h" namespace "baz":
    cdef cppclass Bar:
         Bar(double d)
         double get()

cdef class PyBar: # wrap Bar class
    cdef Bar *thisptr
    def __cinit__(self, double d):
        self.thisptr = new Bar(d)
    def __dealloc__(self):
        del self.thisptr
    property d:
        def __get__(self):
            return self.thisptr.get()

PyBar экземпляры могут использоваться как любые другие объекты Python как из Cython, так и из чистого Python:

class PyClass:
    def __init__(self, PyBar bar):
        self.bar = bar

print(PyClass(PyBar(1)).bar.d)