簡體   English   中英

Boost.Python-將boost :: python :: object作為參數傳遞給python函數嗎?

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

因此,我正在做一個小項目,其中使用Python作為嵌入式腳本引擎。 到目前為止,我使用boost.python並沒有遇到太多麻煩,但是如果可能的話,我想對其進行一些處理。

基本上,Python可以通過向類添加函數甚至數據值來擴展我的C ++類。 我希望能夠將這些保留在C ++端,因此一個python函數可以將數據成員添加到類中,然后在傳遞給另一個函數的同一個實例中仍然保留它們。 這里的目標是用C ++編寫一個通用的核心引擎,並允許用戶以他們需要的任何方式在Python中擴展它,而無需接觸C ++。

所以我認為可行的是,我將在C ++類中將boost::python::object存儲為值self ,並且從C ++調用python時,我會通過boost::python::ptr()發送該python對象boost::python::ptr() ,以便在python端進行的修改將保留回C ++類。 不幸的是,當我嘗試此操作時,出現以下錯誤:

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

有沒有辦法像這樣將對象直接傳遞給python函數,或者我可以通過其他任何方式達到期望的結果?

在此先感謝您的幫助。 :)

從c ++ sig郵件列表中獲得了這個出色的解決方案。

在C ++類中實現std::map<std::string, boost::python::object> ,然后重載__getattr__()__setattr__()來讀取和寫入該std :: map。 然后只需像往常一樣使用boost::python::ptr()將其發送到python,無需在C ++端保留一個對象或將一個對象發送到python。 它運作完美。

編輯:我還發現我不得不以一種特殊的方式重寫__setattr__()函數,因為它破壞了我用add_property()添加的add_property() 這些東西在獲取它們時效果很好,因為python在調用__getattr__()之前檢查了類的屬性,但是__setattr__()沒有這種檢查。 它只是直接調用它。 因此,我必須進行一些更改才能將其變成完整的解決方案。 這是該解決方案的完整實現:

首先創建一個全局變量:

boost::python::object PyMyModule_global;

如下創建一個類(要添加任何其他信息):

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

然后按如下所示定義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);
}

然后初始化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__");
}

完成所有這些操作后,您將能夠將對python實例的更改持久保存到C ++。 在C ++中定義為屬性的所有內容都將得到正確處理,而未定義為屬性的任何內容都將附加到dict而不是類的__dict__

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM