Harry Coder and the Wizard Compiler
I wanted to talk a bit more on compilers and why you should be thrilled to work with them.
Inlining
Here is something I used to do constantly in every program I wrote:
#define POW2(x) ((x)*(x))
For starters, look how awkward it is to define. If you don't put parenthesis around the x variable, writing code like POW2(myval+1) will expand to myval+1*mayval+1 and you will pound your head against the desk wondering why your code is misbehaving. It can also lead to slower code because some things will need to get evaluated twice. For example, POW2(GetResult()) will expand to (GetResult())*(GetResult()), GetResult() will get called each time. Finally, notice how it's not very extensible. If I wanted to find the cube of something I have to write a whole new macro.
Now, the magic of inlining:
double pow( double x, unsigned int p )
{
double result = 1.0;
for( unsigned int i = 0; i < p; i++ )
{
result *= x;
}
return result;
}
The desire to use a macro seems clear now, the above code just looks slower because of its size. But, through the magic of inlining, the compiler makes it just as fast as the macro. If you pass in a constant value for p, the compiler knows exactly how many times the for loop will run, letting it unroll the loop and put the code directly where you called the function from.
Even better is that it can choose not to inline! But why would that be a benefit? With a macro it always gets expanded, even if the resulting code is slower due to bloating of the function that the macro was expanded into. The compiler is smart enough to know when a function would benefit from inlining and when not to. By using functions instead of macros, your letting the countless hours of research and investigation that went into the compiler choose what to do.
Templates
Templates can be your best friend (but they can also be a nuisance since error messages tend to be incomprehensible).
Lets take our pow function again and make it work for any type:
template< typename T >
T pow( T x, unsigned int p )
{
T result = 1;
for( unsigned int i = 0; i < p; i++ )
{
result *= x;
}
return result;
}
Now, not only will the compiler unroll the loop for constant values, it will also create new optimized functions for each type you give it. That means an optimized function for floats, doubles, integers, etc. It also means it will only generate functions for which you need, hence it can reduce your executable size by leaving out types you don't need. Doesn't it feel great to have written 10 different functions with only 10 lines of code?
Metaprogramming
Templated metaprogramming is an example of something you can do, but not necessarily should do. It can be very powerful but it is also clunky and should be used sparingly.
For those unaware, the compiler is actually a Turing complete device meaning it can calculate anything that can be calculated. Lets take our pow example one step farther:
template< unsigned int x, unsigned int p >
class Pow
{
public:
enum
{
val = x * Pow< x, p - 1 >::val
};
};
template< unsigned int x >
class Pow< x, 0 >
{
public:
enum
{
val = 1
};
};
Now, use the following line somewhere:
unsigned int result = Pow< 5, 3 >::val;
The value of result will be set to 125. Not at run time, but at compile time. It would be as if you had written:
unsigned int result = 125;
Except you didn't have to do the work, the compiler did. If you don't see the trick, the compiler first creates the Pow< 5, 3 > class. When assigning the values to the enumeration val, it needs to create Pow< 5, 2 >::val. This requires Pow< 5, 1 > and Pow<5, 0>. This would continue on forever except we explicitly specialized what Pow< x, 0 > is to prevent an infinite recursion. The compiler is left with Pow< 5, 3 >::val equaling 5*5*5*1, which it simplifies to 125.
Now, with optimizations a compiler can do the same thing given an inlined function with constant values for simple functions. Also, double and floating point values in templates are not supported in standard C++. However, there are many uses to this trick, all kinds of control structures like if and while statements can be evaluated at compile time. If you are interested in templates and metaprogramming, I suggest getting a book. C++ Templates: The Complete Guide and C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond are two good places to start.