Remapping a tuple onto another tuple - c++

I am attempting to convert between two types of std::tuples but I'm having trouble getting the implementation right. I want it to map if the type is the same, but it needs to allow duplicate types to get mapped in the original order.
The basic logic is:
while has pair<input, output>
if type(input) == type(output)
then do map, next input, next output
else
next input
assert(all outputs mapped)
With an example mapping being:
My "best" attempt at a solution looks like this:
template <auto> struct value {};
template <auto... Vals> struct value_sequence {};
template <class... Vals> struct placeholder {};
template <auto... As, auto... Bs>
constexpr value_sequence<As..., Bs...> operator+(value_sequence<As...>, value_sequence<Bs...>)
{
return {};
}
template <size_t Idx, size_t... Idxs, size_t OtherIdx, size_t... OtherIdxs, class T, class... Ts, class OtherT, class... OtherTs>
constexpr auto mapper(const std::index_sequence<Idx, Idxs...>&, const std::index_sequence<OtherIdx, OtherIdxs...>&, const placeholder<T, Ts...>&,
const placeholder<OtherT, OtherTs...>&)
{
if constexpr (sizeof...(OtherIdxs) == 0)
{
static_assert(std::is_same_v<T, OtherT>);
return value_sequence<Idx>{};
}
else if constexpr (std::is_same_v<T, OtherT>)
{
return value_sequence<Idx>{} +
mapper(std::index_sequence<Idxs...>{}, std::index_sequence<OtherIdxs...>{}, placeholder<Ts...>{}, placeholder<OtherTs...>{});
}
else
{
return mapper(std::index_sequence<Idx, Idxs...>{}, std::index_sequence<OtherIdxs...>{}, placeholder<T, Ts...>{}, placeholder<OtherTs...>{});
}
}
Called with:
mapper(std::make_index_sequence<sizeof...(Ts)>{}, std::make_index_sequence<sizeof...(OtherTs)>{},
placeholder<Ts...>{}, placeholder<OtherTs...>{})
Which gives the compiler error error C2672: 'mapper': no matching overloaded function found pointing to the else if case of the function mapper
I'm working in c++17, and it looks like all the bits I need are there, I just can't assemble them the right way.
Any help would be really appreciated!

Here's a fairly simple recursive implementation:
// pop_front implementation taken from https://stackoverflow.com/a/39101723/4151599
template <typename Tuple, std::size_t... Is>
auto pop_front_impl(const Tuple& tuple, std::index_sequence<Is...>)
{
return std::make_tuple(std::get<1 + Is>(tuple)...);
}
template <typename Tuple>
auto pop_front(const Tuple& tuple)
{
return pop_front_impl(tuple,
std::make_index_sequence<std::tuple_size<Tuple>::value - 1>());
}
template <typename...>
std::tuple<> map_tuple(std::tuple<>)
{
return {};
}
template <typename FirstOutput, typename... Outputs,
typename FirstInput, typename... Inputs>
std::tuple<FirstOutput, Outputs...>
map_tuple(const std::tuple<FirstInput, Inputs...>& input)
{
if constexpr (std::is_same_v<FirstInput, FirstOutput>) {
return std::tuple_cat(
std::tuple<FirstOutput>(std::get<0>(input)),
map_tuple<Outputs...>(pop_front(input))
);
} else {
return map_tuple<FirstOutput, Outputs...>(pop_front(input));
}
}
Simply call it like
std::tuple<int, double, char, int, int, float> tup = {1, 2.0, '3', 4, 5, 6.0};
auto mapped = map_tuple<int, double, char, float>(tup);
Demo

To begin, need a helper template to peel off the first type from a tuple.
#include <utility>
#include <type_traits>
#include <tuple>
#include <cstdlib>
#include <iostream>
template<typename T>
struct peel_tuple_t;
template<typename T, typename ...Args>
struct peel_tuple_t< std::tuple<T, Args...>> {
typedef std::tuple<Args...> type_t;
};
template<typename T>
using peel_tuple=typename peel_tuple_t<T>::type_t;
static_assert(std::is_same_v< peel_tuple<std::tuple<int, float>>,
std::tuple<float>>);
That's simple enough: std::tuple<int, float> -> std::tuple<float>.
Next, we need an equivalent of tuple_cat, but for index_sequences:
template<typename T, typename T2> struct index_sequence_cat_t;
template<size_t ...A, size_t ...B>
struct index_sequence_cat_t<std::index_sequence<A...>,
std::index_sequence<B...>> {
typedef std::index_sequence<A..., B...> type_t;
};
template<typename T, typename T2>
using index_sequence_cat=typename index_sequence_cat_t<T, T2>::type_t;
static_assert(std::is_same_v<index_sequence_cat<
std::index_sequence<1, 2>,
std::index_sequence<3, 4>
>, std::index_sequence<1, 2, 3, 4>>);
We can now use this to take a std::tuple<float, int, double, double>>, which provides the values, then a std::tuple<float, double, double>, where the values go, and produce a std::index_sequence<0, 2, 3> that will tell us to std::get index 0, 2, and 3 from the source tuple into the destination tuple:
template<typename output_tuple, typename input_tuple, size_t index>
struct map_input_tuple_t : map_input_tuple_t<output_tuple,
peel_tuple<input_tuple>,
index+1> {};
template<typename T, typename ...Args2, size_t index>
struct map_input_tuple_t<std::tuple<T>,
std::tuple<T, Args2...>, index> {
typedef std::index_sequence<index> type_t;
};
template<typename T, typename ...Args, typename ...Args2, size_t index>
struct map_input_tuple_t<std::tuple<T, Args...>,
std::tuple<T, Args2...>, index> {
typedef index_sequence_cat<
std::index_sequence<index>,
typename map_input_tuple_t<std::tuple<Args...>,
std::tuple<Args2...>,
index+1>::type_t> type_t;
};
template<typename output_tuple, typename input_tuple>
using map_input_tuple=
typename map_input_tuple_t<output_tuple, input_tuple, 0>::type_t;
static_assert(std::is_same_v< map_input_tuple<
std::tuple<float, double, double>,
std::tuple<float, int, double, double>>,
std::index_sequence<0, 2, 3>>);
And now, this is a solved problem:
template<typename mapping> struct map_tuple_impl;
template<size_t ...n>
struct map_tuple_impl<std::index_sequence<n...>> {
template<typename T>
static auto doit(const T &t)
{
return std::tuple{ std::get<n>(t)... };
}
};
template<typename output_tuple, typename ...input_types>
auto map_tuple(const std::tuple<input_types...> &input)
{
return map_tuple_impl<map_input_tuple<output_tuple,
std::tuple<input_types...>>>
::doit(input);
}
int main()
{
auto t=map_tuple<std::tuple<float, double, double>
>(std::tuple{1.0f, 2,
2.0,
3.0});
static_assert(std::is_same_v<decltype(t),
std::tuple<float, double, double>>);
if (t == std::tuple{1.0F, 2.0, 3.0})
std::cout << "yes\n";
return 0;
}

