Reputation: 1146
I now have a template which deduces a concrete type from the number of bits to store given as parameter:
template<unsigned char BITS>
struct Register {
using type = unsigned long;
};
template<>
struct Register<0> {
using type = void;
};
template<>
struct Register<8> {
using type = unsigned char;
};
template<>
struct Register<16> {
using type = unsigned short;
};
template<>
struct Register<32> {
using type = unsigned long;
};
Is it possible to extend this template in a way that for a given number of bits it automatically deduces the type of the next higher specialization? That means:
I thought about a solution like Register is deducing the type Register. But I'm not sure if this is the right approach of if this can be done.
However for all values >=33 it should deduce type void or Register<0>.
Example:
Register<4>::type value; // Register<8> will be used
Register<33>::type value = 1; // Compile error -> type is void
Is this feasible?
Upvotes: 2
Views: 55
Reputation: 5352
First, you need to introduce a base template that defines the recursion:
template< int N > struct impl_Int
{
using type = impl_Int<N+1>::type;
};
Next, define specialisations for the fixed sizes:
const int impl_CHAR = sizeof(signed char) != sizeof(short)
? sizeof(signed char) * CHAR_BIT
: INT_MIN;
template<> struct Register<impl_CHAR>
{
using type = char;
};
const int impl_SHORT = sizeof(short) != sizeof(int)
? sizeof(short) * CHAR_BIT
: INT_MIN + 1;
template<> struct Register<impl_SHORT>
{
using type = short;
};
const int impl_INT = sizeof(int) != sizeof(long)
? sizeof(int) * CHAR_BIT
: INT_MIN + 2;
template<> struct Register<impl_INT>
{
using type = int;
};
const int impl_LONG = sizeof(long) * CHAR_BIT;
template<> struct Register<impl_LONG>
{
using type = long;
};
Finally, define a termination specialisation:
template<> struct Register<INT_MAX>
{
using type = void;
};
For a live example, see here.
For a production implementation, you'd probably hide those constant values in an impl
namespace, then write:
template <int N>
using Register = impl::Register<N>;
Note also that you could simplify this somewhat by using the C++11 fixed width integers and hard-code their sizes. This would eliminate the need for the constants:
#include <climits>
#include <cstdint>
template< int N > struct Register
{
using type = typename Register<N+1>::type;
};
template<> struct Register<8>
{
using type = std::int8_t;
};
template<> struct Register<16>
{
using type = std::int16_t;
};
template<> struct Register<32>
{
using type = std::int32_t;
};
template<> struct Register<64>
{
using type = void;
};
int main() {
using T1 = Register<3>::type;
using T2 = Register<35>::type;
T1 v1;
T2 v2; // This line fails to compile: T2 is void.
}
Upvotes: 3