std::tuple同士の加算
いちいち要素ごとに足すのが面倒です.
要素ごとにそれぞれのoperator+に委譲する形で実装しようとしたんですが,
MSVCには未だにVariadic Templateがなくstd::tupleの実装がエミュレーションでしかないので
これまた面倒な事に.
Boost.PPを使って無理やり実装しました.
MSVC11(VS2012RC)
//括弧外し用 #define UNPAREN(...)__VA_ARGS__ //タプルの最大数の設定がよくわからない #define TUPLE_SIZE BOOST_PP_ADD(_VARIADIC_MAX, 3) // typename Txを生成 #define TYPENAME(x, i, name) (typename name##i) // ↑をコンマ連結 #define TEMPLATE_LIST \ BOOST_PP_LIST_REST ( \ BOOST_PP_SEQ_TO_ARRAY( \ BOOST_PP_REPEAT(TUPLE_SIZE, TYPENAME, T) \ ) \ ) // template宣言 #define TUPLE_TEMPLATE_DECLARE template <BOOST_PP_EXPAND(UNPAREN TEMPLATE_LIST)> // Tx #define TEMPLATE_PARAM(x, i, name) (name##i) // ↑をコンマ連結 #define TYPELIST \ BOOST_PP_LIST_REST ( \ BOOST_PP_SEQ_TO_ARRAY( \ BOOST_PP_REPEAT(TUPLE_SIZE, TEMPLATE_PARAM, T) \ ) \ ) // タプル型名 #define TUPLE_TYPE std::tuple<BOOST_PP_EXPAND(UNPAREN TYPELIST)> namespace detail { template<int size, typename T> /** * operator+=の実装部分 テンプレート関数にはパラメータを明示的に渡せないので * 関数オブジェクトを定義して特殊化する */ struct PlusEqImpl { T& operator()(T&, const T&); }; // operator+=を呼ぶ #define ELEM_PLUS_EQ(x, i, name) \ std::get<i>(lhs) += std::get<i>(rhs); // タプルサイズに応じて特殊化する #define PLUS_EQ_IMPL(x, i, name) \ template<typename T> \ struct PlusEqImpl<i, T> \ { \ T& operator()(T& lhs, const T& rhs) \ { \ BOOST_PP_REPEAT(i, ELEM_PLUS_EQ, ) \ return lhs; \ } \ }; BOOST_PP_REPEAT(BOOST_PP_ADD(TUPLE_SIZE, 1), PLUS_EQ_IMPL, ) #undef ELEM_PLUS_EQ #undef PLUS_EQ_IMPL } // namespace detail TUPLE_TEMPLATE_DECLARE TUPLE_TYPE & /** 同じ型のタプルに対してoperator+=を定義 */ operator+=(TUPLE_TYPE & lhs, const TUPLE_TYPE & rhs) { return detail::PlusEqImpl<std::tuple_size<TUPLE_TYPE>::value, TUPLE_TYPE>()(lhs, rhs); } #undef OP_PLUTEQUAL TUPLE_TEMPLATE_DECLARE TUPLE_TYPE /** operator+=からoperator+を定義 */ operator+(const TUPLE_TYPE & lhs, const TUPLE_TYPE & rhs) { return TUPLE_TYPE(lhs)+=rhs; } #undef UNPAREN #undef TYPENAME #undef TEMPLATE_LIST #undef TUPLE_TEMPLATE_DECLARE #undef TEMPLATE_PARAM #undef TYPELIST #undef TUPLE_TYPE
直感的に扱えて満足.
auto x = std::make_tuple(1, 0.5, std::string("abc")); auto y = std::make_tuple(2, 1.5, std::string("xyz")); auto z = x + y; Assert::AreEqual(std::get<0>(z), 3); Assert::AreEqual(std::get<1>(z), 2.0, 0.001); Assert::AreEqual(std::get<2>(z), std::string("abcxyz"));
タプルの型引数が違うとコンパイルエラーです.
多分それが望ましい動作…と思ったけれど,+=が使えるなら通ったほうがいいんですかね.
気が向いたらいじります.
しかし,Variadic Templateが使えればかなり楽に書けるんでしょうねえ.