__eventを使ったイベントハンドリング – CREST’S WEBLOG (」・ω・)」うー!(/・ω・)/にゃー!
http://d.hatena.ne.jp/Crest/20100418/1271603367
__event
http://msdn.microsoft.com/en-us/library/cb1dzt8t(v=vs.100).aspx
MS-C++ では __declspec(property()) でプロパティを作れるよ | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3557
を書いてみて、ひょっとすると event/delegate 系も使えるようになっているのでは?と思ったらありました。
__event/__hook の組み合わせでいけます。C# の event/delegate, Action<>と機能は同じ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | #include "stdafx.h" #include <afxwin.h> #include <iostream> #include <string> using namespace std; // Model側のインターフェース(INotify) class INotifyProperyChange { public : __event void PropertyChange( INotifyProperyChange *model, void *name, void *value, const type_info &info ); }; // View側のインターフェース(ITarget) class ITargetPropertyChanged { public : void OnProperyChanged( INotifyProperyChange *model, void *name, void *value, const type_info &info ); }; class Model : public INotifyProperyChange { private : int m_num; string m_name; public : __declspec(property( get =getNum, put=setNum)) int Num; int getNum() { return m_num ; } void setNum( int value ) { m_num = value; PropertyChange( this , "Num" , &value, typeid(value) ); } __declspec(property( get =getName,put=setName)) string Name; string &getName() { return m_name; } void setName( string &value ) { m_name = value; PropertyChange( this , "Name" , &value, typeid(value) ); } }; // View は Model から独立している class View : public ITargetPropertyChanged { private : int m_num; string m_name; public : __declspec(property( get =getNum, put=setNum)) int Num; int getNum() { return m_num; } void setNum( int value ) { m_num = value; cout << "View::Num: " << value << endl; } __declspec(property( get =getName,put=setName)) string Name; string &getName() { return m_name; } void setName( string &value ) { m_name = value; cout << "View::Name: " << value << endl; } public : void OnProperyChanged( INotifyProperyChange *model, void *name, void *value, const type_info &info ) { // ここを map にする? string propName(( const char *)name); if ( propName == string ( "Num" )) { this ->Num = *( int *)value; } else if ( propName == string ( "Name" )) { this ->Name = *( string *)value; } } }; // ViewModel は View と Model の両方に依存しているが、 // View のほうへの依存度が高い class ViewModel { private : View *m_view; Model *m_model; public : void OnProperyChanged( INotifyProperyChange *model, void *name, void *value, const type_info &info ) { m_view->OnProperyChanged( model, name, value, info ); } void Bind( Model *model, View *view ) { this ->m_model = model; this ->m_view = view; __hook( &Model::PropertyChange, model, &ViewModel::OnProperyChanged, view ); } }; int _tmain( int argc, _TCHAR* argv[]) { Model model; View view; ViewModel vm; vm.Bind( &model, &view ); model.Num = 10; model.Name = string ( "masuda tomoaki" ); } |
1 2 3 | D:\work\blog\src\CppProperty\Debug>CppProperty.exe View::Num: 10 View::Name: masuda tomoaki |
ざっと書くとこんな感じ。C# の一般的な mvvm よりも、Model と View の独立性を強くしてあります。__hook の利用法が面白いところで、__hook( ソースの関数ポインタ、ソースオブジェクト、ターゲットの関数ポインタ) を設定します。ここで仕様バグなのかどうかは不明ですが、ターゲットの関数ポインタは、何も自分のクラスのメソッドである必要はありません。ここでは &ViewModel::OnProperyChanged と自分自身を示していますが、&View::OnProperyChanged のほうに他クラスの関数ポインタも示せるという優れもの…と言いますかいい加減な実装がw。ソースの関数メソッドとターゲットの関数メソッドの「型があっていればOK」といういい加減な実装(意図的?)なために、かなり自由度が高いのです。
これを試しに interface だけ(class継承だけ)を使うと、どうしてもうまく行きません。関数ポインタの型がきつくて、一度 static 関数を通さないと実装ができないという状態なので、__hook は重宝します。
まあ、考えてみれば、c/c++ なのだからインラインアセンブラを作れば良いのでした。なるほど。
さて、これでコマンドラインでは、c++ で(おそらく ms-c 限定ですが) mvvm の INotiry, ITarget が動くことが分かりました。今度は windows form(内部的には *.rc)を使って実装してみましょうか。DDX_Control マクロと UpdateData 関数を使わずに実装できるかも。
そうそう View::OnProperyChanged の実装がいまいちなので、View のプロパティ名=バインド名の部分をなんとかしたいですね。C# ならばリフレクションを使うところですが、C++ の場合はどうするか思案中。