Passing C++ pointer as argument into Cython function

后端 未结 4 2081
萌比男神i
萌比男神i 2021-02-04 00:36
cdef extern from \"Foo.h\":
    cdef cppclass Bar:
        pass

cdef class PyClass:
    cdef Bar *bar

    def __cinit__(self, Bar *b)
        bar = b

相关标签:
4条回答
  • 2021-02-04 01:13

    I came across this problem trying to wrap C code with structs as python classes. The issue seems to be that "special" function including __init__ and __cinit__ must be declared as def rather than cdef. This means that they can be called from normal python, so the type parameters are effectively ignored and everything is treated as object.

    In J.F. Sebastian's answer the fix is not the wrapping - a double is a basic numeric type and so there is a default conversion between the C/C++ type and the Python object. Czarek's answer is basically correct - you need to use a fake constructor idiom, using a global function. It is not possible to use a @staticmethod decorator as they cannot be applied to cdef functions. The answer looks simpler on the original example provided.

    cdef extern from "Foo.h":
        cdef cppclass Bar:
            pass
    
    cdef class PyClass:
        cdef Bar *bar
    
    cdef PyClass_Init(Bar *b):
        result = PyClass()
        result.bar = b
        return result
    
    0 讨论(0)
  • 2021-02-04 01:26

    As of Cython 0.21 it has been possible to declare cdef methods with the @staticmethod decorator. This allows static creator methods that take non-Python arguments:

    cdef extern from "Foo.h":
        cdef cppclass Bar:
            pass
    
    cdef class PyClass:
        cdef Bar *bar
    
        @staticmethod
        cdef create(Bar *bar):
            cdef PyClass pc = PyClass()
            pc.bar = bar
            return pc
    
    0 讨论(0)
  • 2021-02-04 01:26

    For each cdef class create a global cdef function that acts as a constructor, CefResponse is a C++ object, PyResponse a python equivalent of a c++ object:

    cdef object CreatePyResponse(CefRefPtr[CefResponse] cefResponse):
    
        pyResponse = PyResponse()
        pyResponse.cefResponse = cefResponse
        return pyResponse
    
    cdef class PyResponse:
    
        cdef CefRefPtr[CefResponse] cefResponse
    
        def GetStatus(self):
    
            return (<CefResponse*>(self.cefResponse.get())).GetStatus()
    

    So instead of resp = PyResponse(cppObject) call resp = CreatePyResponse(cppObject).

    Example taken from CEF Python: https://code.google.com/p/cefpython/source/browse/cefpython/response.pyx?r=0250b65e046a

    0 讨论(0)
  • 2021-02-04 01:26

    Python class accepts Python arguments. To pass a C++ argument you need to wrap it:

    # distutils: language = c++
    
    cdef extern from "Foo.h" namespace "baz":
        cdef cppclass Bar:
             Bar(double d)
             double get()
    
    cdef class PyBar: # wrap Bar class
        cdef Bar *thisptr
        def __cinit__(self, double d):
            self.thisptr = new Bar(d)
        def __dealloc__(self):
            del self.thisptr
        property d:
            def __get__(self):
                return self.thisptr.get()
    

    PyBar instances can be used as any other Python objects both from Cython and pure Python:

    class PyClass:
        def __init__(self, PyBar bar):
            self.bar = bar
    
    print(PyClass(PyBar(1)).bar.d)
    
    0 讨论(0)
提交回复
热议问题