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 fonction 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
print("You start with Python Code")
print(MyModule.exemple("Hello"))

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

# Récupération des docstrings des fonctions
print("Doc: " + MyModule.exemple.__doc__)
print("Doc: " + MyModule.exemple2.__doc__)
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éfinie les méthodes exposées par le module : il y en a deux, appelées exemple et exemple2. Notez aussi qu'une instance, correspondante à l'exception pouvant être levée par exemple2, 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.

 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 
#include <Python.h>

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

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

static PyObject* exempleException;

static PyObject* exemple(PyObject* self, PyObject* args) {
    const char* chaine;
    if(!PyArg_ParseTuple(args, "s", &chaine)) return NULL;
    printf( "C/C++ code receive %s \n", chaine );

    return PyLong_FromLong(0);
}

static PyObject* exemple2(PyObject* self, PyObject* args) {
    printf( "C/C++ code fire an exception\n" );
    // Levée de l'exception
    PyErr_SetString(exempleException, "Exemple de levée d'erreur en C");
    // Donc pas de retour particulier
    return NULL;
}


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

// Tableau des fonctions exposées, avec la documentation.
static PyMethodDef functions[] = {
    {"exemple", exemple, METH_VARARGS, "Une fonction"},
    {"exemple2", 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
    exempleException = PyErr_NewException( "MyModule.error", NULL, NULL );
    Py_INCREF(exempleException);
    PyModule_AddObject(module, "error", exempleException);

    // On doit renvoyer le module nouvellement créé
    return module;
}
Implémentation 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 traditionnement appelé Setup.py et contient notamment la liste des fichiers C à compiler. En voici son 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 lancez 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
$>