Is too late to play?
I propose the following struct to select the matching indexes
template <typename, typename, std::size_t...>
struct get_match_ind;
template <typename A0, typename ... As, typename ... Bs,
std::size_t I0, std::size_t... Is>
struct get_match_ind<std::tuple<A0, As...>, std::tuple<A0, Bs...>, I0, Is...>
: public get_match_ind<std::tuple<As...>, std::tuple<Bs...>, I0+1u, Is..., I0>
{ };
template <typename A0, typename ... As, typename ... Bs,
std::size_t I0, std::size_t... Is>
struct get_match_ind<std::tuple<A0, As...>, std::tuple<Bs...>, I0, Is...>
: public get_match_ind<std::tuple<As...>, std::tuple<Bs...>, I0+1u, Is...>
{ };
template <typename ... As, std::size_t I0, std::size_t... Is>
struct get_match_ind<std::tuple<As...>, std::tuple<>, I0, Is...>
{ using type = std::index_sequence<Is...>; };
template <typename T1, typename T2>
using match_indexes = typename get_match_ind<T1, T2, 0u>::type;
Now a map_tuple() function with a simple helper
template <typename Tb, typename Ta, std::size_t ... Is>
auto map_tuple_helper (Ta const & t0, std::index_sequence<Is...> const &)
{ return Tb{ std::get<Is>(t0)... }; }
template <typename Tb, typename Ta>
auto map_tuple (Ta const & t0)
{ return map_tuple_helper<Tb>(t0, match_indexes<Ta, Tb>{}); }
The following is a full compiling C++17 example with a static_assert() to verify the mapping
#include <tuple>
template <typename, typename, std::size_t...>
struct get_match_ind;
template <typename A0, typename ... As, typename ... Bs,
std::size_t I0, std::size_t... Is>
struct get_match_ind<std::tuple<A0, As...>, std::tuple<A0, Bs...>, I0, Is...>
: public get_match_ind<std::tuple<As...>, std::tuple<Bs...>, I0+1u, Is..., I0>
{ };
template <typename A0, typename ... As, typename ... Bs,
std::size_t I0, std::size_t... Is>
struct get_match_ind<std::tuple<A0, As...>, std::tuple<Bs...>, I0, Is...>
: public get_match_ind<std::tuple<As...>, std::tuple<Bs...>, I0+1u, Is...>
{ };
template <typename ... As, std::size_t I0, std::size_t... Is>
struct get_match_ind<std::tuple<As...>, std::tuple<>, I0, Is...>
{ using type = std::index_sequence<Is...>; };
template <typename T1, typename T2>
using match_indexes = typename get_match_ind<T1, T2, 0u>::type;
template <typename Tb, typename Ta, std::size_t ... Is>
auto map_tuple_helper (Ta const & t0, std::index_sequence<Is...> const &)
{ return Tb{ std::get<Is>(t0)... }; }
template <typename Tb, typename Ta>
auto map_tuple (Ta const & t0)
{ return map_tuple_helper<Tb>(t0, match_indexes<Ta, Tb>{}); }
int main()
{
// target type
using TT = std::tuple<int, double, char, float>;
std::tuple<int, double, char, int, int, float> t0 {1, 2.0, '3', 4, 5, 6.0};
auto t1 { map_tuple<TT>(t0) };
using T1 = decltype(t1);
static_assert( std::is_same_v<TT, T1> );
}

Related

C++ typelist make sublist

Let's says I have a type
template<typename ...Ts>
struct typelist {};
I need to get a sublist from this list :
template<int startInclusive, int stopExclusive, typename ...Ts>
struct sublist {
using type = ?; //
};
for example
sublist<1, 3, int, float, double, char>::type == typelist<float, double>
When start = 0 I have a working tail implementation :
template<typename ...Ts>
struct typelist {};
template<int N, typename T, typename ...Ts>
struct tail {
using type = typename tail<N - 1, Ts...>::type;
};
template<typename T, typename ...Ts>
struct tail<0, T, Ts...> {
using type = typelist<T, Ts...>;
};
using T = tail<1, int, double>::type;
#include <typeinfo>
#include <cstdio>
int main() {
::printf("%s\n", typeid(T).name());
}
However, I cannot get anything working for start > 0
As usual, std::index_sequence helps here:
template <std::size_t Offset, typename Seq, typename Tuple> struct sublist_impl;
template <std::size_t Offset, std::size_t ... Is, typename Tuple>
struct sublist_impl<Offset, std::index_sequence<Is...>, Tuple>
{
using type = std::tuple<std::tuple_element_t<Offset + Is, Tuple>...>;
};
template<std::size_t startInclusive, std::size_t stopExclusive, typename ...Ts>
using sublist = typename sublist_impl<startInclusive,
std::make_index_sequence<stopExclusive - startInclusive>,
std::tuple<Ts...>>::type;
Demo
It's likely to be an overkill, but it works:
template<typename... Ts>
struct typelist {};
template<class Typelist, typename T>
struct prepend;
template<typename... Ts, typename T>
struct prepend<typelist<Ts...>, T> {
using type = typelist<T, Ts...>;
};
template<int start, int stop, int i, typename... Ts>
struct sublist_impl {
using type = typelist<>;
};
template<int start, int stop, int i, typename T, typename... Ts>
struct sublist_impl<start, stop, i, T, Ts...>
{
private:
static constexpr auto get_sublist_type() {
if constexpr (i < start)
return typename sublist_impl<start, stop, i + 1, Ts...>::type{};
else if constexpr (i < stop)
return typename prepend<typename sublist_impl<
start, stop, i + 1, Ts...>::type, T>::type{};
else
return typelist<>{};
}
public:
using type = decltype(get_sublist_type());
};
template<int start, int stop, typename... Ts>
struct sublist {
using type = typename sublist_impl<start, stop, 0, Ts...>::type;
};
template<int start, int stop, typename... Ts>
using sublist_t = typename sublist<start, stop, Ts...>::type;
static_assert(std::is_same_v<
sublist_t<1, 3, int, float, double, char>, typelist<float, double>>);
static_assert(std::is_same_v<
sublist_t<0, 0, int, float, double, char>, typelist<>>);
static_assert(std::is_same_v<
sublist_t<4, 4, int, float, double, char>, typelist<>>);
static_assert(std::is_same_v<
sublist_t<0, 3, int, float, double, char>, typelist<int, float, double>>);
static_assert(std::is_same_v<
sublist_t<0, 4, int, float, double, char>, typelist<int, float, double, char>>);
Just for fun, the std::tuple_cat() way
#include <tuple>
#include <type_traits>
template<typename ...Ts>
struct typelist
{ };
template <std::size_t sI, std::size_t sE, std::size_t I, typename T>
constexpr std::enable_if_t<(I >= sI) && (I < sE),
std::tuple<typelist<T>>> getTpl ();
template <std::size_t sI, std::size_t sE, std::size_t I, typename T>
constexpr std::enable_if_t<(I < sI) || (I >= sE),
std::tuple<>> getTpl ();
template <typename ... Ts>
constexpr typelist<Ts...> getList (std::tuple<typelist<Ts>...>);
template <std::size_t sI, std::size_t sE, typename ... Ts,
std::size_t ... Is>
constexpr auto getTplList (typelist<Ts...>, std::index_sequence<Is...>)
-> decltype( getList(std::tuple_cat(getTpl<sI, sE, Is, Ts>()...)) );
template <std::size_t startI, std::size_t stopE, typename ... Ts>
struct sublist
{
using type = decltype(getTplList<startI, stopE>
(typelist<Ts...>{},
std::index_sequence_for<Ts...>{}));
};
int main ()
{
using type1 = typename sublist<1u, 3u, int, float, double, char>::type;
using type2 = typelist<float, double>;
static_assert( std::is_same<type1, type2>::value, "!" );
}

