web-dev-qa-db-fra.com

Appeler une méthode python à partir de C/C++ et extraire sa valeur de retour

J'aimerais appeler une fonction personnalisée définie dans un module python à partir de C. J'ai un code préliminaire pour le faire, mais il affiche simplement la sortie sur stdout.

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;
}

Comment puis-je extraire la valeur de retour dans un C double et l'utiliser en C?

41
dzhelil

Comme expliqué précédemment, utiliser PyRun_SimpleString semble être une mauvaise idée.

Vous devez absolument utiliser les méthodes fournies par C-API ( http://docs.python.org/c-api/ ).

La lecture de l'introduction est la première chose à faire pour comprendre son fonctionnement.

Tout d’abord, vous devez connaître PyObject, l’objet de base de l’API C. Il peut représenter tout type de types de base python (string, float, int, ...).

De nombreuses fonctions existent pour convertir par exemple string python en char * ou PyFloat en double.

D'abord, importez votre module:

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

Puis obtenir une référence à votre fonction:

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

Puis obtenir votre résultat:

PyObject* myResult = PyObject_CallObject(myFunction, args)

Et revenons à un double:

double result = PyFloat_AsDouble(myResult);

Vous devriez évidemment vérifier les erreurs (cf. lien donné par Mark Tolonen).

Si vous avez des questions, n'hésitez pas. Bonne chance.

57
ThR37

Voici un exemple de code que j'ai écrit (avec l'aide de diverses sources en ligne) pour envoyer une chaîne à un code Python, puis renvoyer une valeur.

Voici le code 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;
}

Voici le code Python, dans le fichier arbName.py:

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

J'utilise la commande gcc call_function.c -I/usr/include/python2.6 -lpython2.6 ; ./a.out pour exécuter ce processus. Je suis sur Redhat. Je recommande d'utiliser PyErr_Print (); pour la vérification d'erreur.

16
kilojoules

Un exemple complet d'appel d'une fonction Python et d'extraction du résultat se trouve à l'adresse 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;
}
7
Mark Tolonen

Pour empêcher le fichier .py supplémentaire comme dans les autres réponses, vous pouvez simplement récupérer le module __main__, créé par le premier appel à 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
3
lama12345

Vous devez extraire la méthode python d'une manière ou d'une autre et l'exécuter avec PyObject_CallObject(). Pour ce faire, vous pouvez fournir à Python un moyen de définir la fonction, comme le fait l'exemple Extension du didacticiel Python .

Si vous affectez la valeur de retour à une variable, vous pouvez utiliser quelque chose comme PyEval_GetGlobals () et PyDict_GetItemString () pour obtenir PyObject. À partir de là, PyNumber_Float peut vous fournir la valeur souhaitée.

Je suggère de parcourir l'intégralité de l'API - certaines choses deviennent évidentes lorsque vous voyez les différentes méthodes disponibles, et il se peut qu'il existe une meilleure méthode que celle que j'ai décrite.

1
Kylotan

Je l'ai fait en utilisant BOOST pour incorporer Python à C++ [Ce module C fonctionnel devrait aider]

#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
user3758062