在C++中嵌入Python:如何将数据返回给C++

15 投票
4 回答
9967 浏览
提问于 2025-04-11 09:35

在做一个C++项目的时候,我想找一个第三方库来处理一些不是我主要业务的事情。我找到一个非常不错的库,正好满足我的需求,但它是用Python写的。于是我决定尝试在C++中嵌入Python代码,使用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 个回答

1

我觉得你需要的是 PyObject_CallObject(<py function>, <args>),这个函数会返回你调用的那个函数的返回值,结果是一个PyObject对象;或者你可以用 PyRun_String(<expression>, Py_eval_input, <globals>, <locals>),这个可以计算一个表达式并返回它的结果。

4

根据ΤΖΩΤΖΙΟΥ、Josh和Nosklo的回答,我终于用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. 它失败的主要原因是我没有给'exec'或'exec_file'传递一个"local"命名空间 - 现在通过传递命名空间两次解决了这个问题。
  3. 如果Python函数返回的是unicode字符串,这些字符串不能直接转换成'std::string',所以我必须在所有Python字符串后面加上'.encode('ASCII', 'ignore')'。
10

首先,把你的函数改成用 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;
}

撰写回答