2008-10-18 17 views
11

Mientras trabajaba en un proyecto de C++, estaba buscando una biblioteca de terceros para algo que no es mi actividad principal. Encontré una biblioteca realmente buena, haciendo exactamente lo que se necesita, pero está escrita en Python. Decidí experimentar con la incrustación de código Python en C++, utilizando la biblioteca Boost.Python.Python incrustado en CPP: cómo recuperar los datos a CPP

código

El C++ es como la siguiente:

#include <string> 
#include <iostream> 
#include <boost/python.hpp> 

using namespace boost::python; 

int main(int, char **) 
{ 
    Py_Initialize(); 

    try 
    { 
     object module((handle<>(borrowed(PyImport_AddModule("__main__"))))); 

     object name_space = module.attr("__dict__"); 
     object ignored = exec("from myModule import MyFunc\n" 
          "MyFunc(\"some_arg\")\n", 
          name_space); 

     std::string res = extract<std::string>(name_space["result"]); 
    } 
    catch (error_already_set) 
    { 
     PyErr_Print(); 
    } 

    Py_Finalize(); 
    return 0; 
} 

A (muy) versión simplificada del código Python se parece a esto:

import thirdparty 

def MyFunc(some_arg): 
    result = thirdparty.go() 
    print result 

Ahora el problema es el siguiente: 'MyFunc' ejecuta bien, puedo ver la impresión de 'resultado'. Lo que no puedo hacer es leer 'resultado' desde el código C++. El comando extraer nunca encuentra 'resultado' en ningún espacio de nombres. Intenté definir 'resultado' como global, incluso intenté devolver una tupla, pero no puedo hacer que funcione.

Respuesta

6

Antes que nada, cambie su función a return el valor. print Esto complicará las cosas ya que desea recuperar el valor. Suponga que su MyModule.py se ve así:

import thirdparty 

def MyFunc(some_arg): 
    result = thirdparty.go() 
    return result 

Ahora bien, para hacer lo que quiera, tiene que ir más allá de la incrustación de base, según documentation says. Aquí está el código completo para ejecutar su función:

#include <Python.h> 

int 
main(int argc, char *argv[]) 
{ 
    PyObject *pName, *pModule, *pFunc; 
    PyObject *pArgs, *pArg, *pResult; 
    int i; 

    Py_Initialize(); 
    pName = PyString_FromString("MyModule.py"); 
    /* Error checking of pName left out as exercise */ 

    pModule = PyImport_Import(pName); 
    Py_DECREF(pName); 

    if (pModule != NULL) { 
     pFunc = PyObject_GetAttrString(pModule, "MyFunc"); 
     /* pFunc is a new reference */ 

     if (pFunc) { 
      pArgs = PyTuple_New(0); 
      pArg = PyString_FromString("some parameter") 
      /* pArg reference stolen here: */ 
      PyTuple_SetItem(pArgs, 0, pArg); 
      pResult = PyObject_CallObject(pFunc, pArgs); 
      Py_DECREF(pArgs); 
      if (pResult != NULL) { 
       printf("Result of call: %s\n", PyString_AsString(pResult)); 
       Py_DECREF(pResult); 
      } 
      else { 
       Py_DECREF(pFunc); 
       Py_DECREF(pModule); 
       PyErr_Print(); 
       fprintf(stderr,"Call failed\n"); 
       return 1; 
      } 
     } 
     else { 
      if (PyErr_Occurred()) 
       PyErr_Print(); 
      fprintf(stderr, "Cannot find function"); 
     } 
     Py_XDECREF(pFunc); 
     Py_DECREF(pModule); 
    } 
    else { 
     PyErr_Print(); 
     fprintf(stderr, "Failed to load module"); 
     return 1; 
    } 
    Py_Finalize(); 
    return 0; 
} 
+1

respuesta mucho más amplia que la mía, a partir de (supongo) un padre compañero :) nosklo, me sugerimos que amplíe su respuesta con un ejemplo PyRun_String; permite más flexibilidad. – tzot

+0

Creo que su 'pArgs = PyTuple_New (0);' debería pasar un 1 no 0. –

0

Debería poder devolver el resultado de MyFunc, que luego terminaría en la variable que está llamando "ignorado". Esto elimina la necesidad de acceder a él de otra manera.

+0

No importa lo que se devuelve desde el MyFunc, me sale: TypeError: objeto 'NoneType' no admite la asignación elemento –

1

Creo que lo que necesita es PyObject_CallObject(<py function>, <args>), que devuelve el valor de retorno de la función que llama como un PyObject, o PyRun_String(<expression>, Py_eval_input, <globals>, <locals>) que evalúa una sola expresión y devuelve su resultado.

3

Basado en ΤΖΩΤΖΙΟΥ, Josh y respuestas de Nosklo Finalmente llegué funciona usando Boost.Python:

Python:

import thirdparty 

def MyFunc(some_arg): 
    result = thirdparty.go() 
    return result 

C++:

#include <string> 
#include <iostream> 
#include <boost/python.hpp> 

using namespace boost::python; 

int main(int, char **) 
{ 
    Py_Initialize(); 

    try 
    { 
     object module = import("__main__"); 
     object name_space = module.attr("__dict__"); 
     exec_file("MyModule.py", name_space, name_space); 

     object MyFunc = name_space["MyFunc"]; 
     object result = MyFunc("some_args"); 

     // result is a dictionary 
     std::string val = extract<std::string>(result["val"]); 
    } 
    catch (error_already_set) 
    { 
     PyErr_Print(); 
    } 

    Py_Finalize(); 
    return 0; 
} 

Algunos puntos importantes:

  1. He cambiado 'exec' a 'exec_file' de conveniencia, también funciona con plain 'exec'.
  2. La razón principal es que no se i no pasaron un name_sapce "local" a 'exec_file' 'ejecutivo' o - esto es ahora fijo pasando NAME_SPACE dos veces.
  3. Si la función devuelve pitón cadenas Unicode, no son convertibles en 'std :: string', por lo que tuvo que sufijo todas las cadenas pitón con '.encode ('ASCII', 'ignorar')'.