Передача указателя С++ в качестве аргумента в функцию 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)