kevin adam
kevin adam

Reputation: 33

How generate a sequence of integer using template works here

I see this code in gtest source file. this snippet is used to generate a template whose template variable is a sequence of integer. can anybody explain how this works?

template <size_t... Is> struct int_pack { typedef int_pack type; };

template <class Pack, size_t I>
struct append;
template <size_t... Is, size_t I>
struct append<int_pack<Is...>, I> : int_pack<Is..., I> {};

template <size_t C>
struct make_int_pack : append<typename make_int_pack<C - 1>::type, C - 1> {};
template <> struct make_int_pack<0> : int_pack<> {};

we can use make_int_pack<5>() to generate a class instance (int_pack<0,1,2,3,4>()) which is further used to extract 0,1,2,3,4 to do something.

Upvotes: 0

Views: 92

Answers (1)

user9400869
user9400869

Reputation:

The construction is done with recursive substitution:

make_int_pack<5> derives from append<typename make_int_pack<5-1>::type,5-1>.

But make_int_pack<5-1>::type is make_int_pack<4>::type.

But make_int_pack<4>::type is the definition from append<typename make_int_pack<4-1>::type,4-1>, and so on.

So make_int_pack<5> derives from

  append<append<append<append<append<int_pack<>,0>,1>,2>,3>,4>

, which derives from int_pack<0,1,2,3,4>.

To use this int_pack we have to do something like pattern matching (c++11).

template<class T>struct test{};

template<size_t ... Is>
struct test<int_pack<Is...>>{
    // use template parameter pack
};

We could then use the static members of test<make_int_pack<5>> as result of some compiletime compilation or complex type construction.

Another possible way is to have

template<size_t... Is>
size_t test_fun(int_pack<Is...>);

,which would be called like test_fun(make_int_pack<5>());

Edit:

I'm wondering why there's two append struct, what's the difference between them?

You want to be able to write append<int_pack<0,1,2,3>,42>. Therefor append has to take two template parameters. That is your first declaration.

template<class T,size_t i> struct append{};

But actually you do not need the type int_pack<0,1,2,3> but the template paramter pack used. In c++ we have partial template specialization. If the type T happends to be an int_pack we use the special (second) declaration. In this way we can kind of pattern match the templated type T and get the template parameters used do define the int_pack.

template<size_t ... Is,size_t i>
struct append<int_pack<Is...>,i> : int_pack<Is...,i>{};

what's the meaning of append,0> ?

This will fall in the second case. We have an empty template parameter pack, since the brackets are empty int_pack<>. So we derive from int_pack<0> and therefor our append<int_pack<>,0>::type is int_pack<0>.

So in summary: look at template parameter packs and partial template specialization. When working with types it is basicly just substitution of expressions.

Upvotes: 1

Related Questions