O sistema que estou desenvolvendo para o meu TCC terá grande parte de sua lógica implementada em Python e outra parte será implementada em C/C++, para isso preciso embeddar código Python dentro da aplicação.
Esta forma de programação é utilizada em diversas aplicações e é muito utilizada para poder gerar recursos para a criação de plugins, como é feito, por exemplo, no gedit e no gnome-panel(pelo menos na versão do gnome2, não sei na 3).
Neste post quero dar alguns exemplos simples de como podemos realizar esta tarefa.
Nos exemplos deste post estarei utilizando a versão 2.7 do Python e é necessário ter os includes e as bibliotecas compartilhadas instalados, geralmente estão no pacote de desenvolvimento do Python de sua distriuição favorita( ou a que você esta utilizando =) ).
A baixo está um exemplo bem simples, onde as entradas lidas e passadas para o interpretador python executa-las.
/*
file: pyConsole.c
*/
# include <stdio.h>
# include <python2.7/Python.h>
int main(){
char command[200];
Py_Initialize();
for(;;){
printf("-> ");
scanf("%200[^\n]%*c",command);
PyRun_SimpleString(command);
}
Py_Finalize();
return 0;
}
Para compilar o código acima o seguinte comando deve ser executado:
gcc pyConsole.c -o pyConsole -g -Wall -lpython2.7 -lm -L/usr/lib/python2.7/config
# para execução
./pyConsole
Para sair do programa podemos utilizar a função quit() do Python. Neste pequeno console podemos executar qualquer comando Python, mas não poderemos criar blocos de comandos ou classes pois, como o próprio nome sugere, a função PyRun_SimpleString executa apenas uma string passada. A função Py_Initialize inicialização o ambiente Python e a função Py_Finalize encerra a mesma, todas as variáveis alocadas dentro do PyRun_SimpleString serão desalocadas após o Py_Finalize. Bem, o que fizemos nesse primeiro exemplo foi criar um console do Python bem podrinho e isso poderia ser feito de diversas formas possíveis sem utilizar a libpython. O que queremos realmente é uma interação entre o programa C e o programa escrito em Python, como por exemplo recuperar alguma variável. Vejamos agora um exemplo mais complexo. O programa abaixo irá pegar um arquivo texto qualquer, trocar uma string por outra e salvar em um novo arquivo. A lógica será escrita em Python e a coleta dos dados e impressão na tela será feito em C. A função em Python ira receber os parâmetros, abrir o arquivo especificado substituir o texto, salvar em outro arquivo e retornar o texto substituído.
# file: trocar_autor.py
def trocar(entrada,saida,exp,rep):
texto = open(entrada,"r").read()
texto_saida = texto.replace(exp,rep)
arquivo_saida = open(saida,"w")
arquivo_saida.write(texto_saida)
return texto_saida
/*
file: trocar_autor.c
*/
# include <stdio.h>
# include <python2.7/Python.h>
int main(){
PyObject *main_module, *dict;
PyObject *trocar_autor, *resultado;
char expressao[200];
char substituto[200];
char entrada[200];
char saida[200];
char *c_resultado;
FILE *python;
printf("Digite o nome da variavel\n");
scanf("%200[^\n]%*c",expressao);
printf("Digite o nome do valor\n");
scanf("%200[^\n]%*c",substituto);
printf("Arquivo de entrada\n");
scanf("%200[^\n]%*c",entrada);
printf("Arquivo de saida\n");
scanf("%200[^\n]%*c",saida);
Py_Initialize();
main_module = PyImport_AddModule("__main__");
dict = PyModule_GetDict(main_module);
python = fopen("trocar_autor.py", "r");
PyRun_SimpleFile(python, "trocar_autor.py");
trocar_autor = PyDict_GetItemString(dict, "trocar");
resultado = PyObject_CallFunction(trocar_autor,"ssss",entrada,saida,expressao,substituto);
c_resultado = PyString_AsString(resultado);
Py_Finalize();
printf("--Texto Formatado--\n");
printf("%s\n",c_resultado);
return 0;
}
Vamos a algumas explicações.
Diferente do primeiro exemplo, aqui não passamos uma string com o código, mas executamos um arquivo com o código( função PyRun_SimpleFile ), quando essa função é executada a função trocar é declarada. Para a função python ser executada precisamos ter a referencia da mesma, para isso precisamos ter o dicionário de declarações do escopo e as funções PyImport_AddModule e PyModule_GetDict realizam está tarefa. Com o dicionário pegamos a referencia da função através da função PyDict_GetItemString. Tendo a referencia da função, utilizamos PyObject_CallFunction para passar os parâmetros e recuperar o resultado. O resultado é retornado como uma estrutura PyObject. E por fim, para printarmos o resultado através do printf temos que converter o PyObject para um string através da função PyString_AsString.
Há muito mais do que foi mostrado aqui esta é uma pequena introdução.
Os códigos utilizados neste post estão no repositório do blog no github e a documentação da api pode ser encontrado aqui.