简体   繁体   English

Boost.Python-将boost :: python :: object作为参数传递给python函数吗?

[英]Boost.Python - Passing boost::python::object as argument to python function?

So I'm working on a little project in which I'm using Python as an embedded scripting engine. 因此,我正在做一个小项目,其中使用Python作为嵌入式脚本引擎。 So far I've not had much trouble with it using boost.python, but there's something I'd like to do with it if it's possible. 到目前为止,我使用boost.python并没有遇到太多麻烦,但是如果可能的话,我想对其进行一些处理。

Basically, Python can be used to extend my C++ classes by adding functions and even data values to the class. 基本上,Python可以通过向类添加函数甚至数据值来扩展我的C ++类。 I'd like to be able to have these persist in the C++ side, so one python function can add data members to a class, and then later the same instance passed to a different function will still have them. 我希望能够将这些保留在C ++端,因此一个python函数可以将数据成员添加到类中,然后在传递给另一个函数的同一个实例中仍然保留它们。 The goal here being to write a generic core engine in C++, and let users extend it in Python in any way they need without ever having to touch the C++. 这里的目标是用C ++编写一个通用的核心引擎,并允许用户以他们需要的任何方式在Python中扩展它,而无需接触C ++。

So what I thought would work was that I would store a boost::python::object in the C++ class as a value self , and when calling the python from the C++, I'd send that python object through boost::python::ptr() , so that modifications on the python side would persist back to the C++ class. 所以我认为可行的是,我将在C ++类中将boost::python::object存储为值self ,并且从C ++调用python时,我会通过boost::python::ptr()发送该python对象boost::python::ptr() ,以便在python端进行的修改将保留回C ++类。 Unfortunately when I try this, I get the following error: 不幸的是,当我尝试此操作时,出现以下错误:

TypeError: No to_python (by-value) converter found for C++ type: boost::python::api::object

Is there any way of passing an object directly to a python function like that, or any other way I can go about this to achieve my desired result? 有没有办法像这样将对象直接传递给python函数,或者我可以通过其他任何方式达到期望的结果?

Thanks in advance for any help. 在此先感谢您的帮助。 :) :)

Got this fantastic solution from the c++sig mailing list. 从c ++ sig邮件列表中获得了这个出色的解决方案。

Implement a std::map<std::string, boost::python::object> in the C++ class, then overload __getattr__() and __setattr__() to read from and write to that std::map. 在C ++类中实现std::map<std::string, boost::python::object> ,然后重载__getattr__()__setattr__()来读取和写入该std :: map。 Then just send it to the python with boost::python::ptr() as usual, no need to keep an object around on the C++ side or send one to the python. 然后只需像往常一样使用boost::python::ptr()将其发送到python,无需在C ++端保留一个对象或将一个对象发送到python。 It works perfectly. 它运作完美。

Edit: I also found I had to override the __setattr__() function in a special way as it was breaking things I added with add_property() . 编辑:我还发现我不得不以一种特殊的方式重写__setattr__()函数,因为它破坏了我用add_property()添加的add_property() Those things worked fine when getting them, since python checks a class's attributes before calling __getattr__() , but there's no such check with __setattr__() . 这些东西在获取它们时效果很好,因为python在调用__getattr__()之前检查了类的属性,但是__setattr__()没有这种检查。 It just calls it directly. 它只是直接调用它。 So I had to make some changes to turn this into a full solution. 因此,我必须进行一些更改才能将其变成完整的解决方案。 Here's the full implementation of the solution: 这是该解决方案的完整实现:

First create a global variable: 首先创建一个全局变量:

boost::python::object PyMyModule_global;

Create a class as follows (with whatever other information you want to add to it): 如下创建一个类(要添加任何其他信息):

class MyClass
{
public:
   //Python checks the class attributes before it calls __getattr__ so we don't have to do anything special here.
   boost::python::object Py_GetAttr(std::string str)
   {
      if(dict.find(str) == dict.end())
      {
         PyErr_SetString(PyExc_AttributeError, JFormat::format("MyClass instance has no attribute '{0}'", str).c_str());
         throw boost::python::error_already_set();
      }
      return dict[str];
   }

   //However, with __setattr__, python doesn't do anything with the class attributes first, it just calls __setattr__.
   //Which means anything that's been defined as a class attribute won't be modified here - including things set with
   //add_property(), def_readwrite(), etc.
   void Py_SetAttr(std::string str, boost::python::object val)
   {
      try
      {
         //First we check to see if the class has an attribute by this name.
         boost::python::object obj = PyMyModule_global["MyClass"].attr(str.c_str());
         //If so, we call the old cached __setattr__ function.
         PyMyModule_global["MyClass"].attr("__setattr_old__")(ptr(this), str, val);
      }
      catch(boost::python::error_already_set &e)
      {
         //If it threw an exception, that means that there is no such attribute.
         //Put it on the persistent dict.
         PyErr_Clear();
         dict[str] = val;
      }
   }
private:
   std::map<std::string, boost::python::object> dict;
};

Then define the python module as follows, adding whatever other defs and properties you want: 然后按如下所示定义python模块,添加所需的任何其他def和属性:

BOOST_PYTHON_MODULE(MyModule)
{
   boost::python::class_<MyClass>("MyClass", boost::python::no_init)
      .def("__getattr__", &MyClass::Py_GetAttr)
      .def("__setattr_new__", &MyClass::Py_SetAttr);
}

Then initialize python: 然后初始化python:

void PyInit()
{
   //Initialize module
   PyImport_AppendInittab( "MyModule", &initMyModule );
   //Initialize Python
   Py_Initialize();

   //Grab __main__ and its globals
   boost::python::object main = boost::python::import("__main__");
   boost::python::object global = main.attr("__dict__");

   //Import the module and grab its globals
   boost::python::object PyMyModule = boost::python::import("MyModule");
   global["MyModule"] = PyMyModule;
   PyMyModule_global = PyMyModule.attr("__dict__");

   //Overload MyClass's setattr, so that it will work with already defined attributes while persisting new ones
   PyMyModule_global["MyClass"].attr("__setattr_old__") = PyMyModule_global["MyClass"].attr("__setattr__");
   PyMyModule_global["MyClass"].attr("__setattr__") = PyMyModule_global["MyClass"].attr("__setattr_new__");
}

Once you've done all of this, you'll be able to persist changes to the instance made in python over to the C++. 完成所有这些操作后,您将能够将对python实例的更改持久保存到C ++。 Anything that's defined in C++ as an attribute will be handled properly, and anything that's not will be appended to dict instead of the class's __dict__ . 在C ++中定义为属性的所有内容都将得到正确处理,而未定义为属性的任何内容都将附加到dict而不是类的__dict__

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

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