読者です 読者をやめる 読者になる 読者になる

ScopeGuard

C++

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

p_stade - 「Boost.ScopeGuard」

ベイダー日記 - 「ScopeGuard」

ベイダー日記 - 「RAII を ScopeGuard を使って実践」


ライブラリまとめ