C++14 で名前付きタプル

C++14 で名前付きタプルのようなものを書いてみた。

[ソース]

#include <boost/preprocessor/seq.hpp>
#include <boost/preprocessor/tuple.hpp>
#include <boost/preprocessor/cat.hpp>

#define PACK_FILLER_0(X, Y)              \
    ((X, Y)) PACK_FILLER_1
#define PACK_FILLER_1(X, Y)              \
    ((X, Y)) PACK_FILLER_0
#define PACK_FILLER_0_END
#define PACK_FILLER_1_END

#define UNPACK(X, Y)  X, Y


#define VARIABLE(INDEX, NAME, VALUE)    \
    decltype(VALUE) NAME = VALUE;        \
    auto get(index<INDEX>) const{        \
        return NAME;                    \
    }                                    \

#define VARIABLE_MACRO(r, data, i, elem)    \
    VARIABLE(                                \
        i,                                    \
        BOOST_PP_TUPLE_ELEM(2, 0, elem),    \
        BOOST_PP_TUPLE_ELEM(2, 1, elem)        \
    )                                        \

#define VARIABLES(SEQ)                    \
    BOOST_PP_SEQ_FOR_EACH_I(            \
        VARIABLE_MACRO, _,                \
        BOOST_PP_SEQ_TAIL(BOOST_PP_CAT(PACK_FILLER_0(0,0)SEQ,_END))            \
    )                                    \

#define NAMED_TUPLE(SEQ)        \
    []{                            \
        struct X{                \
            VARIABLES(SEQ)        \
        };                        \
        return X{};                \
    }()                        \


#include <iostream>
#include <string>


template<int N>
struct index
    : std::integral_constant<int, N>{};

template<int N, typename T>
auto
get(T&& t){
    return t.get(index<N>{});
}


namespace detail{

template<int Index, typename T>
constexpr int
tuple_size(short){
    return Index;
}

template<int Index, typename T>
constexpr auto
tuple_size(int)
->decltype(std::declval<T>().get(index<Index>{}), 0){
    return tuple_size<Index+1, T>(0);
}

}  // namespace detail


template<typename T>
struct tuple_size
    : std::integral_constant<int, detail::tuple_size<0, T>(0)>{};


template<typename T>
void
print(T&& t){
    std::cout << t.name << std::endl;
    std::cout << t.age << std::endl;
    std::cout << (t.is_majo ? "魔女" : "魔法少女") << std::endl;
}

int
main(){
/*
    // Example
    auto homu = []{
        struct X{
            int age = 14;
            auto
            get(index<0>) const{
                return age;
            }

            char const* name = "homu";
            auto
            get(index<1>) const{
                return name;
            }

            bool is_majo = false;
            auto
            get(index<2>) const{
                return is_majo;
            }
        };
        return X{};
    }();
*/
    auto homu = NAMED_TUPLE((name, "homu")(age, 14)(is_majo, true));

    // タプルのサイズ
    static_assert(tuple_size<decltype(homu)>{} == 3, "");
    // 名前でアクセス
    std::cout << homu.name << std::endl;
    std::cout << homu.age << std::endl;
    std::cout << homu.is_majo << std::endl;

    auto mami = NAMED_TUPLE((name, "mami")(age, 15)(is_majo, false));
    // インデックスアクセス
    std::cout << get<0>(mami) << std::endl;
    std::cout << get<1>(mami) << std::endl;
    std::cout << get<2>(mami) << std::endl;

    print(homu);
    print(mami);
    // 関数の引数で定義したり
    print(NAMED_TUPLE((name, "mado")(age, 14)(is_majo, false)));

    return 0;
}

[出力]

homu
14
1
mami
15
0
homu
14
魔女
mami
15
魔法少女
mado
14
魔法少女


やっていることはマクロを使っていい感じにラムダ内でローカルなクラスを定義しているだけです。
まぁマクロはあれですけど、C++14 だとラムダがだいぶパワーアップしているので割りとやりたい放題ですね。
Boost.Fusion にもアダプトできればかなり使い勝手はなかなかよさそう。
……本当は NAMED_TUPLE(name = "mado", age = 14, is_majo = false) みたいな形で定義したかったなぁ。