Recién estoy comenzando con ctypes y me gustaría usar una clase de C ++ que he exportado en un archivo dll desde python usando ctypes. Entonces, digamos que mi código C ++ se ve así:

class MyClass {
  public:
    int test();
...

Sabría crear un archivo .dll que contenga esta clase y luego cargar el archivo .dll en python usando ctypes. Ahora, ¿cómo crearía un objeto de tipo MyClass y llamaría a su función de prueba? ¿Es eso posible con los ctypes? Alternativamente, consideraría usar SWIG o Boost.Python, pero ctypes parece ser la opción más fácil para proyectos pequeños.

58
jörg 24 oct. 2009 a las 00:51

5 respuestas

La mejor respuesta

La historia corta es que no hay una interfaz binaria estándar para C ++ de la misma manera que existe para C. Los diferentes compiladores generan diferentes binarios para las mismas bibliotecas dinámicas de C ++, debido a la manipulación de nombres y diferentes formas de manejar la pila entre las llamadas a funciones de la biblioteca.

Entonces, desafortunadamente, realmente no hay una forma portátil de acceder a las bibliotecas de C ++ en general . Pero, para un compilador a la vez, no hay problema.

Esta publicación de blog también tiene una breve descripción de por qué esto no funciona actualmente. ¿Quizás después de que salga C ++ 0x, tendremos un ABI estándar para C ++? Hasta entonces, es probable que no tenga ninguna forma de acceder a las clases de C ++ a través de ctypes de Python.

40
Mark Rushakoff 23 oct. 2009 a las 21:59

Extendiendo AudaAero's y Gabriel Devillers respuesta Completaría la creación de la instancia del objeto de clase: stdc=c_void_p(cdll.LoadLibrary("libc.so.6")) El uso del tipo de datos ctypes c_void_p garantiza la representación adecuada del puntero del objeto de clase dentro de Python.

¡También asegúrese de que la gestión de la memoria del dll sea manejada por el dll (la memoria asignada en el dll debe ser desasignada también en el dll, y no en python)!

1
Elektrone Motyo 1 ago. 2017 a las 07:54

Esta es una breve explicación sobre cómo usar C y C ++ con C_types en Python. Cómo escribir una DLL / SO en C ++ para Python

1
ifryed 16 feb. 2014 a las 06:35

La respuesta de AudaAero es muy buena pero no completa (al menos para mí).

En mi sistema (Debian Stretch x64 con GCC y G ++ 6.3.0, Python 3.5.3) tengo segfaults tan pronto como llamo a una función miembro que accede a un valor miembro de la clase. Diagnostiqué imprimiendo valores de puntero para mostrar que el puntero void * codificado en 64 bits en envoltorios se está representando en 32 bits en Python. Por lo tanto, ocurren grandes problemas cuando se devuelve a un contenedor de funciones miembro.

La solución que encontré es cambiar:

spam = myLib.CreateInstanceOfClass()

En

Class_ctor_wrapper = myLib.CreateInstanceOfClass
Class_ctor_wrapper.restype = c_void_p
spam = c_void_p(Class_ctor_wrapper())

Así que faltaban dos cosas: establecer el tipo de retorno en c_void_p (el valor predeterminado es int) y y luego crear un objeto c_void_p (no solo un entero).

Desearía haber escrito un comentario, pero aún me faltan 27 puntos de repetición.

13
Community 20 jun. 2020 a las 09:12

Además de Boost.Python (que probablemente sea una solución más amigable para proyectos más grandes que requieren un mapeo uno a uno de las clases de C ++ a las clases de python), podría proporcionar en el lado de C ++ una interfaz C. Es una solución de muchas, por lo que tiene sus propias compensaciones, pero la presentaré en beneficio de aquellos que no están familiarizados con la técnica. Para una divulgación completa, con este enfoque uno no estaría interconectando C ++ a Python, sino C ++ a C a Python. A continuación incluí un ejemplo que cumple con sus requisitos para mostrarle la idea general de la facilidad externa "c" de los compiladores de C ++.

//YourFile.cpp (compiled into a .dll or .so file)
#include <new> //For std::nothrow
//Either include a header defining your class, or define it here. 

extern "C"  //Tells the compile to use C-linkage for the next scope.
{
    //Note: The interface this linkage region needs to use C only.  
    void * CreateInstanceOfClass( void )
    {
        // Note: Inside the function body, I can use C++. 
        return new(std::nothrow) MyClass;
    }

    //Thanks Chris. 
    void DeleteInstanceOfClass (void *ptr)
    {
         delete(std::nothrow) ptr; 
    }

    int CallMemberTest(void *ptr)
    {

        // Note: A downside here is the lack of type safety. 
        // You could always internally(in the C++ library) save a reference to all 
        // pointers created of type MyClass and verify it is an element in that
        //structure. 
        //
        // Per comments with Andre, we should avoid throwing exceptions.  
        try
        {
            MyClass * ref = reinterpret_cast<MyClass *>(ptr);
            return ref->Test();
        }
        catch(...)
        {
           return -1; //assuming -1 is an error condition. 
        }
    }

} //End C linkage scope.

Puedes compilar este código con

gcc -shared -o test.so test.cpp
#creates test.so in your current working directory.

En su código de Python, podría hacer algo como esto (se muestra un mensaje interactivo de 2.7):

>>> from ctypes import cdll
>>> stdc=cdll.LoadLibrary("libc.so.6") # or similar to load c library
>>> stdcpp=cdll.LoadLibrary("libstdc++.so.6") # or similar to load c++ library
>>> myLib=cdll.LoadLibrary("/path/to/test.so")
>>> spam = myLib.CreateInstanceOfClass()
>>> spam
[outputs the pointer address of the element]
>>> value=CallMemberTest(spam)
[does whatever Test does to the spam reference of the object] 

Estoy seguro de que Boost.Python hace algo similar bajo el capó, pero tal vez sea útil entender los conceptos de los niveles inferiores. Estaría más entusiasmado con este método si intentara acceder a la funcionalidad de una biblioteca C ++ y no fuera necesario un mapeo uno a uno.

Para obtener más información sobre la interacción C / C ++, consulte esta página de Sun: http: // dsc.sun.com/solaris/articles/mixing.html#cpp_from_c

47
AudaAero 15 ago. 2011 a las 02:18