yohhoyの日記

技術的メモをしていきたい日記

SCOPE_EXIT疑似構文の作り方

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