简体   繁体   English

从C ++(或C)回调中调用python方法

[英]Calling python method from C++ (or C) callback

I am trying to call methods in a python class from C++. 我试图从C ++调用python类中的方法。 The C++ method from which this is called is a C++ callback. 调用它的C ++方法是C ++回调。

Within this method when I am trying to call python method, it was giving segmentation fault . 在我尝试调用python方法时,在这种方法中,它给出了segmentation fault

I have saved an instance of python function in a global variable like 我已经在一个全局变量中保存了一个python函数实例

// (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. 其中PlxMsgWrapper是一个python方法,将在回调中使用。

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 在此之后,实际的python方法被称为

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

There are a few things you need to do if you are invoking a Python function from a C/C++ callback. 如果从C / C ++回调调用Python函数,则需要执行一些操作。 First when you save off your python function object, you need to increment the reference count with: 首先,当你保存你的python函数对象时,你需要增加引用计数:

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. 否则,Python不知道您持有对象引用,它可能会垃圾收集它,当您尝试从回调中使用它时会导致分段错误。

Then next thing you need to be concerned about is what thread is running when your C/C++ callback is invoked. 接下来你需要关心的是调用C / C ++回调时正在运行的线程。 If you are getting called back from another non-Python created thread (ie 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. 如果你从另一个非Python创建的线程(即在套接字上接收数据的C / C ++线程)回调,那么在调用任何Python API函数之前,你必须获得Python的全局解释器锁(GIL)。 Otherwise your program's behavior is undefined. 否则你的程序的行为是不确定的。 To acquire the GIL you do: 要获得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);
}

Also, in your extension module's init function, you should do the following to ensure that threading is properly initialized: 此外,在扩展模块的init函数中,您应该执行以下操作以确保正确初始化线程:

// 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. 否则,当您尝试从非Python线程获取GIL时,可能会发生崩溃和奇怪的行为。

See Non-Python Created Threads for more detail on this. 有关此内容的更多详细信息,请参阅非Python创建的线程

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

This doesn't exactly answer your question, but you can greatly simplify your code and avoid reference count issues with Boost::Python . 这并不能完全回答您的问题,但您可以大大简化代码并避免使用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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM