diff --git a/docs/src/userguide/extension_types.rst b/docs/src/userguide/extension_types.rst index ce64181fd1b5014ac2a8f027e83713b3563d5549..ff740f3d036b2a366994c6678ca96f5494d6e685 100644 --- a/docs/src/userguide/extension_types.rst +++ b/docs/src/userguide/extension_types.rst @@ -554,43 +554,67 @@ contructors, this necessitates the use of factory functions. For example, :: cdef class WrapperClass: """A wrapper class for a C/C++ data structure""" cdef my_c_struct *_ptr + cdef bint ptr_owner def __cinit__(self): # On cinit, do not create new structure but set pointer to NULL self._ptr = NULL + self.ptr_owner = True def __dealloc__(self): - # De-allocate if we have a non-null pointer - if self._ptr is not NULL: + # De-allocate if not null and flag is set + if self._ptr is not NULL and self.ptr_owner is True: free(self._ptr) + self._ptr = NULL + # Extension class properties + @property + def a(self): + return self._ptr.a if self._ptr is not NULL else None - cdef WrapperClass PyWrapperClass(my_c_struct *_ptr): - """Factory function to create WrapperClass objects from - given my_c_struct pointer""" - # Call to __new__ bypasses __init__ constructor - cdef WrapperClass wrapper = WrapperClass.__new__(WrapperClass) - wrapper._ptr = _ptr - return wrapper + @property + def b(self): + return self._ptr.b if self._ptr is not NULL else None + @staticmethod + cdef WrapperClass from_ptr(my_c_struct *_ptr): + """Factory function to create WrapperClass objects from + given my_c_struct pointer""" + # Call to __new__ bypasses __init__ constructor + cdef WrapperClass wrapper = WrapperClass.__new__(WrapperClass) + wrapper._ptr = _ptr + return wrapper - cdef WrapperClass PyNewWrapperClass(): - """Factory function to create WrapperClass objects with - newly allocated my_c_struct""" - cdef my_c_struct *_ptr = <my_c_struct *>malloc(sizeof(my_c_struct)) - return PyWrapperClass(_ptr) + @staticmethod + cdef WrapperClass new_struct(): + """Factory function to create WrapperClass objects with + newly allocated my_c_struct""" + cdef my_c_struct *_ptr = <my_c_struct *>malloc(sizeof(my_c_struct)) + if _ptr is NULL: + raise MemoryError + _ptr.a = 0 + _ptr.b = 0 + return WrapperClass.from_ptr(_ptr) To then create a ``WrapperClass`` object from an existing ``my_c_struct`` -pointer, ``PyWrapperClass(ptr)`` can be used. It is possible to create multiple -python objects all from the same C pointer which point to the same in-memory -data, if that is wanted, though care must be taken when de-allocating as can -be seen above with the non-null check. The gil must *not* be released either, -or another lock used if it is, in such cases or race conditions can occur -with multiple de-allocations. - -Attempts to accept ``my_c_struct`` pointers in ``__cinit__`` will result -in errors like:: +pointer, ``WrapperClass.from_ptr(ptr)`` can be used in Cython code. To allocate +a new structure and wrap it at same time, ``WrapperClass.new_struct`` can be +used instead. + +It is possible to create multiple Python objects all from the same pointer +which point to the same in-memory data, if that is wanted, though care must be +taken when de-allocating as can be seen above. +Additionally, the ``ptr_owner`` flag can be used to control which +``WrapperClass`` object owns the pointer and is responsible for de-allocation - +this is set to ``True`` by default in the example. + +The GIL must *not* be released in ``__dealloc__`` either, or another lock used +if it is, in such cases or race conditions can occur with multiple +de-allocations. + +Attempts to accept ``my_c_struct`` pointers in ``__cinit__`` will result in +errors like:: Cannot convert 'my_c_struct *' to Python object