Rechercher
 

Améliorations / Corrections

Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.

Emplacement :

Description des améliorations :

Appel de code natif C/C++ en Python

Dans ce tuto nous allons voir comment invoquer des fonctions codées en C dans un programme Python. Ces fonctions vont être associées à un module que nous nommerons « MyModule »

Le code Python invoquant les fonctions natives

Voici le code Python qui importe ce module et invoque les fonctions natives : notez bien que la seconde fonction déclenche une exception, d'où la présence du try / except.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
#!/usr/bin/python3

import MyModule

# Appel de la première fonction native (example).
print("You start with Python Code")
print(MyModule.example("Hello"))

# Appel de la seconde fonction native levant une exception (example2).
try:
    print(MyModule.example2())
except BaseException as e: 
    print("Trop fort ce Python :", str(e))

# Récupération des docstrings des fonctions
print("Doc:", MyModule.example.__doc__)
print("Doc:", MyModule.example2.__doc__)
Fichier UseModule.py : il contient le code Python utilisant le module natif


Implémentation des fonctions natives

Il est maintenant temps d'implémenter le module en C. Le fichier de code suivant est découpé en deux parties. La première partie définit les méthodes exposées par le module : il y en a deux, appelées example et example2. Notez aussi qu'une instance, correspondante à l'exception pouvant être levée par example2, est définie dans cette première partie.

La seconde partie, correspond à la déclaration et l'initialisation de notre module natif : c'est une partie technique qui doit contenir les éléments suivants :

Voici le code C de notre module (fichier MyModule.cpp).

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
 55 
 56 
 57 
 58 
 59 
 60 
 61 
 62 
 63 
 64 
 65 
 66 
 67 
 68 
 69 
 70 
 71 
 72 
 73 
 74 
 75 
 76 
 77 
 78 
 79 
/*
 * Ce fichier permet d'accèder à l'environnement Python en cours d'exécution
 * à partir de votre programme C. Il fournit aussi les différentes fonctions
 * permettant d'adapter les données entre les deux langages Python <-> C
 */
#include <Python.h>

/*
 * Une fois importe, le module definit deux fonctions :
 *   - example qui prend une chaine en argument et retourne 0
 *   - example2 qui leve une exception creee pour l'occasion
 */

// --- Les fonctions exposées par votre module ---

static PyObject* exampleException;


static PyObject* example(PyObject* self, PyObject* args) {
    // Les types Python ne sont pas directement exploitables en C. Par exemple, 
    // un caractère en Python utilise deux octets alors qu'en C, il en occupe
    // qu'un seul. Il faut donc convertir les paramètres Python au format C. 
    // C'est l'objectif de PyArg_ParseTuple !
    const char* c_string;
    if ( ! PyArg_ParseTuple(args, "s", &c_string) ) return NULL;
   
    // On peut maintenant utiliser la chaîne C issue du paramètre Python.
    printf( "C/C++ code receive %s \n", c_string );

    // On renvoie la valeur 0 convertie au format Python.
    return PyLong_FromLong( 0 );
}


static PyObject* example2(PyObject* self, PyObject* args) {
    printf( "C/C++ code fire an exception\n" );
    
    // Levée d'une exception Python : elle sera rattrapée en Python.
    PyErr_SetString(exampleException, "Exemple de levée d'erreur en C");
    
    // On ne renvoie donc pas de valeur de retour particulière.
    return NULL;
}


// --- Partie configuration du module ---


// Tableau des fonctions exposées, avec la documentation.
static PyMethodDef functions[] = {
    {"example", exemple, METH_VARARGS, "Une fonction"},
    {"example2", exemple2, METH_VARARGS, "Une fonction levant une exception"},
    {NULL, NULL, 0, NULL}
};

// Données de définition du module
static struct PyModuleDef myModule = {
    PyModuleDef_HEAD_INIT,
    "MyModule",   /* nom du module */
    NULL,         /* documentation du module, ou NULL si non proposée */
    -1,           /* -1 => état du module stocké en global */
    functions      /* Tableau des fonctions exposées */
};


// Initialisation du module
PyMODINIT_FUNC PyInit_MyModule(void) {
    // Création du module
    PyObject * module = PyModule_Create( &myModule );
    if ( module == NULL ) return NULL;

    // Création d'une instance d'exception
    exampleException = PyErr_NewException( "MyModule.error", NULL, NULL );
    Py_INCREF( exampleException );
    PyModule_AddObject( module, "error", exampleException );

    // On doit renvoyer le module nouvellement créé
    return module;
}
Fichier MyModule.cpp : il contient l'implémentation C des fonctions natives

Compilation de la partie native

Il faut maintenant compiler la partie de code native (en langage C). Et là j'imagine que vous commencez à paniquer. Il ne faut pas ! Trois étapes simples sont requises pour compiler notre module.

  1. Installez un compilateur C sur votre machine. Sur Linux/Unix/Mac, je vous propose d'utiliser GCC : je vous laisse le soin de procéder à l'installation (si nécessaire) en utilisant le gestionnaire de paquets de votre système. Sous Windows, vous pouvez soit utiliser Visual Studio, soit utiliser MinGW-64 (un portage de GCC pour Windows) : à votre convenance.

  2. Python fournit un outil de « build » pour vos modules natifs : le module distutils. Il vous permet de vous affranchir du lancement du compilateur et de la connaissance des options de compilation à fournir au compilateur :-). Pour l'utiliser, vous devrez fournir un module Python de compilation. Il est traditionnellement appelé Setup.py et contient notamment la liste des fichiers C à compiler. En voici le contenu.

  3.  1 
     2 
     3 
     4 
     5 
     6 
     7 
     8 
     9 
     10 
    
    #!/usr/bin/python3
    
    from distutils.core import setup
    from distutils.core import Extension
    
    setup(
        name = 'MyModule',
        version = '1.0',
        ext_modules = [Extension('MyModule', ['MyModule.c']), ],
    )
    
    Fichier Setup.py permettant la compilation de votre module natif
  4. Dernière étape, il faut lancer la compilation. Veuillez saisir la ligne de commande suivante : normalement, le module compilé doit être produit.

$> python3 Setup.py build_ext --inplace

Test du code

Voici les résultats produits par notre exemple.

$> python3 UseModule.py 
You start with Python Code
C/C++ code receive Hello 
0
C/C++ code fire an exception
Trop fort ce Python : Exemple de levée d'erreur en C
Doc: Une fonction
Doc: Une fonction levant une exception
$>