L’implémentation de référence de Python est écrite en C, et son API est exposée et bien documentée. Il est donc possible de créer des objets Python, charger un module Python ou exécuter une fonction Python depuis un code C/C++ et compiler tout ça.
Mettons que j’ai dans un fichier biblio.py
:
def yolo(arg): return arg.upper() + ' !!' |
Je peux écrire un fichier prog.c
qui l’utilise :
#include <Python.h> int main () { // PyObject est un wrapper Python autour des objets qu'on // va échanger enter le C et Python. PyObject *retour, *module, *fonction, *arguments; char *resultat; // Initialisation de l'interpréteur. A cause du GIL, on ne peut // avoir qu'une instance de celui-ci à la fois. Py_Initialize(); // Import du script. PySys_SetPath("."); // Le dossier en cours n'est pas dans le PYTHON PATH module = PyImport_ImportModule("biblio"); // Récupération de la fonction fonction = PyObject_GetAttrString(module, "yolo"); // Création d'un PyObject de type string. Py_BuildValue peut créer // tous les types de base Python. Voir : // https://docs.python.org/2/c-api/arg.html#c.Py_BuildValue arguments = Py_BuildValue("(s)", "Leroy Jenkins"); // Appel de la fonction. retour = PyEval_CallObject(fonction, arguments); // Conversion du PyObject obtenu en string C PyArg_Parse(retour, "s", &resultat); printf("Resultat: %s\n", resultat); // On ferme cet interpréteur. Py_Finalize(); return 0; } |
Là je mets du C, mais la seule vraie différence avec du C++, c’est qu’on aurait cout
au lieu de printf
.
Pour compiler tout ça, il faut les headers Python et un compilateur. Sous Ubuntu, c’est un simple :
sudo apt-get install python-dev gcc |
Et on a tout nos .h
dans /usr/include/python2.7. On gccise :
gcc -I/usr/include/python2.7 prog.c -lpython2.7 -o prog -Wall && ./prog |
Même pas un warning, c’est beau.
./prog Resultat: LEROY JENKINS !! |
C’est un exemple simple, mais comme vous le savez je suis une grosse burne en C, donc je ne pourrai pas porter l’expérience plus loin.
Je ne le recommande pas pour rendre votre programme scriptable en Python. Il vaut mieux permettre à Python d’appeler votre code C dans ce cas. Des outils comme cffi rendent cela beaucoup plus facile et rentable que tout faire tout le taff à la main. D’ailleurs si quelqu’un est chaud pour faire un tuto sur cffi…
Non, c’est plus dans le cas où vous avez un programme C, un programme Python, et votre programme C veut utiliser le programme Python sans avoir à tout réécrire. Ou pour le cas où vous avez décidé d’écrire une grosse partie de votre programme en Python pour profiter de sa productivité, mais que vous ne pouvez pas installer Python sur la machine sur laquelle vous aller installer le programme. Bon, y a PyInstaller et Nuitka pour ça également hein, donc tentez avant de tout embeder comme un bourrin.
Ça reste intéressant de voir les entrailles de CPython et à quel point il joue bien avec les langages bas niveaux.
Cool ce truc ! Est-ce que le code compilé fait appel aux libs python présentes sur le système ? En d’autres termes, est-ce que le prog fonctionne correctement si python n’est pas installé sur la machine ?
As-tu un lien pour faire la même chose en java ?
Il faut Python installé sur le système sur lequel le programme est compilé, mais pas installé. En Java, il y a http://www.jython.org/.
Cool !!
Pourquoi tu retourne 1 dans ta fonction main? C’est 0 pour dire que tout c’est bien passé ;-) et même EXIT_SUCCESS si tu veux le faire en plus mieux bien XD
Ha yes. Donc là, on peut faire un prog portable :)
@Gnukos : typo.
@Sabcat: python est portable par défaut. En plus l’interpréteur est déjà installé sur Mac et Linux. Par contre, avec la compile, tu peux rentre ton code indépendant de l’installation manuelle de la VM par l’utilisateur. Mais c’est pas le meilleur moyen de le faire. Comme signalé dans l’article, PyInstaller, Nuitka, ou juste un installeur qui installe la VM, on obtient un meilleur résultat en se faisant moins chier.
En C++, si t’as accès à Boost, tu peux aussi utiliser Boost.Python.