INF 739 : Concepts avancés de programmation Interfaçage entre C++ et Python Patrick Hubert Artificial Mind & Movement Le C++ et Python Description sommaire des deux langages… Le langage C++ Conçu par Bjarne Stroustrup en 1983 • • • • • Langage compilé avec phase d’édition de liens Fortement typé Statique Performant et rapide, héritage provenant du C Gestion de la mémoire laissée au programmeur Le langage Python Conçu par Guido Van Rossum en 1991 • • • • • Langage interprété Se base sur l’interface plutôt que le type Dynamique Moins rapide, car interprété Gestion de mémoire automagique (Garbage Collector) Le développement en C++ Les structures et fonctions sont définies explicitement dans les fichiers sources Un cycle comprend habituellement les étapes suivantes: 1. Édition du code 2. Compilation des sources 3. Éditions des liens 4. Démarrage de l’application et validation 5. Arrêt de l’application et retour a 1. Le développement en Python Ce qui à été dit pour le C++ est aussi valide… Par contre, les structures et fonctions peuvent aussi être créées ou modifiées alors que l’application fonctionne Un cycle peut être réduit aux étapes suivantes: 1. Édition du code 2. Démarrage de l’application et interprétation 3. Modifications et mises-à-jour des structures existantes dynamiquement, et/ou retour à 1. Le compilateur C++ • N’est qu’une des composantes requises pour concevoir une application • L’éditeur de lien et le débogueur sont deux autres applications indépendantes requises • L’application résultante est le quatrième maillon de la chaîne L’interpréteur Python • Est à la fois compilateur, éditeur de liens, débogueur et l’application résultante • Il en existe une implémentation C et Java • On imagine souvent à tort qu’il est un outil disponible sur ligne de commande. Ce n’est là qu’une des intégrations possible L’interpréteur Python Évaluation visible à l’usager: • Interface interactive via la ligne de commande • Interprétation immédiate du code % python Python 2.5 (r25:51908, Mar 13 2007, 08:13:14) [GCC 3.4.4 (cygming special, gdc 0.12, using dmd 0.125)] on cygwin Type "help", "copyright", "credits" or "license" for more information. >>> print "Bonjour!" Bonjour! >>> L’interpréteur Python Évaluation invisible à l’usager: • Analyse syntaxique et interprétation des modules • Création dynamique des classes, fonctions, etc. % python Python 2.5 (r25:51908, Mar 13 2007, 08:13:14) [GCC 3.4.4 (cygming special, gdc 0.12, using dmd 0.125)] on cygwin Type "help", "copyright", "credits" or "license" for more information. >>> import Bibliotheque >>> print Bibliotheque.Fonction( 'Foobar' ) 6 >>> Comparaison de code Les langages offrent des bases communes et des éléments structurels similaires: • Fonctions • Classes • Bibliothèques ou modules Exemple de fonction C++ #include <string> namespace Bibliotheque { size_t Fonction( const std::string& pTexte ) { return pTexte.length(); } } // Ne fonctionne qu’avec des arguments de type // std::string Exemple de fonction Python def Fonction( pTexte ): # equivalent a len( pTexte ) return pTexte.__len__() # # # # # # # # Fonctionne aussi bien avec un paramètre de type string qu'une liste, donne '3' dans les 2 cas Fonction( "abc" ) et # Fonction( [ 1, 2, 3 ] ) '__len__()' est présent dans toutes les interfaces de séquences Python. 'template <typename tType> size_t Fonction( tType );' se résout statiquement à la compilation, au contraire de la version Python qui l'est dynamiquement Utilisation de bibliothèques en C++ #include <Bibliotheque.hpp> #include <iostream> int main() { std::string lString( "abc" ); std::cout << Bibliotheque::Fonction( lString ) << std::endl; return 0; } // S'assurer bien sur de mentionner la // Bibliothèque lors de l'édition des liens Utilisation de modules en Python import Bibliotheque print Bibliotheque.Fonction( 'abcd' ); Une classe en C++ #include <Bibliotheque.hpp> #include <string> struct Parent {}; class Enfant : public Parent { std::string mChaine; public: Enfant( const std::string& pChaine ) : mChaine( pChaine ) {} size_t Valeur() const { return Bibliotheque::Fonction( mChaine ); } }; Une classe en Python import Bibliotheque class Parent: pass class Enfant( Parent ): def __init__( self, pChaine ): self.mChaine = pChaine def Valeur( self ): return Bibliotheque.Fonction( self.mChaine ) Métissage des deux langages Il existe trois façons d’interfacer C++ et Python: 1. Écrire de nouveaux modules pour Python en C++ (pour des raisons de performances) 2. Intégrer l’interpréteur Python à même une application (pour exposer de la fonctionnalité existante, ou même en ajouter) 3. Une combinaison des deux options précédentes Quels sont les avantages? • Combiner la vitesse de C++ avec la facilité et la rapidité de développement en Python • Pour modifier le code de l’application, il faut habituellement l’arrêter. De même pour un plug-in ou une DLL/DSO • En Python, ce n’est pas le cas. La construction dynamique des objets permet une modification de ces derniers sans avoir redémarrer l’application Considérations importantes • Le langage C++ est fortement typé • L’interaction entre Python et le C++ devra tenir compte de cette contrainte et s’assurer de la compatibilités des objets et paramètres • Les outils que nous considérerons pour le travail devront offrir cette protection. La couche d’interface entre C++ et Python • L’interpréteur est implémenté en C • L’interface peut donc être en C ou C++ • Elle peut être écrite manuellement, ce qui laisse beaucoup de place à l’erreur. • Elle peut aussi être écrite a l’aide de classes utilitaires, ou bien généré à partir d’un IDL (Interface Definition Language) • Nous présenterons 3 alternatives: manuelle, Boost.Python (classes) et SWIG (générateur) Manuelle • Demande une bonne connaissance de l’interface C de Python • Les erreurs sont très faciles • Requiert beaucoup de maintenance, pour un API qui change beaucoup Manuelle (code 1/2) #include <python.h> #include <Bibliotheque.hpp> static PyObject* Fonction_imp( PyObject* pSelf, PyObject* pArgs ) { const char* lChaine = 0; if( !PyArg_ParseTuple( pArgs, "s", &lChaine )) return 0; size_t lResult = Bibliotheque::Fonction( lChaine ); return Py_BuildValue( "i", lResult ); } Manuelle (code 2/2) static PyMethodDef ManuelleMethods[] = { { "Fonction", Fonction_imp, METH_VARARGS, "" }, {0, 0, 0, 0} }; PyMODINIT_FUNC initManuelle() { Py_InitModule( "Manuelle", ManuelleMethods ); } Boost.Python • Fait partie de la bibliothèque Boost • Met à notre disposition des templates de classes pour définir les classes, méthodes et fonctions à exposer vers Python • Utilise le ‘Run-Time Type Identification’ (RTTI) pour générer les interfaces du bon type • Dépend aussi énormément des algorithmes du compilateur pour déduire les paramètres de templates. • Offre des outils pour communiquer avec les objets Python indigènes Boost.Python (code) #include <boost/python.hpp> using namespace boost::python; #include <Bibliotheque.hpp> BOOST_PYTHON_MODULE( Boost ) { def( "Fonction", Bibliotheque::Fonction ); } SWIG • ‘Simplified Wrapper and Interface Generator’ • À partir d’un fichier de définition d’interface créé par le programmeur, génère du code Python et C/C++ qui fait le lien entre les deux langages • Demande une étape de manipulation supplémentaire, pour la génération du code intermédiaire. • Est très rapide et relativement facile a utiliser, tout en offrant une série de paramétrisations SWIG (code) %module Swig %include "std_string.i" %{ #include <Bibliotheque.hpp> using namespace Bibliotheque; %} size_t Fonction( const std::string& pTexte ); Expérience personnelle Exposition de l’API d’un SDK orienté-objet en C++ en Python, le OpenRealitySDK de MotionBuilder • Correspondance 1 à 1 des deux langages dans 90% des cas. Pour certaines méthodes spéciales, telle 'operator=‘, nous nous devions de trouver des alternatives. • Nous avons choisis Boost.Python • Le code C++ de Boost.Python était généré à partir de fichiers XML décrivant les classes C++ de notre SDK. Points positifs • Accélère le cycle de développement de façon significative • Permet d’exposer une interface à l’application auprès d’un plus grand public • Python ne requiert pas un environnement de développement (MSDEV, GCC, etc.) • Permet la création de nouvelles fonctionnalités en Python exclusivement, sur lesquelles on peut bâtir un nouvel environnement pour des gens moins familiers avec la programmation Points négatifs • La gestion de la couche d’interface, Boost.Python ou bien SWIG, peut être onéreuse et ardue. Surtout si l’API à exposer est complexe et évolue encore • Trouver une façon d’automatiser la génération de la couche d’interface n’est pas facile • Les temps de compilation de la couche utilisant Boost.Python sont importants Points à surveiller • S’assurer qu’une instance d’objet C++ donnée est encapsulée par un seul objet Python. • Certaines méthodes d’objets n’ont pas de correspondance en Python, tel 'operator=‘ • En portant un SDK C++ on risque d’avoir un API Python qui laisse transparaître ses origines. • Modifier les interfaces des objets pour se conformer à ce qui se fait sur Python • Ajouter les éléments syntaxiques nécessaire pour éviter que le code Python ressemble a du C++ (conversion, etc.) Suggestions • A moins de n’avoir que quelque fonctions simples à exposer à Python, ne pas utiliser la méthode manuelle • SWIG est la solution la plus facile pour les projets de moyenne envergure • Boost.Python est excellent pour interfacer directement avec Python, en plus d’offrir de bons outils. Par contre il y a un impact important sur les temps de compilations Vos questions ? • • • • C++ Python Performances Pourquoi le ciel est-il bleu? Références • Python: http://python.org/ • Boost: http://boost.org/ • Swig: http://www.swig.org/ Merci! Commentaires et questions: [email protected]