読者です 読者をやめる 読者になる 読者になる

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 の存在忘れてたよ…つらぽよ…もうだめだ…