webentwicklung-frage-antwort-db.com.de

Wie verwende ich C++ - Klassen mit ctypes?

Ich beginne gerade mit ctypes und möchte eine C++ - Klasse verwenden, die ich in eine dll-Datei aus Python mithilfe von ctypes .. _ exportiert habe. Mein C++ - Code sieht also etwa so aus:

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

Ich würde wissen, erstellen Sie eine DLL-Datei, die diese Klasse enthält, und laden Sie dann die DLL-Datei in Python mit ctypes . Wie würde ich ein Objekt vom Typ MyClass erstellen und seine Testfunktion aufrufen? Ist das überhaupt mit ctypes möglich? Alternativ würde ich erwägen, SWIG oder Boost.Python zu verwenden, aber ctypes scheint die einfachste Option für kleine Projekte zu sein.

46
jörg

Die Kurzgeschichte ist, dass es keine standardmäßige binäre Schnittstelle für C++ gibt, wie bei C. Unterschiedliche Compiler geben unterschiedliche Binärdateien für die gleichen dynamischen C++ - Bibliotheken aus. Dies liegt an der Namensveränderung und der unterschiedlichen Verarbeitung des Stacks zwischen Bibliotheksfunktionsaufrufen.

Leider gibt es leider keine tragbare Möglichkeit, auf C++ - Bibliotheken allgemein zuzugreifen. Für einen Compiler ist dies jedoch kein Problem. 

Dieser Blogbeitrag hat auch einen kurzen Überblick darüber, warum dies derzeit nicht funktioniert. Vielleicht haben wir nach dem Erscheinen von C++ 0x ein Standard-ABI für C++? Bis dahin haben Sie wahrscheinlich keine Möglichkeit, über Pythons ctypes auf C++ - Klassen zuzugreifen.

36
Mark Rushakoff

Neben Boost.Python (die wahrscheinlich eine freundlichere Lösung für größere Projekte ist, für die eine 1: 1-Zuordnung von C++ - Klassen zu Python-Klassen erforderlich ist), könnten Sie auf C++ - Seite eine C-Schnittstelle bereitstellen. Es ist eine Lösung von vielen, also hat es seine eigenen Kompromisse, aber ich werde sie zum Nutzen derjenigen vorstellen, die sich mit der Technik nicht auskennen. Für eine vollständige Offenlegung würde man bei diesem Ansatz nicht C++ mit Python verbinden, sondern C++ mit C zu Python. Im Folgenden habe ich ein Beispiel eingefügt, das Ihren Anforderungen entspricht, um Ihnen die allgemeine Vorstellung der externen "c" - Funktion von C++ - Compilern zu zeigen. 

//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.

Sie können diesen Code mit kompilieren 

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

In Ihrem Python-Code können Sie Folgendes tun (interaktives Prompt aus 2.7 gezeigt):

>>> 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] 

Ich bin sicher, dass Boost.Python etwas Ähnliches unter der Haube tut, aber vielleicht ist es hilfreich, die Konzepte der unteren Ebenen zu verstehen. Ich würde mich mehr über diese Methode freuen, wenn Sie versuchen würden, auf die Funktionalität einer C++ - Bibliothek zuzugreifen, und eine 1: 1-Zuordnung nicht erforderlich war. 

Weitere Informationen zur C/C++ - Interaktion finden Sie auf dieser Seite von Sun: http://dsc.Sun.com/solaris/articles/mixing.html#cpp_from_c

37
AudaAero

Die Antwort von AudaAero ist sehr gut, aber nicht vollständig (zumindest für mich).

Auf meinem System (Debian Stretch x64 mit GCC und G ++ 6.3.0, Python 3.5.3) habe ich segfaults, sobald ich eine Member-Funktion aufgerufen habe, die Zugriff auf einen Member-Wert der Klasse . I durch Drucken von Zeigerwerten auf stdout diagnostiziert, dass der auf 64-Bit-Wrapper kodierte leere Zeiger auf 32-Bit in Python dargestellt wird. Daher treten große Probleme auf, wenn sie an einen Member-Funktions-Wrapper zurückgegeben werden.

Die Lösung, die ich gefunden habe, ist zu ändern:

spam = myLib.CreateInstanceOfClass()

In

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

Es fehlten also zwei Dinge: Setzen Sie den Rückgabetyp auf c_void_p (der Standardwert ist int) und und erstellen Sie dann ein c_void_p-Objekt (nicht nur eine Ganzzahl).

Ich wünschte, ich hätte einen Kommentar schreiben können, aber mir fehlen immer noch 27 Wiederholungspunkte.

5

Dies ist eine kurze Erklärung zur Verwendung von c und c ++ mit C_types in Python So schreiben Sie eine DLL/SO in C++ für Python

0
ifryed

Erweitern von AudaAeros und Gabriel Devillers answer Ich würde die Erstellung der Klassenobjektinstanz folgendermaßen abschließen: stdc=c_void_p(cdll.LoadLibrary("libc.so.6")) Mit ctypesc_void_p wird sichergestellt, dass der Klassenobjektzeiger in Python richtig dargestellt wird.

Stellen Sie außerdem sicher, dass die Speicherverwaltung der DLL von der DLL verwaltet wird (zugewiesener Speicher in der DLL sollte auch in der DLL freigegeben werden und nicht in Python)!

0
Elektrone Motyo