Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC: comparison is always true due to limited range of data type -- in Template Parameter?

I want to write a template that returns me the smallest signed integer type that can represent a given number. This is my solution:

/**
 * Helper for IntTypeThatFits.
 * Template parameters indicate whether the given number fits into 8, 16 or 32
 * bits. If neither of them is true, it is assumed that it fits 64 bits.
 */
template <bool fits8, bool fits16, bool fits32>
struct IntTypeThatFitsHelper { };

// specializations for picking the right type
// these are all valid combinations of the flags
template<> struct IntTypeThatFitsHelper<true, true, true> { typedef int8_t Result; };
template<> struct IntTypeThatFitsHelper<false, true, true> { typedef int16_t Result; };
template<> struct IntTypeThatFitsHelper<false, false, true> { typedef int32_t Result; };
template<> struct IntTypeThatFitsHelper<false, false, false> { typedef int64_t Result; };

/// Finds the smallest integer type that can represent the given number.
template <int64_t n>
struct IntTypeThatFits
{
    typedef typename IntTypeThatFitsHelper<
        (n <= numeric_limits<int8_t>::max()) && (n >= numeric_limits<int8_t>::min()), 
        (n <= numeric_limits<int16_t>::max()) && (n >= numeric_limits<int16_t>::min()), 
        (n <= numeric_limits<int32_t>::max()) && (n >= numeric_limits<int32_t>::min())
    >::Result Result;
};

However, GCC does not accept this code. I get an error "comparison is always true due to limited range of data type [-Werror=type-limits]". Why does that happen? n is a signed 64bit integer, and all of the comparisons may be true or false for different values of n, or am I overlooking something?

I will be glad for any help.

Edit: I should mention that I am using C++11.

like image 461
Benjamin Schug Avatar asked May 03 '12 15:05

Benjamin Schug


1 Answers

It's an issue with gcc, warnings in templated code can be frustrating. You can either change the warning or use another approach.

As you may know, templated code is analyzed twice:

  • once when first encountered (parsing)
  • once when instantiated for a given type/value

The problem here is that at instantiation, the check is trivial (yes 65 fits into an int thank you), and the compiler fails to realize that this warning does not hold for all instantiations :( It is very frustrating indeed for those of us who strive to have a warning-free compiling experience with warnings on.

You have 3 possibilities:

  • deactivate this warning, or demote it to a non-error
  • use a pragma to selectively deactivate it for this code
  • rework the code in another format so that it does not trigger the warning any longer

Note that sometimes the 3rd possibility involves a massive change and much more complicated solution. I advise against complicated one's code just to get rid of clueless warnings.

EDIT:

One possible workaround:

template <int64_t n>
struct IntTypeThatFits {
    static int64_t const max8 = std::numeric_limits<int8_t>::max();
    static int64_t const min8 = std::numeric_limits<int8_t>::min();

    static int64_t const max16 = std::numeric_limits<int16_t>::max();
    static int64_t const min16 = std::numeric_limits<int16_t>::min();

    static int64_t const max32 = std::numeric_limits<int32_t>::max();
    static int64_t const min32 = std::numeric_limits<int32_t>::min();

    typedef typename IntTypeThatFitsHelper<
        (n <= max8 ) && (n >= min8 ), 
        (n <= max16) && (n >= min16), 
        (n <= max32) && (n >= min32)
    >::Result Result;
};

... by changing the type of the data used in the comparison, it should silence the compiler warning. I suppose explicit casting (int64_t(std::numeric_limits<int8_t>::max())) could work too, but I found this more readable.

like image 191
Matthieu M. Avatar answered Oct 26 '22 05:10

Matthieu M.