We can do this with some recursive template magic:
//helper class template which will handle the recursion
template <int... Args>
struct FiltersFor;
//a helper to get the type of concatenating two tuples
template <typename Tuple1, typename Tuple2>
using tuple_cat_t = decltype(std::tuple_cat(std::declval<Tuple1>(),
std::declval<Tuple2>()));
//pop off two ints from the pack, recurse
template <int Ins, int Outs, int... Others>
struct FiltersFor<Ins,Outs,Others...>
{
//the type of concatenating a tuple of Filter<Ins,Outs> with the tuple from recursion
using type = tuple_cat_t<std::tuple<Filter<Ins,Outs>>,
typename FiltersFor<Outs,Others...>::type>;
};
//base case, 1 int left
template <int Dummy>
struct FiltersFor<Dummy>
{
using type = std::tuple<>;
};
//for completeness
template <>
struct FiltersFor<>
{
using type = std::tuple<>;
};
//our front-end struct
template<int... args>
using Chain = typename FiltersFor<args...>::type;
Alternatively, we can get rid of the single int and no int versions and define the primary template like this:
template <int... Args>
struct FiltersFor
{
using type = std::tuple<>;
};
Now we can test this like so:
static_assert(std::is_same<Chain<1,2,3,4>, std::tuple<Filter<1,2>,Filter<2,3>,Filter<3,4>>>::value, "wat");
static_assert(std::is_same<Chain<1,2>, std::tuple<Filter<1,2>>>::value, "wat");
static_assert(std::is_same<Chain<>, std::tuple<>>::value, "wat");
Demo