__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<>と機能は同じ。
#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"); }
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++ の場合はどうするか思案中。