2010-07-20 18 views
34

Me gustaría llamar a una función personalizada que está definida en un módulo python de C. Tengo un código preliminar para hacer eso, pero solo imprime el salida a stdout.Llamando a un método python de C/C++, y extrayendo su valor de retorno

mytest.py

import math 

def myabs(x): 
    return math.fabs(x) 

test.cpp

#include <Python.h> 

int main() { 
    Py_Initialize(); 
    PyRun_SimpleString("import sys; sys.path.append('.')"); 
    PyRun_SimpleString("import mytest;"); 
    PyRun_SimpleString("print mytest.myabs(2.0)"); 
    Py_Finalize(); 

    return 0; 
} 

¿Cómo puedo extraer el valor de retorno en una doble C y utilizarlo en C?

+2

¿Ha leído esto: http: // docs.python.org/c-api/? Parece responder a tu pregunta. http://docs.python.org/c-api/number.html#PyNumber_Float parece ser lo que estás buscando. ¿Qué pasa con eso? ¿Qué más necesitas? –

+1

La pregunta realmente es cómo acceder al objeto devuelto desde 'mytest.myabs (2.0)'. Una vez que tengo un puntero, puedo convertirlo fácilmente en un flotante usando la función PyNumber_Float. – dzhelil

+1

¿Podemos ver una respuesta con un ejemplo de código y terminar con eso? –

Respuesta

51

Como se explicó anteriormente, el uso de PyRun_SimpleString parece ser una mala idea.

Definitivamente, debe utilizar los métodos proporcionados por la C-API (http://docs.python.org/c-api/).

Leer la introducción es lo primero que debe hacer para comprender cómo funciona.

En primer lugar, debe aprender sobre PyObject que es el objeto básico para la API de C. Puede representar cualquier tipo de tipos básicos de Python (cadena, float, int, ...).

Existen muchas funciones para convertir, por ejemplo, una cadena de pitón a char * o PyFloat a la doble.

En primer lugar, importar el módulo:

PyObject* myModuleString = PyString_FromString((char*)"mytest"); 
PyObject* myModule = PyImport_Import(myModuleString); 

a continuación, obtener una referencia a su función:

PyObject* myFunction = PyObject_GetAttrString(myModule,(char*)"myabs"); 
PyObject* args = PyTuple_Pack(1,PyFloat_FromDouble(2.0)); 

A continuación, obtener su resultado:

PyObject* myResult = PyObject_CallObject(myFunction, args) 

y volver a una doble :

double result = PyFloat_AsDouble(myResult); 

Obviamente, debe verificar los errores (cf. enlace dado por Mark Tolonen).

Si tiene alguna pregunta, no lo dude. Buena suerte.

+4

Aceptaré su respuesta debido a los consejos útiles, pero me hubiera sido más útil si pudiera proporcionar un módulo C completo que llame a la función anterior de python. – dzhelil

+0

Si llama al método de clase de un objeto, ¿necesita pasar ese objeto en args? –

+4

Aunque se acepta, esta respuesta lamentablemente no es una buena plantilla para llamar al código de Python (y he visto que se hace referencia en otro lugar). La línea 'PyTuple_Pack' filtra un objeto' float' cada vez que se ejecuta. La función se puede llamar sin conversión manual y creación de tuplas, con 'result = PyObject_CallFunction (myFunction," d ", 2.0)'. La importación se puede escribir como 'module = PyImport_ImportModule (" mytest ")' (sin crear una cadena de Python). – user4815162342

1

Tienes que extraer el método python de alguna manera y ejecutarlo con PyObject_CallObject(). Para hacerlo, puede proporcionar a Python una forma de establecer la función, como lo hace el ejemplo Extending and Embedding Python Tutorial.

6

Un ejemplo completo de llamar a una función de Python y recuperar el resultado se encuentra en http://docs.python.org/release/2.6.5/extending/embedding.html#pure-embedding:

#include <Python.h> 

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

    if (argc < 3) { 
     fprintf(stderr,"Usage: call pythonfile funcname [args]\n"); 
     return 1; 
    } 

    Py_Initialize(); 
    pName = PyString_FromString(argv[1]); 
    /* Error checking of pName left out */ 

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

    if (pModule != NULL) { 
     pFunc = PyObject_GetAttrString(pModule, argv[2]); 
     /* pFunc is a new reference */ 

     if (pFunc && PyCallable_Check(pFunc)) { 
      pArgs = PyTuple_New(argc - 3); 
      for (i = 0; i < argc - 3; ++i) { 
       pValue = PyInt_FromLong(atoi(argv[i + 3])); 
       if (!pValue) { 
        Py_DECREF(pArgs); 
        Py_DECREF(pModule); 
        fprintf(stderr, "Cannot convert argument\n"); 
        return 1; 
       } 
       /* pValue reference stolen here: */ 
       PyTuple_SetItem(pArgs, i, pValue); 
      } 
      pValue = PyObject_CallObject(pFunc, pArgs); 
      Py_DECREF(pArgs); 
      if (pValue != NULL) { 
       printf("Result of call: %ld\n", PyInt_AsLong(pValue)); 
       Py_DECREF(pValue); 
      } 
      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 \"%s\"\n", argv[2]); 
     } 
     Py_XDECREF(pFunc); 
     Py_DECREF(pModule); 
    } 
    else { 
     PyErr_Print(); 
     fprintf(stderr, "Failed to load \"%s\"\n", argv[1]); 
     return 1; 
    } 
    Py_Finalize(); 
    return 0; 
} 
+0

