Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interweave a VT unpack with a meta-sequence

template <int I> struct int_ {};

template < typename ... Pack >
struct thingy
{
    void call()
    {
       f(???);
    }
 };

When instantiated it should end up being:

struct thingy<int,char,double>
{
    void call()
    {
        f(int, int_<1>(), char, int_<2>(), double, int_<3>());
    }
}

What you think, can it be done? How?

The only thing I can think of is to have overloads for thingy with N different parameters like so:

template < typename T0 > struct thingy<T0> { ... };
template < typename T0, typename T1 > struct thingy<T0,T1> { ... };

etc...

With a call implementation in each.

like image 577
Edward Strange Avatar asked May 10 '12 05:05

Edward Strange


1 Answers

Can it be done

Yes, of course.

How ?

In several steps.

  • You need to be able to create a range of integers
  • You need to be able to interleave two sequences

Let us start with the range of integers.

template <size_t...>
struct IntegralPack {};

template <size_t A, size_t... N>
IntegralPack<N..., A> push_back(IntegralPack<N...>);

template <size_t A, size_t... N>
IntegralPack<A, N...> push_front(IntegralPack<N...>);

template <size_t L, size_t H>
struct IntegralRangeImpl {
    typedef typename IntegralRangeImpl<L+1, H>::type Base;
    typedef decltype(push_front<L>((Base()))) type;
};

template <size_t X>
struct IntegralRangeImpl<X, X> {
    typedef IntegralPack<> type;
};

template <size_t L, size_t H>
struct IntegralRange {
     static_assert(L <= H, "Incorrect range");
     typedef typename IntegralRangeImpl<L, H>::type type;
};

The conversion step is easy enough (thankfully):

template <typename...>
struct TypePack {};

template <size_t... N>
TypePack<int_<N>...> to_int(IntegralPack<N...>);

So the next difficulty is the merge.

template <typename... As, typename... Bs>
TypePack<As..., Bs...> cat(TypePack<As...>, TypePack<Bs...>);

template <typename, typename> struct Interleaver;

template <>
struct Interleaver<TypePack<>, TypePack<>> {
  typedef TypePack<> type;
};

template <typename A0, typename B0, typename... As, typename... Bs>
struct Interleaver<TypePack<A0, As...>, TypePack<B0, Bs...>> {
  typedef typename Interleaver<TypePack<As...>, TypePack<Bs...>>::type Base;
  typedef decltype(cat(TypePack<A0, B0>{}, Base{})) type;
};

Putting it altogether:

template <typename... Pack>
struct thingy {
    typedef typename IntegralRange<1, sizeof...(Pack) + 1>::type Indexes;
    typedef decltype(to_int(Indexes{})) Ints;
    typedef typename Interleaver<TypePack<Pack...>, Ints>::type Types;

    void call() { this->callImpl(Types{}); }

    template <typename... Ts>
    void callImpl(TypePack<Ts...>) {
        f(Ts{}...);
    }
};

Tadaam!

like image 174
Matthieu M. Avatar answered Oct 02 '22 18:10

Matthieu M.