More C++ Idioms/ポインタ参照前後での実行(Execute-Around Pointer)
目次 |
ポインタ参照前後での実行(Execute-Around Pointer) [編集]
意図 [編集]
あるオブジェクトに対する各関数呼び出し前後で、透過的に(全ての関数について同じ)何らかの動作を実行するスマートポインタオブジェクトを提供する。[1]
別名 [編集]
スマートポインタの二重適用
動機 [編集]
しばしば、あるクラスのメンバ関数呼び出しの度に、何らかの機能を実行する必要がある場合がある。 例えば、マルチスレッドアプリケーションでは、データ構造を変更する前にロックし、その後でロックを解除しなくてはならない。 データ構造の可視化アプリケーションでは、毎回の挿入・削除操作後のデータ構造のサイズに興味があるかもしれない。
using namespace std; class Visualizer { std::vector <int> & vect; public: Visualizer (vector<int> &v) : vect(v) {} void data_changed () { std::cout << "現在のサイズ: " << vect.size(); } }; int main () // データ可視化アプリケーション { std::vector <int> vector; Visualizer visu (vector); //... vector.push_back (10); visu.data_changed (); vector.push_back (20); visu.data_changed (); // もっとたくさんの insert/remove の呼び出しと // 対応する可視化コードの呼び出し }
このような関数呼び出しの繰り返しはエラーを招きやすく退屈である。 自動的に可視化コードが呼び出されるのが理想だろう。 Visualizer は std::list<int> についても同様にも使われうるだろう。 このように、単一のクラスの一部分ではなく、むしろ複数のクラスに渡るものは、一般にアスペクト(aspect)として知られている。 このイディオムは、単純なアスペクトを設計し実装するのに有用である。
Solution and Sample Code [編集]
class VisualizableVector { public: class proxy { public: proxy (vector<int> *v) : vect (v) { std::cout << "呼び出し前のサイズ: " << vect->size (); } vector<int> * operator -> () { return vect; } ~proxy () { std::cout << "呼び出し後のサイズ: " << vect->size (); } private: vector <int> * vect; }; VisualizableVector (vector<int> *v) : vect(v) {} proxy operator -> () { return proxy (vect); } private: vector <int> * vect; }; int main() { VisualizableVector vector (new vector<int>); //... vector->push_back (10); // . 演算子ではなく -> 演算子が使われていることに注意せよ vector->push_back (20); }
VisualizableVector のオーバーロードされた -> 演算子は一時的な proxy オブジェクトを生成し、返す。 proxy オブジェクトのコンストラクタでは vector のサイズをログ出力する。 その後、proxy オブジェクトのオーバーロードされた -> 演算子が呼び出され、 ベースとしている vector オブジェクトに対する素のポインタを返すことによって、 単に vector オブジェクトに呼び出しを転送する。 vector への実際の呼び出しが終了した後、proxy のデストラクタが再びサイズをログ出力する。 このように、可視化用のログは透過的であり、main 関数はぐちゃぐちゃにならずに済む。 このイディオムは、より汎用的で強力な Execute Around Proxy idiom の特別な場合である。
賢くテンプレートと組み合わせ、オーバーロードされた -> 演算子を連鎖させた時に、 このイディオムの真の力が現れる。
template <class NextAspect, class Para> class Aspect { protected: Aspect (Para p): para_(p) {} Para para_; public: NextAspect operator -> () { return para_; } }; template <class NextAspect, class Para> struct Visualizing : Aspect <NextAspect, Para> { public: Visualizing (Para p) : Aspect <NextAspect, Para> (p) { std::cout << "可視化アスペクト前" << std::endl; } ~Visualizing () { std::cout << "可視化アスペクト後" << std::endl; } }; template <class NextAspect, class Para> struct Locking : Aspect <NextAspect, Para> { public: Locking (Para p) : Aspect <NextAspect, Para> (p) { std::cout << "ロックアスペクト前" << std::endl; } ~Locking () { std::cout << "ロックアスペクト後" << std::endl; } }; template <class NextAspect, class Para> struct Logging : Aspect <NextAspect, Para> { public: Logging (Para p) : Aspect <NextAspect, Para> (p) { std::cout << "ログアスペクト前" << std::endl; } ~Logging () { std::cout << "ログアスペクト後" << std::endl; } }; template <class Aspect, class Para> class AspectWeaver { public: AspectWeaver (Para p) : para_(p) {} Aspect operator -> () { return Aspect (para_); } private: Para para_; }; #define AW1(T,U) AspectWeaver <T <U, U>, U > #define AW2(T,U,V) AspectWeaver <T < U <V, V> , V>, V > #define AW3(T,U,V,X) AspectWeaver <T < U <V <X, X>, X> , X>, X > int main() { AW3(Visualizing, Locking, Logging, vector <int> *) X (new vector<int>); //... X->push_back (10); // . 演算子ではなく -> 演算子が使われていることに注意せよ X->push_back (20); return 0; }
既知の利用 [編集]
関連するイディオム [編集]
References [編集]
- ^ Execute Around Sequences - Kevlin Henney