Variadic template for expanding into std :: tuple

I have a filter class that takes two template parameters, number of inputs and number of outputs.

template<int Ins, int Outs>
class Filter
{
    // implementation
};

      

Sometimes I need to chain multiple filters in series, so I thought about completing them in a class

template<int... args>
class Chain
{
};

      

so when i use chaining

Chain<5, 10, 25, 15> chain;

      

it expands args into a tuple to end up with something like this in the Chain class

std::tuple<Filter<5, 10>, Fiter<10, 25>, Filter<25, 15>> filters;

      

Is something like this possible? I'm new to these concepts and can't wrap my head around myself.

+3


source to share


3 answers


We can do this in three lines and without recursion:

template<int... args>
struct Chain
{
    // put args... into a constexpr array for indexing
    static constexpr int my_args[] = {args...};

    // undefined helper function that computes the desired type in the return type
    // For Is... = 0, 1, ..., N-2, Filter<my_args[Is], my_args[Is+1]>...
    // expands to Filter<my_args[0], my_args[1]>,
    //            Filter<my_args[1], my_args[2]>, ...,
    //            Filter<my_args[N-2], my_args[N-1]>

    template<size_t... Is>
    static std::tuple<Filter<my_args[Is], my_args[Is+1]>...>
                helper(std::index_sequence<Is...>);

    // and the result
    using tuple_type = decltype(helper(std::make_index_sequence<sizeof...(args) - 1>()));
};

      



Demo .

+9


source


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 as follows:

template <int... Args>
struct FiltersFor
{
    using type = std::tuple<>;
};

      



Now we can check it like this:

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

+5


source


I already faced a similar problem and ended up with the * operator for the Filter class tha takes an object Filter<Ins1, Ins>

and builds Filter<Ins1, Outs>

.

template<int Ins, int Outs>
class Filter
{
    template <int Ins1>
    Filter<Ins1, Outs> operator*(const Filter<Ins1, Ins> &rhs) const
    {
    // implementation
    }
};

      

Now the question is, what does your filter do? Is composition possible (maybe I'm inclined to think that in my context Filter was a function and in my case the * operator was a composite function)

0


source







All Articles