Python, встроенный в CPP:как вернуть данные обратно в CPP

StackOverflow https://stackoverflow.com/questions/215752

  •  03-07-2019
  •  | 
  •  

Вопрос

Работая над проектом на C ++, я искал стороннюю библиотеку для чего-то, что не является моей основной деятельностью.Я нашел действительно хорошую библиотеку, делающую именно то, что нужно, но она написана на Python.Я решил поэкспериментировать с внедрением кода Python в C ++, используя Boost.Библиотеку Python.

Код на C ++ выглядит примерно так:

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

(Очень) упрощенная версия кода Python выглядит следующим образом:

import thirdparty

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

Теперь проблема заключается в следующем:'myFunc' выполняется нормально, я вижу вывод 'result'.Чего я не могу сделать, так это прочитать "результат" обратно из кода на C ++.Команда extract никогда не находит "результат" ни в одном пространстве имен.Я попытался определить "результат" как глобальный, я даже попытался вернуть кортеж, но я не могу заставить его работать.

Это было полезно?

Решение

Прежде всего, измените свою функцию на return значение. print это усложнит ситуацию, так как вы хотите вернуть значение. Предположим, ваш MyModule.py выглядит так:

import thirdparty

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

Теперь, чтобы сделать то, что вы хотите, вы должны выйти за рамки простого встраивания, как говорится в документации . . Вот полный код для запуска вашей функции:

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

Другие советы

Основываясь на ответах ΤΖΩΤΖΙΟΥ, Джоша и Носкло, я, наконец, заставил это работать, используя boost.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;
}

Некоторые важные моменты:

  1. Я изменил 'exec' на 'exec_file' из соображений удобства, это также работает с простым 'exec'.
  2. Главная причина, по которой это не удалось, заключается в том, что я не передавал "локальный" name_sapce в 'exec' или 'exec_file' - это сейчас исправлена путем передачи name_space дважды.
  3. Если функция python возвращает строки в юникоде, они не являются конвертируемыми в 'std::string', поэтому мне пришлось добавить суффикс ко всем строкам python с помощью '.encode('ASCII', 'игнорировать')'.

Я думаю, что вам нужно либо PyObject_CallObject(<py function>, <args>), который возвращает возвращаемое значение функции, которую вы вызываете как PyObject, либо PyRun_String(<expression>, Py_eval_input, <globals>, <locals>), которая оценивает одно выражение и возвращает его результат.

Вы должны иметь возможность вернуть результат из MyFunc, который затем окажется в переменной, которую вы в данный момент вызываете " ignored " ;. Это устраняет необходимость доступа к нему любым другим способом.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top