INF 739 : Concepts Avancés de Programmation - h-deb

publicité
Interfaçage entre C++ et Python
Patrick Hubert
Chef Technique @ Ubisoft
2014-03-20
Titre alternatif
Accélérer la boucle de rétroaction par l'utilisation
d'un langage interprété dans le contexte d'une
application écrite avec un langage compilé.
Mon expérience

Études à l’Université de Sherbrooke (1er cycle)
 Math-Info (où j’ai rencontré Patrice)

Développement logiciel
 Discreet Logic / Alias / Autodesk

Développement de moteurs de jeux vidéo
 Artificial Mind & Movement / Behaviour

Développement Web
 Whisky Echo Bravo

Développement de composantes réseaux pour des jeux
 Quazal / Ubisoft
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, descendant du langage ‘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 / Ref Counting)
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 (‘First-class objects’)
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 (le premier étant le code source)

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 (CPython), Java
(Jython) et C#/.NET (IronPython)

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 introspection et
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 (interface vs
type statique)
 Les outils que nous considérerons pour le travail
devront offrir cette protection. (e.g. conversion
automatique, validation, émission d’erreurs, etc.)

La couche d’interface entre C++ et
Python
L’interpréteur est implémenté en 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 templates) et SWIG
(générateur)

Manuelle
Demande une bonne connaissance de l’interface
C de Python
 Peu ou pas de validation, donc possibilité
d’erreurs
 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 à 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 (Alias)
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 utilisé Boost.Python
 Le code C++ de Boost.Python était généré à partir
de fichiers XML décrivant les classes C++ de notre
SDK
Expérience personnelle (Behaviour)
Ré-écriture en C++ de modules jugés peu performants
dans le contexte d’une application en Python.
 En utilisant du profilage, des modules Python peu
performants ont été identifiés.
 Ces derniers ont été recodé en C++ et nous avons
observé des gains significatifs de performance.
 Nous avons utilisé Boost.Python et Swig.
Expérience personnelle (Quazal)
Ajout de modules écrits en Python à un programme
serveur en C++.
 Permet de facilement d’ajouter et de tester de
nouveaux services sur le serveur.
 Les services peuvent être activés et désactivé sans
avoir à redémarrer le serveur.
 Nous avons utilisé Swig.
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, XCode, 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
 Jeux vidéos
 Consoles

Références
Python:
http://python.org/
 Boost:
http://boost.org/
 Swig:
http://www.swig.org/

Commentaires et questions:
[email protected]
Téléchargement