简体   繁体   English

从Python向C ++回调传递不透明数据

[英]Passing opaque data to C++ callback from Python

I'm embedding Python (using boost::python) into an application plugin that uses callbacks. 我将Python(使用boost :: python)嵌入到使用回调的应用程序插件中。 Essentially, I want to do something like: 本质上,我想做类似的事情:

In Python (say test.py): 在Python中(例如test.py):

def do_something():
  ...

register_callback(do_something)

And on the C++ side I register the register_callback() function: 在C ++方面,我注册了register_callback()函数:

void register_callback(boost::python::object& o)
{
    // Access some persistent state information
}

BOOST_PYTHON_MODULE(foo)
{
  boost::python::def("register_callback", register_callback);
}

void library_entry()
{
  PyImport_AppendInittab("foo", initfoo);

  PyInitialize();

  // Create some persistent state information

  boost::python::exec_file("test.py", ...);
}

The issue here is that I need to create the Python context and store things away in an opaque pointer I return to the application. 这里的问题是,我需要创建Python上下文并将其存储在返回到应用程序的不透明指针中。 And when Python calls back into the C++, I need to recover that opaque value. 当Python回调回C ++时,我需要恢复该不透明值。 For example: 例如:

Application -- (calls) --> my C++ library -- (runs) --> Python script -- (calls) --> my C++ library 应用程序-(调用)->我的C ++库-(运行)-> Python脚本-(调用)->我的C ++库

For that last step, I need to recover some opaque data. 对于最后一步,我需要恢复一些不透明的数据。 Further, I need that opaque data to be persistent. 此外,我需要这些不透明的数据来持久化。 The calls into my C++ library are callbacks from the application. 对我的C ++库的调用是来自应用程序的回调。 The issue I'm having is trying to figure out how to pass that state information to the C++ register_callback() function. 我遇到的问题是试图弄清楚如何将状态信息传递给C ++ register_callback()函数。 I've tried something like: 我已经尝试过类似的东西:

namespace bp = boost::python;

class state_info_t
{
};

void register_callback(std::shared_ptr<state_info_t>& state, bp::object& o);
{
  // Access some persistent state information
}

// Create some persistent state information
std::shared_ptr<state_info_t> state = std::make_shared<state_info_t>();

PyImport_AppendInittab("foo", initfoo);

Py_Initialize();

std::shared_ptr<bp::object> main_module = std::make_shared<bp::object>(bp::handle<>(bp::borrowed(PyImport_AddModule("__main__"))));
bp::object main_namespace = main_module->attr("__dict__");
std::shared_ptr<bp::object> foo_module = std::make_shared<bp::object>(bp::handle<>(PyImport_ImportModule("foo")));

main_namespace["foo"] = *foo_module;

bp::scope foo_scope(*foo_module);

// Both of these fail with _a lot_ of errors, most related to "no matching function call to 'get_signature'
bp::def("register_callback",
        [&](bp::object& o) { register_callback(state, o); },
        bp::arg("func"));

bp::def("register_callback",
        std::bind(register_callback, state, std::placeholders::_1),
        bp::arg("func"));

The other thought I had was to store the persistent data in the module's dictionary. 我的另一个想法是将持久性数据存储在模块的字典中。 But I don't know how to recover it in the callback. 但是我不知道如何在回调中恢复它。 For example: 例如:

// Create some persistent state information
std::shared_ptr<state_info_t> state = std::make_shared<state_info_t>();

PyImport_AppendInittab("foo", initfoo);

Py_Initialize();

std::shared_ptr<bp::object> main_module = std::make_shared<bp::object>(bp::handle<>(bp::borrowed(PyImport_AddModule("__main__"))));
bp::object main_namespace = main_module->attr("__dict__");
std::shared_ptr<bp::object> foo_module = std::make_shared<bp::object>(bp::handle<>(PyImport_ImportModule("foo")));
bp::object foo_namespace = main_module->attr("__dict__");

main_namespace["foo"] = *foo_module;
foo_namespace["state"] = bp::handle<>(state); // Whatever the appropriate wrapper is

Then in C++ side of register_callback: 然后在register_callback的C ++端:

void register_callback(bp::object& o)
{
  // How do I extract "state" from the context?
}

I'm not keen on this last one, since it exposes the state information to the script. 我不热衷于最后一个,因为它向脚本公开了状态信息。

I don't want to make the state information global since there may be multiple running Python instances. 我不想使状态信息成为全局信息,因为可能有多个正在运行的Python实例。 And regardless, with multiple Python instances I still need a way to determine which Python instance is running to select the appropriate state information. 无论如何,对于多个Python实例,我仍然需要一种方法来确定运行哪个Python实例以选择适当的状态信息。

I think I found a way to accomplish what I want. 我想我找到了实现自己想要的方法。

Rather than actually create a module, I can create a class, then put an instance of that class into sys.modules (see this answer ). 我可以创建一个类,而不是实际创建模块,然后将该类的实例放入sys.modules中(请参阅此答案 )。

So I do something like this: 所以我做这样的事情:

namespace bp = boost::python;

class foo
{
  public:
    void register_callback(bp::object&);
};

...

// Register foo with Python (in the current scope)
bp::object foo_class = bp::class_<foo>("foo")
  .def("register_callback", &foo::register_callback);

// Get sys.modules
bp::object obj(bp::handle<>(bp::borrowed(PyImport_GetModuleDict())));

// Add an instance of foo to sys.modules
obj["foo"] = foo_class();

Now, in python: 现在,在python中:

import foo

def func():
  ...

foo.register_callback(func)

This seems to make a class instance appear as a module, and work as module. 这似乎使类实例作为模块出现,并作为模块工作。

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

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