この素晴らしいソリューションは、C++のsigメーリングリストで入手できました。
std::map<std::string, boost::python::object>
をC++クラスに実装し、__getattr__()
と__setattr__()
をオーバーロードしてstd :: mapとの間で読み書きを行います。それから、いつものようにboost::python::ptr()
というPythonに送るだけで、C++側でオブジェクトを保持したり、Pythonに送る必要はありません。それは完全に動作します。
編集:私はで追加したものを壊していたので、特別な方法で__setattr__()
関数をオーバーライドしなければならないことも発見しました。それらを取得するとうまくいきました。なぜなら、Pythonは__getattr__()
を呼び出す前にクラスの属性をチェックしますが、__setattr__()
というようなチェックはありません。それは単にそれを直接呼び出します。だから、これを完全な解決策に変えるためにいくつかの変更を加えなければならなかった。ここではソリューションの完全な実装です:
まず、グローバル変数を作成します。
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;
};
:
boost::python::object PyMyModule_global;
(あなたがそれに追加する他のどんな情報で)次のようにクラスを作成します。次に、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__");
}
あなたはこのすべてを完了したら、あなたはC++にかけpythonで作られたインスタンスへの変更を永続化することができるでしょう。属性としてC++で定義されたものはすべて正しく処理され、クラスの__dict__
の代わりにdict
には何も追加されません。
1つの解決策が見つかりました。Pythonでは、私は関数を追加しました: def ProcessEvent(event、obj):return globals()[obj.PyObj] C++で、私は 'boost :: python :: object'を追加しました。私のC++クラスに 'PyObj'という名前で' boost :: python :: ptr(this) 'に初期化され、それにmyイベントが呼び出され、最初のパラメータとして呼び出すイベントの名前と' boost :: python :: ptr'を2番目に渡したいオブジェクトに追加します。それは私が望むように機能します。あるイベントで属性を追加したとき、別のイベントに属性を渡したときに属性が追加されました。多分最高の解決策ではないかもしれません...しかし、それは動作します。 –