__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++ の場合はどうするか思案中。