Specific types from a pack based on position

take_from_args<foo<int, bool, char, float>, 0,2>::type is to be
foo<int, char> based on the positions 0 and 2. The implementation is easy:
template <typename Class, std::size_t... Positions>
struct take_from_args;
template <template <typename...> class P, typename... Ts, std::size_t... Is>
struct take_from_args<P<Ts...>, Is...> {
using type = P<std::tuple_element_t<Is, std::tuple<Ts...>>...>;
};
Now, let's try to apply this to this class:
template <int V, bool B, typename... Args>
struct bar {};
The problem is the int and bool parameters of bar, so it cannot be passed into take_from_args. So let's define:
template <int V, bool B>
struct bar_h {
template <typename... Args>
using templ = bar<V, B, Args...>;
};
Unfortunately, take_from_args<bar_h<5, true>::templ<int, bool, char, float>, 0,2>::type won't compile. How do I redefine take_from_args so that it can template classes like bar?
My entire code:
#include <tuple>
template <typename Class, std::size_t... Positions> struct take_from_args;
template <template <typename...> class P, typename... Ts, std::size_t... Is>
struct take_from_args<P<Ts...>, Is...> {
using type = P<std::tuple_element_t<Is, std::tuple<Ts...>>...>;
};
// Testing
template <typename... Args>
struct foo {};
template <int V, bool B, typename... Args>
struct bar {};
template <int V, bool B>
struct bar_h {
template <typename... Args>
using templ = bar<V, B, Args...>;
};
int main() {
static_assert(std::is_same<
take_from_args<foo<int, bool, char, float>, 0,2>::type,
foo<int, char>>::value);
// static_assert(std::is_same<
// take_from_args<bar_h<5, true>::templ<int, bool, char, float>, 0,2>::type,
// bar<5, true, int, char>>::value);
}
Your workaround with bar_h does not work because bar_h<5, true>::templ<int, bool, char, float> is just an alias for bar<5, true, int, bool, char, float>:
static_assert(// compiles without error
std::is_same_v<
bar_h<5, true>::templ<int, bool, char, float>,
bar<5, true, int, bool, char, float>
>
);
I see two options:
1. avoid non-type template arguments in `bar`
see std::integral_constant
Edit: As you followed this approach in your answer, but I experience issues with clang: Here is a modified version which works for me with GCC 7.2 and Clang 5.0.
template<auto...> struct Vals {};
template<class T>
struct HasVals : std::false_type {};
template<auto... Vs>
struct HasVals<Vals<Vs...>> : std::true_type {};
template<class T, size_t... is>
struct take_from_args;
template<template<class...> class P, class... Ts, size_t... is>
struct take_from_args<P<Ts...>, is...> {
// convention: pass-through first argument if it `HasVals`
using Vs = std::tuple_element_t<0, std::tuple<Ts...>>;
using type = std::conditional_t<
HasVals<Vs>::value,
P<Vs, std::tuple_element_t<1u+is, std::tuple<Ts...>>...>,
P<std::tuple_element_t<is, std::tuple<Ts...>>...>
>;
};
// Testing
template<class Vs, class... Args>
struct bar;
template<int v, bool b, class... Args>
struct bar<Vals<v, b>, Args...> {
static constexpr int value = v;
static constexpr bool truth = b;
};
2. provide more specialization for `take_from_args`
Since you specifically ask for the latter, here is an example:
// there could be 1 value(s) at the beginning...
template<
template<auto, auto, typename...> class P,
auto v0, class... Ts, std::size_t... is
> struct take_from_args<P<v0, Ts...>, is...> {
using type = P<v0, std::tuple_element_t<is, std::tuple<Ts...>>...>;
};
// ... 2 ...
template<
template<auto, auto, typename...> class P,
auto v0, auto v1, class... Ts, std::size_t... is
> struct take_from_args<P<v0, v1, Ts...>, is...> {
using type = P<v0, v1, std::tuple_element_t<is, std::tuple<Ts...>>...>;
};
// ... 3 ... and more?
template<
template<auto, auto, typename...> class P,
auto v0, auto v1, auto v2, class... Ts, std::size_t... is
> struct take_from_args<P<v0, v1, v2, Ts...>, is...> {
using type = P<v0, v1, v2, std::tuple_element_t<is, std::tuple<Ts...>>...>;
};
Unfortunately, I had no success using auto... for the deduction of leading template arguments.
Following Julius' last suggestion for redesigning the class bar:
#include <tuple>
#include <type_traits>
template <auto...> struct Vals {};
template <typename Class> struct HasVals : std::false_type {};
template <auto... Vs>
struct HasVals<Vals<Vs...>> : std::true_type {};
template <typename Class, std::size_t... Positions> struct take_from_args;
template <template <typename, typename...> class P, typename VALS, typename... Ts, std::size_t... Is>
struct take_from_args<P<VALS, Ts...>, Is...> {
using type = std::conditional_t<HasVals<VALS>::value,
P<VALS, std::tuple_element_t<Is, std::tuple<Ts...>>...>,
P<std::tuple_element_t<Is, std::tuple<VALS, Ts...>>...> // VALS is part of the tuple in this case
>;
};
// Testing
template <typename... Args>
struct foo {};
template <typename VALS, typename... Args>
struct bar;
template <int v, bool b, typename... Args>
struct bar<Vals<v, b>, Args...> {
static constexpr int value = v;
static constexpr bool truth = b;
};
int main() {
static_assert(std::is_same<
take_from_args<foo<int, bool, char, float>, 0,2>::type,
foo<int, char>>::value);
using new_bar = take_from_args<bar<Vals<5, true>, int, bool, char, float>, 0,2>::type;
static_assert(std::is_same<
new_bar,
bar<Vals<5, true>, int, char>>::value);
static_assert(new_bar::value == 5);
static_assert(new_bar::truth == true);
//static_assert(std::is_same<decltype(new_bar::value), int>::value); // why fails?
//static_assert(std::is_same<decltype(new_bar::truth), bool>::value);
}

