Comment utiliser les classes C++ avec les ctypes?

je commence à peine avec ctypes et je voudrais utiliser une Classe C++ que j'ai exportée dans un fichier dll depuis python en utilisant ctypes. Alors disons que mon code C++ ressemble à quelque chose comme ceci:

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

je saurais créer un .dll fichier qui contient cette classe et ensuite charger le .fichier dll en python à l'aide de ctypes. Comment créer un objet de type MyClass et appeler sa fonction test? Est-ce possible avec les ctypes? Alternativement je considérerais utiliser SWIG ou Stimuler.Python mais ctypes semble être l'option la plus facile pour les petits projets.

45
demandé sur Deduplicator 2009-10-24 00:51:00

5 réponses

la courte histoire est qu'il n'y a pas d'interface binaire standard pour C++ de la même façon qu'il y en a pour C. différents compilateurs produisent des binaires différents pour les mêmes bibliothèques dynamiques C++, en raison de la modification des noms et des différentes façons de gérer la pile entre les appels de fonction de bibliothèque.

donc, malheureusement, il n'y a vraiment pas de moyen portable pour accéder aux bibliothèques C++en général. Mais, pour un compilateur à la fois, ce n'est pas un problème.

ce blog post possède également un bref aperçu de pourquoi cela ne fonctionne pas. Peut-être qu'après la sortie de C++0x, nous aurons un ABI standard pour C++? D'ici là, vous n'aurez probablement aucun moyen d'accéder aux classes C++ par L'intermédiaire de ctypes.

35
répondu Mark Rushakoff 2009-10-23 21:59:45

À Part Boost.Python (qui est probablement une solution plus conviviale pour les grands projets qui nécessitent une mise en correspondance des classes C++ avec les classes python), vous pouvez fournir une interface C du côté C++. C'est une solution parmi tant d'autres donc elle a ses propres compromis, mais je vais la présenter pour le bénéfice de ceux qui ne sont pas familiers avec la technique. Pour une divulgation complète, avec cette approche, on n'interfacerait pas C++ vers python, mais C++ vers C vers Python. Ci-dessous, j'ai inclus un exemple qui répond vos exigences pour vous montrer l'idée générale de la facilité externe "c" des compilateurs 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.

Vous pouvez compiler ce code avec

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

dans votre code python vous pouvez faire quelque chose comme ceci (invite interactive à partir de 2.7 montré):

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

je suis sûr Boost.Python fait quelque chose de similaire sous le capot, mais peut-être comprendre les concepts des niveaux inférieurs est utile. Je serais plus excité à propos de cette méthode si vous essayiez d'accéder la fonctionnalité D'une bibliothèque C++ et une mise en correspondance n'étaient pas nécessaires.

pour plus d'informations sur l'interaction C / C++, consultez cette page de Sun:http://dsc.sun.com/solaris/articles/mixing.html#cpp_from_c

35
répondu AudaAero 2011-08-15 02:18:24

réponse par AudaAero est très bon mais pas complet (du moins pour moi).

sur mon système (Debian Stretch x64 avec GCC et G++ 6.3.0, Python 3.5.3) j'ai segfaults dès que j'appelle une fonction membre qui accéder à une valeur membre de la classe. J'ai diagnostiqué en imprimant des valeurs de pointeur vers stdout que le pointeur vide * codé sur 64 bits dans les wrappers est représenté sur 32 bits en Python. Ainsi de gros problèmes se produisent quand il est passé à un enveloppe de la fonction membre.

la solution que j'ai trouvée est de changer:

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

il manquait donc deux choses: définir le type de retour à c_void_p (la valeur par défaut est int) et puis créer un objet c_void_p (pas seulement un entier).

j'aurais aimé pouvoir écrire un commentaire mais je manque toujours de 27 points de rep.

4
répondu Gabriel Devillers 2017-05-23 11:54:06

voici une brève explication sur la façon d'utiliser c et c++ avec C_types en python. Comment écrire une DLL / SO en C++ pour Python

0
répondu ifryed 2014-02-16 06:35:50

Extension Audaaero's et Gabriel Devillers réponse je remplirais la création d'instance de l'objet de classe par: stdc=c_void_p(cdll.LoadLibrary("libc.so.6")) en utilisant ctypesc_void_p le type de données assure la représentation correcte du pointeur d'objet de classe dans python.

assurez-vous également que la gestion de la mémoire de la dll soit gérée par la dll (la mémoire allouée dans la dll doit être désallouée aussi dans la dll, et pas en python)!

0
répondu Elektrone Motyo 2017-08-01 07:54:27