2013-05-17 56 views
13

我想从C++中调用python类中的方法。从中调用它的C++方法是C++回调。从C++(或C)回调中调用python方法

在我试图调用python方法的这种方法中,它给出segmentation fault

我已经在全局变量中像

// (pFunc is global variable of type PyObject*) 
pFunc = PyDict_GetItemString(pDict, "PlxMsgWrapper"); 

PlxMsgWrapper哪里是一个Python方法,这将在回调被用于保存蟒函数的一个实例。

在回调,参数为

PyObject* args = PyTuple_Pack(2, PyString_FromString(header.c_str()), 
           PyString_FromString(payload.c_str())); 

创建在创建

PyObject * pInstance = PyObject_CallObject(pFunc, args); 

在这一行其给段故障。在此之后,实际的Python方法称为

PyObject* recv_msg_func = PyObject_GetAttrString(module, (char *)"recvCallback"); 
args = PyTuple_Pack(1, pInstance); 
PyObject_CallObject(recv_msg_func, args); 
+0

我已经尽我所能来正确阐明。如果有任何问题,请回复评论。 – Chaitanya

+0

您是否检查过PyObject_GetAttrString实际返回可用的东西?可能由于某种原因查找失败。也许'模块'没有正确初始化? – djf

+0

@djf其实,控制本身并没有到那个时候。 'PyObject_CallObject(pFunc,args)'。它在调用这个方法本身时崩溃。为了摆脱模块问题,我没有在回调中加载它。 – Chaitanya

回答

27

有你需要做的,如果你是从C/C++回调调用Python函数的几件事情。首先,当你保存了你的Python函数对象,你需要增加与引用计数:

Py_INCREF(pFunc) 

否则Python有不知道你持有到一个对象的引用,它可能的垃圾收集起来,产生了分割当您尝试从回调中使用它时出现错误。

然后,您需要关注的下一件事是当您的C/C++回调被调用时,哪个线程正在运行。如果您从另一个非Python创建的线程(即在套接字上接收数据的C/C++线程)回调,则您在调用任何Python API函数之前获取Python的全局解释器锁(GIL)。否则你的程序的行为是不确定的。为了获取GIL你:

void callback() { 
    PyGILState_STATE gstate; 
    gstate = PyGILState_Ensure(); 

    // Get args, etc. 

    // Call your Python function object 
    PyObject * pInstance = PyObject_CallObject(pFunc, args); 

    // Do any other needed Python API operations 

    // Release the thread. No Python API allowed beyond this point. 
    PyGILState_Release(gstate); 
} 

此外,在扩展模块的初始化函数,你应该做到以下几点,以确保线程正确初始化:

// Make sure the GIL has been created since we need to acquire it in our 
// callback to safely call into the python application. 
if (! PyEval_ThreadsInitialized()) { 
    PyEval_InitThreads(); 
} 

否则,崩溃和奇怪的行为可能当您尝试从非Python线程获取GIL时会出现。

有关详细信息,请参阅Non-Python Created Threads

+1

非常感谢。 :)这是确切的问题。 – Chaitanya

+0

非常感谢 - 没有考虑到我可能在错误的线程。 – eddiewould

2

的Python应该寻找它正在然而从,运行 目录中的模块,如果你认为这个问题是,Python是没有找到 文件,你可以在你的程序中您的计算机上添加任意目录到 模块搜索路径:

// Initialize the Python Interpreter 
Py_Initialize(); 

// The following two lines to the trick: 
// add path to your module to python's search paths 
PyRun_SimpleString("import sys"); 
PyRun_SimpleString("sys.path.append(\"/path/to/python/module/here\")"); 

// Build the name object 
pName = PyString_FromString("your_module"); 

// Load the module object 
pModule = PyImport_Import(pName); 

// pDict is a borrowed reference 
pDict = PyModule_GetDict(pModule); 

// pFunc is also a borrowed reference 
pFunc = PyDict_GetItemString(pDict, "PlxMsgWrapper"); 

pArgs = ... 

if (PyCallable_Check(pFunc)) 
{ 
    PyObject_CallObject(pFunc, pArgs); 
} else { 
    PyErr_Print(); 
} 
2

这并不完全回答你的问题,但你可以极大地简化你的代码并避免引用计数问题与Boost::Python

#include "boost/python.hpp" 

using namespace boost::python; 

int main() 
{ 
    Py_Initialize(); 

    object pyFunPlxMsgWrapper = import("your_module").attr("PlxMsgWrapper"); 
    pyFunPlxMsgWrapper(2, "string", "data"); 
    return 0; 
}