range-based for マクロを書いてみた
range-based for は案5に決まったらしいので、勉強がてらに自分で実装してみました。せっかくなので、VC10 でも動くようにしています。まずは使い方から。
int main() { // http://www.kmonos.net/alang/boost/classes/foreach.html int array[] = {1, 2, 3, 4, 5}; RANGE_BASED_FOR(int &e, array) if (e % 2 == 0) e = 0; RANGE_BASED_FOR(int e, array) { std::cout << e << std::endl; } std::vector<std::pair<int, int>> v; v.push_back(std::make_pair(1, 1)); v.push_back(std::make_pair(2, 4)); v.push_back(std::make_pair(3, 9)); // 宣言がカンマを含む場合も括弧で囲めば OK RANGE_BASED_FOR((std::pair<int, int> e), v) std::cout << e.first << ": " << e.second << std::endl; // [GCC 4.5 以降] initializer-list はそのまま渡してください RANGE_BASED_FOR(auto i, {1, 2, 3}) std::cout << i << std::endl; }
実装ですが、まずメンバー関数 begin/end を探しに行き、それで見つからなければ ADL にフォールバックします。このとき、std 名前空間を associated namespace として付け加えなければなりませんが、直接それはできないので、通常の unqualified name lookup で見つかる位置に std::begin/end またはそれ相当のものを配置します。
#include <cstddef> #include <iterator> #include <utility> namespace range_based_for { // メンバー関数を探す template <class Range> auto begin_impl(Range &&r, int) -> decltype(r.begin()) { return r.begin(); } template <class Range> auto end_impl(Range &&r, int) -> decltype(r.end()) { return r.end(); } // メンバー関数が見つからなかった場合は、ADL にフォールバックする namespace adl { #if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 5 using std::begin; using std::end; #else template <class Range> auto begin(Range &r) -> decltype(r.begin()) { return r.begin(); } template <class Range> auto begin(Range const &r) -> decltype(r.begin()) { return r.begin(); } template <class Range> auto end(Range &r) -> decltype(r.end()) { return r.end(); } template <class Range> auto end(Range const &r) -> decltype(r.end()) { return r.end(); } template <class T, std::size_t N> T *begin(T (&a)[N]) { return a; } template <class T, std::size_t N> T *end(T (&a)[N]) { return a + N; } #endif template <class Range> auto begin_impl(Range &&r, ...) -> decltype(begin(r)) { return begin(r); } template <class Range> auto end_impl(Range &&r, ...) -> decltype(end(r)) { return end(r); } } using adl::begin_impl; using adl::end_impl; template <class Range> auto begin(Range &&r) -> decltype(range_based_for::begin_impl(std::forward<Range>(r), 0)) { return range_based_for::begin_impl(std::forward<Range>(r), 0); } template <class Range> auto end(Range &&r) -> decltype(range_based_for::end_impl(std::forward<Range>(r), 0)) { return range_based_for::end_impl(std::forward<Range>(r), 0); } } #define PP_REM(x) PP_REM_I(PP_REM_HELPER x) #define PP_REM_I(x) PP_REM_II(x) #define PP_REM_II(...) PP_REM_DEF_ ## __VA_ARGS__ #define PP_REM_DEF_PP_REM_HELPER #define PP_REM_HELPER(...) PP_REM_HELPER __VA_ARGS__
#define RANGE_BASED_FOR(decl, ...) \ if (bool FOR_1 = true) \ for (auto &&FOR_range = __VA_ARGS__; FOR_1; FOR_1 = false) \ for (decltype(range_based_for::begin(FOR_range)) \ FOR_begin = range_based_for::begin(FOR_range), \ FOR_end = range_based_for::end(FOR_range); \ FOR_begin != FOR_end; \ ++FOR_begin) \ if (bool FOR_2 = true) \ for (PP_REM(decl) = *FOR_begin; FOR_2; FOR_2 = false)
最後に簡単なマクロを書いて仕上げです。
追記: break, continue に対応できていなかったので直しました。ご指摘感謝。
namespace range_based_for { template <class T> struct holder { holder(T &&x) : value(std::forward<T>(x)) {} operator bool() const { return false; } T value; }; template <class T> holder<T> make_holder(T &&x) { return std::forward<T>(x); } } } #define RANGE_BASED_FOR(decl, ...) \ if (bool FOR_once_flag = false) {} \ else for (auto &&FOR_range = __VA_ARGS__; \ !FOR_once_flag; \ FOR_once_flag = true) \ if (auto &&FOR_begin = \ range_based_for::make_holder(range_based_for::begin(FOR_range))) {} \ else if (auto &&FOR_end = \ range_based_for::make_holder(range_based_for::end(FOR_range))) {} \ else for (bool FOR_continue = true; \ FOR_continue && FOR_begin.value != FOR_end.value; \ ++FOR_begin.value) \ if ((FOR_continue = false)) {} \ else for (PP_REM(decl) = *FOR_begin.value; \ !FOR_continue; \ FOR_continue = true)
追記: これはとっても冗長だなって
より良い解
これでかんぺきちゃん URL
2011-04-13 13:48:12 via web
#define RANGE_BASED_FOR(decl, ...) \ if (bool FOR_1 = true) \ for (auto &&FOR_range = __VA_ARGS__; FOR_1; FOR_1 = false) \ for (decltype(range_based_for::begin(FOR_range)) \ FOR_begin = range_based_for::begin(FOR_range), \ FOR_end = range_based_for::end(FOR_range); \ FOR_1 && FOR_begin != FOR_end; \ FOR_1 && ((void)++FOR_begin, 0)) \ if (!(FOR_1 = false)) \ for (PP_REM(decl) = *FOR_begin; !FOR_1; FOR_1 = true)