Python, встроенный в CPP: как вернуть данные в CPP - PullRequest
12 голосов
/ 19 октября 2008

Работая над проектом 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» работает нормально, я вижу печать «результата». Что я не могу сделать, это прочитать «результат» обратно из кода C ++. Команда извлечения никогда не находит «результат» в любом пространстве имен. Я попытался определить «результат» как глобальный, я даже попытался вернуть кортеж, но не могу заставить его работать.

Ответы [ 4 ]

7 голосов
/ 19 октября 2008

Прежде всего, измените вашу функцию на 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;
}
3 голосов
/ 19 октября 2008

Основываясь на ответах NΩΤΖΙΟΥ, Джоша и Носкло, я наконец заработал, используя boost.python:

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. Основная причина неудачи в том, что i не передал "локальный" name_sapce 'exec' или 'exec_file' - теперь это исправлено передачей name_space дважды.
  3. Если функция python возвращает Unicode-строки, они не конвертируется в 'std :: string', поэтому я должен был суффикс всех строк Python с '.encode (' ASCII ',' ignore ')'.
1 голос
/ 19 октября 2008

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

0 голосов
/ 19 октября 2008

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

...