ScopeGuardというテクニックがある
簡単にいえば、あとから付け足すデストラクタ。もしくは関数内デストラクタといったところ
BoostにあるScopeGuardを使ってのサンプル
#include <string> #include <boost/multi_index/detail/scope_guard.hpp> using namespace boost::multi_index::detail; int main() { FILE *fp = fopen("C:\\a.txt", "wb"); scope_guard guard = make_guard(fclose, fp); // ファイル(fp)を使った処理... std::string str = "abc"; fwrite(str.c_str(), str.length(), 1, fp); return 0; // ←ここでfcloseされる }
実装方法
まずベースとなるものを用意
class scope_guard_impl_base { public: void dismiss() const { dismissed_ = true; } protected: scope_guard_impl_base() : dismissed_(false) {} scope_guard_impl_base(const scope_guard_impl_base& other) : dismissed_(other.dismissed_) {} ~scope_guard_impl_base() {} // nonvirtual mutable bool dismissed_; private: scope_guard_impl_base& operator=(const scope_guard_impl_base&); };
↑で作ったクラスを継承し、関数とその引数を保持し、保持した関数をデストラクタで実行
template <typename Func, typename Param> class scope_guard_impl1 : public scope_guard_impl_base { Func func_; const Param param_; public: scope_guard_impl1(Func func, const Param& param) : func_(func), param_(param) {} ~scope_guard_impl1() { if (!dismissed_) func_(param_); } };
scope_guard_impl1のヘルパ関数
template <typename Func, typename Param> scope_guard_impl1<Func, Param> make_guard(Func func, const Param& param) { return scope_guard_impl1<Func, Param>(func, param); }
scope_guard_impl_baseに別名を付けて完成
typedef const scope_guard_impl_base& scope_guard;
あとはお好みに合わせて↓を作ってください
・複数の引数を取るscope_guard_implN
・make_guardのオーバーロード
・メンバ関数版(インスタンスと、実行するメンバ関数へのポインタを引数に取る)
ちなみにscope_guard(scope_guard_impl_base)内にあるメンバ関数dismissを実行すると
make_guardで登録した動作をさせずに終了することができる
それはもちろん例外安全のため
Commit/Rollbackに使ってください
参考サイト
Generic: Change the Way You Write Exception-Safe Code - Forever
ベイダー日記 - 「RAII を ScopeGuard を使って実践」