Thursday 27 June 2013

Limits and type predicates

Two things packed into one post, and neither has been well publicised as a feature. I do think both will help you to develop better code.


The limits header

You know how you love the constants defined in float.h (<cfloat>) and limits.h (<climits>)? No? Certainly I imagine you're used to using these constants as they're critical for avoiding whole classes of overflow and underflow bug. There are several problems with these constants, not least their names, which can be confusing and hard to guess, and also the fact that if the type of your variable changes (as it so often does during development) you have to remember to update the constant. This is because there's no fundamental link between the constants and their types and absolutely nothing stops you writing code like this:
 const int wat = CHAR_MIN;  

I grant you, there are situations where you might want to do this, but it's pretty horrible, right? In almost all situations, I want the limit tied to the type and this is where C++11's numeric limits can help:
 #include "stdafx.h"  
   
 #include <iostream>  
 #include <limits>  
   
 int main(int argc, char* argv[])  
 {  
     const int  intMax  = std::numeric_limits<decltype(intMax)>::max();  
     const float floatMin = std::numeric_limits<decltype(floatMin)>::min();  
   
     std::cout << "Max int is " << intMax << " and min float is " << floatMin << std::endl;  
     return 0;  
 }  

(Note, these values should be constexpr rather than const, but my compiler doesn't currently support this)


On my machine, this prints out:

Max int is 2147483647 and min float is 1.17549e-038

which is good. Now we can stop using those constants. Now, if I were you, I'd be scheming of evil things to do with std::numeric_limits to break it. What happens if you pass in a string? Or a class A you've just whipped up yourself?
 const std::string strMin = std::numeric_limits<decltype(strMin)>::min();  

Who would do such a thing? Ah well, surely the compiler can protect you from this kind of thing? Nope, it builds for me. Let's give it a go...



Well that went badly. So Microsoft at least haven't done anything to stop you taking mins and maxes of things that aren't numeric. Could they have?

Type predicates

The new <type_traits> header contains some very interesting predicates to allow you to evaluate, at build time, whether a type has a particular property. Some of the more interesting ones are:
  • has_virtual_destructor
  • is_base_of
  • is_abstract
  • is_union
  • is_polymorphic
There are plenty more, they're quite neat. As you may have guessed from the first half of this post, there's also a predicate is_arithmetic, with which you can test if a type is, well, a floating point or an integral type. Handy. Let's try... 
 #include <iostream>  
 #include <string>  
 #include <type_traits>  
   
 int main(int argc, char* argv[])  
 {  
     const bool intIsArithmetic = std::is_arithmetic<int>();  
     const bool stringIsArithmetic = std::is_arithmetic<std::string>();  
   
     std::cout << "Int is arithmetic: " << (intIsArithmetic ? "yes" : "no") << std::endl;  
     std::cout << "String is arithmetic: " << (stringIsArithmetic ? "yes" : "no") << std::endl;  
   
     return 0;  
 }  
   

On my machine (and yours too I hope), this prints out:

Int is arithmetic: yes

String is arithmetic: no

With this information, we could go on and create a feature that returns min and max values only in the case where the requested type is arithmetic. The shame is, I see no way to make this work for our own, custom numeric types (think complex numbers or long long long ints). Anyone got any ideas?