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