Related
I'm implementing compile time unit system, I am able to multiply different units together such that, for example:
Scalar<int, M_>{2} * Scalar<int, M_>{2} == Scalar<int, M_, M_>{4};
I want also to be able to do this:
Scalar<int, M_, M_>{4} / Scalar<int, M_>{2} == Scalar<int, M_>{2};
And I think a good place to start would be to group similar units to a template< typename T, int P> struct UnitPower type, so that
Scalar<int, UnitPower<M_, 1>>{2} * Scalar<int, UnitPower<M_, 1>>{2} == Scalar<int, UnitPower<M_, 2>>{4};
and
Scalar<int, UnitPower<M_, 2>>{4} / Scalar<int, UnitPower<M_, 1>>{2} == Scalar<int, UnitPower<M_, 1>>{2};
This will come in handy for a more general case:
Scalar<double, UnitPower<M_, 1>, UnitPower<S_, -2>, UnitPower<G_, 1>>{70} / Scalar<double, UnitPower<M_, 1>, UnitPower<S_, -1>>{10} == Scalar<double, UnitPower<S_, -1>, UnitPower<G_, 1>>{7}
I would also need to make the operators agnostic to the order of these UnitPowers...
Here's the code so far:
struct M_;
struct S_;
struct Mps_;
template<typename T, int P>
struct UnitPower {};
template<typename T, int P, typename... R>
struct group_units {
//static constexpr type = ?
};
template <typename T, class... C>
struct Scalar
{
protected:
T value;
public:
constexpr explicit Scalar(const T value) : value(value) {}
template<typename U>
constexpr auto operator<=>(const Scalar<U, C...> rhs) {
return value <=> static_cast<U>(rhs.value);
}
template<typename U>
constexpr bool operator==(const Scalar<U, C...> rhs) const { return value == static_cast<T>(rhs); }
template<typename U, typename... D>
constexpr Scalar<std::common_type_t<T, U>, C..., D...> operator/(const Scalar<U, D...> rhs) const {
using V = std::common_type_t<T, U>;
return Scalar<V, C..., D...>{static_cast<V>(value) / static_cast<V>(rhs)};
}
template<typename U, typename... D>
constexpr Scalar<std::common_type_t<T, U>, C..., D...> operator*(const Scalar<U, D...> rhs) const {
using V = std::common_type_t<T, U>;
return Scalar<V, C..., D...>{static_cast<V>(value) * static_cast<V>(rhs)};
}
template<typename U>
constexpr std::common_type_t<T, U> operator/(const Scalar<U, C...> rhs) const {
using V = std::common_type_t<T, U>;
return static_cast<V>(value) / static_cast<V>(rhs);
}
template<typename U>
constexpr Scalar<std::common_type_t<T, U>, C...> operator/(const U rhs) const {
using V = std::common_type_t<T, U>;
return Scalar<V, C...>{static_cast<V>(value) / static_cast<V>(rhs)};
}
constexpr explicit operator T() const { return value; }
template<typename U>
constexpr explicit operator U() const { return static_cast<U>(value); }
};
template<typename T>
struct Meters : Scalar<T, M_> { using Scalar<T, M_>::Scalar; };
template<typename T>
struct Seconds : Scalar<T, S_> { using Scalar<T, S_>::Scalar; };
As you can see, the division operator is currently only defined for same unit (in the same order), and returns just a number (which is the correct return type in this case), or takes just a number, returning the Scalar with no unit modification, which is also the correct behavior.
The multiplication operator just appends the units, and I need it to group them first.
I've added the
template<int P, typename T>
struct UnitPower {};
template<int P, typename T, typename... R>
struct group_units {
//static constexpr type = ?
};
Part, but I don't really know how to go about it..
I'm also not sure how to make the operators unit order agnostic.
After learning how to group the units for the multiplication operator, the division operator would be similar to the multiplication with regards to units - just using negative powers for the right hand side.
So my question is two-fold:
How to make the function unit order agnostic?
How to group similar units into a UnitPower<U, int> structs, and omit units whose power is 0? (and decay Scalar to the underlying value type if all UnitPowers are omitted).
Pretty challenging... Even though I only did some basic tests below code seems to work, at very least it should give you quite a number of hints how to solve the problem. There's yet pretty much potential for beautifying the code (even though got less with latest edit) and naming of my templates sure is anything else but optimal – but I leave that to you to fix... As a little compensation division is already supplied, too ;)
The idea is always based on the same fundamental principle: We need to recurse into the template arguments to make the type changes we need. Keeping that in mind it should be possible to understand the code below. If questions remain, feel free to leave a comment.
template <typename Unit, int>
struct UnitPower { };
template <typename T, typename ... Units>
struct Scalar
{
T value;
};
template <typename ... Units>
struct concat;
template <typename ... Units>
using concat_t = typename concat<Units...>::type;
template <typename U, typename ... Units>
struct concat<U, std::tuple<Units...>>
{
using type = std::tuple<U, Units...>;
};
template <typename ... UnitsX, typename ... UnitsY>
struct concat<std::tuple<UnitsX...>, std::tuple<UnitsY...>>
{
using type = std::tuple<UnitsX..., UnitsY...>;
};
template <typename ... Units>
struct powers;
template <typename ... Units>
using powers_t = typename powers<Units...>::type;
template <>
struct powers<>
{
using type = std::tuple<>;
};
template <typename U, typename ... Units>
struct powers<U, Units...>
{
using type = concat_t<UnitPower<U, 1>, powers_t<Units...>>;
};
template <typename U, int N, typename ... Units>
struct powers<UnitPower<U, N>, Units...>
{
using type = concat_t<UnitPower<U, N>, powers_t<Units...>>;
};
template <typename ... Units>
struct count;
template <typename ... Units>
using count_t = typename count<Units...>::type;
template <typename U, int P>
struct count<UnitPower<U, P>>
{
using type = UnitPower<U, P>;
};
template <typename U, int PX, int PY, typename ... Units>
struct count<UnitPower<U, PX>, UnitPower<U, PY>, Units...>
{
using type = count_t<UnitPower<U, PX + PY>, Units...>;
};
template <typename UX, int PX, typename UY, int PY, typename ... Units>
struct count<UnitPower<UX, PX>, UnitPower<UY, PY>, Units...>
{
using type = count_t<UnitPower<UX, PX>, Units...>;
};
template < typename ... Units>
struct count<std::tuple<Units...>>
{
using type = count_t<Units...>;
};
template <typename ... Units>
struct remain;
template <typename ... Units>
using remain_t = typename remain<Units...>::type;
template <typename U, int P>
struct remain<UnitPower<U, P>>
{
using type = std::tuple<>;
};
template <typename U, int PX, int PY, typename ... Units>
struct remain<UnitPower<U, PX>, UnitPower<U, PY>, Units...>
{
using type = remain_t<UnitPower<U, PX>, Units...>;
};
template <typename UX, int PX, typename UY, int PY, typename ... Units>
struct remain<UnitPower<UX, PX>, UnitPower<UY, PY>, Units...>
{
using type = concat_t<
UnitPower<UY, PY>,
remain_t<UnitPower<UX, PX>, Units...>
>;
};
template < typename ... Units>
struct remain<std::tuple<Units...>>
{
using type = remain_t<Units...>;
};
template <typename ... Units>
struct combine;
template <typename ... Units>
using combine_t = typename combine<Units...>::type;
template <>
struct combine<>
{
using type = std::tuple<>;
};
template <typename U, int P>
struct combine<UnitPower<U, P>>
{
using type = std::tuple<UnitPower<U, P>>;
};
template <typename U, int P, typename ... Units>
struct combine<UnitPower<U, P>, Units...>
{
using type = concat_t<
count_t<UnitPower<U, P>, Units...>,
combine_t<remain_t<UnitPower<U, P>, Units...>>
>;
};
template <typename ... Units>
struct combine<std::tuple<Units...>>
{
using type = combine_t<Units...>;
};
template <typename ... Units>
struct normalize;
template <typename ... Units>
using normalize_t = typename normalize<Units...>::type;
template <>
struct normalize<>
{
using type = std::tuple<>;
};
template <typename U, typename ... Units>
struct normalize<UnitPower<U, 0>, Units...>
{
using type = normalize_t<Units...>;
};
template <typename U, typename ... Units>
struct normalize<UnitPower<U, 1>, Units...>
{
using type = concat_t<U, normalize_t<Units...>>;
};
template <typename U, int N, typename ... Units>
struct normalize<UnitPower<U, N>, Units...>
{
using type = concat_t<UnitPower<U, N>, normalize_t<Units...>>;
};
template <typename ... Units>
struct normalize<std::tuple<Units...>>
{
using type = normalize_t<Units...>;
};
template <typename T, typename ... Units>
struct scalar;
template <typename ... Units>
using scalar_t = typename scalar<Units...>::type;
template <typename T, typename ... Units>
struct scalar<T, std::tuple<Units...>>
{
using type = Scalar<T, Units...>;
};
template <typename ... T>
struct multiply;
template <typename ... T>
using multiply_t = typename multiply<T...>::type;
template <typename TX, typename TY, typename ... UnitsX, typename ... UnitsY>
struct multiply<TX, TY, std::tuple<UnitsX...>, std::tuple<UnitsY...>>
{
using type = scalar_t<
decltype(std::declval<TX>() * std::declval<TY>()),
normalize_t<combine_t<concat_t<
powers_t<UnitsX...>, powers_t<UnitsY...>
>>>
>;
};
template <typename TX, typename TY, typename ... UnitsX, typename ... UnitsY>
auto operator*(Scalar<TX, UnitsX...> x, Scalar<TY, UnitsY...> y)
-> multiply_t<TX, TY, std::tuple<UnitsX...>, std::tuple<UnitsY...>>
{
return {x.value * y.value};
}
template <typename ... Units>
struct negate;
template <typename ... Units>
using negate_t = typename negate<Units...>::type;
template <>
struct negate<>
{
using type = std::tuple<>;
};
template <typename U, int N, typename ... Units>
struct negate<UnitPower<U, N>, Units...>
{
using type = concat_t<UnitPower<U, -N>, negate_t<Units...>>;
};
template <typename ... Units>
struct negate<std::tuple<Units...>>
{
using type = negate_t<Units...>;
};
template <typename ... T>
struct divide;
template <typename ... T>
using divide_t = typename divide<T...>::type;
template <typename TX, typename TY, typename ... UnitsX, typename ... UnitsY>
struct divide<TX, TY, std::tuple<UnitsX...>, std::tuple<UnitsY...>>
{
using type = scalar_t<
decltype(std::declval<TX>() / std::declval<TY>()),
normalize_t<combine_t<concat_t<
powers_t<UnitsX...>, negate_t<powers_t<UnitsY...>>
>>>
>;
};
template <typename TX, typename TY, typename ... UnitsX, typename ... UnitsY>
auto operator/(Scalar<TX, UnitsX...> x, Scalar<TY, UnitsY...> y)
-> divide_t<TX, TY, std::tuple<UnitsX...>, std::tuple<UnitsY...>>
{
return {x.value / y.value};
}
Here's what I came up with.
#include <tuple>
#include <type_traits>
template <typename T, typename Tuple>
struct remove_from_tuple;
template <typename T, typename Tuple>
using remove_from_tuple_t = typename remove_from_tuple<T, Tuple>::type;
template <typename T, typename... ElemT>
struct remove_from_tuple<T, std::tuple<ElemT...>> {
using type = decltype(std::tuple_cat(std::declval<
std::conditional_t<std::is_same_v<T, ElemT>, std::tuple<>, std::tuple<ElemT>>
>()...));
static constexpr std::size_t removed =
sizeof...(ElemT) - std::tuple_size<type>::value;
};
template <class Tuple1, class Tuple2>
struct is_tuple_permutation : std::false_type {};
template <class Tuple1, class Tuple2>
constexpr bool is_tuple_permutation_v = is_tuple_permutation<Tuple1, Tuple2>::value;
template <>
struct is_tuple_permutation<std::tuple<>, std::tuple<>>
: public std::true_type {};
template <typename T, typename... List1, typename Tuple2>
struct is_tuple_permutation<std::tuple<T, List1...>, Tuple2> {
private:
using remove1_t = remove_from_tuple<T, std::tuple<List1...>>;
using remove2_t = remove_from_tuple<T, Tuple2>;
public:
static constexpr bool value =
1 + remove1_t::removed == remove2_t::removed &&
is_tuple_permutation_v<typename remove1_t::type, typename remove2_t::type>;
};
struct M_;
struct S_;
struct KG_;
template <class Tag, int P>
struct UnitPower {};
// Trait: UnitPower<Tag0, P0>, UnitPower<Tag1, P1>, ... -> Tag0
template <class... Units>
struct first_tag;
template <class... Units>
using first_tag_t = typename first_tag<Units...>::type;
template <class Tag0, int P0, class... Units>
struct first_tag<UnitPower<Tag0, P0>, Units...> {
using type = Tag0;
};
// Trait: Sum powers of all UnitPower with matching Tag in Units...;
// Put all Units... with different Tag in tuple remainder.
template <class Tag, class... Units>
struct collect_unit;
template <class Tag, class... Units>
constexpr int collect_unit_power = collect_unit<Tag, Units...>::power;
template <class Tag, class... Units>
using collect_unit_remainder_t = typename collect_unit<Tag, Units...>::remainder;
template <class Tag>
struct collect_unit<Tag> {
static constexpr int power = 0;
using remainder = std::tuple<>;
};
template <class Tag, int P0, class... Units>
struct collect_unit<Tag, UnitPower<Tag, P0>, Units...> {
static constexpr int power = P0 + collect_unit_power<Tag, Units...>;
using remainder = collect_unit_remainder_t<Tag, Units...>;
};
template <class Tag, class Unit0, class... Units>
struct collect_unit<Tag, Unit0, Units...> {
static constexpr int power = collect_unit_power<Tag, Units...>;
using remainder = decltype(std::tuple_cat(
std::declval<std::tuple<Unit0>>(),
std::declval<collect_unit_remainder_t<Tag, Units...>>()));
};
// Trait: Combine any units with the same Tag.
template <class Tuple>
struct group_units;
template <class Tuple>
using group_units_t = typename group_units<Tuple>::type;
template <>
struct group_units<std::tuple<>> {
using type = std::tuple<>;
};
template <class... Units>
struct group_units<std::tuple<Units...>> {
private:
using Tag0 = first_tag_t<Units...>;
using collect_t = collect_unit<Tag0, Units...>;
public:
using type = decltype(std::tuple_cat(
std::declval<std::conditional_t<
collect_t::power != 0,
std::tuple<UnitPower<Tag0, collect_t::power>>,
std::tuple<>>>(),
std::declval<group_units_t<typename collect_t::remainder>>()));
};
template <typename T, class... Units>
class Scalar;
// Trait: Do two Scalars have the same underlying type and the same units
// in any order?
template <class S1, class S2>
struct Scalars_compatible : public std::false_type {};
template <class S1, class S2>
constexpr bool Scalars_compatible_v = Scalars_compatible<S1, S2>::value;
template <typename T1, class... Units1, typename T2, class... Units2>
struct Scalars_compatible<Scalar<T1, Units1...>, Scalar<T2, Units2...>>
: public std::bool_constant<is_tuple_permutation_v<
std::tuple<Units1...>, std::tuple<Units2...>>>
{};
template <typename T, class Tuple>
struct tuple_to_Scalar;
template <typename T, class Tuple>
using tuple_to_Scalar_t = typename tuple_to_Scalar<T, Tuple>::type;
template <typename T, class... Units>
struct tuple_to_Scalar<T, std::tuple<Units...>> {
using type = Scalar<T, Units...>;
};
template <class S1, class S2>
struct Scalar_product;
template <class S1, class S2>
using Scalar_product_t = typename Scalar_product<S1, S2>::type;
template <typename T1, class... Units1, typename T2, class... Units2>
struct Scalar_product<Scalar<T1, Units1...>, Scalar<T2, Units2...>> {
using type = tuple_to_Scalar_t<
std::common_type_t<T1, T2>,
group_units_t<std::tuple<Units1..., Units2...>>>;
};
template <class Unit>
struct invert_unit;
template <class Unit>
using invert_unit_t = typename invert_unit<Unit>::type;
template <class Tag, int P>
struct invert_unit<UnitPower<Tag, P>> {
using type = UnitPower<Tag, -P>;
};
template <class S1, class S2>
struct Scalar_quotient;
template <class S1, class S2>
using Scalar_quotient_t = typename Scalar_quotient<S1, S2>::type;
template <typename T1, class... Units1, typename T2, class... Units2>
struct Scalar_quotient<Scalar<T1, Units1...>, Scalar<T2, Units2...>> {
using type = tuple_to_Scalar_t<
std::common_type_t<T1, T2>,
group_units_t<std::tuple<Units1..., invert_unit_t<Units2>...>>>;
};
using Distance_t = Scalar<double, UnitPower<M_, 1>>;
using Time_t = Scalar<double, UnitPower<S_, 1>>;
using Speed_t = Scalar_quotient_t<Distance_t, Time_t>;
using Acceleration_t = Scalar_quotient_t<Speed_t, Time_t>;
using Mass_t = Scalar<double, UnitPower<KG_, 1>>;
using Energy_t = Scalar_product_t<Mass_t, Acceleration_t>;
static_assert(Scalars_compatible_v<Energy_t, Scalar<double, UnitPower<KG_, 1>, UnitPower<M_, 1>, UnitPower<S_, -2>>>);
static_assert(Scalars_compatible_v<Scalar_quotient_t<Speed_t, Acceleration_t>, Time_t>);
Note you'll probably want to restrict your operator+, operator==, etc. to require Scalars_compatible_v.
See it on godbolt.
The answer to template metaprogramming questions is to always use Boost.Mp11.
So first, you probably don't want to support both Scalar<int, M, M> and Scalar<int, Power<M, 2>>, the former has limited value anyway. But I'll start motivating Mp11 by demonstrating how to convert just a list of powers (some of which may be Powers) into a normalized set (not yet grouped):
// using a type (that is an integral constant)
// for power rather than an integer, for convenience
template <typename Unit, typename Power>
struct UnitPower { };
// if T is a list (it would be a UnitPower)
// otherwise it's a base unit with power 1
template <typename T>
using normalize_unit = mp_if<
mp_is_list<T>, T, UnitPower<T, mp_int<1>>>;
template <typename L>
using normalize = mp_transform<normalize_unit, L>;
I could've written this to take a pack of T... and just produce an mp_list there but I wanted to start using algorithms earlier. So here, normalize<mp_list<M, M, UnitPower<M, mp_int<2>>> yields the type mp_list<UnitPower<M, mp_int<1>>, UnitPower<M, mp_int<1>>, UnitPower<M, mp_int<2>>>
Alright, so now that we have a bunch of UnitPowers, we can group them. The way to do this is to keep a map of Unit to Power. A map in the Mp11 sense is just a "list" of "pairs", where each key only appears once. But UnitPower is a "pair" in the Mp11 sense, and the grouping algorithm we want is basically the example for mp_map_update:
template <typename M, typename T>
using update_powers = mp_map_update_q<M, T,
mp_bind<mp_plus, _2, mp_second<T>>>;
template <typename L>
using grouped_powers = mp_fold<normalize<L>, mp_list<>, update_powers>;
And... that's it actually. The slightly awkward function there is because mp_map_update takes a function that gets called with the unit and then the power of the element already in the map, so we need to pull out its power (_2) and add it (mp_plus) to the power we're currently updating (mp_second<T>).
Then, with that, multiplication is grouping both lists together:
template <typename L1, typename L2>
using multiply_powers = grouped_powers<mp_append<L1, L2>>;
And dividing is the same but we have to negate the right-hand list first:
template <typename UP>
using negate = mp_replace_second<UP, mp_int<-mp_second<UP>::value>>;
template <typename L1, typename L2>
using divide_powers = multiply_powers<L1, mp_transform<negate, normalize<L2>>>;
And that's all we need. Demo.
static_assert(std::same_as<
multiply_powers<mp_list<M, M>, mp_list<M, S>>,
mp_list<UnitPower<M, mp_int<3>>, UnitPower<S, mp_int<1>>>>);
static_assert(std::same_as<
divide_powers<mp_list<M, M>, mp_list<M, S>>,
mp_list<UnitPower<M, mp_int<1>>, UnitPower<S, mp_int<-1>>>>);
I need to make a function take only the first value of every std::pair passed to its arguments. Values passed that are not of type std::pair will be used unaltered. My following solution only works for functions that takes two arguments. I need to know how to generalize this to any number of arguments passed.
#include <type_traits>
template <typename T, typename = void>
struct has_first_type : std::false_type { };
template <typename T>
struct has_first_type<T, std::void_t<typename T::first_type>> : std::true_type { };
template <typename R, typename T, typename U, typename = void> struct act_on_first;
template <typename R, typename T, typename U>
struct act_on_first<R, T, U, std::enable_if_t<has_first_type<T>::value && has_first_type<U>::value>> {
template <typename F>
static R execute (const T& t, const U& u, F f) {
return f(t.first, u.first);
}
};
template <typename R, typename T, typename U>
struct act_on_first<R, T, U, std::enable_if_t<has_first_type<T>::value && !has_first_type<U>::value>> {
template <typename F>
static R execute (const T& t, const U& u, F f) {
return f(t.first, u);
}
};
template <typename R, typename T, typename U>
struct act_on_first<R, T, U, std::enable_if_t<!has_first_type<T>::value && has_first_type<U>::value>> {
template <typename F>
static R execute (const T& t, const U& u, F f) {
return f(t, u.first);
}
};
template <typename R, typename T, typename U>
struct act_on_first<R, T, U, std::enable_if_t<!has_first_type<T>::value && !has_first_type<U>::value>> {
template <typename F>
static R execute (const T& t, const U& u, F f) {
return f(t, u);
}
};
struct foo {
template <typename... Args>
std::size_t operator()(Args&&...) { return sizeof...(Args); } // Simple example only.
};
template <typename T, typename U>
std::size_t bar (const T& t, const U& u) {
return act_on_first<std::size_t, T, U>::execute(t, u, foo());
}
// Testing
#include <iostream>
int main() {
std::pair<int, bool> k = {3, true};
std::pair<int, char> m = {5, 't'};
std::cout << bar(k,m) << '\n'; // 2
std::cout << bar(k,5) << '\n'; // 2
std::cout << bar(3,m) << '\n'; // 2
std::cout << bar(3,5) << '\n'; // 2
}
Write a transformer, that either gives you .first for pairs or just returns its argument:
template <typename T> T const& take_first(T const& x) { return x; }
template <typename T, typename U>
T const& take_first(std::pair<T, U> const& p) { return p.first; }
template <typename... Args>
std::size_t bar(Args const&... args) {
return foo{}(take_first(args)...);
}
I would like to know how can I do the following in C++:
Consider these classes :
C1 < C2 < C3 < ... < Cn, Ci < Cj means sizeof(Ci) < sizeof(Cj)
I want a structure that uses variadic templates as a sequence of Ci's,
OrderBySize<AnySequenceOfCis>, for example : OrderBySize<C1,C2,C3,...,Cn> or
OrderBySize<C2,C1,C3,C4,...,Cn> ... all possible combinations
and gives the following structure as a result :
class result{
Cn elem1;
Cn-1 elem2;
.
.
.
C1 elemn;
}
I read this article, it shows how we can define a Tuple<typename ...T>, however, this is different, much more difficult to implement and very useful.
EDIT :
order_by_size<T1, ..., Tn> would contain a tuple of the ordered combination of T1, ..., Tn
However I don't want the user to know that I am ordering the fields, the user would use it like a tuple. And thus, in order to access the fields, the user will use :
template<typename... Tn>
get<size_t>(const MyStructure<Tn ...>& m) to get the size_t'th element which is has an other index in the new tuple.
Basically this problem reduces to just sorting a list of types based on a given comparator. Once you have that, everything else follows. So this answer is just the sorting part. We'll start with a typelist:
template <typename...>
struct typelist {
using type = typelist;
};
I'm going to assume a bunch of metafunctions which are very short (head, tail, concat, size). I will omit them for brevity.
So let's just jump into writing merge sort:
template <typename TL, typename Cmp = LessSize>
struct sort
{
using left_right = typename split<TL, size<TL>::value/2>::type;
using left = typename sort<head_t<left_right>, Cmp>::type;
using right = typename sort<head_t<tail_t<left_right>>, Cmp>::type;
using type = typename merge<left, right, Cmp>::type;
};
// base case for exactly 1 element
template <typename T, typename Cmp>
struct sort<typelist<T>, Cmp> {
using type = typelist<T>;
};
// potentially add a base case for exactly 2 elements here?
The general structure here should look familiar. We split our typelist, TL, into two equal parts, sort both, and then merge. Of course, this is metaprogramming, so everything is unnecessarily complicated.
Let's start with split. split takes a typelist and a size, and returns a typelist of two typelists: the first having the given size, and the second being the remainder:
template <typename A, typename B, size_t N>
struct split_impl
: std::conditional<
size<A>::value < N,
split_impl<concat_t<A, typelist<head_t<B>>>, tail_t<B>, N>,
typelist<A, B>
>::type
{ };
template <typename TL, size_t N>
struct split
: split_impl<typelist<>, TL, N>
{ };
So that gives us left and right (at least once we apply head_t<> and head_t<tail_t<>>). All that's left is the merge step. I'm using the Boost MPL idea of what a metafunction class is, so LessSize is:
struct LessSize {
template <typename A, typename B>
using apply = std::integral_constant<bool, sizeof(A) < sizeof(B)>;
};
merge just has to walk both typelists and pick the smallest element based on the comparator between the two typelists. First, we'll start with all of our base cases:
template <typename L, typename R, typename Cmp>
struct merge;
// R empty
template <typename... T, typename Cmp>
struct merge<typelist<T...>, typelist<>, Cmp> {
using type = typelist<T...>;
};
// L empty
template <typename... T, typename Cmp>
struct merge<typelist<>, typelist<T...>, Cmp> {
using type = typelist<T...>;
};
And then the recursive step, which is somewhat ugly:
template <typename A, typename... As, typename B, typename... Bs, typename Cmp>
struct merge<typelist<A, As...>, typelist<B, Bs...>, Cmp>
: std::conditional<
Cmp::template apply<A, B>::value,
concat_t<typelist<A>, typename merge<typelist<As...>, typelist<B, Bs...>, Cmp>::type>,
concat_t<typelist<B>, typename merge<typelist<A, As...>, typelist<Bs...>, Cmp>::type>
>::type
{ };
Basically, given two typelists, {A, As...} and {B, Bs...}, we select the smallest based on Cmp, and that's the side we're popping the element off of. If Cmp::apply<A,B>, then we're concatenating A with the result of merging {As...} with {B, Bs...}. And vice versa.
And that's all she wrote:
template <typename T>
struct TD;
int main()
{
using T = sort<typelist<int, double, char, float>, LessSize>::type;
TD<T> r;
}
main.cpp: In function 'int main()':
main.cpp:131:11: error: aggregate 'TD<typelist<char, float, int, double> > r' has incomplete type and cannot be defined
TD<T> r;
^
Once you have the sorted types, making a tuple is straightforward:
template <template <typename...> class C>
struct meta_quote {
template <typename... T>
using apply = C<T...>;
};
template <typename F, typename TL>
struct meta_apply;
template <typename F, typename... T>
struct meta_apply<F, typelist<T...>> {
using type = typename F::template apply<T...>;
};
template <typename... T>
struct my_tuple
: meta_apply<meta_quote<std::tuple>,
typename sort<typelist<T...>>::type
>::type;
{
using base_tuple = meta_apply<...>;
};
Now just add overloads for get<> on my_tuple<T...>:
template <size_t I, typename... T>
auto get(my_tuple<T...>& t) {
using type = std::tuple_element_t<I, std::tuple<T...>>;
return std::get<type>(static_cast<typename my_tuple<T...>::base_type&>(t));
}
This is probably not the most efficient implementation (it uses cyclic permutation of types to determine the one with the largest size), and may contain errors, but the whole idea should be clear. The result is std::tuple with types ordered by descending size. The main function checks if it actually works (and it works on my gcc-4.8.2).
#include <iostream>
#include <tuple>
#include <iomanip>
constexpr std::size_t max (std::size_t x, std::size_t y)
{
return (x < y) ? y : x;
}
template <typename ... Ts>
struct max_size;
template < >
struct max_size < >
{
static constexpr std::size_t result = 0;
};
template <typename T, typename ... Ts>
struct max_size <T, Ts...>
{
static constexpr std::size_t result = max(sizeof(T), max_size<Ts...>::result);
};
template <typename R, typename ... Ts>
struct order_by_size_impl;
template <bool M, typename R, typename ... Ts>
struct order_by_size_helper;
template <typename ... Rs, typename T, typename ... Ts>
struct order_by_size_helper<true, std::tuple<Rs...>, T, Ts...>
: order_by_size_impl<std::tuple<Rs..., T>, Ts...>
{ };
template <typename ... Rs, typename T, typename ... Ts>
struct order_by_size_helper<false, std::tuple<Rs...>, T, Ts...>
: order_by_size_impl<std::tuple<Rs...>, Ts..., T>
{ };
template <typename ... Rs, typename T, typename ... Ts>
struct order_by_size_impl<std::tuple<Rs...>, T, Ts...>
: order_by_size_helper<sizeof(T) >= max_size<Ts...>::result, std::tuple<Rs...>, T, Ts...>
{ };
template <typename ... Rs>
struct order_by_size_impl<std::tuple<Rs...>>
{
typedef std::tuple<Rs...> result;
};
template <typename ... Ts>
struct order_by_size
: order_by_size_impl<std::tuple<>, Ts...>
{ };
struct test
{
std::uint8_t data[128];
};
template <std::size_t I, typename T, typename R>
bool check (R const & r)
{
return std::is_same<typename std::remove_cv<typename std::remove_reference<decltype(std::get<I>(r))>::type>::type, T>::value;
}
int main ( )
{
order_by_size<std::uint8_t, std::uint32_t, std::uint16_t, std::uint64_t, test>::result r;
std::cout << std::boolalpha;
std::cout << check<0, test>(r) << std::endl;
std::cout << check<1, std::uint64_t>(r) << std::endl;
std::cout << check<2, std::uint32_t>(r) << std::endl;
std::cout << check<3, std::uint16_t>(r) << std::endl;
std::cout << check<4, std::uint8_t>(r) << std::endl;
}
I'd like to create the cross product of a list of types using variadic templates.
Here's what I have so far:
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
template<typename...> struct type_list {};
template<typename T1, typename T2> struct type_pair {};
template<typename T, typename... Rest>
struct row
{
typedef type_list<type_pair<T,Rest>...> type;
};
template<typename... T>
struct cross_product
{
typedef type_list<typename row<T,T...>::type...> type;
};
int main()
{
int s;
typedef cross_product<int, float, short>::type result;
std::cout << abi::__cxa_demangle(typeid(result).name(), 0, 0, &s) << std::endl;
return 0;
}
This program outputs:
$ g++ -std=c++0x cross_product.cpp ; ./a.out
type_list<type_list<type_pair<int, int>, type_pair<int, float>, type_pair<int, short> >, type_list<type_pair<float, int>, type_pair<float, float>, type_pair<float, short> >, type_list<type_pair<short, int>, type_pair<short, float>, type_pair<short, short> > >
But I'd like it to output:
type_list<type_pair<int,int>, type_pair<int,float>, type_pair<int,short>, type_pair<float,int>,...>
That is, without the nested type_lists.
Is there a direct way to do this without the row helper, or should the solution "unwrap" the nested type_lists somehow?
A nice clean version I think:
cross_product.cpp:
#include "type_printer.hpp"
#include <iostream>
template<typename ...Ts> struct type_list {};
template<typename T1, typename T2> struct pair {};
// Concatenation
template <typename ... T> struct concat;
template <typename ... Ts, typename ... Us>
struct concat<type_list<Ts...>, type_list<Us...>>
{
typedef type_list<Ts..., Us...> type;
};
// Cross Product
template <typename T, typename U> struct cross_product;
// Partially specialise the empty case for the first type_list.
template <typename ...Us>
struct cross_product<type_list<>, type_list<Us...>> {
typedef type_list<> type;
};
// The general case for two type_lists. Process:
// 1. Expand out the head of the first type_list with the full second type_list.
// 2. Recurse the tail of the first type_list.
// 3. Concatenate the two type_lists.
template <typename T, typename ...Ts, typename ...Us>
struct cross_product<type_list<T, Ts...>, type_list<Us...>> {
typedef typename concat<
type_list<pair<T, Us>...>,
typename cross_product<type_list<Ts...>, type_list<Us...>>::type
>::type type;
};
struct A {};
struct B {};
struct C {};
struct D {};
struct E {};
struct F {};
template <typename T, typename U>
void test()
{
std::cout << print_type<T>() << " \u2a2f " << print_type<U>() << " = "
<< print_type<typename cross_product<T, U>::type>() << std::endl;
}
int main()
{
std::cout << "Cartesian product of type lists\n";
test<type_list<>, type_list<>>();
test<type_list<>, type_list<A>>();
test<type_list<>, type_list<A, B>>();
test<type_list<A, B>, type_list<>>();
test<type_list<A>, type_list<B>>();
test<type_list<A>, type_list<B, C, D>>();
test<type_list<A, B>, type_list<B, C, D>>();
test<type_list<A, B, C>, type_list<D>>();
test<type_list<A, B, C>, type_list<D, E, F>>();
return 0;
}
type_printer.hpp:
#ifndef TYPE_PRINTER_HPP
#define TYPE_PRINTER_HPP
#include "detail/type_printer_detail.hpp"
template <typename T>
std::string print_type()
{
return detail::type_printer<T>()();
}
#endif
detail/type_printer_detail.hpp:
#ifndef DETAIL__TYPE_PRINTER_DETAIL_HPP
#define DETAIL__TYPE_PRINTER_DETAIL_HPP
#include <typeinfo>
#include <cxxabi.h>
#include <string>
template <typename ...Ts> struct type_list;
template <typename T1, typename T2> struct pair;
namespace detail {
// print scalar types
template <typename T>
struct type_printer {
std::string operator()() const {
int s;
return abi::__cxa_demangle(typeid(T).name(), 0, 0, &s);
}
};
// print pair<T, U> types
template <typename T, typename U>
struct type_printer<pair<T, U>> {
std::string operator()() const {
return "(" + type_printer<T>()() + "," + type_printer<U>()() + ")";
}
};
// print type_list<T>
template <>
struct type_printer<type_list<>> {
std::string operator()() const {
return "\u2205";
}
};
template <typename T>
struct type_printer<type_list<T>> {
std::string operator()() const {
return "{" + type_printer<T>()() + "}";
}
std::string operator()(const std::string& sep) const {
return sep + type_printer<T>()();
}
};
template <typename T, typename ...Ts>
struct type_printer<type_list<T, Ts...>> {
std::string operator()() const {
return "{" + type_printer<T>()() + type_printer<type_list<Ts...>>()(std::string(", ")) + "}";
}
std::string operator()(const std::string& sep) const {
return sep + type_printer<T>()() + type_printer<type_list<Ts...>>()(sep);
}
};
}
#endif
Run:
g++ -std=c++0x cross_product.cpp && ./a.out
Output:
Cartesian product of type lists
∅ ⨯ ∅ = ∅
∅ ⨯ {A} = ∅
∅ ⨯ {A, B} = ∅
{A, B} ⨯ ∅ = ∅
{A} ⨯ {B} = {(A,B)}
{A} ⨯ {B, C, D} = {(A,B), (A,C), (A,D)}
{A, B} ⨯ {B, C, D} = {(A,B), (A,C), (A,D), (B,B), (B,C), (B,D)}
{A, B, C} ⨯ {D} = {(A,D), (B,D), (C,D)}
{A, B, C} ⨯ {D, E, F} = {(A,D), (A,E), (A,F), (B,D), (B,E), (B,F), (C,D), (C,E), (C,F)}
(I noticed on Windows using Chrome that the cross product unicode character is not coming out well. Sorry, I don't know how to fix that.)
Somehow my brain is fried: I think I'm using more code than is needed but, at least, it has the desired results (although I didn't fix the memory leak):
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
template<typename...> struct type_list {};
template<typename T1, typename T2> struct type_pair {};
template<typename T, typename... Rest>
struct row
{
typedef type_list<type_pair<T,Rest>...> type;
};
template <typename... T> struct concat;
template <typename... S, typename... T>
struct concat<type_list<S...>, type_list<T...>>
{
typedef type_list<S..., T...> type;
};
template <typename... T>
struct expand
{
typedef type_list<T...> type;
};
template <> struct expand<> { typedef type_list<> type; };
template <typename... T, typename... L>
struct expand<type_list<T...>, L...>
{
typedef typename concat<typename expand<T...>::type, typename expand<L...>::type>::type type;
};
template<typename... T>
struct cross_product
{
typedef typename expand<type_list<typename row<T,T...>::type...>>::type type;
};
int main()
{
int s;
typedef cross_product<int, float, short>::type result;
std::cout << abi::__cxa_demangle(typeid(result).name(), 0, 0, &s) << std::endl;
return 0;
}
Maybe something like this:
template <typename ...Args> struct typelist { };
template <typename S, typename T> struct typelist_cat;
template <typename ...Ss, typename ...Ts>
struct typelist_cat<typelist<Ss...>, typelist<Ts...>>
{
typedef typelist<Ss..., Ts...> type;
};
template <typename S, typename T> struct product;
template <typename S, typename ...Ss, typename ...Ts>
struct product<typelist<S, Ss...>, typelist<Ts...>>
{
// the cartesian product of {S} and {Ts...}
// is a list of pairs -- here: a typelist of 2-element typelists
typedef typelist<typelist<S, Ts>...> S_cross_Ts;
// the cartesian product of {Ss...} and {Ts...} (computed recursively)
typedef typename product<typelist<Ss...>, typelist<Ts...>>::type
Ss_cross_Ts;
// concatenate both products
typedef typename typelist_cat<S_cross_Ts, Ss_cross_Ts>::type type;
};
// end the recursion
template <typename ...Ts>
struct product<typelist<>, typelist<Ts...>>
{
typedef typelist<> type;
};
Now you should be able to use product<typelist<A,B,C>, typelist<D,E,F>>::type.
C++17
Working Demo
Logic to concatenate type_lists to avoid nested type_list like you are asking for:
// base case: 2 type_lists
template<class... Ts, class... Us>
auto concat(type_list<Ts...>, type_list<Us...>) -> type_list<Ts..., Us...>;
// recursive case: more than 2 type_lists
template<class... Ts, class... Us, class... Rest>
auto concat(type_list<Ts...>, type_list<Us...>, Rest...) -> decltype(concat(type_list<Ts..., Us...>{}, Rest{}...));
Note that these functions don't have (or need) implementations; this is a trick to avoid class template specialization (I learned it from Hana Dusikova's compile time regular expressions)
Then, simplifying your row and cross_product impls as pairs and cross_product_impl, respectively:
template<class T, class... Ts>
using pairs = type_list<type_pair<T, Ts>...>;
template<class... T>
auto cross_product_impl()
{
if constexpr(sizeof...(T) == 0)
return type_list<> {};
if constexpr(sizeof...(T) == 1)
return type_list<type_pair<T, T>...>{};
if constexpr(sizeof...(T) > 1)
return concat(pairs<T, T...>{}...);
}
if constexpr allows us to more easily express the logic, I think.
Finally a type alias for cross_product that gives us what the type would be if we theoretically invoked cross_product_impl:
template<class... T>
using cross_product = decltype(cross_product_impl<T...>());
Usage basically the same as before:
cross_product<int, float, short> result;
So far all solutions have drawbacks, unnecessary dependencies, unnecessary helpers and all are restricted to the Cartesian power of two. The following solution has no such drawbacks and supports:
Any cartesian power including 0.
Returning the empty set if any of the factors is an empty set.
The code is self contained and does not depend on any include files.
The inputs of the function can be of any template type.
The type of the output list can be specified via the first template
parameter.
It was actually to harder to implement (but good as homework) then I thought. I am actually thinking about creating a little generator which allows me an extended template syntax which makes these things really easy.
Simplified the code works as follows: product converts an input list tuple<A...>,tuple<B...>,tuple<C...> into tuple<tuple<A>...>, tuple<B...>, tuple<C...>. This second list is then passed to product_helper which does the recursive Cartesian product computation.
template <typename... T> struct cat2;
template <template<typename...> class R, typename... As, typename... Bs>
struct cat2 <R<As...>, R<Bs...> > {
using type = R <As..., Bs...>;
};
template <typename... Ts> struct product_helper;
template <template<typename...> class R, typename... Ts>
struct product_helper < R<Ts...> > { // stop condition
using type = R< Ts...>;
};
template <template<typename...> class R, typename... Ts>
struct product_helper < R<R<> >, Ts... > { // catches first empty tuple
using type = R<>;
};
template <template<typename...> class R, typename... Ts, typename... Rests>
struct product_helper < R<Ts...>, R<>, Rests... > { // catches any empty tuple except first
using type = R<>;
};
template <template<typename...> class R, typename... X, typename H, typename... Rests>
struct product_helper < R<X...>, R<H>, Rests... > {
using type1 = R <typename cat2<X,R<H> >::type...>;
using type = typename product_helper<type1, Rests...>::type;
};
template <template<typename...> class R, typename... X, template<typename...> class Head, typename T, typename... Ts, typename... Rests>
struct product_helper < R<X...>, Head<T, Ts...>, Rests... > {
using type1 = R <typename cat2<X,R<T> >::type...>;
using type2 = typename product_helper<R<X...> , R<Ts...> >::type;
using type3 = typename cat2<type1,type2>::type;
using type = typename product_helper<type3, Rests...>::type;
};
template <template<typename...> class R, typename... Ts> struct product;
template <template<typename...> class R>
struct product < R > { // no input, R specifies the return type
using type = R<>;
};
template <template<typename...> class R, template<typename...> class Head, typename... Ts, typename... Tail>
struct product <R, Head<Ts...>, Tail... > { // R is the return type, Head<A...> is the first input list
using type = typename product_helper< R<R<Ts>...>, Tail... >::type;
};
Here is a compilable example of how the code can be used.
Here's another solution.
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
template <typename ...Args> struct typelist { };
template <typename, typename> struct typepair { };
template <typename S, typename T> struct product;
template <typename S, typename T> struct append;
template<typename ...Ss, typename ...Ts>
struct append<typelist<Ss...>, typelist<Ts...>> {
typedef typelist<Ss..., Ts...> type;
};
template<>
struct product<typelist<>, typelist<>> {
typedef typelist<> type;
};
template<typename ...Ts>
struct product<typelist<>, typelist<Ts...>> {
typedef typelist<> type;
};
template<typename ...Ts>
struct product<typelist<Ts...>, typelist<>> {
typedef typelist<> type;
};
template<typename S, typename T, typename ...Ss, typename ...Ts>
struct product<typelist<S, Ss...>, typelist<T, Ts...>> {
typedef typename
append<typelist<typepair<S, T>,
typepair<S, Ts>...,
typepair<Ss, T>...>,
typename product<typelist<Ss...>, typelist<Ts...>>::type>::type type;
};
int main(void)
{
int s;
std::cout << abi::__cxa_demangle(
typeid(product<typelist<int, float>, typelist<short, double>>::type).name(), 0, 0, &s) << "\n";
return 0;
}
Note: This is NOT what the OP asked for... but may be of relevance to others (like me) who stumble upon this question. Here is how it can be done using a Loki::TypeList (i.e. prior C++-11), perhaps of historical interest or for compatability sake.
Also, perhaps it is presumptuous of me to pollute loki's namespace. YMMV.
crossproduct.h
#include "loki/NullType.h"
#include "loki/Typelist.h"
namespace Loki {
namespace TL {
/// a pair of two types
template <typename A_t, typename B_t>
struct TypePair
{
typedef A_t A;
typedef B_t B;
};
/// a template which takes one type and pairs it with all other types
/// in another typelist
template <class T, class TList > struct DistributePair;
/// specialization of Distribute for the nulltype
template < class TList >
struct DistributePair< NullType, TList >
{
typedef NullType type;
};
/// specialization of Distribute where the second parameter is nulltype
template <class T >
struct DistributePair< T, NullType >
{
typedef NullType type;
};
/// specialization of Distribute where the first parameter is a
/// typelist
template <class T, class Head, class Tail >
struct DistributePair< T, Typelist<Head,Tail> >
{
typedef Typelist<
TypePair<T,Head>,
typename DistributePair<T,Tail>::type
> type;
};
/// performs cartesion product of two typelists
template <class TListA, class TListB> struct CrossProduct;
/// specialization of CrossProduct for NullType
template <class TListB>
struct CrossProduct< NullType, TListB >
{
typedef NullType type;
};
/// specialization of CrossProduct for recursion
template <class Head, class Tail, class TListB>
struct CrossProduct< Typelist<Head,Tail>, TListB >
{
typedef typename Append<
typename DistributePair< Head,TListB >::type,
typename CrossProduct< Tail, TListB >::type
>::Result type;
};
} // namespace TL
} // namespace Loki
test.cpp
#include <crossproduct.h>
#include <loki/HierarchyGenerators.h>
#include <iostream>
struct A{};
struct B{};
struct C{};
struct D{};
struct E{};
struct F{};
typedef LOKI_TYPELIST_3(A,B,C) TypeListA_t;
typedef LOKI_TYPELIST_3(D,E,F) TypeListB_t;
typedef typename Loki::TL::CrossProduct< TypeListA_t, TypeListB_t >::type Cross_t;
template <typename T> const char* toString();
template <> const char* toString<A>(){ return "A"; };
template <> const char* toString<B>(){ return "B"; };
template <> const char* toString<C>(){ return "C"; };
template <> const char* toString<D>(){ return "D"; };
template <> const char* toString<E>(){ return "E"; };
template <> const char* toString<F>(){ return "F"; };
template <typename T> struct Printer
{
Printer()
{
std::cout << toString<T>() << ", ";
}
};
template <typename T1, typename T2>
struct Printer< Loki::TL::TypePair<T1,T2> >
{
Printer()
{
std::cout << "(" << toString<T1>() << "," << toString<T2>() << "), ";
}
};
typedef Loki::GenScatterHierarchy< TypeListA_t, Printer > PrinterA_t;
typedef Loki::GenScatterHierarchy< TypeListB_t, Printer > PrinterB_t;
typedef Loki::GenScatterHierarchy< Cross_t, Printer > PrinterCross_t;
int main(int argc, char** argv)
{
std::cout << "\nType list A: \n ";
PrinterA_t a;
std::cout << "\nType list B: \n ";
PrinterB_t b;
std::cout << "\nType list Cross: \n ";
PrinterCross_t cross;
return 0;
}
output
Type list A:
A, B, C,
Type list B:
D, E, F,
Type list Cross:
(A,D), (A,E), (A,F), (B,D), (B,E), (B,F), (C,D), (C,E), (C,F),
With Boost.Mp11, this is a short one-liner (as always):
using input = type_list<int, float, short>;
using result = mp_product<
type_pair,
input, input>;
Demo.
We can generalize this to picking N things, with repetition, from that input. We can't use type_pair anymore to group our elements, so we'll just have a list of type_list of elements. To do that, we basically need to write:
mp_product<type_list, input, input, ..., input>
// ~~~~~~~ N times ~~~~~~~~
Which is also the same as:
mp_product_q<mp_quote<type_list>, input, input, ..., input>
// ~~~~~~~ N times ~~~~~~~~
One way to do that is:
template <int N>
using product = mp_apply<
mp_product_q,
mp_append<
mp_list<mp_quote<type_list>>,
mp_repeat_c<mp_list<input>, N>
>>;
Demo.
Really enjoyed this "homework" assignment :)
Both solutions below create a class full of type_list typedefs, along with member functions that will check to see if a given list of types exist in the class as a type_list.
The first solution creates all possible combinations of types from 1 to N types per type_list (the width parameter defines N). The second solution creates only pairs of types.
First Solution
template<typename... Ts> struct type_list { typedef type_list<Ts...> type; };
template<size_t, typename...> struct xprod_tlist_ {};
template<typename... Ts, typename... Us>
struct xprod_tlist_<1, type_list<Ts...>, Us...> {};
template<size_t width, typename... Ts, typename... Us>
struct xprod_tlist_<width, type_list<Ts...>, Us...>
: type_list<Ts..., Us>...
, xprod_tlist_<width - 1, type_list<Ts..., Us>, Us...>... {};
template<size_t width, typename... Ts> struct xprod_tlist
: type_list<Ts>..., xprod_tlist_<width, type_list<Ts>, Ts...>... {
template<typename... Us> struct exists
: std::is_base_of<type_list<Us...>, xprod_tlist<width, Ts...>> {};
template<typename... Us> struct assert_exists {
static_assert(exists<Us...>::value, "Type not present in list");
};
};
Usage:
typedef xprod_tlist<5, int, char, string, float, double, long> X;
//these pass
X::assert_exists<int, int, int, int, int> assert_test1;
X::assert_exists<double, float, char, int, string> assert_test2;
//these fail
X::assert_exists<char, char, char, char, char, char> assert_test3;
X::assert_exists<int, bool> assert_test4;
//true
auto test1 = X::exists<int, int, int, int, int>::value;
auto test2 = X::exists<double, float, char, int, string>::value;
//false
auto test3 = X::exists<char, char, char, char, char, char>::value;
auto test4 = X::exists<int, bool>::value;
Second Solution
template<class T, class U> struct type_pair { typedef type_pair<T, U> type; };
template<class... Ts> struct type_list {};
template<class...> struct xprod_tlist_ {};
template<class T, class... Ts, class... Us>
struct xprod_tlist_<type_list<T, Ts...>, type_list<Us...>>
: type_pair<T, Us>..., xprod_tlist_<type_list<Ts...>, type_list<Us...>> {};
template<class... Ts>
struct xprod_tlist : xprod_tlist_<type_list<Ts...>, type_list<Ts...>> {
template<class T, class U> struct exists
: std::is_base_of<type_pair<T, U>, xprod_tlist<Ts...>> {};
template<class T, class U> struct assert_exists {
static_assert(exists<T, U>::value, "Type not present in list");
};
};
Usage:
typedef xprod_tlist<int, float, string> X;
//these pass
X::assert_exists<int, int> assert_test1;
X::assert_exists<int, float> assert_test2;
X::assert_exists<int, string> assert_test3;
X::assert_exists<float, int> assert_test4;
X::assert_exists<float, float> assert_test5;
X::assert_exists<float, string> assert_test6;
X::assert_exists<string, int> assert_test7;
X::assert_exists<string, float> assert_test8;
X::assert_exists<string, string> assert_test9;
//this fails
X::assert_exists<int, char> assert_test10;
//true
auto test1 = X::exists<int, int>::value;
auto test2 = X::exists<int, float>::value;
auto test3 = X::exists<int, string>::value;
auto test4 = X::exists<float, int>::value;
auto test5 = X::exists<float, float>::value;
auto test6 = X::exists<float, string>::value;
auto test7 = X::exists<string, int>::value;
auto test8 = X::exists<string, float>::value;
auto test9 = X::exists<string, string>::value;
//false
auto test10 = X::exists<int, char>::value;
Currently, I have:
template <unsigned I,
unsigned N,
typename Tuple,
typename UnaryFunction>
struct for_;
template <unsigned N, typename Tuple, typename UnaryFunction>
struct for_<N, N, Tuple, UnaryFunction> {
static
void call(const Tuple&, UnaryFunction) {}
};
template <unsigned I,
unsigned N,
typename Tuple,
typename UnaryFunction>
struct for_ {
static
void call(Tuple&& x, UnaryFunction f) {
f(get<I>(x));
for_<I + 1, N, Tuple, UnaryFunction>::call(std::forward<Tuple>(x), f);
}
};
template <typename Tuple, typename UnaryFunction>
inline
void for_each(Tuple&& x, UnaryFunction f) {
for_<0,
tuple_size<
typename std::remove_const<
typename std::remove_reference<Tuple>::type
>::type
>::value,
Tuple,
UnaryFunction>::call(std::forward<Tuple>(x), f);
}
Is it possible to generalize this, probably by variadic templates, to take any number of tuple arguments?
EDIT:
Here is how I would use what I am unable to define:
if (i != e) {
std::array<Tuple, 2> x;
std::get<0>(x) = *i;
std::get<1>(x) = *i;
++i;
std::for_each (i, e, [&x](const Tuple& y) {
for_each(std::get<0>(x), y, assign_if(std::less));
for_each(std::get<1>(x), y, assign_if(std::greater));
});
}
EDIT: changed to use rvalue references and std::forward
I'm not sure is it what you expected, but I'll post it - maybe someone will find it helpful.
namespace std {
template<int I, class Tuple, typename F> struct for_each_impl {
static void for_each(const Tuple& t, F f) {
for_each_impl<I - 1, Tuple, F>::for_each(t, f);
f(get<I>(t));
}
};
template<class Tuple, typename F> struct for_each_impl<0, Tuple, F> {
static void for_each(const Tuple& t, F f) {
f(get<0>(t));
}
};
template<class Tuple, typename F>
F for_each(const Tuple& t, F f) {
for_each_impl<tuple_size<Tuple>::value - 1, Tuple, F>::for_each(t, f);
return f;
}
}
Functor:
struct call_tuple_item {
template<typename T>
void operator()(T a) {
std::cout << "call_tuple_item: " << a << std::endl;
}
};
Main function:
std::tuple<float, const char*> t1(3.14, "helloworld");
std::for_each(t1, call_tuple_item());
You can check my answer here for a hint on expanding tuples
How do I expand a tuple into variadic template function's arguments?
See below for the map(UnaryFunction, Tuple&&...) implementation I will be using, as well as the code I had been messing with in an attempt to get it working completely as I wanted (for_aux, last, etc.).
#include <array>
#include <iostream>
#include <tuple>
namespace detail {
struct static_ {
private:
static_() = delete;
static_(const static_&) = delete;
static_& operator=(const static_&) = delete;
};
template <unsigned... Args>
struct max;
template <unsigned Head, unsigned... Tail>
struct max<Head, Tail...>: private static_ {
static const unsigned value = Head > max<Tail...>::value
? Head
: max<Tail...>::value;
};
template <>
struct max<>: private static_ {
static const unsigned value = 0;
};
template <unsigned... Args>
struct min;
template <unsigned Head, unsigned... Tail>
struct min<Head, Tail...>: private static_ {
static const unsigned value = Head < min<Tail...>::value
? Head
: min<Tail...>::value;
};
template <>
struct min<>: private static_ {
static const unsigned value = 0;
};
template <typename... Args>
struct for_aux;
template <typename A, typename B>
struct for_aux<A, B>: private static_ {
static
void call(A&& a, B b) {
b(std::forward(a));
}
};
template <typename A, typename B, typename C>
struct for_aux<A, B, C>: private static_ {
static
void call(A&& a, B&& b, C c) {
c(std::forward(a), std::forward(b));
}
};
template <typename A, typename B, typename C, typename D>
struct for_aux<A, B, C, D>: private static_ {
static
void call(A&& a, B&& b, C&& c, D d) {
d(std::forward(a), std::forward(b), std::forward(c));
}
};
// template <typename Head, typename... Tail>
// struct for_aux: private static_ {
// static
// void call(Tail&&... x, Head f) {
// f(std::forward(x)...);
// }
// };
template <typename... Args>
struct last;
template <typename X>
struct last<X>: private static_ {
typedef X type;
};
template <typename Head, typename... Tail>
struct last<Head, Tail...>: private static_ {
typedef typename last<Tail...>::type type;
};
template <unsigned I,
unsigned N,
typename UnaryFunction,
typename... Tuples>
struct map;
template <unsigned N, typename UnaryFunction, typename... Tuples>
struct map<N, N, UnaryFunction, Tuples...>: private static_ {
static
void call(UnaryFunction, const Tuples&...) {}
};
template <unsigned I,
unsigned N,
typename UnaryFunction,
typename... Tuples>
struct map: private static_ {
static
void call(UnaryFunction f, Tuples&&... x) {
f(std::get<I>(std::forward<Tuples>(x))...);
map<I + 1,
N,
UnaryFunction,
Tuples...>::call(f, std::forward<Tuples>(x)...);
}
};
template <typename Tuple>
struct tuple_size: private static_ {
enum {
value = std::tuple_size<
typename std::remove_const<
typename std::remove_reference<Tuple>::type
>::type
>::value
};
};
}
template <typename UnaryFunction, typename... Tuples>
inline
void map(UnaryFunction f, Tuples&&... x) {
detail::map<0,
detail::max<
detail::tuple_size<Tuples>::value...
>::value,
UnaryFunction,
Tuples...
>::call(f, std::forward<Tuples>(x)...);
}
using namespace std;
struct f {
template <typename T, typename U>
void operator()(const T& i, const U& j) {
cout << i << " " << j << endl;
}
};
int main() {
const array<int, 2> x = {{2}};
const tuple<double, char> y(1.1, 'a');
map(f(), x, y);
}