I am trying to call methods in a python class from C++. The C++ method from which this is called is a C++ callback.
Within this method when I am trying to call python method, it was giving segmentation fault
.
I have saved an instance of python function in a global variable like
// (pFunc is global variable of type PyObject*)
pFunc = PyDict_GetItemString(pDict, "PlxMsgWrapper");
where PlxMsgWrapper
is a python method, which will be used in the callback.
In the callback, the arguments are created as
PyObject* args = PyTuple_Pack(2, PyString_FromString(header.c_str()),
PyString_FromString(payload.c_str()));
When creating the
PyObject * pInstance = PyObject_CallObject(pFunc, args);
In this line its giving segmentation fault. After this the actual python method is called as
PyObject* recv_msg_func = PyObject_GetAttrString(module, (char *)"recvCallback");
args = PyTuple_Pack(1, pInstance);
PyObject_CallObject(recv_msg_func, args);
If a C interface makes use of callbacks, the equivalent Python often needs to provide a callback mechanism to the Python programmer; the implementation will require calling the Python callback functions from a C callback. Other uses are also imaginable.
In simple language, If a reference of a function is passed to another function as an argument to call it, then it will be called as a Callback function. In C, a callback function is a function that is called through a function pointer.
PyObject is an object structure that you use to define object types for Python. All Python objects share a small number of fields that are defined using the PyObject structure. All other object types are extensions of this type. PyObject tells the Python interpreter to treat a pointer to an object as an object.
There are a few things you need to do if you are invoking a Python function from a C/C++ callback. First when you save off your python function object, you need to increment the reference count with:
Py_INCREF(pFunc)
Otherwise Python has no idea you are holding onto an object reference, and it may garbage collect it, resulting in a segmentation fault when you try to use it from your callback.
Then next thing you need to be concerned about is what thread is running when your C/C++ callback is invoked. If you are getting called back from another non-Python created thread (i.e. a C/C++ thread receiving data on a socket), then you MUST acquire Python's Global Interpreter Lock (GIL) before calling any Python API functions. Otherwise your program's behavior is undefined. To acquire the GIL you do:
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);
}
Also, in your extension module's init function, you should do the following to ensure that threading is properly initialized:
// 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();
}
Otherwise, crashes and strange behavior may ensue when you attempt to acquire the GIL from a non-Python thread.
See Non-Python Created Threads for more detail on this.
Python should look for a module in the directory where it is being run from, however, if you think that the issue is that python is not finding your file, you can add an arbitrary directory on your computer to the module search path within your program:
// 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();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With