class typed_saver : boost::noncopyable
{
public:
- // 普通のコンストラクタ
+ // t の値を記録して
explicit typed_saver( T& t );
- // t の値を退避してから t1 に書き換える
- typed_saver( T& t, const T& t1 );
-
+ // デストラクタで元に戻す
~typed_saver();
+ // 明示的に値を戻したい場合に呼ぶ
+ // これを呼んでも、デストラクタでは再度値が巻き戻される
+ void invoke();
// これを呼ぶと、デストラクタで値が戻されることはなくなる
void release();
-
- // 明示的な復帰(通常はデストラクタで復帰されるので書く必要なし)
- void restore();
+ // invoke して release する
+ void reset();
};
コンストラクタで受け取った変数の値を記録し、デストラクタで書き戻します。
value_saver と違い変数の型を指定する必要がありますが、その分高速に動作します。
- デストラクタ前でも restore() 関数を呼び出せば、強制的に書き戻すことが可能です。
- その時はデストラクタでの値の書き戻しは行われませんので注意してください。
- またデストラクタでの書き戻しを抑制したいときは、 release() 関数を呼び出せば OK です。
+ 詳しくは value_saver の項を確認して下さい。
補足事項:
typed_saver, value_saver を利用できるオブジェクトの条件は、
- 「コピーコンストラクタか所有権移動コンストラクタを持っている」「 swap 可能」の二点です。
- なので、ちゃんとした swap さえ実装すれば std::auto_ptr 等の変則クラスでも利用できます。
+ ・CopyConstructible
+ ・Assignable
+ の2点です(version1.0.0で変更)。
+ また、例外を投げない swap() 関数呼び出しが存在すれば、例外安全です。
*/
{
// コンストラクタの時の値に、デストラクタで強制復帰させるオブジェクト
template< typename T >
- class typed_saver
- : boost::noncopyable
+ struct typed_saver
+ : private boost::noncopyable
{
- public:
// t の値を記録し、デストラクタで巻き戻す
explicit typed_saver( T& t )
: target_( boost::addressof(t) ), saver_(t) {}
- // t の値を記録してから、t1 に変更、デストラクタで戻す
- typed_saver( T& t, const T& t1 )
- : target_( boost::addressof(t) ), saver_(t)
- {
- t = t1;
- }
-
-
// 復帰
- ~typed_saver()
+ ~typed_saver() { reset(); }
+
+ // 明示的な復帰
+ // この呼出が行われたとしても、デストラクタでは再度 値の復帰が行われる
+ void invoke()
{
- restore();
+ if( target_ )
+ {
+ *target_ = saver_;
+ }
}
// 復帰処理の抑制
{
target_ = 0;
}
-
- // 明示的な復帰(通常はデストラクタで復帰されるので書く必要なし)
- void restore()
+ // 明示的に復帰してから release する
+ void reset()
{
if( target_ )
{
{
public:
template< typename T > explicit value_saver( T& t );
- // t の値を退避してから t1 に書き換える
- template< typename T > value_saver( T& t, const T& t1 );
-
~value_saver();
// ともに typed_saver と同じ
+ void invoke();
void release();
- void restore();
+ void reset();
};
機能:
コンストラクタで受け取った変数の値を記録し、デストラクタで書き戻します。
typed_saver は変数の型を指定する必要がありますが高速に動作し、
- value_saver は変数の型に依らない動作をしますが比較的低速です。
+ value_saver は変数の型に依らない動作をしますが、その分のコストがかかります。
どちらも、release() 関数を呼び出すことでデストラクタでの書き戻しを抑制できます。
- また、デストラクタ前でも restore() 関数により強制的に書き戻すことが可能です。
+ また、デストラクタ前でも invoke() 関数により強制的に書き戻すことが可能です。
+ なお、単に invoke しただけでは、その後のデストラクタでの書き戻しは抑制されません。
+ デストラクタ呼び出し前に書き戻しを行い、かつ release するには、 reset() を呼びます。
補足事項:
typed_saver, value_saver を利用できるオブジェクトの条件は、
- 「コピーコンストラクタか所有権移動コンストラクタを持っている」「 swap 可能」の二点です。
- なので、ちゃんとした swap さえ実装すれば std::auto_ptr 等の変則クラスでも利用できます。
+ ・CopyConstructible
+ ・Assignable
+ の2点です(version1.0.0で変更)。
+ また、デストラクタによる書き戻しにおいては、 swap 関数を利用して無駄なコピーを省いています。
+ 例外を投げない swap() 関数呼び出しが存在すれば、例外安全です。
+
*/
namespace gintenlib
{
// 万能セーバ
- class value_saver
- : boost::noncopyable
+ struct value_saver
+ : private boost::noncopyable
{
- public:
// t の値を記録し、デストラクタで巻き戻す
template< typename T >
explicit value_saver( T& t )
: p( new holder<T>(t) ) {}
- // t の値を記録してから、t1 に変更、デストラクタで戻す
- template< typename T >
- value_saver( T& t, const T& t1 )
- : p( new holder<T>(t) )
- {
- t = t1;
- }
-
-
// 復帰
- ~value_saver()
+ ~value_saver() { reset(); }
+
+ // 明示的な復帰
+ // この呼出が行われたとしても、デストラクタでは再度 値の復帰が行われる
+ void invoke()
{
- restore();
+ if(p)
+ {
+ p->invoke();
+ }
}
-
+ // 復帰抑止
void release()
{
p.reset();
}
- // 明示的な復帰(通常はデストラクタで復帰されるので書く必要なし)
- void restore()
+ // invoke して release する
+ void reset()
{
if(p)
{
- p->restore();
+ p->reset();
p.reset();
}
}
private:
- class basic_holder : boost::noncopyable
+ struct basic_holder
+ : private boost::noncopyable
{
- public:
virtual ~basic_holder(){}
- virtual void restore() = 0;
+ virtual void invoke() = 0;
+ virtual void reset() = 0;
};
-
boost::scoped_ptr<basic_holder> p;
template< typename T >
- class holder : public basic_holder
+ struct holder
+ : basic_holder
{
- public:
holder( T& t )
: target_(t), saver_(t) {}
- ~holder(){}
-
- virtual void restore()
+ virtual void invoke()
+ {
+ target_ = saver_;
+ }
+ virtual void reset()
{
using std::swap;
swap( target_, saver_ );
// ここで元に戻るはず
}
BOOST_CHECK( x == x0 );
-
+ x = x0;
{
// また保存
Saver saver(x);
BOOST_CHECK( x == x1 );
// 明示的な値の復元
- saver.restore();
+ saver.invoke();
// 復元できたかチェック
BOOST_CHECK( x == x0 );
- // restore() 後はデストラクタで復元しない(いいか悪いかは別として)
+ // invoke() 後もきちっと復帰してくれる
x = x1;
}
- BOOST_CHECK( x == x1 );
-
+ BOOST_CHECK( x == x0 );
x = x0;
{
- // 保存と同時に値を書き換える、というのは割とよくある動作なので
- // 楽できるように専用コンストラクタを用意してあります
- Saver saver( x, x1 );
+ // また保存
+ Saver saver(x);
+ // 値を変更
+ x = x1;
BOOST_CHECK( x == x1 );
- // きちんと復元できる?
+ // 復元をしない
+ saver.release();
+ // 値は変更されない
+ BOOST_CHECK( x == x1 );
}
- BOOST_CHECK( x == x0 );
-
+ // 復元されない
+ BOOST_CHECK( x == x1 );
+ x = x0;
{
- // 保存と同時に値を書き換える、というのは割とよくある動作なので
- // 楽できるように専用コンストラクタを用意してあります
- Saver saver( x, x1 );
+ // また保存
+ Saver saver(x);
+ // 値を変更
+ x = x1;
BOOST_CHECK( x == x1 );
- // release() を呼ぶと
- saver.release();
- BOOST_CHECK( x == x1 );
+ // 復帰してから解放
+ saver.reset();
+ // 復帰されたかチェック
+ BOOST_CHECK( x == x0 );
+
+ // 値を変更しても
+ x = x1;
}
- // 復元されない
+ // 復元されないかチェック
BOOST_CHECK( x == x1 );
}