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) みたいな形で定義したかったなぁ。