Tutorial embeber Python en C++

¿Qué es embeber?

Cuando hablamos de embeber Python o el intérprete Python, lo que estamos diciendo es que tenemos un programa escrito en otro lenguaje, en este caso c++, y queremos utilizar desde nuestro programa código en Python. La ventaja que tenemos es que podemos mezclar código compilado en C++ y código que se compila en tiempo de ejecución (runtime), así no tendremos que estar recompilándolo el núcleo de nuestro programa y en cambio podemos estar modificando y añadiendo extensiones al programa como quien edita un texto. Incluso podemos añadir funcionalidades mientras se ejecuta el programa.

Configurando nuestro IDE

Cualquier IDE sirve para comenzar a embeber Python en nuestro código C/C++ y en todos el proceso es similar. Como no puedo explicar todos, me he centrado en usar Visual Studio que es el que voy a utilizar en mis proyectos actuales. Os dejo un vídeo en el que explico cómo configurar VS y hacer nuestro primer "hola mundo" de Python embebido para comprobar que efectivamente nuestra configuración funciona.

https://www.youtube.com/watch?v=BNh24VHpnIE

Python/C API

Las santas escrituras del proceso de embeber Python en C/C++ es precisamente la llamada "Python/C API". Es una API que nos proporcionan desde la página oficial de Python y que contiene todo lo necesario, y mucho más de lo estrictamente necesario para llevar a cabo nuestros proyectos mezclando C y Python. Para echarle un vistazo puedes utilizar el siguiente link: Python/C API

Descifrando la teoría fundamental

Como cualquier manual técnico que se precie, la API puede resultar farragosa. Por eso a continuación vamos a presentar unas herramientas con las cuales podemos satisfacer la mayoría de las necesidades a la hora de embeber Python.

  • Para Inicializar y finalizar el intérprete.

//Agregamos la librería Python para usar la API #include <Python.h> int main () { /*Instanciamos un interprete que crea un __main__ Podemos pasarle argumumentos a ese __main__ pero en este caso, lo instanciamos sin pasar nada */ Py_Initialize(); //Aquí podremos usar la API /*Cerramos el interprete, podremos instanciar otro más adelante */ Py_Finalize(); return 1; }

  • Para ejecutar un script.py:

//Opcion 1. Creo que puede ser mas conflictiva FILE *script; script = fopen("script.py","r"); PyRun_SimpleFile(PyFile_AsFile(script),"r"); //Opcion 2. Mas independiente del compilador PyObject *script; script = PyFile_FromString("script.py","r");    PyRun_SimpleFile(PyFile_AsFile(script),"r");

  • Pasar texto al intérprete:

Es una comunicación muy pobre, ya que no estás pasando ninguna variable, sólo pasas texto y el intérprete, como su nombre indica, lo interpreta. Aunque para tareas fijas y repetitivas puede ser una opción muy válida.

//Pasar instrucciones como texto //En este ejemplo importamos la variable pi //Y la mostramos por pantalla PyRun_SimpleString("from math import pi"); PyRun_SimpleString("pi"); /*Como vemos, no podemos hacer nada del interprete para fuera, ni hemos enviado el valor desde nuestro codigo, tan solo hemos enviado texto*/

  • Instanciar objetos con datos que pasemos y retornar valores:

La joya de la corona, el mecanismo que sin duda es nos será más útil es pasar datos al intérprete, procesarlos de alguna manera y que nos devuelva los datos procesados. Antes de empezar, vamos a definir un script.py para que se entienda mejor el ejemplo. Nuestro script.py será el siguiente:

class Numeros: def __init__(self, num1, num2): self.num1=num1 self.num2=num2 def suma(self): print self.num1, self.num2 return self.num1+self.num2 Con el siguiente mecanismo vamos a instanciar un objeto de clase Numeros y a usar el número que nos devuelve el método suma. PyObject *FileScript; FileScript = PyFile_FromString("script.py","r"); PyRun_SimpleFile(PyFile_AsFile(FileScript),"r"); PyObject *retorno, *modulo, *clase, *metodo, *argumentos, *objeto; int *resultado; modulo = PyImport_ImportModule("script"); clase = PyObject_GetAttrString(modulo, "Numeros"); argumentos = Py_BuildValue("ii",5,11); objeto = PyEval_CallObject(clase, argumentos); metodo = PyObject_GetAttrString(objeto, "suma"); argumentos = Py_BuildValue("()"); retorno = PyEval_CallObject(metodo,argumentos); PyArg_Parse(retorno, "i", &resultado); cout<<"El resultado es: "<<int(resultado)<<endl;

Código Completo en GitHub

Os dejo el código del script.py y del main.cpp completo en el siguiente enlace: Código en GitHub ¡Espero que os haya gustado! Nos vemos en otros tutoriales de Python o Arduino.