Передача объекта C ++ в Python - PullRequest
0 голосов
/ 30 мая 2018

Этот вопрос касается как передать объект C ++ в функцию Python, которая вызывается во встроенном (C ++) интерпретаторе Python .

Следующий класс C ++ (MyClass.h) предназначен для тестирования:

#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

Модуль python, представляющий класс C ++, может быть сгенерирован с помощью следующего файла интерфейса Swig:

%module passmetopython

%{    #include "MyClass.h"    %}

%include "std_string.i"

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

Ниже приведен скрипт Python с использованием модуля 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()

Этот вопрос о том, как заставить работать функцию python execute () при вызове из c ++, с объектом C ++ в качестве аргумента.

Следующая программа C ++ была написана для проверки функций (минимальное количество проверки ошибок):

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

При запуске приведенного выше кода, execute () Python-функция, с объектом MyClass в качестве аргумента, завершается с ошибкой и возвращает NULL.Тем не менее, функция Python введена, как я вижу вывод ( Ввод функции выполнения ) в выводе консоли, указывая, что переданный объект не является, действительно , действительным объектом MyClass.

Существует множество примеров того, как передавать простые типы, такие как int, double или string, в Python из C / C ++.Но есть очень немного примеров, показывающих, как передать объект / указатель C / C ++, что немного озадачивает.

Приведенный выше код с файлом CMake можно извлечь из github: https://github.com/TotteKarlsson/miniprojects/tree/master/passMeToPython

Этот код не предназначен для использования каких-либо надстроек Python или других API.Хотя Cython звучит интересно, и если его можно использовать для упрощения на стороне C ++, он может быть приемлемым.

Ответы [ 2 ]

0 голосов
/ 07 июня 2018
PyObject *pValue;
pValue = PyObject_CallMethod(pInstance, "add","(i)",x);
if (pValue)
    Py_DECREF(pValue);
else
    PyErr_Print();
0 голосов
/ 01 июня 2018

Это частичный ответ на мой собственный вопрос.Я говорю частично, потому что я верю, что есть лучший способ.

Опираясь на этот пост http://swig.10945.n7.nabble.com/Pass-a-Swig-wrapped-C-class-to-embedded-Python-code-td8812.html Я сгенерировал заголовок среды выполнения swig, как описано здесь, раздел 15.4: http://www.swig.org/Doc2.0/Modules.html#Modules_external_run_time

Включая сгенерированный заголовок в приведенный выше код C ++, разрешите записать следующий код:

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

Этот код использует информацию из исходных файлов оболочки Python Swig, а именно «swig»имя типа MyClass, то есть _p_MyClass .

С указанным выше PyObject * в качестве аргумента функции PyObject_CallObject функция python execute () в приведенном выше кодевыполняется нормально, и код Python, используя сгенерированный модуль python, имеет надлежащий доступ к внутренним данным объектов MyClass.Это замечательно.

Хотя приведенный выше код иллюстрирует, как передавать и получать данные между C ++ и Python довольно простым способом, на мой взгляд, он не идеален.

ИспользованиеЗаголовочный файл swig в коде C ++ на самом деле не так хорош, и, кроме того, он требует, чтобы пользователь «вручную» изучил сгенерированный swig код оболочки, чтобы найти код «_p_MyClass».

Должен быть лучший путь !?Возможно, что-то должно быть добавлено в файл интерфейса Swig, чтобы это выглядело лучше?

...