patterncppMinor
C++ template range
Viewed 0 times
rangetemplatestackoverflow
Problem
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.
Version 1
The template code I started with:
But it seems the trend nowadays is to use inheritance to get rid of the ugly
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.
I can specialize `printNum
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
struct printNumberRange;
// Only a specialization for my range is implemented.
template
struct printNumberRange>
{
static void call()
{
std::vector v {N...};
std::copy(std::begin(v), std::end(v), std::ostream_iterator(std::cout, "\n"));
}
};
// Function to deduce the arguments and
// Call the print correctly.
template
void printRange()
{
print::type>::call();
}
int main()
{
// Print the range using a template
printRange();
}Version 1
The template code I started with:
template
struct Sizes
{
typedef Sizes type;
};
template
struct GetRange
{
typedef typename GetRange::type type;
};
template
struct GetRange
{
typedef typename Sizes::type type;
};
template
struct Range
{
typedef typename GetRange::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
struct Sizes
{
typedef Sizes type;
};
template
struct GetRange: GetRange
{};
template
struct GetRange: Sizes
{};
template
struct Range: GetRange
{};I can specialize `printNum
Solution
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
By this one:
I tried it and it even works for
-
Currently, your
These ranges are
-
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
-
Also, in your function
-
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
struct Range: GetRange
{};By this one:
template
struct Range: GetRange
{};I tried it and it even works for
Range 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
struct GetIncreasingRange:
GetIncreasingRange
{};
template
struct GetDecreasingRange:
GetDecreasingRange
{};
template
struct GetIncreasingRange:
Sizes
{};
template
struct GetDecreasingRange:
Sizes
{};
template
struct Range;
template
struct Range:
GetIncreasingRange
{};
template
struct Range:
GetDecreasingRange
{};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 v { N... };
std::copy(std::begin(v), std::end(v), std::ostream_iterator(std::cout, "\n"));
}Code Snippets
template<int S, int E>
struct Range: GetRange<E-S+1, S>
{};template<int S, int E>
struct Range: GetRange<E-S, S>
{};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>
{};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"));
}Context
StackExchange Code Review Q#45549, answer score: 8
Revisions (0)
No revisions yet.