'How can I specialize a C++ template for multiple ranges of integer values?
I'm trying to create a template class for handling bit streams. I'd like to have an underlying integer type declared in the template that would resolve to either uint8_t, uint16_t, uint32_t or uint64_t, depending on the template argument (an int, number of bits). I found the two answers regarding this subject (How can I specialize a C++ template for a range of integer values? and Integer range based template specialisation) and implemented the following code:
template<int BITS>
class MyClass {
typedef typename
std::conditional< BITS <= 8, uint8_t,
std::conditional< BITS <= 16, uint16_t,
std::conditional< BITS <= 32, uint32_t, uint64_t > > >::type
int_type;
...
}
In my program, I instantiate MyClass<32>, but when compiling this, I'm getting the following error:
no known conversion for argument 1 from ‘uint32_t {aka unsigned int}’ to ‘MyClass<32>::int_type {aka std::conditional<false, short unsigned int, std::conditional<true, unsigned int, long unsigned int> >}’
If I'm instantiating MyClass<8> instead, everything works. So it looks like only the first level of std::conditional is actually expanded.
Any idea how to do this correctly?
Edit: I haven't stated this before, but I'm looking for a solution which would also work for any bit size instantiation (as long as it's 64 bits at most). So I'd like MyClass<27> to work as well (selecting uint32_t).
Solution 1:[1]
To answer your edit and make your original code work.
template<int BITS>
class MyClass {
using int_type =
typename std::conditional< BITS <= 8, uint8_t,
typename std::conditional< BITS <= 16, uint16_t,
typename std::conditional< BITS <= 32, uint32_t, uint64_t >::type >::type >::type;
public:
int_type i;
};
Solution 2:[2]
The simplest the better:
template<unsigned nbits> struct uint {};
template<> struct uint<8> { using type = uint8_t; };
template<> struct uint<16> { using type = uint16_t; };
template<> struct uint<32> { using type = uint32_t; };
template<> struct uint<64> { using type = uint64_t; };
template<int nbits>
struct MyClass { using int_type = typename uint<(nbits/8)*8>::type; };
Solution 3:[3]
The solution to the problem is already provided in the answer by @super.
It was instructive for me to understand the error message.
You have defined int_type as:
typedef typename
std::conditional< BITS <= 8, uint8_t,
std::conditional< BITS <= 16, uint16_t,
std::conditional< BITS <= 32, uint32_t, uint64_t > > >::type
int_type;
It works correctly for BITS <= 8 since the
std::conditional< BITS <= 16, uint16_t,
std::conditional< BITS <= 32, uint32_t, uint64_t > >
part is essentially ignored.
Let's say you use MyClass<16>. Then, the uint8_t is ignored. What you have for int_type is:
std::conditional< BITS <= 16, uint16_t,
std::conditional< BITS <= 32, uint32_t, uint64_t > >
That can be simplified to:
std::conditional< true, uint16_t,
std::conditional< true, uint32_t, uint64_t > >
Unfortunately, that is the type you get without using std::conditional<...>::type.
Let's say you use MyClass<32>. Then what you have for int_type is:
std::conditional< BITS <= 16, uint16_t,
std::conditional< BITS <= 32, uint32_t, uint64_t > >
That can be simplified to:
std::conditional< false, uint16_t,
std::conditional< true, uint32_t, uint64_t > >
That is the type you get.
You can get some idea of those types by printing name of the corresponding type_info objects.
Sample program:
#include <iostream>
#include <typeinfo>
#include <type_traits>
#include <cstdint>
template<int BITS>
struct MyClass
{
typedef typename
std::conditional< BITS <= 8, uint8_t,
std::conditional< BITS <= 16, uint16_t,
std::conditional< BITS <= 32, uint32_t, uint64_t > > >::type
int_type;
};
int main()
{
typename MyClass<8>::int_type a;
std::cout << typeid(a).name() << std::endl;
typename MyClass<16>::int_type b;
std::cout << typeid(b).name() << std::endl;
typename MyClass<32>::int_type c;
std::cout << typeid(c).name() << std::endl;
typename MyClass<60>::int_type d;
std::cout << typeid(d).name() << std::endl;
}
Output, using g++ 5.4.0:
h
St11conditionalILb1EtS_ILb1EjmEE
St11conditionalILb0EtS_ILb1EjmEE
St11conditionalILb0EtS_ILb0EjmEE
Solution 4:[4]
You should have a look at Boost.Integer: its Integer Type Selection does exactly what you are looking for. In your case you should use boost::uint_t<N>::least:
#include <boost/integer.hpp>
template<int BITS>
class MyClass {
using int_type = typename boost::uint_t<BITS>::least;
};
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | super |
| Solution 2 | |
| Solution 3 | R Sahu |
| Solution 4 | Giovanni Cerretani |
