What is the best way to wrap a custom type C++ pointer using Cython?
For example:
import numpy as np
cimport numpy as np
cdef extern from "A_c.h"
cdef cppclass A:
A();
void Foo(A* vec);
cdef class pyA:
cdef A *thisptr
def ___cinit___(self):
self.thisptr = new A()
def __dealloc___(self):
del self.thisptr
How should I use cython to wrap Foo? I have tried the following but I have gotten assertion errors from Buffer.py or the error that A is not a base type of memoryview slice
def Foo(self, np.ndarray[A, mode='c'] vec)
def Foo(self, A[::1] vec)
Basically every time you want to pass an object of type A
or a pointer to it you should use a Python object of type pyA
- which is actually very similar to a pointer apart from the fact that it has reference counting, so it is like a shared_ptr in C++11, just that it only knows about references in Python (or Cython). [Edit] Note that Python objects can be None
, you can easily prevent this using the not None
clause.
Of course this is not only true for parameters but also for return types, so every method that returns a pointer to an object of type A
should return a pyA
-object instead. For doing this you can create a cdef
method name for example setThis
that allows to set the pointer that is contained.
As mentioned already above, memory management is done in Python if you wrap pointers this way so on the one hand you need to ensure that if your C++ objects holds a pointer to an object that the Python object is not deleted (e.g. by storing a reference to the Python object in Cython) and on the other hand you should not delete objects from C++ if they are still contained in a Python object. You could also add flags to your Cython objects if the C++ object shall be deleted or not if you already have some kind of memory management in C++.
I've extended your example a bit in order to illustrate how this can be implemented in Cython:
cdef extern from "A_c.h":
cdef cppclass A:
A()
void Foo(A* vec)
A* Bar()
cdef cppclass AContainer:
AContainer(A* element)
cdef class pyA:
cdef A *thisptr
def ___cinit___(self):
self.thisptr = new A()
def __dealloc___(self):
del self.thisptr
cdef setThis(self, A* other):
del self.thisptr
self.thisptr = other
return self
def Foo(self, pyA vec not None): # Prevent passing None
self.thisptr.Foo(vec.thisptr)
def Bar(self):
return pyA().setThis(self.thisptr.Bar())
cdef class pyAContainer:
cdef AContainer *thisptr
cdef pyA element
def __cinit__(self, pyA element not None):
self.element = element # store reference in order to prevent deletion
self.thisptr = new AContainer(element.thisptr)
def __dealloc__(self):
del self.thisptr # this should not delete the element
# reference counting for the element is done automatically