C++ 模板元编程:对互斥量列表进行排序以强制执行锁定顺序
C++ template meta-programming: sort list of mutexes to enforce locking order
我有存储在 POSIX 共享内存中的数据结构(让我们称每个为 "resource")。对每个资源的访问都由每个资源的互斥锁来调节。一个进程有时可能需要以原子方式更新多个资源。此进程必须在 updating/modifying 相关资源之前获取所有先决条件互斥体。必须以明确定义的顺序获取互斥锁,以避免经典的死锁情况。我想开发一个编译时方法来确保以正确的顺序获取锁。
每个资源以任意顺序单独映射到每个进程。因此,我无法按资源地址顺序获取资源。除了在编译时不会确定正确顺序这一事实之外,资源地址的相对顺序可能因进程而异,因为每个资源可能(甚至可能)映射到不同的虚拟地址。
幸运的是,每个由结构表示的资源类型都有一个由 constexpr 定义的唯一整数 ID。我想按ID顺序获取资源
假设每个数据结构如下所示:
template<typename ResourceStruct, int UniqueId>
struct SharedResource
{
static constexpr int ID = UniqueId;
ResourceStruct resource;
};
我有一个类似于 C++11 的函数 std::lock that receives the list of mutexes to lock as template parameters. I believe that it should be possible to sort these template parameters at compile-time according to each resource's ID. Unfortunately, I have been struggling with the necessary template meta-programming gymnastics to pull it off. I have studied several approaches (e.g., quicksort #1, quicksort #2) 用于对模板参数进行排序,但它们似乎都过于复杂。我是不是想多了我的问题?有更简单的方法吗?如果可能的话,我更喜欢纯 C++11 解决方案(我宁愿避免依赖 Boost)。
带上你的身份证件。将它们打包成模板序列。
将每个元素变成一对(value, index)。
对这些元素编写编译时排序。我发现合并排序易于编写,或者您可以使用冒泡排序或选择排序(假设计数很小)。快速排序可能有点矫枉过正。
现在,删除索引。按排序顺序生成一组这些索引。
将您的原始参数包装在一个元组中,对它们执行 std::get<Is>
(其中 Is
来自包含剥离索引的 std::index_sequence
)以获得新排序的参数, 并调用一个函数。现在已订购锁具。
手动编写排序是可行的。如果你不想使用boost实现,你必须自己写一个排序。
template<class...Ts> struct types {using type=types;};
template<class types, size_t N> struct get_nth; // ::type is the nth element of types
template<class types, size_t N> struct remove_nth; // ::type is types without the nth
template<class types, class pred> struct min_index; // returns the index of the least element
template<class...Types> struct append; // appens the types in Types... into one types<?>
template<class types, class pred> struct selection_sort;
// if non-empty, gets the min element, generates
// a types<> containing just it, and appends it to the front of
// the remaining elements with the nth element removed, then sorted
// if types is empty, returns `types<>`.
最多应该是100行左右的代码。 200如果你喜欢马车returns.
转换为与索引位成对只是确保值可以遵循周围的类型。
考虑到我们讨论的是编译时排序,我假设参数的数量相当少。在这种情况下,我会建议忘记实施通用解决方案并简单地使用 Sorting Networks.
在 C++14 中会容易得多(由于自动 return 类型推导),但在 C++11 中仍然可行。模板深度限制甚至会非常好:
template <typename T0, typename T1>
struct cmp { static uint64_t const value = T0::ID < T1::ID; };
//
// Sort 1
//
template <typename T0>
struct sort_1 {
typedef std::tuple<T0&> type;
type sort(T0& t0) { return {t0}; }
};
//
// Sort 2
//
template <uint8_t C, typename T0, typename T1>
struct sort_2_impl;
// T0 >= T1
template <typename T0, typename T1>
struct sort_2_impl<0, T0, T1> {
typedef std::tuple<T1&, T0&> type;
type sort(T0& t0, T1& t1) { return {t1, t0}; }
};
// T0 < T1
template <typename T0, typename T1>
struct sort_2_impl<1, T0, T1> {
typedef std::tuple<T0&, T1&> type;
type sort(T0& t0, T1& t1) { return {t0, t1}; }
};
template <typename T0, typename T1>
struct sort_2:
sort_2_impl<
cmp<T0,T1>::value,
T0, T1
> {};
//
// Sort 3
//
template <uint8_t C, typename T0, typename T1, typename T2>
struct sort_3_impl;
// 0: T0 >= T1 & T0 >= T2 & T1 >= T2 -> T2 <= T1 <= T0
template <typename T0, typename T1, typename T2>
struct sort_3_impl<0, T0, T1, T2> {
typedef std::tuple<T2&, T1&, T0&> type;
type sort(T0& t0, T1& t1, T2& t2) { return {t2, t1, t0}; }
};
// 1: T0 < T1 & T0 >= T2 & T1 >= T2 -> T2 <= T0 < T1
template <typename T0, typename T1, typename T2>
struct sort_3_impl<1, T0, T1, T2> {
typedef std::tuple<T2&, T0&, T1&> type;
type sort(T0& t0, T1& t1, T2& t2) { return {t2, t0, t1}; }
};
// 2: T0 >= T1 & T0 < T2 & T1 >= T2 -> impossible
// 3: T0 < T1 & T0 < T2 & T1 >= T2 -> T0 < T2 <= T1
template <typename T0, typename T1, typename T2>
struct sort_3_impl<3, T0, T1, T2> {
typedef std::tuple<T0&, T2&, T1&> type;
type sort(T0& t0, T1& t1, T2& t2) { return {t0, t2, t1}; }
};
// 4: T0 >= T1 & T0 >= T2 & T1 < T2 -> T1 < T2 <= T0
template <typename T0, typename T1, typename T2>
struct sort_3_impl<4, T0, T1, T2> {
typedef std::tuple<T1&, T2&, T0&> type;
type sort(T0& t0, T1& t1, T2& t2) { return {t1, t2, t0}; }
};
// 5: T0 < T1 & T0 >= T2 & T1 < T2 -> impossible
// 6: T0 => T1 & T0 < T2 & T1 < T2 -> T1 <= T0 < T2
template <typename T0, typename T1, typename T2>
struct sort_3_impl<6, T0, T1, T2> {
typedef std::tuple<T1&, T0&, T2&> type;
type sort(T0& t0, T1& t1, T2& t2) { return {t1, t0, t2}; }
};
// 7: T0 < T1 & T0 < T2 & T1 < T2 -> T0 < T1 < T2
template <typename T0, typename T1, typename T2>
struct sort_3_impl<7, T0, T1, T2> {
typedef std::tuple<T0&, T1&, T2&> type;
type sort(T0& t0, T1& t1, T2& t2) { return {t0, t1, t2}; }
};
template <typename T0, typename T1, typename T2>
struct sort_3:
sort_3_impl<
(cmp<T0, T1>::value << 0) |
(cmp<T0, T2>::value << 1) |
(cmp<T1, T2>::value << 2),
T0, T1, T2
> {};
哦,也许值得使用脚本来生成所有样板...
根据 Yakk 概述的算法,并使用对此 question 的答案,我设计了以下内容。感谢反馈! (使用 clang++ 7.0.0 和 -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;
}
输出:
unsorted tuple:
5
3
2
4
min-sorted tuple:
2
3
4
5
max-sorted tuple:
5
4
3
2
我有存储在 POSIX 共享内存中的数据结构(让我们称每个为 "resource")。对每个资源的访问都由每个资源的互斥锁来调节。一个进程有时可能需要以原子方式更新多个资源。此进程必须在 updating/modifying 相关资源之前获取所有先决条件互斥体。必须以明确定义的顺序获取互斥锁,以避免经典的死锁情况。我想开发一个编译时方法来确保以正确的顺序获取锁。
每个资源以任意顺序单独映射到每个进程。因此,我无法按资源地址顺序获取资源。除了在编译时不会确定正确顺序这一事实之外,资源地址的相对顺序可能因进程而异,因为每个资源可能(甚至可能)映射到不同的虚拟地址。 幸运的是,每个由结构表示的资源类型都有一个由 constexpr 定义的唯一整数 ID。我想按ID顺序获取资源
假设每个数据结构如下所示:
template<typename ResourceStruct, int UniqueId>
struct SharedResource
{
static constexpr int ID = UniqueId;
ResourceStruct resource;
};
我有一个类似于 C++11 的函数 std::lock that receives the list of mutexes to lock as template parameters. I believe that it should be possible to sort these template parameters at compile-time according to each resource's ID. Unfortunately, I have been struggling with the necessary template meta-programming gymnastics to pull it off. I have studied several approaches (e.g., quicksort #1, quicksort #2) 用于对模板参数进行排序,但它们似乎都过于复杂。我是不是想多了我的问题?有更简单的方法吗?如果可能的话,我更喜欢纯 C++11 解决方案(我宁愿避免依赖 Boost)。
带上你的身份证件。将它们打包成模板序列。
将每个元素变成一对(value, index)。
对这些元素编写编译时排序。我发现合并排序易于编写,或者您可以使用冒泡排序或选择排序(假设计数很小)。快速排序可能有点矫枉过正。
现在,删除索引。按排序顺序生成一组这些索引。
将您的原始参数包装在一个元组中,对它们执行 std::get<Is>
(其中 Is
来自包含剥离索引的 std::index_sequence
)以获得新排序的参数, 并调用一个函数。现在已订购锁具。
手动编写排序是可行的。如果你不想使用boost实现,你必须自己写一个排序。
template<class...Ts> struct types {using type=types;};
template<class types, size_t N> struct get_nth; // ::type is the nth element of types
template<class types, size_t N> struct remove_nth; // ::type is types without the nth
template<class types, class pred> struct min_index; // returns the index of the least element
template<class...Types> struct append; // appens the types in Types... into one types<?>
template<class types, class pred> struct selection_sort;
// if non-empty, gets the min element, generates
// a types<> containing just it, and appends it to the front of
// the remaining elements with the nth element removed, then sorted
// if types is empty, returns `types<>`.
最多应该是100行左右的代码。 200如果你喜欢马车returns.
转换为与索引位成对只是确保值可以遵循周围的类型。
考虑到我们讨论的是编译时排序,我假设参数的数量相当少。在这种情况下,我会建议忘记实施通用解决方案并简单地使用 Sorting Networks.
在 C++14 中会容易得多(由于自动 return 类型推导),但在 C++11 中仍然可行。模板深度限制甚至会非常好:
template <typename T0, typename T1>
struct cmp { static uint64_t const value = T0::ID < T1::ID; };
//
// Sort 1
//
template <typename T0>
struct sort_1 {
typedef std::tuple<T0&> type;
type sort(T0& t0) { return {t0}; }
};
//
// Sort 2
//
template <uint8_t C, typename T0, typename T1>
struct sort_2_impl;
// T0 >= T1
template <typename T0, typename T1>
struct sort_2_impl<0, T0, T1> {
typedef std::tuple<T1&, T0&> type;
type sort(T0& t0, T1& t1) { return {t1, t0}; }
};
// T0 < T1
template <typename T0, typename T1>
struct sort_2_impl<1, T0, T1> {
typedef std::tuple<T0&, T1&> type;
type sort(T0& t0, T1& t1) { return {t0, t1}; }
};
template <typename T0, typename T1>
struct sort_2:
sort_2_impl<
cmp<T0,T1>::value,
T0, T1
> {};
//
// Sort 3
//
template <uint8_t C, typename T0, typename T1, typename T2>
struct sort_3_impl;
// 0: T0 >= T1 & T0 >= T2 & T1 >= T2 -> T2 <= T1 <= T0
template <typename T0, typename T1, typename T2>
struct sort_3_impl<0, T0, T1, T2> {
typedef std::tuple<T2&, T1&, T0&> type;
type sort(T0& t0, T1& t1, T2& t2) { return {t2, t1, t0}; }
};
// 1: T0 < T1 & T0 >= T2 & T1 >= T2 -> T2 <= T0 < T1
template <typename T0, typename T1, typename T2>
struct sort_3_impl<1, T0, T1, T2> {
typedef std::tuple<T2&, T0&, T1&> type;
type sort(T0& t0, T1& t1, T2& t2) { return {t2, t0, t1}; }
};
// 2: T0 >= T1 & T0 < T2 & T1 >= T2 -> impossible
// 3: T0 < T1 & T0 < T2 & T1 >= T2 -> T0 < T2 <= T1
template <typename T0, typename T1, typename T2>
struct sort_3_impl<3, T0, T1, T2> {
typedef std::tuple<T0&, T2&, T1&> type;
type sort(T0& t0, T1& t1, T2& t2) { return {t0, t2, t1}; }
};
// 4: T0 >= T1 & T0 >= T2 & T1 < T2 -> T1 < T2 <= T0
template <typename T0, typename T1, typename T2>
struct sort_3_impl<4, T0, T1, T2> {
typedef std::tuple<T1&, T2&, T0&> type;
type sort(T0& t0, T1& t1, T2& t2) { return {t1, t2, t0}; }
};
// 5: T0 < T1 & T0 >= T2 & T1 < T2 -> impossible
// 6: T0 => T1 & T0 < T2 & T1 < T2 -> T1 <= T0 < T2
template <typename T0, typename T1, typename T2>
struct sort_3_impl<6, T0, T1, T2> {
typedef std::tuple<T1&, T0&, T2&> type;
type sort(T0& t0, T1& t1, T2& t2) { return {t1, t0, t2}; }
};
// 7: T0 < T1 & T0 < T2 & T1 < T2 -> T0 < T1 < T2
template <typename T0, typename T1, typename T2>
struct sort_3_impl<7, T0, T1, T2> {
typedef std::tuple<T0&, T1&, T2&> type;
type sort(T0& t0, T1& t1, T2& t2) { return {t0, t1, t2}; }
};
template <typename T0, typename T1, typename T2>
struct sort_3:
sort_3_impl<
(cmp<T0, T1>::value << 0) |
(cmp<T0, T2>::value << 1) |
(cmp<T1, T2>::value << 2),
T0, T1, T2
> {};
哦,也许值得使用脚本来生成所有样板...
根据 Yakk 概述的算法,并使用对此 question 的答案,我设计了以下内容。感谢反馈! (使用 clang++ 7.0.0 和 -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;
}
输出:
unsorted tuple:
5
3
2
4
min-sorted tuple:
2
3
4
5
max-sorted tuple:
5
4
3
2