Question

Je commence tout juste à utiliser ctypes et voudrais utiliser une classe C ++ que j'ai exportée dans un fichier dll à partir de python à l'aide de ctypes. Disons donc que mon code C ++ ressemble à ceci:

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

Je saurais créer un fichier .dll contenant cette classe, puis charger le fichier .dll en python à l'aide de ctypes. Maintenant, comment pourrais-je créer un objet de type MyClass et appeler sa fonction de test? Est-ce même possible avec des types? Sinon, je pourrais envisager d’utiliser SWIG ou Boost.Python, mais ctypes semble être l’option la plus simple pour les petits projets.

Était-ce utile?

La solution

La nouvelle est qu’il n’existe pas d’interface binaire standard pour C ++, contrairement à C. Pour différents compilateurs, différents binaires sont générés pour les mêmes bibliothèques dynamiques C ++, en raison du changement de nom et de différentes manières de gérer la pile entre bibliothèques. appels de fonction.

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

Cet article de blog donne également un bref aperçu des raisons pour lesquelles cela ne fonctionne pas actuellement. 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 ++ via les ctypes de Python.

Autres conseils

Outre Boost.Python (qui constitue probablement une solution plus conviviale pour les projets plus volumineux nécessitant un mappage un à un des classes C ++ en classes python), vous pouvez fournir du côté C ++ une interface en C. C’est une solution parmi tant d’autres, elle a donc ses propres compromis, mais je la présenterai au profit de ceux qui ne sont pas familiers avec la technique. Pour une divulgation complète, avec cette approche, on n’interfacerait pas C ++ avec Python, mais entre C ++ et C vers Python. Ci-dessous, j’ai inclus un exemple qui répond à vos besoins pour vous montrer l’idée générale de l’externe "& c;". installation 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 de 2.7 affichée):

>>> 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 que 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 enthousiaste à propos de cette méthode si vous tentiez d'accéder aux fonctionnalités d'une bibliothèque C ++ et qu'un mappage un à un n'était pas nécessaire.

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

La réponse de AudaAero est très bonne mais pas complète (du moins pour moi).

Sur mon système (Debian Stretch x64 avec GCC et G ++ 6.3.0, Python 3.5.3), j’ai des erreurs de segmentation dès que j’appelle une fonction membre qui accède à une valeur membre de la classe. J'ai diagnostiqué en imprimant sur stdout les valeurs de pointeur indiquant que le pointeur void * codé sur 64 bits dans des wrappers était représenté sur 32 bits en Python. Cela pose donc de gros problèmes lorsqu’il est renvoyé à un wrapper de fonctions membres.

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

spam = myLib.CreateInstanceOfClass()

Dans

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 sur c_void_p (la valeur par défaut est int) et , puis créer un objet c_void_p (et pas uniquement un entier).

J'aurais aimé pouvoir écrire un commentaire, mais il me manque encore 27 points de repère.

Ceci est 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

Extension de de AudaAero et de Gabriel Devillers répond Je terminerais la création d'instance d'objet de classe par: stdc = c_void_p (cdll.LoadLibrary (" libc.so.6 "))) L'utilisation de ctypes c_void_p garantit 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 est gérée par la DLL (la mémoire allouée dans la DLL doit être désallouée également dans la DLL, et non en python)!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top