Compare two multisets of types for equality

As this question doesn't seem to cover all useful cases I decided to fill the gap with this little question of mine. Is there a way to answer if two multisets of types are equal?
#include <tuple>
#include <type_traits>
template <typename, typename>
struct type_multiset_eq : std::false_type
{
};
template <typename ... Types1, typename ... Types2>
struct type_multiset_eq<std::tuple<Types1...>, std::tuple<Types2...>>
: std::true_type
{
// Should only be true_type if the multisets of types are equal
};
int main() {
static_assert(type_multiset_eq<std::tuple<char, int, double, float, int, float>, std::tuple<float, char, int, double, int, float>>::value, "err");
static_assert(!type_multiset_eq<std::tuple<char, int, double, float, int, float>, std::tuple<char, int, double, int, float>>::value, "err");
static_assert(type_multiset_eq<std::tuple<char, char, char, float, float, float>, std::tuple<char, float, char, float, char, float>>::value, "err");
static_assert(!type_multiset_eq<std::tuple<int, int>, std::tuple<int, int, int>>::value, "err");
}
In the answer I focused a bit on efficiency. The method can be divided on four basic steps:
Rank types in each pack
Sort types in packs in base of given rank
Create two sets of unique elements (also types) containing information about the types and its frequencies from the previous version of packs (inheriting from these types)
Investigate if the other multiset of types is deriving from the same types (with frequencies)
The approach should be O(N log N) depending on the number of type
C++14 approach:
#include <utility>
#include <array>
template <class T>
constexpr T ilog2(T n) {
return n == static_cast<T>(1) ? static_cast<T>(0) : ilog2(n >> static_cast<T>(1)) + 1;
}
template <class T>
constexpr T ipow2(T n) {
return static_cast<T>(1) << n;
}
template <std::size_t N>
struct s_exp {
static constexpr std::size_t exp = ipow2(ilog2(N-1)+1);
};
template <std::size_t I, class T>
struct itag { };
template <std::size_t D, std::size_t I, class T>
struct vvtag { };
template <std::size_t S, std::size_t I, class T>
struct vtag: virtual vvtag<I/S, (I%S) / ((s_exp<S>::exp + (2 << (I/S)) - 1)/(2 << (I/S))), T> { };
template <class... Ts>
struct pack {
static constexpr std::size_t size = sizeof...(Ts);
};
template <class P, class = std::make_index_sequence<P::size>>
struct ipack;
template <class... Ts, std::size_t... Is>
struct ipack<pack<Ts...>, std::index_sequence<Is...>>: itag<Is, Ts>... {
static constexpr std::size_t size = sizeof...(Ts);
};
template <std::size_t I, class T>
T ipack_element(itag<I, T>);
template <class IP, class = std::make_index_sequence<IP::size * (ilog2(IP::size - 1) + 1) >>
struct vpack;
template <class IP, std::size_t... Is>
struct vpack<IP, std::index_sequence<Is...>>: vtag<IP::size, Is, decltype(ipack_element<Is % IP::size>(IP{}))>... {
static constexpr std::size_t size = IP::size;
};
template <class A, class CompArr>
constexpr int partition(A &a, int lo, int hi, const CompArr &ca) {
int x = a[lo];
int i = lo, j = hi;
while (true) {
while (ca[a[j]] > ca[x])
j--;
while (ca[a[i]] < ca[x])
i++;
if (i < j) {
auto w = a[i];
a[i] = a[j];
a[j] = w;
i++;
j--;
} else
return j;
}
}
template <class A, class CompArr>
constexpr void quicksort(A &a, int lo, int hi, const CompArr &ca) {
if (lo < hi) {
auto q = partition(a, lo, hi, ca);
quicksort(a, lo, q, ca);
quicksort(a, q+1, hi, ca);
}
}
template <class... Ts, std::size_t... Is>
constexpr std::array<std::size_t, sizeof...(Ts)> rank(itag<0, ipack<pack<Ts...>, std::index_sequence<Is...>>>) {
return {{!std::is_base_of<vvtag<0, 0, decltype(ipack_element<Is>(ipack<pack<Ts...>>{}))>, vpack<ipack<pack<Ts...>>>>::value...}};
}
template <std::size_t N, class... Ts, std::size_t... Is>
constexpr std::array<std::size_t, sizeof...(Ts)> rank(itag<N, ipack<pack<Ts...>, std::index_sequence<Is...>>>) {
constexpr auto prev = rank(itag<N - 1, ipack<pack<Ts...>>>{});
return {{prev[Is]*2 + !std::is_base_of<vvtag<N, prev[Is]*2, decltype(ipack_element<Is>(ipack<pack<Ts...>>{}))>, vpack<ipack<pack<Ts...>>>>::value...}};
}
template <class... Ts, std::size_t... Is>
constexpr std::array<std::size_t, sizeof...(Ts)> sort_types_impl(ipack<pack<Ts...>, std::index_sequence<Is...>>) {
constexpr std::size_t TS = sizeof...(Ts);
auto compare_enabler = rank(itag<ilog2(TS - 1), ipack<pack<Ts...>, std::index_sequence<Is...>>>{});
std::size_t result[TS] { Is... };
quicksort(result, 0, sizeof...(Is) - 1, compare_enabler);
return {{ result[Is]... }};
}
template <class>
struct sort_types;
template <class... Ts>
struct sort_types<pack<Ts...>>: sort_types<ipack<pack<Ts...>>> { };
template <class... Ts, std::size_t... Is>
struct sort_types<ipack<pack<Ts...>, std::index_sequence<Is...>>> {
static constexpr auto idxs = sort_types_impl(ipack<pack<Ts...>>{});
using type = pack<decltype(ipack_element<idxs[Is]>(ipack<pack<Ts...>>{}))...>;
};
struct dummy { };
template <class... Ts>
struct unique_pack: Ts... {
static constexpr std::size_t size = sizeof...(Ts);
template <class Up>
constexpr bool operator==(Up) {
bool result = size == Up::size;
bool ibo[sizeof...(Ts)] = { std::is_base_of<Ts, Up>::value... };
for (std::size_t i = 0; i < sizeof...(Ts); i++)
result &= ibo[i];
return result;
}
};
template <class>
struct multiset;
template <class... Ts>
struct multiset<pack<Ts...>>: multiset<ipack<pack<Ts...>>> {};
template <class... Ts, std::size_t... Is>
struct multiset<ipack<pack<Ts...>, std::index_sequence<Is...>>> {
using sorted_pack = typename sort_types<pack<Ts..., dummy>>::type;
static constexpr std::array<bool, sizeof...(Ts)> const unique_types() {
return {{ !std::is_same< decltype(ipack_element<Is>(ipack<sorted_pack>{})), decltype(ipack_element<Is + 1>(ipack<sorted_pack>{})) >::value... }};
}
static constexpr std::size_t unique_count() {
constexpr std::array<bool, sizeof...(Ts)> const ut = unique_types();
std::size_t result = 0;
for (std::size_t i = 0; i < sizeof...(Ts); i++)
result += ut[i];
return result;
}
template <std::size_t... Is2>
static constexpr std::array<std::size_t, unique_count()> const unique_idxs(std::index_sequence<Is2...>) {
std::size_t result[unique_count()] {};
std::size_t cur = 0;
constexpr std::array<bool, sizeof...(Ts)> const ut = unique_types();
for (std::size_t i = 0; i < sizeof...(Ts); i++) {
if (ut[i])
result[cur++] = i;
}
return {{ result[Is2]... }};
}
template <std::size_t... Is2>
static constexpr std::array<std::size_t, unique_count()> const unique_counts(std::index_sequence<Is2...>) {
std::size_t result[unique_count()] {};
std::size_t cur = 0;
constexpr auto ut = unique_types();
for (std::size_t i = 0; i < sizeof...(Ts); i++) {
if (ut[i])
result[cur++]++;
else
result[cur]++;
}
return {{ result[Is2]... }};
}
template <std::size_t... Is2>
static auto make_type(std::index_sequence<Is2...>) {
constexpr std::array<std::size_t, unique_count()> const idxs = unique_idxs(std::index_sequence<Is2...>{});
constexpr std::array<std::size_t, unique_count()> const counts = unique_counts(std::index_sequence<Is2...>{});
return unique_pack<itag<counts[Is2], decltype(ipack_element<idxs[Is2]>(ipack<sorted_pack>{}))>...>{};
}
template <class T = multiset, std::size_t UC = T::unique_count()>
using type = decltype(make_type(std::make_index_sequence<UC>{}));
};
template <class P1, class P2>
constexpr bool multiset_equality(P1, P2) {
return typename multiset<P1>::template type<>{} == typename multiset<P2>::template type<>{} && typename multiset<P2>::template type<>{} == typename multiset<P1>::template type<>{};
}
int main() {
static_assert(multiset_equality(pack<char, int, double, float, int, float>{}, pack<float, char, int, double, int, float>{}),"!");
static_assert(!multiset_equality(pack<char, int, double, float, int, float>{}, pack<char, int, double, int, float>{}),"!");
static_assert(multiset_equality(pack<char, char, char, float, float, float>{}, pack<char, float, char, float, char, float>{}),"!");
static_assert(!multiset_equality(pack<int, int>{}, pack<int, int, int>{}),"!");
}
[live demo]
Interesting question...
Taking inspiration from your answer (well... copying it) in the original question (the type_set_eq one), adding a type counter (countT) and removing the helper struct and the tag struct, I suppose that you can simply write something as follows
#include <tuple>
#include <type_traits>
template <typename ...>
struct countT;
template <typename T>
struct countT<T>
{ static constexpr std::size_t value { 0U }; };
template <typename T, typename T0, typename ... Ts>
struct countT<T, T0, Ts...>
{ static constexpr std::size_t value { countT<T, Ts...>::value }; };
template <typename T, typename ... Ts>
struct countT<T, T, Ts...>
{ static constexpr std::size_t value { 1U + countT<T, Ts...>::value }; };
template <bool ...>
struct bool_pack
{ };
template <bool ... Bs>
using my_and = std::is_same<bool_pack<Bs..., true>, bool_pack<true, Bs...>>;
template <typename, typename, typename = void>
struct type_multiset_eq : std::false_type
{ };
template <template <typename ...> class C1, typename ... Ts1,
template <typename ...> class C2, typename ... Ts2>
struct type_multiset_eq<C1<Ts1...>, C2<Ts2...>,
typename std::enable_if<
(sizeof...(Ts1) == sizeof...(Ts2))
&& (my_and<( countT<Ts1, Ts1...>::value
== countT<Ts1, Ts2...>::value)...>::value)
>::type>
: std::true_type
{ };
int main()
{
static_assert( type_multiset_eq<
std::tuple<char, int, double, float, int, float>,
std::tuple<float, char, int, double, int, float>>::value, "err");
static_assert( ! type_multiset_eq<
std::tuple<char, int, double, float, int, float>,
std::tuple<char, int, double, int, float>>::value, "err");
static_assert( type_multiset_eq<
std::tuple<char, char, char, float, float, float>,
std::tuple<char, float, char, float, char, float>>::value, "err");
static_assert( ! type_multiset_eq<
std::tuple<int, int>,
std::tuple<int, int, int>>::value, "err");
}
In case you can use C++14, you can substitute the countT type trait with the following constexpr function
template <typename T, typename ... Ts>
constexpr std::size_t cntT ()
{
using unused = std::size_t[];
std::size_t ret { 0U };
(void)unused { 0U, ret += (std::is_same<T, Ts>::value ? 1U : 0U)... };
return ret;
}
You can use the following:
it removes from both side type until first is empty, or second doesn't match:
template <typename T, typename Tuple, typename Res = std::tuple<>>
struct remove_type_from_tuple;
template <typename T, typename ... Ts, typename ...Res>
struct remove_type_from_tuple<T, std::tuple<T, Ts...>, std::tuple<Res...>>
{
using type = std::tuple<Res..., Ts...>;
};
template <typename T, typename T2, typename ... Ts, typename ...Res>
struct remove_type_from_tuple<T, std::tuple<T2, Ts...>, std::tuple<Res...>>
{
using type = typename remove_type_from_tuple<T,
std::tuple<Ts...>,
std::tuple<Res..., T2>>::type;
};
template <typename T, typename Res>
struct remove_type_from_tuple<T, std::tuple<>, Res>
{
using type = void;
};
template <typename T, typename Res>
struct remove_type_from_tuple<T, void, Res>
{
using type = void;
};
template <typename Tuple1, typename Tuple2>
struct diff_types_from_tuple;
template <typename T, typename ...Ts, typename Tuple>
struct diff_types_from_tuple<std::tuple<T, Ts...>, Tuple>
{
using type =
typename diff_types_from_tuple<std::tuple<Ts...>,
typename remove_type_from_tuple<T, Tuple>::type
>::type;
};
template <typename Tuple>
struct diff_types_from_tuple<std::tuple<>, Tuple>
{
using type = Tuple;
};
template <typename Tuple1, typename Tuple2>
struct type_multiset_eq :
std::is_same<std::tuple<>,
typename diff_types_from_tuple<Tuple1, Tuple2>::type>
{
};
Demo
Just for fun, I propose another solution based on type counting (like the first one) with the same complexity (O(n^2), I suppose) but a little smarter (end the check at the first difference)
#include <tuple>
#include <type_traits>
template <typename ...>
struct countT;
template <typename T>
struct countT<T>
{ static constexpr std::size_t value { 0U }; };
template <typename T, typename T0, typename ... Ts>
struct countT<T, T0, Ts...>
{ static constexpr std::size_t value { countT<T, Ts...>::value }; };
template <typename T, typename ... Ts>
struct countT<T, T, Ts...>
{ static constexpr std::size_t value { 1U + countT<T, Ts...>::value }; };
template <typename, typename, typename>
struct eqCountT;
template <template <typename ...> class C, typename T, typename ... Ts1,
typename ... Ts2, typename ... Ts3>
struct eqCountT<C<T, Ts1...>, C<Ts2...>, C<Ts3...>>
: std::integral_constant<bool,
(countT<T, Ts2...>::value == countT<T, Ts3...>::value)>
{ };
template <template <typename ...> class C, typename T2, typename T3>
struct eqCountT<C<>, T2, T3> : std::true_type
{ };
template <typename T1, typename T2, typename T3,
bool = eqCountT<T1, T2, T3>::value>
struct mseqH;
template <template <typename ...> class C, typename T2, typename T3>
struct mseqH<C<>, T2, T3, true> : std::true_type
{ };
template <typename T1, typename T2, typename T3>
struct mseqH<T1, T2, T3, false> : std::false_type
{ };
template <template <typename ...> class C, typename T, typename ... Ts1,
typename T2, typename T3>
struct mseqH<C<T, Ts1...>, T2, T3, true> : mseqH<C<Ts1...>, T2, T3>
{ };
template <typename, typename>
struct type_multiset_eq;
template <template <typename ...> class C1, typename ... Ts1,
template <typename ...> class C2, typename ... Ts2>
struct type_multiset_eq<C1<Ts1...>, C2<Ts2...>>
: std::integral_constant<bool,
(sizeof...(Ts1) == sizeof...(Ts2))
&& mseqH<C1<Ts1...>, C1<Ts1...>, C1<Ts2...>>::value>
{ };
int main()
{
static_assert( type_multiset_eq<
std::tuple<char, int, double, float, int, float>,
std::tuple<float, char, int, double, int, float>>::value, "err");
static_assert( ! type_multiset_eq<
std::tuple<char, int, double, float, int, float>,
std::tuple<char, int, double, int, float>>::value, "err");
static_assert( type_multiset_eq<
std::tuple<char, char, char, float, float, float>,
std::tuple<char, float, char, float, char, float>>::value, "err");
static_assert( ! type_multiset_eq<
std::tuple<int, int>,
std::tuple<int, int, int>>::value, "err");
}
In case you can use C++14, you can substitute the countT type trait with the following constexpr function
template <typename T, typename ... Ts>
constexpr std::size_t cntT ()
{
using unused = std::size_t[];
std::size_t ret { 0U };
(void)unused { 0U, ret += (std::is_same<T, Ts>::value ? 1U : 0U)... };
return ret;
}

