Reputation: 2016
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
Reputation: 10546
The compiler flag to suppress the warning is -Wno-type-limits
for gcc.
Upvotes: 2
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
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
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