daroo
daroo

Reputation: 2016

Comparison is always false due to limited range ... with templates

I have a templated function that operates on a template-type variable, and if the value is less than 0, sets it to 0. This works fine, but when my templated type is unsigned, I get a warning about how the comparison is always false. This obviously makes sense, but since its templated, I'd like it to be generic for all data types (signed and unsigned) and not issue the warning.

I'm using g++ on Linux, and I'm guessing there's a way to suppress that particular warning via command line option to g++, but I'd still like to get the warning in other, non-templated, cases. I'm wondering if there's some way, in the code, to prevent this, without having to write multiple versions of the function?

template < class T >
T trim(T &val)
{
  if (val < 0)
  {
    val = 0;
  }
  return (val);
}
int main()
{
  char cval = 5;
  unsigned char ucval = 5;

  cout << "Untrimmed: " << (int)cval;
  cval = trim(cval);
  cout << " Trimmed: " << (int)cval << endl;

  cout << "Untrimmed: " << (int)ucval;
  cval = trim(ucval);
  cout << " Trimmed: " << (int)ucval << endl;

 return (0);
}

Upvotes: 6

Views: 1417

Answers (4)

pythonic metaphor
pythonic metaphor

Reputation: 10546

The compiler flag to suppress the warning is -Wno-type-limits for gcc.

Upvotes: 2

valyala
valyala

Reputation: 17830

All types of -Wtype-limits warnings can be suppressed case-by-case by converting each comparison, which generates the warning, into a dummy function, which accepts both operands used in the comparison and returns the result of operands' comparison. For example, trim() function from the original question can be converted into:

// Dummy function for -Wtype-limits warnings suppression.
template < class T >
static inline dummy_less(const T &a, const T &b)
{
  return (a < b);
}

template < class T >
T trim(T &val)
{
  if (dumy_less(val, 0))
  {
    val = 0;
  }
  return (val);
}

Upvotes: 1

Kornel Kisielewicz
Kornel Kisielewicz

Reputation: 57555

For the simple case presented, you'd be definitively better of with the solution presented by Roger Pate.

What you need for the general metaprogramming solution is type_traits. You can either use those from boost, or ones supplied with your STL if modern enough.

namespace detail {

  template < class T >
  T trim_impl(T &val, const std::tr1::true_type& )
  {
    if (val < 0)
    {
      val = 0;
    }
    return (val);
  }

  template < class T >
  T trim_impl(T &val, const std::tr1::false_type& )
  {
    return (val);
  }  

} // end namespace detail

template < class T >
T trim(T &val)
{
  return detail::trim_impl( val, std::tr1::is_signed<T>() );
}

Take note however that is_signed is false_type for floating point numbers (don't ask why). To make the above code work with floating points you'd need to typedef another trait, e.g.

typedef std::tr1::integral_constant< bool, 
            std::tr1::is_signed<T>::value || 
            std::tr1::is_floating_point<T>::value > has_sign;

... and yeah, the deeper you get into metaprogramming the uglier it gets so... disregard this solution and go with the simple one listed by Roger :P.

Upvotes: 5

Roger Pate
Roger Pate

Reputation:

#include <algorithm>

template<class T>
T& trim(T& val) {
  val = std::max(T(0), val);
  return val;
}

It's not apparent from the question that passing by non-const reference is appropriate. You can change the above return nothing (void), pass by value and return by value, or pass by const& and return by value:

template<class T>
T trim(T const& val);

// example use:
value = trim(value); // likely the most clear solution

Generalize a bit more, even though outside the scope of your question:

template<class T>
T constrain(T const& value, T const& lower, T const& upper) {
  // returns value if value within [lower, upper] (inclusive end points)
  // returns lower if value < lower
  // otherwise returns upper
  assert(lower <= upper); // precondition
  return std::min(std::max(value, lower), upper);
}

template<class T>
T constrain_range(T const& value, T const& lower, T const& upper) {
  // returns value if value within [lower, upper) (exclusive upper)
  // returns lower if value < lower
  // otherwise returns upper - 1
  assert(lower < upper); // precondition
  if      (value <  lower) return lower;
  else if (value >= upper) return upper - 1;
  else                     return value;
}

Upvotes: 6

Related Questions