The dangers of Macros

Another developer came to me with a problem today that he couldn’t figure out. He couldn’t believe what he was seeing in the debugger and needed a second set of eyes. He had a line of code like the following;

return max( eRetVal, GetNumber() );

While debugging, he noticed that he was stepping into the GetNumber() method twice, and in his code, it had side-effects. We both puzzled over it for awhile. We looked at the disassembly and sure enough, it was getting called twice, but why? Then it hit me, max is a macro, not a function. If you go to the definition of max, you find;

#ifndef max
#define max(a,b)            (((a) > (b)) ? (a) : (b))
#endif

So, if you expand that macro out, the original code gets compiles as;

return (((eRetVal) > (GetNumber()) ? (eRetVal) : (GetNumber());

Once the macro is expanded, it is easy to see why the method is called twice. Obviously I have not being doing enough C++ lately. There was a time when I would have seen that immediately as it is the classic example, but I have been working in .NET so long now that I am forgetting all of the little ways that C++ can bite you.