diff --git a/docs/src/userguide/extension_types.rst b/docs/src/userguide/extension_types.rst index 54198dd0354c89d2c4b196621dfca914310835e6..ce64181fd1b5014ac2a8f027e83713b3563d5549 100644 --- a/docs/src/userguide/extension_types.rst +++ b/docs/src/userguide/extension_types.rst @@ -532,6 +532,76 @@ statically sized freelist of ``N`` instances for a given type. Example:: .. _making_extension_types_weak_referenceable: +.. _existing-pointers-instantiation: + +Instantiation from existing C/C++ pointers +=========================================== + +It is quite common to want to instantiate an extension class from an existing +(pointer to a) data structure, often as returned by external C/C++ functions. + +As extension classes can only accept Python objects as arguments in their +contructors, this necessitates the use of factory functions. For example, :: + + from libc.stdlib cimport malloc, free + + # Example C struct + ctypedef struct my_c_struct: + int a + int b + + + cdef class WrapperClass: + """A wrapper class for a C/C++ data structure""" + cdef my_c_struct *_ptr + + def __cinit__(self): + # On cinit, do not create new structure but set pointer to NULL + self._ptr = NULL + + def __dealloc__(self): + # De-allocate if we have a non-null pointer + if self._ptr is not NULL: + free(self._ptr) + + + 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 + + + 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) + + +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:: + + Cannot convert 'my_c_struct *' to Python object + +This is because Cython cannot automatically convert the structure to a Python +object, unlike with native types like ``int``. + +Note that for native types, Cython will copy the value and create a new Python +object while in the above case, data is not copied and it is responsibility of +the extension class to correctly de-allocate. + + Making extension types weak-referenceable ==========================================