Std :: tuple as template argument?
I am trying to write a pattern comparison class std::sort
that needs to get an unknown number of tuples (variational pattern). Each tuple should consist of a column (some type that we have in our code) and a bool indicating whether this column should be sorted in ascending or descending order.
Basically, I want something similar to this:
// doesn't compile - conceptual code
template <typename std::tuple<Col, bool>>
struct Comparator
{
bool operator() (int lhs, int rhs)
{
// lhs and rhs are row indices. Depending on the columns
// and the bools received, decide which index should come first
}
}
Is this possible in C ++ 11?
source to share
Is it possible? Yes, but you need some pretty ugly boilerplate tricks for that.
//a trait for checking if a type is of the form std::tuple<T,bool>
template <class Tuple>
struct is_col_bool_tuple : false_type{};
template <typename Col>
struct is_col_bool_tuple<std::tuple<Col,bool>> : true_type{};
//a helper struct for checking if all the values in a boolean pack are true
template<bool...> struct bool_pack;
template<bool... bs>
using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;
//a trait to check if a list of types are all of the form std::tuple<T,bool>
template <class... Tuples>
using are_col_bool_tuples = all_true<is_col_bool_tuple<Tuples>::value...>;
//an incomplete type for when we pass incorrect template arguments
//this impl helper is needed because variadic parameters need to be last
template <typename Enable, class... Tuples>
struct ComparatorImpl;
//our specialized implementation for when the template arguments are correct
template <class... Tuples>
struct ComparatorImpl<std::enable_if_t<are_col_bool_tuples<Tuples...>::value>,
Tuples...>
{
bool operator() (int lhs, int rhs)
{
//do your comparison
}
};
//a nice alias template for forwarding our types to the SFINAE-checked class template
template <class... Tuples>
using Comparator = ComparatorImpl<void, Tuples...>;
source to share
Typically, yes, you can partially customize variation patterns.
In your case, I would use these 3 specializations:
First, a general definition, as you can see, you don't need to define its body:
template <typename ...ColOrderType>
class Comparator;
Second, an intermediate step:
template <typename Col, typename ...ColOrderType>
struct Comparator<std::tuple<Col,bool>, ColOrderType...>
: public Comparator<ColOrderType...>
{
typedef Comparator<ColOrderType...> Base;
Comparator(std::tuple<Col,bool> const& firstOrder,
ColOrderType&&... nextOrders)
: Base(std::forward<ColOrderType>(nextOrders)...),
currectOrder(firstOrder)
{}
// this is implementation of `operator <` - feel free to change
template <typename T>
bool operator() (T const& lhs, T const& rhs)
{
auto&& lcol = get(std::get<0>(currentOrder), lhs);
auto&& rcol = get(std::get<0>(currentOrder), rhs);
bool order = std::get<1>(currentOrder);
if (lcol < rcol) return order;
if (rcol < lcol) return not order;
return Base::operator()(lhs, rhs);
}
std::tuple<Col,bool> currentOrder;
};
And the sentinel class:
template <>
struct Comparator<>
{
template <typename T>
bool operator() (T const& lhs, T const& rhs)
{
return false;
}
};
Note that you will not be able to pass types other than std :: tuple of two elements - the definition Comparator
does not allow this - therefore the general definition is undefined.
And about the function get
in this part:
auto&& lcol = get(std::get<0>(currentOrder), lhs);
auto&& rcol = get(std::get<0>(currentOrder), rhs);
I don't know how you will get the column value from the row based on the first passed value tuple
- so I assume you know how to do it - replace that part with your code.
source to share