make_append_tuple を書いた
template の練習がてら書いてみようと思ったらウンコードが完成した。
tupleをくっつけるだけにしようと思っていたのだが、面白く無いのでめちゃくちゃにしてやった。
後悔はしていない。
まずはそれを見て貰いたいと思う。
#include <iostream> #include <type_traits> #include <tuple> namespace mpl { template <bool, typename T1, typename T2> struct typeif_c { using type = T1; }; template <typename T1, typename T2> struct typeif_c<false, T1, T2> { using type = T2; }; template <bool B, typename T1, typename T2> struct eval_typeif_c : typeif_c<B, T1, T2>::type {}; template <class M, typename T1, typename T2> struct typeif : typeif_c<M::value, T1, T2> {}; template <class M, typename T1, typename T2> struct eval_typeif : typeif_c<M::value, T1, T2>::type {}; template <typename> struct is_tuple : std::false_type {}; template <typename... Types> struct is_tuple<std::tuple<Types...>> : std::true_type {}; template <typename...> struct append_tuple_impl { using type = append_tuple_impl; }; template <typename T> struct append_tuple_impl<T> { using type = typename typeif_c< is_tuple<T>::value, T, std::tuple<T> >::type; }; template <typename T, typename... Args> struct append_tuple_impl<T, Args...> : append_tuple_impl<std::tuple<T>, Args...> {}; template <typename... Types> struct append_tuple_impl<std::tuple<Types...>, append_tuple_impl<>> { using type = std::tuple<Types...>; }; template <typename... Types1, typename... Types2> struct append_tuple_impl<std::tuple<Types1...>, std::tuple<Types2...>> { using type = std::tuple<Types1..., Types2...>; }; template <typename... Types1, typename T, typename... Types2> struct append_tuple_impl<std::tuple<Types1...>, T, Types2...> : append_tuple_impl<std::tuple<Types1..., T>, typename append_tuple_impl<Types2...>::type> {}; template <typename... Types1, typename... Types2, typename... Types3> struct append_tuple_impl<std::tuple<Types1...>, std::tuple<Types2...>, Types3...> : append_tuple_impl<std::tuple<Types1..., Types2...>, typename append_tuple_impl<Types3...>::type> {}; template <typename T> using remove_r_t = typename std::remove_reference<T>::type; template <typename... Types> struct append_tuple : append_tuple_impl<remove_r_t<Types>...> {}; template < index_t... N, typename... Types, typename T > auto make_append_tuple_helper1( std::tuple<Types...>&& t, T&& a, index_tuple<N...> ) -> decltype( std::make_tuple( std::get<N>( t )..., a ) ) { return std::make_tuple( std::get<N>( t )..., a ); } template < index_t... N, typename... Types, typename T > auto make_append_tuple_helper1( std::tuple<Types...>& t, T&& a, index_tuple<N...> ) -> decltype( std::make_tuple( std::get<N>( t )..., a ) ) { return std::make_tuple( std::get<N>( t )..., a ); } template < index_t... N, typename... Types, typename T > auto make_append_tuple_helper2( T&& a, std::tuple<Types...>&& t, index_tuple<N...> ) -> decltype( std::make_tuple( a, std::get<N>( t )... ) ) { return std::make_tuple( a, std::get<N>( t )... ); } template < index_t... N, typename... Types, typename T > auto make_append_tuple_helper2( T&& a, std::tuple<Types...>& t, index_tuple<N...> ) -> decltype( std::make_tuple( a, std::get<N>( t )... ) ) { return std::make_tuple( a, std::get<N>( t )... ); } template < typename... Types1, typename... Types2, index_t... N, index_t... M > auto make_append_tuple_helper3( std::tuple<Types1...>&& t1, std::tuple<Types2...>&& t2, index_tuple<N...>, index_tuple<M...> ) -> decltype( std::make_tuple( std::get<N>( t1 )..., std::get<M>( t2 )... ) ) { return std::make_tuple( std::get<N>( t1 )..., std::get<M>( t2 )... ); } template < typename... Types1, typename... Types2, index_t... N, index_t... M > auto make_append_tuple_helper3( std::tuple<Types1...>& t1, std::tuple<Types2...>& t2, index_tuple<N...>, index_tuple<M...> ) -> decltype( std::make_tuple( std::get<N>( t1 )..., std::get<M>( t2 )... ) ) { return std::make_tuple( std::get<N>( t1 )..., std::get<M>( t2 )... ); } template < typename T1, typename T2 > auto make_append_tuple_helper( T1&& t1, T2&& t2 ) -> decltype( std::make_tuple( t1, t2 ) ) { return std::make_tuple( t1, t2 ); } template < typename... Types, typename T > std::tuple<Types..., remove_r_t<T>> make_append_tuple_helper( std::tuple<Types...>& t, T&& arg ) { return make_append_tuple_helper1( std::forward<std::tuple<Types...>>( t ), std::forward<T>( arg ), index_range<0, sizeof...(Types), 1>{} ); } template < typename... Types, typename T > std::tuple<Types..., remove_r_t<T>> make_append_tuple_helper( std::tuple<Types...>&& t, T&& arg ) { return make_append_tuple_helper1( std::forward<std::tuple<Types...>>( t ), std::forward<T>( arg ), index_range<0, sizeof...(Types), 1>{} ); } template < typename T, typename... Types > std::tuple<remove_r_t<T>, Types...> make_append_tuple_helper( T&& arg, std::tuple<Types...>& t ) { return make_append_tuple_helper2( std::forward<T>( arg ), std::forward<std::tuple<Types...>>( t ), index_range<0, sizeof...(Types), 1>{} ); } template < typename T, typename... Types > std::tuple<remove_r_t<T>, Types...> make_append_tuple_helper( T&& arg, std::tuple<Types...>&& t ) { return make_append_tuple_helper2( std::forward<T>( arg ), std::forward<std::tuple<Types...>>( t ), index_range<0, sizeof...(Types), 1>{} ); } template <typename... Types1, typename... Types2> std::tuple<Types1..., Types2...> make_append_tuple_helper( std::tuple<Types1...>&& t1, std::tuple<Types2...>&& t2 ) { return make_append_tuple_helper3( std::forward<std::tuple<Types1...>>( t1 ), std::forward<std::tuple<Types2...>>( t2 ), index_range<0, sizeof...(Types1), 1>{}, index_range<0, sizeof...(Types2), 1>{} ); } template <typename... Types1, typename... Types2> std::tuple<Types1..., Types2...> make_append_tuple_helper( std::tuple<Types1...>& t1, std::tuple<Types2...>& t2 ) { return make_append_tuple_helper3( std::forward<std::tuple<Types1...>>( t1 ), std::forward<std::tuple<Types2...>>( t2 ), index_range<0, sizeof...(Types1), 1>{}, index_range<0, sizeof...(Types2), 1>{} ); } template <typename... Types1, typename... Types2> std::tuple<Types1..., Types2...> make_append_tuple_helper( std::tuple<Types1...>& t1, std::tuple<Types2...>&& t2 ) { return make_append_tuple_helper3( std::forward<std::tuple<Types1...>>( t1 ), std::forward<std::tuple<Types2...>>( t2 ), index_range<0, sizeof...(Types1), 1>{}, index_range<0, sizeof...(Types2), 1>{} ); } template <typename... Types1, typename... Types2> std::tuple<Types1..., Types2...> make_append_tuple_helper( std::tuple<Types1...>&& t1, std::tuple<Types2...>& t2 ) { return make_append_tuple_helper3( std::forward<std::tuple<Types1...>>( t1 ), std::forward<std::tuple<Types2...>>( t2 ), index_range<0, sizeof...(Types1), 1>{}, index_range<0, sizeof...(Types2), 1>{} ); } template <typename T> auto make_append_tuple( T&& t ) -> decltype( std::make_tuple( std::forward<T>( t ) ) ) { return std::make_tuple( std::forward<T>( t ) ); } template <typename... Types> std::tuple<Types...> make_append_tuple( std::tuple<Types...>& t ) { return t; } template <typename... Types> auto make_append_tuple( std::tuple<Types...>&& t ) -> decltype( std::move( t ) ) { return std::move( t ); } template <typename T1, typename T2, typename... Args> typename append_tuple<T1, T2, Args...>::type make_append_tuple( T1&& t1, T2&& t2, Args&&... args ) { return make_append_tuple( make_append_tuple_helper( std::forward<T1>( t1 ), std::forward<T2>( t2 ) ), std::forward<Args>( args )... ); } } int main() { auto tuple1 = std::make_tuple( 5, "hoge", true ); // tuple<int, const char*, bool, float> auto tuple2 = mpl::make_append_tuple( tuple1, 1.5f ); std::cout << std::get<0>( tuple2 ) << std::endl; // tuple<float, int, const char*, bool, float> auto tuple3 = mpl::make_append_tuple( 1.5f, tuple1 ); std::cout << std::get<0>( tuple3 ) << std::endl; // tuple<int, const char*, bool, int, const char*, bool, float> auto tuple4 = mpl::make_append_tuple( tuple1, tuple2 ); std::cout << std::get<4>( tuple4 ) << std::endl; // tuple<int, int, int> なぜかmake_tupleみたいな使い方もできる auto tuple5 = mpl::make_append_tuple( 1, 2, 3 ); std::cout << std::get<2>( tuple5 ) << std::endl; // tuple<bool, int, int, int, double> auto tuple6 = mpl::make_append_tuple( false, tuple5, 2.2 ); std::cout << std::get<4>( tuple6 ) << std::endl; }
(´・_・`)(´・_・`)(´・_・`)
なんというウンコード。計算量なんて目も当てられないレベルである。
(計算してみてあまりにもひどかったので言わない)。
気が向いた方はもっと効率よく実装してみてください。
僕はたとえ改善案が思いついたとしてもこのコードには触りたくないです。では
【追記】
@kester44 さんがもっとわかりやすい実装を書いてくださいました。
template <typename T> struct is_tuple : std::false_type {}; template <typename ... Elements> struct is_tuple<std::tuple<Elements ...> > : std::true_type {}; namespace detail { template < typename T, typename std::enable_if<is_tuple<typename std::decay<T>::type>::value>::type* = nullptr > inline T wrap_tuple( T && x ) { return std::forward<T>( x ); } template < typename T, typename std::enable_if<!is_tuple<typename std::decay<T>::type>::value>::type* = nullptr > inline std::tuple<T> wrap_tuple( T && x ) { return std::make_tuple( std::forward<T>( x )); } } // END: namespace (detail) template <typename ... Elements> inline auto make_append_tuple( Elements && ... elements ) ->decltype( std::tuple_cat( detail::wrap_tuple( std::forward<Elements>( elements )) ... )) { return std::tuple_cat( detail::wrap_tuple( std::forward<Elements>( elements )) ... ); }
めっちゃ簡潔ですね。俺のコードがいかにウンコだったかお分かりいただけたと思います。
std::tuple_cat の存在忘れてたよ…つらぽよ…もうだめだ…