Matthew van Eerde's web log
I am a Software Development Engineer in Test working for the Windows Sound team. You can contact me via email: mateer at microsoft dot com
Friend key:28904932216450_59cd9d55374be03d8167d37c8ff4196b
We figured they were more actual guidelines. -- Pirates of the Caribbean
Macros are wonderful things. I love macros. They're exciting and dangerous. Every time I write a macro I salivate at the power I am wielding. It's like using a blowtorch.
It is perhaps no coincidence that many coding standards put big warning signs around macroland. "Consider carefully before using macros", some say. "When considering using a macro, see if you can use an inline function instead," some say. "Macros are frequent sources of bugs," some say. "C++ has evolved to the point where, with inline functions and templates, macros are no longer necessary".
Heresy.
All absolutely true, and yet heresy nonetheless. It is true that there are many bugs out there due to misuse of macros. And yet only a few simple tips are necessary to prevent misuse of macros: (The problem is that the tips don't always get followed).
She generally gave herself very good advice, (though she very seldom followed it) -- Alice in Wonderland
Let's talk about the "no expressions with side effects" rule a little more. It is OK to do things like
if (SUCCEEDED(CoInitialize(...))) { ... }
if (FAILED(CoCreateInstance(...))) { ... }
return HRESULT_FROM_WIN32(GetLastError());
... but for different reasons. SUCCEEDED(...) and FAILED(...) only evaluate their arguments once, as can be verified from their definitions in winerror.h. I'm using the Vista RTM version of winerror.h:
//// Generic test for success on any status value (non-negative numbers// indicate success).//#define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0)//// and the inverse//#define FAILED(hr) (((HRESULT)(hr)) < 0)
Note the heavy (and correct) use of parentheses. I still personally don't think it's a good idea to rely on this, since macros have a habit of changing... but I won't ding anyone on a code review for it.
HRESULT_FROM_WIN32(GetLastError()) is OK for a totally different reason. HRESULT_FROM_WIN32(...), classically, does evaluate its argument multiple times... this time I'm using the Windows 2000 version of winerror.h:
#define HRESULT_FROM_WIN32(x) (x ? ((HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000)) : 0 )
... so this looks like a bug... until you realize that GetLastError() is idempotent. If you call it a bunch of times in a row on the same thread, it will (per its contract) always return the same value.
Note that the macro is not completely parenthesized! The bold x is missing its protective parentheses. This was fixed in a Windows 2000 service pack. For example, here's the Win2K SP3 version:
#define HRESULT_FROM_WIN32(x) ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : ((HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000)))
Much better.
I'm still personally leery of using side effects in HRESULT_FROM_WIN32 though. Why? Because it sets a bad precedent. It's easier to have a simple rule (no side effects in a macro call!) than to remember for an infinitude of macros and function calls which macros evaluate the arguments multiple times and which function calls really have side effects. You also get legitimate bugs like this.
Macros, macros, macros... you deviate just a little bit from the straight-and-narrow and what happens? You get blown out of the water. Wonderful things, macros.
Nasty little bugs arising from harmless-looking violations of the safety tips are the cause of the dirty secret I am about to reveal...
As of Vista RTM...
HRESULT_FROM_WIN32 is NO LONGER a macro!
This is one of those annoying little facts I try so hard to forget because they complicate life unnecessarily. From my point of view, there is no bad consequence of treating HRESULT_FROM_WIN32 as the macro it looks like (and was... and who knows, may be again.) The inline function it has become is uniformly safer.
This has apparently been a long time in coming. Here are some excerpts of winerror.h from various releases of Windows:
Windows 2000 RTM:
Windows XP RTM:
// __HRESULT_FROM_WIN32 will always be a macro.// The goal will be to enable INLINE_HRESULT_FROM_WIN32 all the time,// but there's too much code to change to do that at this time.#define __HRESULT_FROM_WIN32(x) ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : ((HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000)))#ifdef INLINE_HRESULT_FROM_WIN32#ifndef _HRESULT_DEFINED#define _HRESULT_DEFINEDtypedef long HRESULT;#endif#ifndef __midl__inline HRESULT HRESULT_FROM_WIN32(long x) { return x < 0 ? (HRESULT)x : (HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000);}#else#define HRESULT_FROM_WIN32(x) __HRESULT_FROM_WIN32(x)#endif#else#define HRESULT_FROM_WIN32(x) __HRESULT_FROM_WIN32(x)#endif
Whew. The net result is that HRESULT_FROM_WIN32 is a macro. Oh, unless you decide to be avant-garde and #define INLINE_HRESULT_FROM_WIN32 before you #include <winerror.h>. Which probably very few people did, except for build engineers running experiments. So this is an "opt-in" inline function.
Windows Vista RTM:
//// HRESULT_FROM_WIN32(x) used to be a macro, however we now run it as an inline function// to prevent double evaluation of 'x'. If you still need the macro, you can use __HRESULT_FROM_WIN32(x)//#define __HRESULT_FROM_WIN32(x) ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : ((HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000)))#if !defined(_HRESULT_DEFINED) && !defined(__midl)#define _HRESULT_DEFINEDtypedef __success(return >= 0) long HRESULT;#endif#ifndef __midlFORCEINLINE HRESULT HRESULT_FROM_WIN32(unsigned long x) { return (HRESULT)(x) <= 0 ? (HRESULT)(x) : (HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000);}#else#define HRESULT_FROM_WIN32(x) __HRESULT_FROM_WIN32(x)#endif
The experiments appear to have been completed. Apparently no imaginable consequence of changing to an inline function was worse than the known consequence of bugs.
People who want the macro can still use __HRESULT_FROM_WIN32.
Why would you want to? Hmmm... maybe you're building a switch statement. Something like:
#define CASE_HRESULT(x) case x: return #x#define CASE_ERR(x) case HRESULT_FROM_WIN32(x): return #x // need __HRESULT_FROM_WIN32 here?LPCSTR FriendlyNameOf(HRESULT hr) { switch(hr) { CASE_HRESULT(S_OK); CASE_HRESULT(S_FALSE); ... many more... CASE_ERR(ERROR_SUCCESS); CASE_ERR(ERROR_INVALID_FUNCTION); ... } return "Unrecognized";}
LPCSTR FriendlyNameOf(HRESULT hr) { switch(hr) { CASE_HRESULT(S_OK); CASE_HRESULT(S_FALSE); ... many more... CASE_ERR(ERROR_SUCCESS); CASE_ERR(ERROR_INVALID_FUNCTION); ... } return "Unrecognized";}
I haven't tried it out, but I suspect the compiler will reject the attempt to squeeze the result of an inline function into the value of a case, but will accept a complicated expression involving a bunch of constants (i.e., the macro will work.)