Reputation: 171
I have two classes (let's assume the most simple ones, implementation is not important). My defs.pxd
file (with cython defs) looks like this:
cdef extern from "A.hpp":
cdef cppclass A:
A() except +
cdef extern from "B.hpp":
cdef cppclass B:
B() except +
A func ()
My pyx
file (with python defs) looks like this:
from cython.operator cimport dereference as deref
from libcpp.memory cimport shared_ptr
cimport defs
cdef class A:
cdef shared_ptr[cquacker_defs.A] _this
@staticmethod
cdef inline A _from_this(shared_ptr[cquacker_defs.A] _this):
cdef A result = A.__new__(A)
result._this = _this
return result
def __init__(self):
self._this.reset(new cquacker_defs.A())
cdef class B:
cdef shared_ptr[cquacker_defs.B] _this
@staticmethod
cdef inline B _from_this(shared_ptr[cquacker_defs.B] _this):
cdef B result = B.__new__(B)
result._this = _this
return result
def __init__(self):
self._this.reset(new cquacker_defs.B())
def func(self):
return deref(self._this).func()
The thing is that I cannot return non-python object from Python. Actually, I don't want to change my c++ code to return pointer instead of new object (because there are many functions like that). Now it gives me the error:
Cannot convert 'B' to Python object
How can I return one python object holding internal c++ object from another one's method in python? If I can only do that after some c++ changes, I want the most elegant solution, if possible.
Upvotes: 0
Views: 843
Reputation: 30938
Your issue is that your wrapper class requires a pointer (to a new
allocated object) but your function returns an C++ object on the stack. To solve this you have to copy or move the object from the stack.
First ensure that your C++ class A
has a working copy or move constructor. A move constructor is better if you're c++ class contains large members. Wrap this is Cython like so:
cdef extern from "A.hpp":
cdef cppclass A:
A() except +
A(const A&) except +
# or A(A&&) except +
(Don't tell Cython about both a copy constructor and a move constructor - it gets confused! C++ finds the right one when it's compiled anyway).
Then, in func
use your copy/move constructor with new
to pass to your python wrapper:
def func(self):
return A._from_this(new cquacker_defs.A(self._this.func()))
Upvotes: 1