Saltar al contenido

¿Cómo envuelvo una clase de C++ con Cython?

Te doy la bienvenida a proyecto on line, en este lugar vas a encontrar la resolución que buscabas.

Solución:

Incluso Cython generalmente se usa con Cpuede generar C++ código, también. Al compilar, se agrega el --cplus bandera.

Ahora, crear un envoltorio para la clase es simple y no muy diferente de envolver una estructura. Difiere principalmente de declarar el externpero eso no es mucha diferencia en absoluto.

Supongamos que tienes una clase MyCppClass en mycppclass.h.

cdef extern from "mycppclass.h":
    cppclass MyCppClass:
        int some_var

        MyCppClass(int, char*)
        void doStuff(void*)
        char* getStuff(int)

cdef class MyClass:

    # the public-modifier will make the attribute public for cython,
    # not for python. Maybe you need to access the internal C++ object from
    # outside of the class. If not, you better declare it as private by just
    # leaving out the `private` modifier.
    # ---- EDIT ------
    # Sorry, this statement is wrong. The `private` modifier would make it available to Python,
    # so the following line would cause an error es the Pointer to MyCppClass
    # couldn't be converted to a Python object.
    #>> cdef public MyCppClass* cobj
    # correct is:
    cdef MyCppClass* obj

    def __init__(self, int some_var, char* some_string):
        self.cobj = new MyCppClass(some_var, some_string)
        if self.cobj == NULL:
            raise MemoryError('Not enough memory.')

    def __del__(self):
        del self.cobj

    property some_var:
        def __get__(self):
            return self.cobj.some_var
        def __set__(self, int var):
            self.cobj.some_var = var

Tenga en cuenta que el new La palabra clave solo está disponible cuando la --cplus la bandera está configurada, de lo contrario, use malloc desde al exteriorizarlo.

También tenga en cuenta que no necesita desreferenciar el puntero (->) para llamar al método. Cython rastrea el tipo de objeto y aplica lo que se ajusta.

Los archivos .pxd son para separar las declaraciones de la implementación o para evitar la colisión de espacios de nombres. Imagina que te gustaría nombrar tu Python-wrapper como la clase C++. Simplemente coloque en su archivo .pxd el extern declaraciones y cimport el archivo pxd en el .pyx.

cimport my_pxd
cdef my_pxd.SomeExternedType obj

Tenga en cuenta que no puede escribir implementaciones en un archivo .pxd.

Entonces, después de muchos pinchazos, prueba y error, gritos y tirones de pelo, finalmente conseguí que esto funcionara. Sin embargo, primero tuve que volver a escribir mi C++ en C, lo que para mí solo implicaba convertir todos mis std::string variables a char* y hacer un seguimiento de algunas longitudes.

Una vez hecho, tenía mis archivos .h y .c. Quería hacer una sola función del código C disponible en Python. Resulta que Cython puede compilar sus archivos C en la extensión por usted y vincular cualquier biblioteca de una sola vez, por lo que, comenzando con mi setup.py, terminó luciendo así:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_modules=[
  Extension("myext",
    ["myext.pyx", "../stuff.c"],
    libraries=["ssl", "crypto"]
  )
]

setup(
  name = "myext",
  cmdclass = "build_ext": build_ext,
  ext_modules = ext_modules
)

Como puede ver, el segundo argumento de la Extensión simplemente enumera todos los archivos que deben compilarse, Cython resuelve cómo compilarlos según su extensión de archivo, por lo que puedo decir. las bibliotecas array le dice al compilador de Cython lo que debe vincularse (en este caso, estaba envolviendo algunas cosas criptográficas que parecía que no podía imitar directamente a través de las bibliotecas de Python existentes).

Para hacer que mi función C esté disponible en el archivo .pyx, escribe un pequeño contenedor en el .pxd. Mi myext.pxd tenía el siguiente aspecto:

cdef extern from "../stuff.h":
    char* myfunc(char* arg1, char* arg2, char* arg3)

En el .pyx, luego usa la declaración cimport para importar esta función, que luego está disponible para usar como si fuera cualquier otra función de Python:

cimport myext

def my_python_func(arg1, arg2, arg3):
    result = myext.myfunc(arg1, arg2, arg3)
    return result

Cuando construyes esto (al menos en Mac) obtienes un .para que puedas importar en python y ejecutar las funciones desde .pyx. Puede haber una forma mejor y más correcta de hacer que todo funcione, pero eso viene de la experiencia y este fue un primer encuentro que logré resolver. Estaría muy interesado en los punteros donde puedo haber ido mal.

Actualizar:

Después de seguir usando Cython, descubrí que era muy sencillo integrarlo también con C++, una vez que sabes lo que estás haciendo. Haciendo C++ string disponible es tan simple como from libcpp.string cimport string en tu pyx/pyd. Declarar la clase C++ es tan fácil como:

cdef extern from "MyCPPClass.h":
    cdef cppclass MyCPPClass:
        int foo;
        string bar;

Claro, básicamente tiene que volver a declarar la definición .h de su clase en un formato Pythonic, pero ese es un pequeño precio a pagar por obtener acceso a sus funciones C++ ya escritas.

Cython es principalmente para el desarrollo de C, para integrar C ++ con Python, recomendaría Boost.Python. Su excelente documentación debería ayudarlo a comenzar bastante rápido.

Te mostramos comentarios y puntuaciones

Si guardas algún incógnita o forma de medrar nuestro división puedes realizar una crítica y con mucho placer lo interpretaremos.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *