在C++中嵌入Python:如何将数据返回给C++
在做一个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;
}
一些重要的要点:
- 我把'exec'改成了'exec_file',这样更方便,其实用'exec'也可以。
- 它失败的主要原因是我没有给'exec'或'exec_file'传递一个"local"命名空间 - 现在通过传递命名空间两次解决了这个问题。
- 如果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;
}