# C++ template range

From a previous question I got an answer that included some template magic (that to be blunt was mind-boggling (as I could not understand it)).

So I have been trying to achieve the same results (because trying helps me learn).
To make sure I have learn correctly I am putting it here for comment. Hopefully it will also help somebody else (and you never know it may encourage me to write a blog post about it).

Template based ranges (I am sure it has been done to death).

The idea you provide a range that expanded by the template to make writing code easier. So the code I use to test it is working correctly.

template<typename>
struct printNumberRange;

// Only a specialization for my range is implemented.
template<int... N>
struct printNumberRange<Sizes<N...>>
{
static void call()
{
std::vector<int> v {N...};
std::copy(std::begin(v), std::end(v), std::ostream_iterator<int>(std::cout, "\n"));
}
};

// Function to deduce the arguments and
// Call the print correctly.
template<int S, int E>
void printRange()
{
print<typename Range<S,E>::type>::call();
}

int main()
{
// Print the range using a template
printRange<3,8>();
}


### Version 1

The template code I started with:

template<int... N>
struct Sizes
{
typedef Sizes<N...>       type;
};

template<int C, int P, int... N>
struct GetRange
{
typedef typename GetRange<C-1, P+1, N..., P>::type      type;
};
template<int P, int... N>
struct GetRange<0, P, N...>
{
typedef typename Sizes<N..., P>::type                   type;
};

template<int S, int E>
struct Range
{
typedef typename GetRange<E-S, S>::type       type;
};


But it seems the trend nowadays is to use inheritance to get rid of the ugly typedef typename .... at each level:

### Version 2

This should be exactly the same.
But we use inheritance to get the type of the terminal class in the recursion. Personally I find this much harder to read than the previous version. But it is more compact.

template<int... N>
struct Sizes
{
typedef Sizes<N...>       type;
};

template<int C, int P, int... N>
struct GetRange: GetRange<C-1, P+1, N..., P>
{};
template<int P, int... N>
struct GetRange<0, P, N...>: Sizes<N...>
{};

template<int S, int E>
struct Range: GetRange<E-S+1, S>
{};


I can specialize printNumberRange to take a range directly.

template<int S, int E>
struct printNumberRange<Range<S, E>>:
printNumberRange<typename Range<S,E>::type>  // Inherits from the version
{};                                               // That takes a Sizes<int...>


Then the print becomes:

int main()
{
printNumberRange<Range<4,18>>::call();
}


Any comments on the Range stuff or the test harness welcome.

Generally speaking, it's good and works great (version 2 seems better). There are some things that could be changed/added though:

• In C++, ranges tend to be [begin, end) ranges. Your compile-time integer ranges are actually [begin, end] ranges. The last element should not be included in the range. Therefore, you change this code:

template<int S, int E>
struct Range: GetRange<E-S+1, S>
{};


By this one:

template<int S, int E>
struct Range: GetRange<E-S, S>
{};


I tried it and it even works for Range<8,8> by producing an empty range.

• Currently, your Range only works for increasing ranges of values (and empty ones). You could modify your code so that it also works with decreasing ranges of values. What I did is probably not really clean, but it works (take it as a proof of concept). I replaced Range and GetRange by the following classes:

template<int C, int P, int... N>
struct GetIncreasingRange:
GetIncreasingRange<C-1, P+1, N..., P>
{};

template<int C, int P, int... N>
struct GetDecreasingRange:
GetDecreasingRange<C+1, P-1, N..., P>
{};

template<int P, int... N>
struct GetIncreasingRange<0, P, N...>:
Sizes<N...>
{};

template<int P, int... N>
struct GetDecreasingRange<0, P, N...>:
Sizes<N...>
{};

template<int S, int E, bool Increasing=(S<E)>
struct Range;

template<int S, int E>
struct Range<S, E, true>:
GetIncreasingRange<E-S, S>
{};

template<int S, int E>
struct Range<S, E, false>:
GetDecreasingRange<E-S, S>
{};


These ranges are [begin, end) ranges and also work for empty ranges.

• Another possible improvement would be to let the user choose the integer type he wants to use for his range. That is actually what is done in the C++14 class std::integer_sequence, provided along with std::index_sequence which is its specialization for a range of std::size_t values. Here is a possible implementation.

• Also, in your function call, you can drop the std::vector and replace it by std::array. The number of values is known at compile time (thanks to the operator sizeof...), so there's no need to use a dynamic storage container.

static void call()
{
std::array<int, sizeof...(N)> v { N... };
std::copy(std::begin(v), std::end(v), std::ostream_iterator<int>(std::cout, "\n"));
}