fhw72
fhw72

Reputation: 1146

Deduce value type for missing template specializations recursively

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

Answers (1)

Andrew
Andrew

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.
}

Live example

Upvotes: 3

Related Questions