(对不起的标题很抱歉,但是它显示了我对问题的震惊程度)。
因此,我正在按照此处描述的方法从C++程序运行Python代码:https://docs.python.org/2/extending/embedding.html。
这是C++代码:
#include <Python.h>
#include <iostream>
int main(int argc, char *argv[])
{
PyObject *pName, *pModule, *pDict, *pFunc;
PyObject *pArgs, *pValue;
int i;
if (argc < 3) {
fprintf(stderr,"Usage: call pythonfile funcname [args]\n");
return 1;
}
Py_SetProgramName(argv[0]);
Py_Initialize();
PySys_SetArgv(argc, argv);
PyObject *sys = PyImport_ImportModule("sys");
PyObject *path = PyObject_GetAttrString(sys, "path");
PyList_Append(path, PyString_FromString("."));
pName = PyString_FromString((char*)argv[1]);
/* Error checking of pName left out */
pModule = PyImport_Import(pName);
Py_DECREF(pName);
if (pModule != NULL) {
pFunc = PyObject_GetAttrString(pModule, argv[2]);
/* pFunc is a new reference */
if (pFunc && PyCallable_Check(pFunc)) {
PyObject *pArgs = PyList_New(4);
PyList_SetItem(pArgs,0,PyString_FromString("H-SAMPLE1-OH"));
PyList_SetItem(pArgs,1,PyInt_FromLong(2));
PyList_SetItem(pArgs,2,PyString_FromString("H-SAMPLE2-OH"));
PyList_SetItem(pArgs,3,PyInt_FromLong(3));
PyObject *arglist = Py_BuildValue("(O)", pArgs);
Py_DECREF(pArgs);
for(int run = 0; run < 2; run++)
{
std::cout << "begin" << std::endl;
pValue = PyObject_CallObject(pFunc, arglist);
//Py_DECREF(arglist);
if (pValue != NULL)
{
int py_list_size = PyList_Size(pValue);
printf("list size = %d\n",py_list_size);
int sub_list_size = 0;
for(Py_ssize_t i = 0; i < py_list_size; ++i)
{
PyObject *pList = PyList_GetItem(pValue, i);
sub_list_size = PyList_Size(pList);
if(PyList_Check(pList))
{
for(Py_ssize_t j = 0; j < sub_list_size; ++j)
{
PyObject *pListItem = PyList_GetItem(pList, j);
double pyNumber = PyFloat_AsDouble(pListItem);
std::cout << "pynumber ok" << std::endl;
Py_DECREF(pListItem);
printf("Result of call: %f\n", pyNumber);
}
}
else
{
printf("Not list!\n");
}
Py_DECREF(pList);
}
Py_DECREF(pValue);
}
else {
std::cout << "Else" << std::endl;
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 \"%s\"\n", argv[2]);
}
Py_XDECREF(pFunc);
Py_DECREF(pModule);
}
else {
PyErr_Print();
fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
return 1;
}
Py_Finalize();
return 0;
}
这是玩具Python代码:
def test(a):
print a
return [[1.2,2.6],[4.7,5.6]]
注意C++代码中的主循环,遍历变量“run”。当循环内的代码仅执行一次时,它就像一种魅力。如果尝试运行更多次(例如仅运行两次),它将出错,则会出现分段错误。显然,当尝试执行时,故障发生在第61行
double pyNumber = PyFloat_AsDouble(pListItem);
我觉得这很奇怪。它在第一次执行期间运行良好,然后突然无法再正确地从pListItem中获取某些内容(尽管它确实收到了一些内容,它可以识别为大小为2的列表,并且似乎可以正确处理所有其他pyObject指针)。对发生的事情有任何想法吗?
重现:
我编译如下:
g++ -L/usr/lib/python2.7/config-x86_64-linux-gnu -L/usr/lib -I/usr/include/python2.7 -o ms2pip ms2pip.c -lpthread -ldl -lutil -lm -lpython2.7 -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions
然后执行如下:
$ ./ms2pip python_code test
(so ./executable < python_file_without_py_extension > < function_name >)
最佳答案
我认为您的问题是PyList_GetItem()返回借来的引用。因此,问题在于同时使用Py_DECREF()
和pList
调用pListItem
:
PyObject *pList = PyList_GetItem(pValue, i);
// ...
if(PyList_Check(pList))
{
for(Py_ssize_t j = 0; j < sub_list_size; ++j)
{
PyObject *pListItem = PyList_GetItem(pList, j);
double pyNumber = PyFloat_AsDouble(pListItem); // <-- Segfault in second iteration after released from first iteration.
// ...
Py_DECREF(pListItem); // <-- Bad, released in first iteration.
// ...
}
}
//...
Py_DECREF(pList); // <-- Bad, released in first iteration.
pList
是借来的引用,您不负责与Py_DECREF()
一起发布。另外,pListItem
也是借用的引用。因此,在第一次迭代中,您将释放pList
以及每个不好的pListItem
。在第二次迭代中,获取所有已发布的pList
和每个pListItem
,并将它们视为仍然稳定,而不是那样对待。由于您正在访问释放的对象,因此该程序可能真的失败或在涉及它们的任何函数调用中给出不好的结果(例如PyList_Size(pList)
,PyList_GetItem(pList, j)
,PyFloat_AsDouble(pListItem)
,Py_DECREF(pListItem)
和Py_DECREF(pList)
)。关于python - 在C++中嵌入python:奇怪的段错误,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/28302709/