Reputation: 1
I've been wrapping C++ Classes with Cython. Now I have the following problem: Class A has a method foo() which returns an instance of Class B. Both are C++ classes. Both are wrapped in Cython to Class PyA and PyB. I want to make the method foo() accessible for users in a way, that the user calls PyA_object.foo() and gets a PyB_object. How do I implement the method?
cimport cppClassA
cdef class A:
cdef cppClassA.A *ptr
...
def foo():
return self.ptr.foo()
This way I return the direct C++ Class B, but I want to return a PyB. Therefore I need to create a PyB Object, but how do I construct the cinit() method of PyB? How can I give the returned object from self.ptr.foo() to a new PyB object and return it instead?
Upvotes: 0
Views: 645
Reputation: 30941
It isn't entirely clear from your question whether foo
returns by pointer or value (i.e. whether it has the signature B* foo()
or B foo()
). What you have to do depends on this - I've shown both.
The problem that I think you're run into is that you the __init__
or __cinit__
method of PyB
can only take Python objects as arguments, so you've got no way of passing the C++ B
object in. The solution is to create a PyB
that is either empty or has the default C++ object as ptr
and then replace it.
The wrapping of the C++ code looks like this
cdef extern from "somefile.hpp":
cdef cppclass A:
B foo1()
B* foo2()
cdef cppclass B:
# only need to provide these for foo1_alternative
B()
B(const B&)
Note that I've provided two versions of foo
- foo1
which returns by value and foo2
which returns a pointer.
The Python B
holder (PyB
) looks like
cdef class PyB:
cdef B* ptr
def __cinit__(self,make_ptr=True):
if make_ptr:
self.ptr = new B()
else:
self.ptr = NULL
def __dealloc__(self):
del self.ptr
I've provided an optional make_ptr
argument which you can set to False
to avoid setting self.ptr
to anything.
The Python A
holder (PyA
) is then
cdef class PyA:
cdef A* ptr
# code goes here
def foo1(self):
cdef PyB val = PyB()
val.ptr[0] = self.ptr.foo1() # use move (or copy) assignment operator
def foo1_alternative(self):
cdef PyB val = PyB(make_ptr=False)
val.ptr = new B(self.ptr.foo1()) # use move (or copy) constructor
def foo2(self):
cdef PyB val = PyB(make_ptr=False)
val.ptr = self.ptr.foo2()
For foo2
, which returns a pointer, you just create an empty PyB
and assign the Foo*
you get back to self.ptr
. For foo1
you have two options, one of which involves creating a PyB
with something in then using move (or copy) assignment operator to copy the data across. The second option involves creating an empty PyB
and then using B
s move (or copy) constructor to create a B*
.
You're better off using the move constructor rather than the copy constructor if you can, but you don't need to tell Cython about either (C++ will pick the right one automatically).
Upvotes: 1