Wrapping custom type C++ pointer in Cython

2019-05-14 02:10发布

问题:

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)   

回答1:

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