[英]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.