2012-01-19 6 views
19

Tengo una clase de C++. Está compuesto por un archivo .ccp y un archivo .h. Compila (puedo escribir un método principal que lo use con éxito en C++). ¿Cómo envuelvo esta clase con Cython para que esté disponible en Python?¿Cómo envuelvo una clase de C++ con Cython?

He leído los documentos y no los sigo. Hablan de generar el archivo cpp. Cuando traté de seguir los documentos, mi cpp ya existente se desvanece ...

¿Qué debo poner en el archivo pyx? Me han dicho la definición de clase, pero ¿cuánto? ¿Solo los métodos públicos?

¿Necesito un archivo .pxd? No entiendo cuándo se requiere o no este archivo.

He intentado hacer estas preguntas en el canal #python IRC y no puedo obtener una respuesta.

+0

Si su pregunta ha sido respondida, por favor marque esta pregunta como respondida. De lo contrario, por favor especifique su pregunta una vez más. –

+0

@NiklasR Tu respuesta cubrió algunas de mis áreas de confusión pero no me ayudó a llegar al final. Le di un voto positivo, pero tenía la intención de escribir una respuesta completa de los otros aspectos que resolví cuando tengo tiempo libre. – Endophage

Respuesta

6

Así que después de mucho hurgar, probar y error, gritar y arrancarme el pelo, finalmente conseguí que esto funcionara. Sin embargo, primero tuve que volver a escribir mi C++ en C, lo que para mí realmente implicaba la conversión de todas mis variables std::string al char* y el seguimiento de algunas longitudes.

Una vez hecho tuve mis archivos .h y .c. Quería hacer una sola función desde el código C disponible en Python. Resulta que Cython puede compilar tus archivos C en la extensión y vincular todas las bibliotecas de una sola vez, comenzando con mi configuración.py, terminó con este aspecto:

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 se puede ver, el segundo argumento de la extensión se limita a enumerar todos los archivos que necesitan ser compilado, Cython funciona como compilar ellos en función de su extensión de archivo Por lo que yo puedo decir. La matriz de bibliotecas le dice al compilador de Cython a qué se debe vincular (en este caso, estaba envolviendo algunas cosas de criptografía que no podía imitar directamente a través de las libs de Python existentes).

Para hacer realidad mi función C disponible en el archivo .pyx, se escribe un pequeño envoltorio en el .pxd. Mi myext.pxd veía como a continuación:

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

En el .pyx a continuación, utiliza la declaración cimport importar esta función, que está entonces disponible para su uso como si se tratara de 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 .so que puedes importar en python y ejecutar las funciones desde el .pyx. Puede haber una forma mejor y más correcta de hacer que todo funcione, pero eso viene de la experiencia y este fue el primer encuentro que logré resolver. Me interesarían mucho los punteros en los que podría haberme equivocado.

Actualización:

Después de seguir utilizando Cython, encontré que era muy fácil de integrar con C++ también, una vez que sabes lo que estás haciendo. Hacer disponible en C++ string es tan simple como from libcpp.string cimport string en su pyx/pyd. La declaración de la clase C++ es tan fácil como:

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

Claro que tiene que redeclare básicamente la definición .h de la clase en un formato Pythonic, pero eso es un pequeño precio a pagar por tener acceso a las funciones que ya se han escrito en C++ .

+0

¡Por favor, ayúdenme! Realmente me gustaría usar Python 3.3 con Cython y C++, ¿podría tener algunos archivos de ejemplo? Tengo los últimos Cython y Python instalados. – PascalVKooten

+0

No tengo ningún archivo de ejemplo para Python 3, sin embargo, asegúrese de que está configurando correctamente las banderas del compilador http://wiki.cython.org/FAQ#WhatPythonversionsdoesCythonsupport.3F Haga una pregunta aquí en SO con más problemas específicos que estás enfrentando Si lo vincula aquí en los comentarios puedo echarle un vistazo. – Endophage

+0

No estoy seguro si esto será particularmente útil como ejemplo, pero escribí [este contenedor de Python 3.3 para una biblioteca C++] (https://github.com/wmayner/pyemd). – Will

3

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.

+0

Además de Boost.Python, Shinboken (del proyecto PySide) y SIP (del proyecto PyQT) y creo que SWIG también puede envolver objetos C++. En teoría, podría envolver el código C++ con Cython, pero tendría que usar nombres mutilados y hacer muchas cosas a mano, no lo recomendaría yo mismo. – synthesizerpatel

+0

Como tengo * muy * aprecio por Boost y la calidad de muchas de sus bibliotecas, generalmente es mi primera opción cuando se me da una lista de opciones. En cuanto a las opciones que presentó: PySide es relativamente nuevo y, en lo que a mí respecta, no ha sido probado. SIP tiene una licencia GPL que puede disuadir a algunas personas. He tenido alguna experiencia desagradable con SWIG, por lo que no puedo recomendarlo. Como siempre, YMMV, pero en mi opinión, difícilmente puedes equivocarte con Boost. – spatz

+0

@spatz Lo siento, pero no puedo aceptar una excelente documentación para Boost.Python, o incluso para Boost en general. He encontrado que su documentación simplemente es engañosa o falta por completo en el pasado. Estuve trabajando con sus bibliotecas de red/socket hace un par de meses y fue una gran cantidad de prueba y error. – Endophage

14

Incluso Cython es en general para su uso con C, que puede generar C++ código, también. Al compilar, agrega la bandera --cplus.

Ahora, crear un contenedor para la clase es simple y no muy diferente de envolver una estructura. Principalmente difiere de declarar el extern, pero eso no es mucha diferencia.

Supongamos que tiene 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 la palabra clave new sólo está disponible cuando se establece el indicador --cplus, de lo contrario usar malloc de <stdlib.h> por externing ella.

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 el colisionamiento del espacio de nombres. Imagine que le gustaría ponerle el nombre Python-wrapper como la clase C++. Simplemente ponga en su archivo .pxd las declaraciones extern y cimport el archivo pxd en el .pyx.

cimport my_pxd 
cdef my_pxd.SomeExternedType obj 

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

+1

Si tienes más preguntas, no te molestes en preguntar. :) –

+0

Así que mi C++ utiliza la funcionalidad de OpenSSL. ¿Son esas funciones lo que necesito declarar en mi pxd? – Endophage

+0

Además, puedo convertir el C++ en C, pero eso realmente no me ha ayudado a seguir adelante con el problema ... – Endophage

Cuestiones relacionadas