C++ タスクシステム⑩ ~ReactiveProperty~
あいさつ
どうも、はかせです。
今回はReactivePropertyの実装をしていきます。
ReactivePropertyとは
変数+Subjectのようなものです。
値が変化したら通知してくれます。
実装方法
前回の能動的に動くSubjectの実装とほとんど同じです。
前回は別スレッドで通知を行っていましたが、
今回は値が変わったら通知を行います。
オペレータのオーバーロード
値の変更を検出する方法はいくつかあると思います。
今回はc++のオペレータのオーバーロードで実装したいと思います。
理由としてはReactivePropertyは変数+Subjectという説明をしました。
なので使い方をなるべく普通の変数に寄せたいということで
この方法を選択しました。
オペレータのオーバーロードは簡単です。
普段メンバ関数を作るときと同じ要領で作成できます。
違うのは+や-をメソッド名にできないので
operatorというキーワードをつけて宣言します。
T operator +(T value) { mValue += value; std::cout << "RP" << std::endl; mSubject->OnNext(mValue); return mValue; }
オーバーロードした中身でOnNextをしています。
C++は組み込み型の初期化をしてくれない
今回の実装をしていく中で詰まって調べたことです。
C#やJava等の言語では組み込み型やプリミティブ型と呼ばれる型は
初期値を設定しない限り0なりnullなりの既定の初期値が入ります。
ですが、C++では既定の初期値は代入されず値は不定となります。
なぜそうなっているかの根拠や理由は発見できませんでした。
「変数の初期化ぐらいプログラマーが責任もってやれや!」
というC++からのメッセージなのかもしれませんね。
ということで初期化を自前実装しました。
といっても何かテクいことをしているわけではなく
オーバーロードでひたすらメソッドを増産してやってます。
//今後便利クラスとか作ったらここに追加していこう namespace MyLib { /* Init 各組み込み型を初期化する c++は組み込み型を初期化してくれないみたいだから自前実装 */ void Init(int & value) { value = 0; } void Init(double & value) { value = 0.0; } void Init(float & value) { value = 0.0f; } }
私はReactivePropertyをスコア管理とかに使おうと考えているので
よく使う数値型3つを実装しました。
今後必要に応じて増やします。
実装
では実際のコードです。
template <class T> class ReactiveProperty { public: //初期値なし ReactiveProperty() { mSubject = new Subject<T>(); MyLib::Init(mValue); }; //初期値あり ReactiveProperty(T value) { mSubject = new Subject<T>(); mValue = value; }; //関数登録 virtual void Subscribe (std::function<void(T value)> next); virtual void Subscribe (std::function<void(T value)> next, std::function<void()> completed); virtual void Subscribe (std::function<void(T value)> next, std::function<void()> completed, std::function<void()> error); //オペレータオーバーロード T operator +(T value) { mValue += value; std::cout << "RP" << std::endl; mSubject->OnNext(mValue); return mValue; } //値を取得 T GetValue() const { return mValue; } //解放 void Dispose() { mSubject->OnCompleted(); delete this; }; private: Subject<T>* mSubject; T mValue; ~ReactiveProperty() { std::cout << "Delete RP!" << std::endl; }; }; template<class T> inline void ReactiveProperty<T>::Subscribe (std::function<void(T value)> next) { mSubject->Subscribe(next); } template<class T> inline void ReactiveProperty<T>::Subscribe (std::function<void(T value)> next, std::function<void()> completed) { mSubject->Subscribe(next, completed); } template<class T> inline void ReactiveProperty<T>::Subscribe (std::function<void(T value)> next, std::function<void()> completed, std::function<void()> error) { mSubject->Subscribe(next, completed, error); }
使い方です。
int main(int argc, char const* argv[]) { #if _DEBUG _CrtSetDbgFlag (_CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); #endif //int型のReactiveProperty生成 auto intrp = new ReactiveProperty<int>(); //初期値設定版 auto intrp2 = new ReactiveProperty<int>(10); //処理登録 intrp->Subscribe ( [&](int value)->void {std::cout << "Value:" << value << std::endl; }, [&]()->void {std::cout << "OnCompleted!" << std::endl; } ); intrp2->Subscribe ( [&](int value)->void {std::cout << "Value:" << value << std::endl; }, [&]()->void {std::cout << "OnCompleted!" << std::endl; } ); getchar(); //オペレータのオーバーロードは実体じゃなきゃ無理ぽ? auto rp = *intrp + 10; auto rp2 = *intrp2 + 10; getchar(); //解放 intrp->Dispose(); intrp2->Dispose(); getchar(); }
実行結果です。
あとがき
今回はReactivePropertyの実装でした。
実装はシンプルですが結構使い勝手のいいものです。
体力をReactivePropertyで作って体力に応じた処理をさせたり
(自身のテクスチャを無傷のものから中破ぐらいのものに変えたり)
スコアを管理したり
(スコアの変更を受けて表示するテキストを変更したり)
本プロジェクト導入が楽しみです。
それでは今回はこの辺でノシ