Creating a sub-tuple starting from a std::tuple<some_types...>

Let us suppose that a std::tuple<some_types...> is given. I would like to create a new std::tuple whose types are the ones indexed in [0, sizeof...(some_types) - 2]. For instance, let's suppose that the starting tuple is std::tuple<int, double, bool>. I would like to obtain a sub-tuple defined as std::tuple<int, double>.
I'm quite new to variadic templates. As a first step I tried to write a struct in charge of storing the different types of the original std::tuple with the aim of creating a new tuple of the same kind (as in std::tuple<decltype(old_tuple)> new_tuple).
template<typename... types>
struct type_list;
template<typename T, typename... types>
struct type_list<T, types...> : public type_list<types...> {
typedef T type;
};
template<typename T>
struct type_list<T> {
typedef T type;
};
What I would like to do is something like:
std::tuple<type_list<bool, double, int>::type...> new_tuple // this won't work
And the next step would be of discarding the last element in the parameter pack. How can I access the several type's stored in type_list? and how to discard some of them?
Thanks.
Here is a way to solve your problem directly.
template<unsigned...s> struct seq { typedef seq<s...> type; };
template<unsigned max, unsigned... s> struct make_seq:make_seq<max-1, max-1, s...> {};
template<unsigned...s> struct make_seq<0, s...>:seq<s...> {};
template<unsigned... s, typename Tuple>
auto extract_tuple( seq<s...>, Tuple& tup ) {
return std::make_tuple( std::get<s>(tup)... );
}
You can use this as follows:
std::tuple< int, double, bool > my_tup;
auto short_tup = extract_tuple( make_seq<2>(), my_tup );
auto skip_2nd = extract_tuple( seq<0,2>(), my_tup );
and use decltype if you need the resulting type.
A completely other approach would be to write append_type, which takes a type and a tuple<...>, and adds that type to the end. Then add to type_list:
template<template<typename...>class target>
struct gather {
typedef typename type_list<types...>::template gather<target>::type parent_result;
typedef typename append< parent_result, T >::type type;
};
which gives you a way to accumulate the types of your type_list into an arbitrary parameter pack holding template. But that isn't required for your problem.
This kind of manipulation is fairly easy with an index sequence technique: generate an index sequence with two fewer indices than your tuple, and use that sequence to select fields from the original. Using std::make_index_sequence and return type deduction from C++14:
template <typename... T, std::size_t... I>
auto subtuple_(const std::tuple<T...>& t, std::index_sequence<I...>) {
return std::make_tuple(std::get<I>(t)...);
}
template <int Trim, typename... T>
auto subtuple(const std::tuple<T...>& t) {
return subtuple_(t, std::make_index_sequence<sizeof...(T) - Trim>());
}
In C++11:
#include <cstddef> // for std::size_t
template<typename T, T... I>
struct integer_sequence {
using value_type = T;
static constexpr std::size_t size() noexcept {
return sizeof...(I);
}
};
namespace integer_sequence_detail {
template <typename, typename> struct concat;
template <typename T, T... A, T... B>
struct concat<integer_sequence<T, A...>, integer_sequence<T, B...>> {
typedef integer_sequence<T, A..., B...> type;
};
template <typename T, int First, int Count>
struct build_helper {
using type = typename concat<
typename build_helper<T, First, Count/2>::type,
typename build_helper<T, First + Count/2, Count - Count/2>::type
>::type;
};
template <typename T, int First>
struct build_helper<T, First, 1> {
using type = integer_sequence<T, T(First)>;
};
template <typename T, int First>
struct build_helper<T, First, 0> {
using type = integer_sequence<T>;
};
template <typename T, T N>
using builder = typename build_helper<T, 0, N>::type;
} // namespace integer_sequence_detail
template <typename T, T N>
using make_integer_sequence = integer_sequence_detail::builder<T, N>;
template <std::size_t... I>
using index_sequence = integer_sequence<std::size_t, I...>;
template<size_t N>
using make_index_sequence = make_integer_sequence<size_t, N>;
#include <tuple>
template <typename... T, std::size_t... I>
auto subtuple_(const std::tuple<T...>& t, index_sequence<I...>)
-> decltype(std::make_tuple(std::get<I>(t)...))
{
return std::make_tuple(std::get<I>(t)...);
}
template <int Trim, typename... T>
auto subtuple(const std::tuple<T...>& t)
-> decltype(subtuple_(t, make_index_sequence<sizeof...(T) - Trim>()))
{
return subtuple_(t, make_index_sequence<sizeof...(T) - Trim>());
}
Live at Coliru.
Subrange from tuple with boundary checking, without declaring "helper classes":
template <size_t starting, size_t elems, class tuple, class seq = decltype(std::make_index_sequence<elems>())>
struct sub_range;
template <size_t starting, size_t elems, class ... args, size_t ... indx>
struct sub_range<starting, elems, std::tuple<args...>, std::index_sequence<indx...>>
{
static_assert(elems <= sizeof...(args) - starting, "sub range is out of bounds!");
using tuple = std::tuple<std::tuple_element_t<indx + starting, std::tuple<args...>> ...>;
};
Usage:
struct a0;
...
struct a8;
using range_outer = std::tuple<a0, a1, a2, a3, a4, a5, a6, a7, a8>;
sub_range<2, 3, range_outer>::tuple; //std::tuple<a2, a3, a4>
One way to do it is to recursively pass two tuples to a helper struct that takes the first element of the "source" tuple and adds it to the end of the another one:
#include <iostream>
#include <tuple>
#include <type_traits>
namespace detail {
template<typename...>
struct truncate;
// this specialization does the majority of the work
template<typename... Head, typename T, typename... Tail>
struct truncate< std::tuple<Head...>, std::tuple<T, Tail...> > {
typedef typename
truncate< std::tuple<Head..., T>, std::tuple<Tail...> >::type type;
};
// this one stops the recursion when there's only
// one element left in the source tuple
template<typename... Head, typename T>
struct truncate< std::tuple<Head...>, std::tuple<T> > {
typedef std::tuple<Head...> type;
};
}
template<typename...>
struct tuple_truncate;
template<typename... Args>
struct tuple_truncate<std::tuple<Args...>> {
// initiate the recursion - we start with an empty tuple,
// with the source tuple on the right
typedef typename detail::truncate< std::tuple<>, std::tuple<Args...> >::type type;
};
int main()
{
typedef typename tuple_truncate< std::tuple<bool, double, int> >::type X;
// test
std::cout << std::is_same<X, std::tuple<bool, double>>::value; // 1, yay
}
Live example.