Esto funcionó en 2 minutos, ¡muchas gracias! – jhegedus

1

Si asigna el valor devuelto a una variable, entonces se puede usar algo como PyEval_GetGlobals() y PyDict_GetItemString () para obtener el PyObject. Desde allí, PyNumber_Float puede obtener el valor que desea.

Sugiero navegar por toda la API: ciertas cosas se vuelven obvias cuando ves los diferentes métodos que están disponibles para ti, y es posible que haya un método mejor que el que he descrito.

0

lo he hecho usando BOOST a Python incrustado en C++ [Este módulo C de trabajo debe ayudar a]

#include <boost/python.hpp> 

void main() 
{ 
using namespace boost::python; 
Py_Initialize(); 
PyObject* filename = PyString_FromString((char*)"memory_leak_test"); 
    PyObject* imp = PyImport_Import(filename); 
    PyObject* func = PyObject_GetAttrString(imp,(char*)"begin"); 
    PyObject* args = PyTuple_Pack(1,PyString_FromString("CacheSetup")); 
    PyObject* retured_value = PyObject_CallObject(func, args); // if you have arg 
    double retured_value = PyFloat_AsDouble(myResult); 
std::cout << result << std::endl; 
Py_Finalize(); 
} 
+0

¿Dónde está el impulso en este script? – Antonello

12

Aquí es un ejemplo de código que escribí (con la ayuda de varias fuentes en línea) para enviar una cadena a un código de Python, luego devuelve un valor.

Este es el código C call_function.c:

#include <Python.h> 
#include <stdlib.h> 
int main() 
{ 
    // Set PYTHONPATH TO working directory 
    setenv("PYTHONPATH",".",1); 

    PyObject *pName, *pModule, *pDict, *pFunc, *pValue, *presult; 


    // Initialize the Python Interpreter 
    Py_Initialize(); 


    // Build the name object 
    pName = PyString_FromString((char*)"arbName"); 

    // Load the module object 
    pModule = PyImport_Import(pName); 


    // pDict is a borrowed reference 
    pDict = PyModule_GetDict(pModule); 


    // pFunc is also a borrowed reference 
    pFunc = PyDict_GetItemString(pDict, (char*)"someFunction"); 

    if (PyCallable_Check(pFunc)) 
    { 
     pValue=Py_BuildValue("(z)",(char*)"something"); 
     PyErr_Print(); 
     printf("Let's give this a shot!\n"); 
     presult=PyObject_CallObject(pFunc,pValue); 
     PyErr_Print(); 
    } else 
    { 
     PyErr_Print(); 
    } 
    printf("Result is %d\n",PyInt_AsLong(presult)); 
    Py_DECREF(pValue); 

    // Clean up 
    Py_DECREF(pModule); 
    Py_DECREF(pName); 

    // Finish the Python Interpreter 
    Py_Finalize(); 


    return 0; 
} 

Aquí está el código Python, en el archivo de arbName.py:

def someFunction(text): 
    print 'You passed this Python program '+text+' from C! Congratulations!' 
    return 12345 

utilizo el comando gcc call_function.c -I/usr/include/python2.6 -lpython2.6 ; ./a.out para ejecutar este proceso. Estoy en redhat. Recomiendo usar PyErr_Print(); para la comprobación de errores.

+0

@ Palec- No puedo entender lo que has cambiado. ¿Puedes ayudarme a ver mi error? – kilojoules

+2

Llamar a 'a.out' sería mejor que'./A.out' porque su llamada a 'a.out' se basa en que el directorio de trabajo está en PATH, que no es una configuración muy común. También debería considerar proporcionar la opción '-o' a GCC para darle un mejor nombre al ejecutable. – Palec

+1

Ver [¿Cómo funciona la edición?] (Http://meta.stackexchange.com/a/21789/238706) en Meta SE. Si puede acceder al historial de revisión pero no pudo ver los cambios en el marcado, simplemente cambie la vista de diff a 'side-by-side markdown'. Hay botones en la parte superior de cada revisión en el historial de revisiones que cambian la vista de diferencias. Si no comprende lo que hace el marcado, consulte [ayuda de edición] (http://stackoverflow.com/editing-help). Si está editando una publicación, el editor tiene ayuda incorporada, vea el signo de interrogación naranja en la parte superior derecha del editor. – Palec

2

Para evitar que el archivo .py adicional como en las otras respuestas, sólo puede recuperar el módulo de __main__, que se crea por la primera llamada a PyRun_SimpleString:

PyObject *moduleMainString = PyString_FromString("__main__"); 
PyObject *moduleMain = PyImport_Import(moduleMainString); 

PyRun_SimpleString(
    "def mul(a, b):         \n"\ 
    " return a * b        \n"\ 
); 

PyObject *func = PyObject_GetAttrString(moduleMain, "mul"); 
PyObject *args = PyTuple_Pack(2, PyFloat_FromDouble(3.0), PyFloat_FromDouble(4.0)); 

PyObject *result = PyObject_CallObject(func, args); 

printf("mul(3,4): %.2f\n", PyFloat_AsDouble(result)); // 12 
Cuestiones relacionadas