Following the algorithm outlined by Yakk, and using an answer to this question, I have devised the following. I appreciate feedback! (Compiled with clang++ 7.0.0 with -std=c++11)
#include <iostream>
#include <type_traits>
#include <tuple>
namespace detail
{
// pairs a tuple element with an index value
template <typename TUP, std::size_t I>
struct enumerated_tuple
{
static constexpr std::size_t index = I;
using type = typename std::tuple_element<I, TUP>::type;
};
// implement index_sequence and make_index_sequence for C++11
template <std::size_t...> struct index_sequence {};
template <std::size_t N, std::size_t... Is>
struct make_index_sequence : make_index_sequence<N - 1, N - 1, Is...> {};
template <std::size_t... Is>
struct make_index_sequence<0u, Is...> : index_sequence<Is...>
{
using type = index_sequence<Is...>;
};
// functions for binding an index to a tuple
// returns a tuple of tuple elements paired with an index value
template <typename TUP, std::size_t... I>
auto make_indexed_tuple(TUP&& t, index_sequence<I...>) -> decltype(std::make_tuple(enumerated_tuple<TUP, I>()...))
{
return std::make_tuple(enumerated_tuple<TUP, I>()...);
}
// pairs each tuple element with an index
template <typename TUP>
struct indexed_tuple
{
using type = decltype(make_indexed_tuple(TUP(), typename make_index_sequence<std::tuple_size<TUP>::value>::type()));
};
// functions for generating index sequences used for removing a selected element from a tuple
// join two sequences, with the second sequence shifted by Offset
template <typename Seq1, std::size_t Offset, typename Seq2> struct concat_seq;
template <std::size_t ... Is1, std::size_t Offset, std::size_t ... Is2>
struct concat_seq<index_sequence<Is1...>, Offset, index_sequence<Is2...>>
{
using type = index_sequence<Is1..., (Offset + Is2)...>;
};
// generate a sequence 0..N without E, where E >= 0 and E <= N
template <std::size_t N, std::size_t E>
struct gen_seq
{
using type = typename detail::concat_seq<typename make_index_sequence<E>::type, E + 1, typename make_index_sequence<(N > E) ? (N - E - 1) : 0>::type>::type;
};
// generate a subtuple, picking out elements based upon the order and value of supplied integer sequence
template <typename TUP, std::size_t... I>
auto subtuple(TUP&& t, index_sequence<I...>) -> decltype(std::make_tuple(std::get<I>(t)...))
{
return std::make_tuple(std::get<I>(t)...);
}
// remove the nth element from a tuple
template <typename TUP, std::size_t N>
struct remove_nth
{
using type = decltype(subtuple(TUP(), typename gen_seq<std::tuple_size<TUP>::value, N>::type()));
};
// get the nth element from a tuple. (wrap std::tuple_element for the sake of consistency (flips template params))
template <typename TUP, std::size_t N>
struct get_nth
{
using type = typename std::tuple_element<N, TUP>::type;
};
// concatenates two tuples
template <typename TUP1, typename TUP2>
struct append
{
using type = decltype(std::tuple_cat(TUP1(), TUP2()));
};
// select the minimum type
template <typename T0, typename T1, template<typename, typename> class CMP>
struct select_min
{
using type = typename std::conditional<CMP<typename T0::type, typename T1::type>::value, T0, T1>::type;
};
// functions for finding the minimum element in a tuple
template <typename TUP, std::size_t size, template<typename, typename> class CMP>
struct min_tuple_element_helper
{
using type = typename select_min<typename get_nth<TUP, 0>::type, typename min_tuple_element_helper<typename remove_nth<TUP, 0>::type, size-1, CMP>::type, CMP>::type;
};
template <typename TUP, template<typename, typename> class CMP>
struct min_tuple_element_helper<TUP, 1, CMP>
{
using type = typename std::tuple_element<0, TUP>::type;
};
// find the minimum tuple element, using the comparator CMP
template <typename TUP, template<typename, typename> class CMP>
struct min_tuple_element
{
using indexed = typename indexed_tuple<TUP>::type;
using type_and_index = typename min_tuple_element_helper<indexed, std::tuple_size<indexed>::value, CMP>::type;
using type = typename type_and_index::type;
static constexpr std::size_t index = type_and_index::index;
};
template <typename TUP, std::size_t size, template<typename, typename> class CMP>
struct selection_sort_helper
{
using index = typename indexed_tuple<TUP>::type;
using selected = typename min_tuple_element<TUP, CMP>::type_and_index;
using remaining = typename remove_nth<TUP, selected::index>::type;
using remaining_sorted = typename selection_sort_helper<remaining, size-1, CMP>::type;
using type = typename append<std::tuple<typename selected::type>, remaining_sorted>::type;
};
template <typename TUP, template<typename, typename> class CMP>
struct selection_sort_helper<TUP, 1, CMP>
{
using type = TUP;
};
} // end namespace
template <typename L, typename R>
struct less_than
{
static constexpr bool value = (L::id < R::id);
};
template <typename L, typename R>
struct greater_than
{
static constexpr bool value = (L::id > R::id);
};
// sort the elements in tuple, using the comparator CMP
template <typename TUP, template<typename, typename> class CMP>
struct selection_sort
{
using type = typename detail::selection_sort_helper<TUP, std::tuple_size<TUP>::value, CMP>::type;
};
// all tuple elements are a concrete type of this type
template <std::size_t ID>
struct Value
{
static constexpr std::size_t id = ID;
};
int main(void)
{
using example = typename std::tuple<Value<5>, Value<3>, Value<2>, Value<4>>;
printf("unsorted tuple:\n");
printf("%d\n", (int)std::tuple_element<0, example>::type::id);
printf("%d\n", (int)std::tuple_element<1, example>::type::id);
printf("%d\n", (int)std::tuple_element<2, example>::type::id);
printf("%d\n", (int)std::tuple_element<3, example>::type::id);
using min_sorted_example = typename selection_sort<example, less_than>::type;
printf("min-sorted tuple:\n");
printf("%d\n", (int)std::tuple_element<0, min_sorted_example>::type::id);
printf("%d\n", (int)std::tuple_element<1, min_sorted_example>::type::id);
printf("%d\n", (int)std::tuple_element<2, min_sorted_example>::type::id);
printf("%d\n", (int)std::tuple_element<3, min_sorted_example>::type::id);
using max_sorted_example = typename selection_sort<example, greater_than>::type;
printf("max-sorted tuple:\n");
printf("%d\n", (int)std::tuple_element<0, max_sorted_example>::type::id);
printf("%d\n", (int)std::tuple_element<1, max_sorted_example>::type::id);
printf("%d\n", (int)std::tuple_element<2, max_sorted_example>::type::id);
printf("%d\n", (int)std::tuple_element<3, max_sorted_example>::type::id);
return 0;
}
Output:
unsorted tuple:
5
3
2
4
min-sorted tuple:
2
3
4
5
max-sorted tuple:
5
4
3
2