问题 嵌入在CPP中的Python:如何将数据恢复到CPP


在从事C ++项目时,我一直在寻找第三方库,而不是我的核心业务。我找到了一个非常好的库,完全按照需要做了,但它是用Python编写的。我决定尝试使用Boost.Python库在C ++中嵌入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 ++代码中读取'结果'。 extract命令永远不会在任何命名空间中找到“结果”。 我尝试将'结果'定义为全局,我甚至尝试返回一个元组,但我无法让它工作。


11423
2017-10-18 23:59


起源



答案:


首先,将您的功能更改为 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;
}

6
2017-10-19 01:53



比我的更全面的答案,从(我想)一个父母的同胞:) nosklo,我建议你用PyRun_String示例扩展你的答案;它允许更多的灵活性。 - tzot
我想你的 pArgs = PyTuple_New(0); 应该传递1而不是0。 - Björn Lindqvist


答案:


首先,将您的功能更改为 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;
}

6
2017-10-19 01:53



比我的更全面的答案,从(我想)一个父母的同胞:) nosklo,我建议你用PyRun_String示例扩展你的答案;它允许更多的灵活性。 - tzot
我想你的 pArgs = PyTuple_New(0); 应该传递1而不是0。 - Björn Lindqvist


根据ΤΖΩΤΖΙΟΥ,Josh和Nosklo的回答我终于使用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'了 方便,它也适用 普通'执行'。
  2. 失败的主要原因是 一世 没有传递“本地”name_sapce 'exec'或'exec_file'  - 现在是这样 通过传递name_space两次来修复。
  3. 如果python函数返回 unicode字符串,它们不是 可转换为'std :: string',所以我 不得不为所有python字符串添加后缀 用'.encode('ASCII','忽略')'。

3
2017-10-19 09:47





我想你需要的是 PyObject_CallObject(<py function>, <args>),它返回您调用为PyObject的函数的返回值,或者 PyRun_String(<expression>, Py_eval_input, <globals>, <locals>) 它评估单个表达式并返回其结果。


1
2017-10-19 01:20





您应该能够从MyFunc返回结果,然后结果将在您当前称为“忽略”的变量中结束。这消除了以任何其他方式访问它的需要。


0
2017-10-19 00:19



无论从MyFunc返回什么,我得到:TypeError:'NoneType'对象不支持项目赋值 - yoav.aviram