Here is an example of problem: a constant variable's template should expand its type based on parameters. While direct way is possible, by giving size of type, or underlying typename, it's error-prone.
#include <iostream>
template<size_t bit>
constexpr const uint16_t BIT = 1 << bit;
template<size_t... bits>
constexpr const uint16_t BITS = (uint16_t(1 << bits)|...);
int main()
{
std::cout << BITS<0,1,3,12> << std::endl;
}
Idea is to implement template data type which would return type
which is unsigned integer at least of size of greatest value in parameter pack. This also would allow to check if template arguments are sane.
Pretty straight forward in C++17. The maximum value we can calculate with a simple invocation of std::max
(the initializer list overload is constexpr
since C++14).
The result we'll need to plug into a utility that maps sizes to integer types, but that's fairly simple to write now:
template<std::size_t N>
struct size2Type {
static auto type_calculator() {
static_assert( N < 64 );
if constexpr ( N < 8 )
return uint8_t{};
else if constexpr ( N < 16 )
return uint16_t{};
else if constexpr ( N < 32 )
return uint32_t{};
else
return uint64_t{};
}
using type = decltype(type_calculator());
};
Then, putting it to use in your original example:
template<size_t bit>
constexpr typename size2Type<bit>::type BIT = (typename size2Type<bit>::type)(1) << bit;
template<size_t... bits>
constexpr typename size2Type<std::max({std::size_t(0), bits...})>::type BITS = (BIT<bits> | ... | 0);
I didn't prettify the cast, but a utility can be written to accomplish that too.
You can see it live.