Removing the first type of a std::tuple

This seems to be a very simple question: How does one remove the first (the n-th) type in a std::tuple?
Example:
typedef std::tuple<int, short, double> tuple1;
typedef std::tuple<short, double> tuple2;
The operation described above would transform tuple1 into tuple2. Is it possible?
You can use a simple type function based on partial specialization of a class template:
#include <type_traits>
#include <tuple>
using namespace std;
template<typename T>
struct remove_first_type
{
};
template<typename T, typename... Ts>
struct remove_first_type<tuple<T, Ts...>>
{
typedef tuple<Ts...> type;
};
int main()
{
typedef tuple<int, bool, double> my_tuple;
typedef remove_first_type<my_tuple>::type my_tuple_wo_first_type;
static_assert(
is_same<my_tuple_wo_first_type, tuple<bool, double>>::value,
"Error!"
);
}
Also, this solution can be easily generalized to remove the i-th type of a tuple:
#include <type_traits>
#include <tuple>
using namespace std;
template<size_t I, typename T>
struct remove_ith_type
{
};
template<typename T, typename... Ts>
struct remove_ith_type<0, tuple<T, Ts...>>
{
typedef tuple<Ts...> type;
};
template<size_t I, typename T, typename... Ts>
struct remove_ith_type<I, tuple<T, Ts...>>
{
typedef decltype(
tuple_cat(
declval<tuple<T>>(),
declval<typename remove_ith_type<I - 1, tuple<Ts...>>::type>()
)
) type;
};
int main()
{
typedef tuple<int, bool, double> my_tuple;
typedef remove_ith_type<1, my_tuple>::type my_tuple_wo_2nd_type;
static_assert(
is_same<my_tuple_wo_2nd_type, tuple<int, double>>::value,
"Error!"
);
}
I wrote a proposal which was accepted into the C++14 standard making it quite easy to do for any "tuple-like" type, i.e. one that supports the tuple_size and tuple_element API:
template<typename T, typename Seq>
struct tuple_cdr_impl;
template<typename T, std::size_t I0, std::size_t... I>
struct tuple_cdr_impl<T, std::index_sequence<I0, I...>>
{
using type = std::tuple<typename std::tuple_element<I, T>::type...>;
};
template<typename T>
struct tuple_cdr
: tuple_cdr_impl<T, std::make_index_sequence<std::tuple_size<T>::value>>
{ };
And you can transform a tuple object into the new type with only a couple of functions:
template<typename T, std::size_t I0, std::size_t... I>
typename tuple_cdr<typename std::remove_reference<T>::type>::type
cdr_impl(T&& t, std::index_sequence<I0, I...>)
{
return std::make_tuple(std::get<I>(t)...);
}
template<typename T>
typename tuple_cdr<typename std::remove_reference<T>::type>::type
cdr(T&& t)
{
return cdr_impl(std::forward<T>(t),
std::make_index_sequence<std::tuple_size<T>::value>{});
}
This creates an integer sequence [0,1,2,...,N) where N is tuple_size<T>::value, then creates a new tuple with make_tuple(get<I>(t)...) for I in [1,2,...,N)
Testing it:
using tuple1 = std::tuple<int, short, double>;
using tuple2 = std::tuple<short, double>;
using transformed = decltype(cdr(std::declval<tuple1>()));
static_assert(std::is_same<transformed, tuple2>::value, "");
static_assert(std::is_same<tuple_cdr<tuple1>::type, tuple2>::value, "");
#include <iostream>
int main()
{
auto t = cdr(std::make_tuple(nullptr, "hello", "world"));
std::cout << std::get<0>(t) << ", " << std::get<1>(t) << '\n';
}
My reference implementation for the proposal is at https://gitlab.com/redistd/integer_seq/blob/master/integer_seq.h
I came up with a solution very similar to that proposed by #Andy, but that tries to be a bit more generic by working directly on the parameter pack (using a dummy wrapper) rather than on std::tuple. This way, the operation can be applied to other variadic templates as well, not only to tuples:
#include <type_traits>
#include <tuple>
template <typename... Args> struct pack {};
template <template <typename...> class T, typename Pack>
struct unpack;
template <template <typename...> class T, typename... Args>
struct unpack<T, pack<Args...>>
{
typedef T<Args...> type;
};
template <typename T, typename Pack>
struct prepend;
template <typename T, typename... Args>
struct prepend<T, pack<Args...>>
{
typedef pack<T, Args...> type;
};
template <std::size_t N, typename... Args>
struct remove_nth_type;
template <std::size_t N, typename T, typename... Ts>
struct remove_nth_type<N, T, Ts...>
: prepend<T, typename remove_nth_type<N-1, Ts...>::type>
{};
template <typename T, typename... Ts>
struct remove_nth_type<0, T, Ts...>
{
typedef pack<Ts...> type;
};
template <typename T, int N>
struct remove_nth;
template <template <typename...> class T, int N, typename... Args>
struct remove_nth<T<Args...>, N>
{
typedef typename
unpack<
T, typename
remove_nth_type<N, Args...>::type
>::type type;
};
template <typename... Args>
struct my_variadic_template
{
};
int main()
{
typedef std::tuple<int, bool, double> my_tuple;
typedef remove_nth<my_tuple, 1>::type my_tuple_wo_2nd_type;
static_assert(
is_same<my_tuple_wo_2nd_type, tuple<int, double>>::value,
"Error!"
);
typedef my_variadic_template<int, double> vt;
typedef remove_nth<vt, 0>::type vt_wo_1st_type;
static_assert(
is_same<vt_wo_1st_type, my_variadic_template<double>>::value,
"Error!"
);
}
pack is an helper structure whose sole purpose is to store a template parameter pack. unpack can then be used to unpack the parameters into an arbitrary class template (thanks to #BenVoigt for this trick). prepend simply prepends a type to a pack.
remove_nth_type uses partial template specialization to remove the nth type from a parameter pack, storing the result into a pack. Finally, remove_nth takes a specialization of an arbitrary class template, remove the nth type from its template parameters, and return the new specialization.
Beside that crazy TMP stuff, there is a very easy way using the C++17 STL function std::apply:
#include <string>
#include <tuple>
template <class T, class... Args>
auto tail(const std::tuple<T, Args...>& t)
{
return std::apply(
[](const T&, const Args&... args)
{
return std::make_tuple(args...);
}, t);
}
template <class T>
using tail_t = decltype(tail(T{}));
int main()
{
std::tuple<int, double, std::string> t{1, 2., "3"};
auto _2_3 = tail(t);
using tuple_t = tail_t<std::tuple<int, double, std::string>>;
static_assert(std::is_same_v<std::tuple<double, std::string>, tuple_t>);
}
DEMO.
This is an over engineered bit of template metaprogramming for this task. It includes the ability to do arbitrary reorders/duplications/removals on the types of a tuple via a filter template:
#include <utility>
#include <type_traits>
template<typename... Ts> struct pack {};
template<std::size_t index, typename Pack, typename=void> struct nth_type;
template<typename T0, typename... Ts>
struct nth_type<0, pack<T0, Ts...>, void> { typedef T0 type; };
template<std::size_t index, typename T0, typename... Ts>
struct nth_type<index, pack<T0, Ts...>, typename std::enable_if<(index>0)>::type>:
nth_type<index-1, pack<Ts...>>
{};
template<std::size_t... s> struct seq {};
template<std::size_t n, std::size_t... s>
struct make_seq:make_seq<n-1, n-1, s...> {};
template<std::size_t... s>
struct make_seq<0,s...> {
typedef seq<s...> type;
};
template<typename T, typename Pack> struct conc_pack { typedef pack<T> type; };
template<typename T, typename... Ts> struct conc_pack<T, pack<Ts...>> { typedef pack<T, Ts...> type; };
template<std::size_t n, typename Seq> struct append;
template<std::size_t n, std::size_t... s>
struct append<n, seq<s...>> {
typedef seq<n, s...> type;
};
template<typename S0, typename S1> struct conc;
template<std::size_t... s0, std::size_t... s1>
struct conc<seq<s0...>, seq<s1...>>
{
typedef seq<s0..., s1...> type;
};
template<typename T, typename=void> struct value_exists:std::false_type {};
template<typename T> struct value_exists<T,
typename std::enable_if< std::is_same<decltype(T::value),decltype(T::value)>::value >::type
>:std::true_type {};
template<typename T, typename=void> struct result_exists:std::false_type {};
template<typename T> struct result_exists<T,
typename std::enable_if< std::is_same<typename T::result,typename T::result>::value >::type
>:std::true_type {};
template<template<std::size_t>class filter, typename Seq, typename=void>
struct filter_seq { typedef seq<> type; };
template<template<std::size_t>class filter, std::size_t s0, std::size_t... s>
struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<value_exists<filter<s0>>::value>::type>
: append< filter<s0>::value, typename filter_seq<filter, seq<s...>>::type >
{};
template<template<std::size_t>class filter, std::size_t s0, std::size_t... s>
struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<!value_exists<filter<s0>>::value && result_exists<filter<s0>>::value>::type>
: conc< typename filter<s0>::result, typename filter_seq<filter, seq<s...>>::type >
{};
template<template<std::size_t>class filter, std::size_t s0, std::size_t... s>
struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<!value_exists<filter<s0>>::value && !result_exists<filter<s0>>::value>::type>
: filter_seq<filter, seq<s...>>
{};
template<typename Seq, typename Pack>
struct remap_pack {
typedef pack<> type;
};
template<std::size_t s0, std::size_t... s, typename Pack>
struct remap_pack< seq<s0, s...>, Pack >
{
typedef typename conc_pack< typename nth_type<s0, Pack>::type, typename remap_pack< seq<s...>, Pack >::type >::type type;
};
template<typename Pack>
struct get_indexes { typedef seq<> type; };
template<typename... Ts>
struct get_indexes<pack<Ts...>> {
typedef typename make_seq< sizeof...(Ts) >::type type;
};
template<std::size_t n>
struct filter_zero_out { enum{ value = n }; };
template<>
struct filter_zero_out<0> {};
template<std::size_t n>
struct filter_zero_out_b { typedef seq<n> result; };
template<>
struct filter_zero_out_b<0> { typedef seq<> result; };
#include <iostream>
int main() {
typedef pack< int, double, char > pack1;
typedef pack< double, char > pack2;
typedef filter_seq< filter_zero_out, typename get_indexes<pack1>::type >::type reindex;
typedef filter_seq< filter_zero_out_b, typename get_indexes<pack1>::type >::type reindex_b;
typedef typename remap_pack< reindex, pack1 >::type pack2_clone;
typedef typename remap_pack< reindex_b, pack1 >::type pack2_clone_b;
std::cout << std::is_same< pack2, pack2_clone >::value << "\n";
std::cout << std::is_same< pack2, pack2_clone_b >::value << "\n";
}
Here we have a type pack that holds an arbitrary list of types. See #LucTouraille 's neat answer for how to move between tuple and pack.
seq holds a sequence of indexes. remap_pack takes a seq and a pack, and builds a resulting pack by grabbing the nth element of the original pack.
filter_seq takes a template<size_t> functor and a seq, and uses the functor to filter the elements of the seq. The functor can return either a ::value of type size_t or a ::result of type seq<...> or neither, allowing one-to-one or one-to-many functors.
A few other helper functions, like conc, append, conc_pack, get_indexes, make_seq, nth_type round things out.
I tested it with filter_zero_out which is a ::value based filter that removes 0, and filter_zero_out_b which is a ::result based filter that also removes 0.