Question Passer le pointeur C ++ comme argument dans la fonction Cython


cdef extern from "Foo.h":
    cdef cppclass Bar:
        pass

cdef class PyClass:
    cdef Bar *bar

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

Cela me donnera toujours quelque chose comme:
Cannot convert Python object argument to type 'Bar *'

Y a-t-il un moyen d'y parvenir, ou dois-je tout extraire d'un Bar objet, créer un équivalent Python, le transmettre, puis le reconstruire PyClass?


20
2017-08-30 19:53


origine


Réponses:


Je suis tombé sur ce problème en essayant d’emballer du code C avec des structures en tant que classes python. La question semble être que la fonction "spéciale", y compris __init__ et __cinit__ doit être déclaré comme def plutôt que cdef. Cela signifie qu'ils peuvent être appelés à partir de python normal, donc les paramètres de type sont effectivement ignorés et tout est traité comme objet.

Dans la réponse de J.F. Sebastian, le correctif n'est pas le wrapping - un double est un type numérique de base et il y a donc une conversion par défaut entre le type C / C ++ et l'objet Python. La réponse de Czarek est fondamentalement correcte - vous devez utiliser un idiome de constructeur faux, en utilisant une fonction globale. Il n'est pas possible d'utiliser un décorateur @staticmethod, car ils ne peuvent pas être appliqués aux fonctions cdef. La réponse semble plus simple sur l'exemple original fourni.

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

9
2018-05-01 20:33



À partir de Cython 0.21 il a été possible de déclarer cdef méthodes avec le @staticmethod décorateur. Cela permet des méthodes de création statiques qui prennent des arguments non-Python:

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

6
2017-09-27 12:08



Pour chaque classe cdef, créez une fonction cdef globale agissant comme un constructeur, CefResponse est un objet C ++, PyResponse un équivalent python d'un objet c ++:

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()

Donc au lieu de resp = PyResponse(cppObject) appel resp = CreatePyResponse(cppObject).

Exemple tiré de CEF Python: https://code.google.com/p/cefpython/source/browse/cefpython/response.pyx?r=0250b65e046a


5
2017-08-30 20:55



La classe Python accepte les arguments Python. Pour transmettre un argument C ++, vous devez l’emballer:

# 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 les instances peuvent être utilisées comme tout autre objet Python à la fois de Cython et de Python pur:

class PyClass:
    def __init__(self, PyBar bar):
        self.bar = bar

print(PyClass(PyBar(1)).bar.d)

2
2017-09-01 22:45