Rodando Código Python Dentro de Uma Aplicação C

07 May 2011 . category: article . Comments
#c #cpp #python

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.

Codificando

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.

Conclusões

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.


Me

Tenho estudado esse mundo mágico da programação desde 2005. Já consegui sustentar minha família usando Ruby, Java, Python, C++ e Javascript. O resto tenho usado para diversão ou aprendizado.