30 votes

Passer un objet C ++ à Python

Cette question est au sujet de la façon de passer d'un objet C++ une fonction python qui est appelée dans un (C++) interpréteur Python embarqué.

Le C++ suivant (de la classe Maclasse.h) est conçu pour tester:

#ifndef MyClassH
#define MyClassH
#include <string>

using std::string;
class MyClass
{
    public:
                        MyClass(const string& lbl): label(lbl) {}
                        ~MyClass(){}
        string          getLabel() {return label;}

    private:
        string          label;
};
#endif

Un module python, l'exposition de la classe C++, peut être généré par le texte suivant Rasade fichier d'interface:

%module passmetopython

%{    #include "MyClass.h"    %}

%include "std_string.i"

//Expose to Python
%include "MyClass.h"

Ci-dessous est un script Python à l'aide du module python

import passmetopython as pmtp

def execute(obj):
    #This function is to be called from C/C++, with a
    #MyClass object as an argument
    print ("Entering execute function")
    lbl = obj.getLabel();
    print ("Printing from within python execute function. Object label is: " + lbl)
    return True

def main():
    c = pmtp.MyClass("Test 1")
    retValue = execute(c)
    print("Return value: " + str(retValue))

#Test function from within python
if __name__ == '__main__':
    main()

Cette question est de savoir comment tirer le python execute() de la fonction de travail, lorsqu'il est appelé à partir de c++, avec un objet C++ comme un argument.

Le C++ suivant le programme a été écrit pour tester les fonctions (montant minimum de vérification des erreurs):

#include "Python.h"
#include <iostream>
#include <sstream>
#include "MyClass.h"

using namespace std;

int main()
{
    MyClass obj("In C++");
    cout << "Object label: \"" << obj.getLabel() << "\"" << endl;

    //Setup the Python interpreter and eventually call the execute function in the
    //demo python script
    Py_Initialize();

    //Load python Demo script, "passmetopythonDemo.py"
    string PyModule("passmetopythonDemo");
    PyObject* pm = PyUnicode_DecodeFSDefault(PyModule.c_str());

    PyRun_SimpleString("import sys");
    stringstream cmd;
    cmd << "sys.path.append(\"" << "." << "\")";
    PyRun_SimpleString(cmd.str().c_str());
    PyObject* PyModuleP = PyImport_Import(pm);
    Py_DECREF(pm);

    //Now create PyObjects for the Python functions that we want to call
    PyObject* pFunc = PyObject_GetAttrString(PyModuleP, "execute");

    if(pFunc)
    {
        //Setup argument
        PyObject* pArgs = PyTuple_New(1);

        //Construct a PyObject* from long
        PyObject* pObj(NULL);

        /* My current attempt to create avalid argument to Python */
        pObj = PyLong_FromLong((long) &obj);


        PyTuple_SetItem(pArgs, 0, pObj);

        /***** Calling python here *****/
        cout<<endl<<"Calling function with an MyClass argument\n\n";
        PyObject* res = PyObject_CallObject(pFunc, pArgs);
        if(!res)
        {
            cerr << "Failed calling function..";
        }
    }

    return 0;
}

Lors de l'exécution du code ci-dessus, le execute() fonction python, avec un MyClass objet comme argument, échoue et renvoie la valeur NULL. Cependant, la fonction Python est entré, que je puisse voir la sortie (Entrant en fonction execute) dans la console de sortie, indiquant que l'objet passé n'est pas, en effet, un valide MyClass objet.

Il y a beaucoup d'exemples sur la façon de passer d'un type simple, comme les entiers, doubles ou chaîne types de Python, C/C++. Mais il y a très peu d'exemple montrant comment passer un C/C++ objet/ pointeur, ce qui est un peu déroutant.

Le code ci-dessus, avec un fichier CMake, peut être vérifié à partir de github: https://github.com/TotteKarlsson/miniprojects/tree/master/passMeToPython

Ce code est de ne pas utiliser le boost de python ou d'autres API. Cython semble intéressant, et si elle peut être utilisée pour simplifier sur le C++ côté, il pourrait être acceptable.

9voto

Totte Karlsson Points 447

C'est une réponse partielle à ma propre question. Je dis partielle, parce que je crois qu'il y a une meilleure façon.

Bâtiment de sur ce post http://swig.10945.n7.nabble.com/Pass-a-Swig-wrapped-C-class-to-embedded-Python-code-td8812.html J'ai généré le rasade d'exécution de l'en-tête, comme décrit ici, l'article 15.4: http://www.swig.org/Doc2.0/Modules.html#Modules_external_run_time

Y compris l'en-tête généré dans le code C++ ci-dessus, permettent le code suivant à écrit:

    PyObject* pObj = SWIG_NewPointerObj((void*)&obj, SWIG_TypeQuery("_p_MyClass"),  0 ); 

Ce code est en utilisant les informations de la Rasade python envelopper les fichiers source, à savoir le "rasade" nom du type Maclasse, c'est à dire _p_MyClass.

Ci-dessus PyObject* comme un argument de la PyObject_CallObject fonction, le python execute() de la fonction dans le code ci-dessus s'exécute correctement, et le code Python, en utilisant le générés module python, ont un accès approprié à l'MyClass objets de données interne. Ce qui est excellent.

Bien que le code ci-dessus illustrent comment passer des, et de récupérer des données entre le C++ et Python dans un très simple de la mode, ce n'est pas l'idéal, à mon avis.

L'utilisation de la rasade fichier d'en-tête dans le code C++ est vraiment pas assez, et en plus, il exige d'un utilisateur "à la main" regarde rasade généré code wrapper pour trouver le "_p_MyClass" du code.

Il doit y avoir une meilleure façon!? Peut-être que quelque chose doit être ajouté à la rasade fichier d'interface afin d'obtenir ce à la recherche de plus agréable?

-1voto

PewDie Points 9
PyObject *pValue;
pValue = PyObject_CallMethod(pInstance, "add","(i)",x);
if (pValue)
    Py_DECREF(pValue);
else
    PyErr_Print();

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X