2012-12-05 38 views
5

私は組み込みのスクリプトエンジンとしてPythonを使用している小さなプロジェクトに取り組んでいます。これまではboost.pythonを使用することでそれほど問題はありませんでしたが、可能ならばそれを使ってやりたいことがあります。Boost.Python - boost :: python :: objectをPython関数の引数として渡しますか?

基本的にPythonを使用すると、関数やデータ値をクラスに追加してC++クラスを拡張することができます。私はC + +側でこれらの永続化を持つことができるようにしたい、1つのPython関数は、クラスにデータメンバーを追加することができますし、後で別の関数に渡された同じインスタンスはまだそれらを持っています。ここでの目標は、C++で一般的なコアエンジンを作成し、ユーザーがPythonでC++に触れることなく必要な方法で拡張できるようにすることです。私は仕事だろうと思ったので、何

は、私は、値self、およびC++からのpythonを呼び出すときに、私はboost::python::ptr()ているPythonのオブジェクトを送信したいとC++クラスのboost::python::objectを保存するというものであったように修正Python側はC++クラスを継承します。残念ながら、私はこれをしようとすると、私は次のエラーを取得:

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

をそのようなPythonの関数に直接オブジェクトを渡すのいずれかの方法、または私は私の希望を達成するために、このついて行くことができ、他の方法はあります結果?

ご協力いただきありがとうございます。 :)

+0

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番目に渡したいオブジェクトに追加します。それは私が望むように機能します。あるイベントで属性を追加したとき、別のイベントに属性を渡したときに属性が追加されました。多分最高の解決策ではないかもしれません...しかし、それは動作します。 –

答えて

5

この素晴らしいソリューションは、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には何も追加されません。

+0

素晴らしい!共有ありがとう! – mrmclovin

関連する問題