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)


追記: これはとっても冗長だなって

より良い解

追記: さらに削ってみました

#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)