I think that the C preprocessor is a very powerful tool, but I like to limit my use of #defines. I have already touched on this when i talked about why I liked FORCEINLINE and I want to talk about it some more. I realize I can't eliminate the use of #defines throughout all of my code for various reasons, among them being:
The main reason I try to limit my usage of the preprocessor is that because it is a pure substitution, you generally don't have type information available to you when debugging the application or driver. A great example of this is constant numeric values within your application. You could do this to define values in a bit field:
#define OPTION_ONE (0x00000001) #define OPTION_TWO (0x00000002) #define OPTION_THREE (0x00000004)
but you will not be able to evaluate OPTION_ONE in the debugger because there is no OPTION_ONE symbol in the PDB. The preprocessor substituted OPTION_ONE with 0x0000001 before the linker was ever involved. To look up the value of OPTION_ONE you would have to open up a source file. Instead, I do the following when I have bit field values that I want to define and use within my app/driver:
#include <windows.h> typedef enum _OPTION_FLAGS { OptionOne = 0x00000001, OptionTwo = 0x00000002, OptionThree = 0x00000004, } OPTION_FLAGS; typedef union _OPTIONS { ULONG Bits; // NOTE: this is for debugging only, *always* use Bits in code struct { ULONG One : 1; // OptionOne ULONG Two : 1; // OptionTwo ULONG Three: 1; // OptionThree } BitsByName; } OPTIONS; int _cdecl main(int argc, char *argv[]) { OPTIONS o; o.Bits = OptionTwo; return 0; }
For now, let's ignore the fact that I can use protected or public to abstract how the values are set and read and that I am using an unnamed union. (Some folks feel that unnamed unions are unholy, but I find them very practical in this pattern.) There are 2 key concepts to what I am doing here:
I have broken into the debugger right before returning. [1] 0:000> dt OptionsFlags OptionOne = 1 OptionTwo = 2 OptionThree= 4 [2] 0:000> dt o Bits Local var @ 0x6ff78 Type Options +0x000 Bits : 2 [3] 0:000> dt o BitsByName. Local var @ 0x6ff78 Type Options +0x000 BitsByName : +0x000 One : 0y0 +0x000 Two : 0y1 +0x000 Three : 0y0 [4] 0:000> dt o Local var @ 0x6ff78 Type Options +0x000 Bits : 2 +0x000 BitsByName : Options::<unnamed-tag>
March 28, 2006: Updated the enum and union definitions to be C compliant and match my style guidelines.