Tuesday, 10 January 2012

Prefer compile-time to run-time value range checks: use enumerations

In a perfect world, compiler should be able to detect all errors in the code. If types mismatch or value is out of the valid range - compiler should complain. How can we help compiler to achieve this? One answer is: by defining our custom types.


When we define enumeration, we are introducing a new type. Let's say some integer variable can be assigned only certain values, from a predefined set. Function that assigns value to a variable could look like this:

Ok, what happens if someone writes

This code compiles fine but it sets variable to a value out of the valid range and this can lead to some logical errors.

How can we improve this code? Obviously, we can check whether value is valid. If it is not, we can either throw exception, set variable to some default value or just quietly return from a function:

Each time this function is called it will use CPU resources to perform value check. But is it necessary? Can we make this function more optimal in a run-time? Yes, we can: by moving value check from run-rime to compile time! If we define our custom type - enumeration - which defines valid range, we will be able to use compiler to find all places where invalid value is passed:

SetLevel(2) will cause compiler to report an error.

One example of the real-life usage of this approach could be writing a wrapper around Windows Thread API. E.g. SetThreadPriority has an argument nPriority which is of type int and so can accept any integer value despite the fact that valid values are from a predefined set (THREAD_PRIORITY_ABOVE_NORMAL = 1, THREAD_PRIORITY_BELOW_NORMAL = -1, THREAD_PRIORITY_HIGHEST = 2, ...). Passing invalid integer values can be prevented by introducing our enumeration which enumerates all valid values for thread priority:

NOTE: enum value is implicitly casted to int but int value must be explicitly casted to enum type.


Stefan Naewe said...

I don't know which compiler you're using but mine says:

g++ level.cpp -o level
level.cpp: In function ‘int main()’:
level.cpp:18: error: invalid conversion from ‘int’ to ‘Level’
level.cpp:18: error: initializing argument 1 of ‘void SetLevel(Level)’
level.cpp:19: error: invalid conversion from ‘int’ to ‘Level’
level.cpp:19: error: initializing argument 1 of ‘void SetLevel(Level)’
level.cpp:20: error: invalid conversion from ‘int’ to ‘Level’
level.cpp:20: error: initializing argument 1 of ‘void SetLevel(Level)’

If I do:

SetLevel(0); // Line 18

Bojan Komazec said...

I wrote at the end of the article that integers must be explicitly casted to enum type which you didn't do and therefore you got compiler error. You can use static_cast operator to achieve that: SetLevel(static_cast<Level>(0)). C cast would do the job as well (but avoid C casts in C++ code): SetLevel((Level)0). Of course, the best way is to use argument of the enum type: SetLevel(ZERO). Cheers

micheal pan said...

BE SMART AND BECOME RICH IN LESS THAN 3DAYS....It all depends on how fast 
you can be to get the new PROGRAMMED blank ATM card that is capable of
hacking into any ATM machine,anywhere in the world. I got to know about 
this BLANK ATM CARD when I was searching for job online about a month 
ago..It has really changed my life for good and now I can say I'm rich and 
I can never be poor again. The least money I get in a day with it is about 
$50,000.(fifty thousand USD) Every now and then I keeping pumping money 
into my account. Though is illegal,there is no risk of being caught 
,because it has been programmed in such a way that it is not traceable,it 
also has a technique that makes it impossible for the CCTVs to detect 
you..For details on how to get yours today, email the hackers on : (
atmmachinehackers1@gmail.com ). Tell your 
loved once too, and start to live large. That's the simple testimony of how 
my life changed for good...Love you all ...the email address again is ;