Llamando al método python desde C ++ (o C) callback

Estoy tratando de llamar a los métodos en una clase python desde C ++. El método C ++ desde el que se llama es una callback de C ++.

Dentro de este método cuando trato de llamar al método python, estaba dando una segmentation fault .

He guardado una instancia de la función python en una variable global como

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

donde PlxMsgWrapper es un método de Python, que se utilizará en la callback.

En la callback, los argumentos se crean como

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

Al crear el

 PyObject * pInstance = PyObject_CallObject(pFunc, args); 

En esta línea se da la falla de segmentación. Después de esto, se llama al método Python real como

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

Hay algunas cosas que debe hacer si está invocando una función de Python desde una callback de C / C ++. Primero, cuando salves de tu función de Python, necesitas incrementar el conteo de referencia con:

 Py_INCREF(pFunc) 

De lo contrario, Python no tiene idea de que se aferra a una referencia de objeto, y puede que sea recolectado por la basura, lo que resulta en una falla de segmentación cuando intenta usarlo desde su callback.

Luego, lo siguiente que debe preocuparle es qué hilo se está ejecutando cuando se invoca su callback de C / C ++. Si recibe una llamada desde otro subproceso no creado por Python (es decir, un subproceso C / C ++ que recibe datos en un socket), DEBE adquirir el Bloqueo global de intérprete (GIL) de Python antes de llamar a cualquier función API de Python. De lo contrario el comportamiento de su progtwig es indefinido. Para adquirir el GIL haces:

 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); } 

Además, en la función init de su módulo de extensión, debe hacer lo siguiente para asegurarse de que el subproceso se inicialice correctamente:

 // 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(); } 

De lo contrario, pueden producirse lockings y comportamientos extraños cuando intenta adquirir GIL de un subproceso que no es Python.

Consulte los subprocesos creados que no sean de Python para obtener más detalles sobre esto.

Python debe buscar un módulo en el directorio desde donde se ejecuta, sin embargo, si piensa que el problema es que Python no encuentra su archivo, puede agregar un directorio arbitrario en su computadora a la ruta de búsqueda de módulos dentro de su progtwig :

 // 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(); } 

Esto no responde exactamente a su pregunta, pero puede simplificar enormemente su código y evitar los problemas de recuento de referencias con 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; }