C++ and Beyond 2012 "Systematic Error Handling in C++"スライド「ScopeGuard11」より。マクロとラムダ式を組合せてスコープガード文を疑似的に実現している。(注意:末尾のセミコロン必須)
{ ... SCOPE_EXIT { /* 処理A */ }; ... { ... SCOPE_EXIT { /* 処理B */ }; ... } // 処理Bが呼ばれる } // 処理Aが呼ばれる
しかけ
// pp46-47: Implementation template <class Fun> class ScopeGuard { /*(snip)*/ }; // p50: Pseudo-Statement namespace detail { enum class ScopeGuardOnExit {}; template <typename Fun> ScopeGuard<Fun> operator+(ScopeGuardOnExit, Fun&& fn) { return ScopeGuard<Fun>(std::forward<Fun>(fn)); } } #define SCOPE_EXIT \ auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \ = ::detail::ScopeGuardOnExit() + [&]() // p51: Preprocessor Wonders #define CONCATENATE_IMPL(s1, s2) s1##s2 #define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2) #define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__)
SCOPE_EXIT疑似構文の本体部({...}
)は、マクロ展開後の [&]()
と結合してラムダ式の本体部として扱われる。疑似構文全体ではScopeGuard型の変数定義となるため末尾セミコロンは必須。(変数名はANONYMOUS_VARIABLE(SCOPE_EXIT_STATE)
で展開される一意な識別子名)
SCOPE_EXIT {...}; // ↓プリプロセス処理 auto SCOPE_EXIT_STATE42 = ::detail::ScopeGuardOnExit() + [&](){...}; // ↓等価な展開結果(LTはラムダの'型') auto SCOPE_EXIT_STATE42 = ScopeGuard<LT>( [&](){...} );
タグ型ScopeGuardOnExit
と演算子オーバーロードを組合せて、疑似構文「keyword { } ;」を実現している。単にラムダ式だけ使う場合は、疑似構文は「keyword ( { } )」の形をとる(→id:yohhoy:20120629:p1)。結局は好みの問題だが…
関連URL