Enums, Macros, Unicode and Token-Pasting

Enums, Macros, Unicode and Token-Pasting

Rate This
  • Comments 18

Enums, Macros, Unicode and Token-Pasting

Hi, I am Rocky a developer on the Visual C++ IDE team.  I would like to discuss the C++ programming technique of creating macro generated enums.  I recently used this for distinguishing various C++ types such as class, variable, and function.  Having the list of types in one file makes it easy to add types and allows for many different types of uses.  The examples I have below mirror uses in code that I have worked with.  I have also seen this technique used in many places in various other source bases, but I have not seen it discussed much in text books - so I thought I would highlight this technique.

Consider this enum:

enum Animal { dog, cat, fish, bird };

Now dog can be used in place of 0.  You can get compiler enforced type safety that macros do not provide.  The VS debugger will also show the friendly value of the enum instead of integers.  However, functions that print out enum values need better formatting.  This code can help:

wchar_t* AnimalDiscription[] = { L"dog", L"cat", L"fish", L"bird" };

With this array, debugging code can now print the friendly value of the enum by using the enum to index into the string array.

With macro generated enums both the enum and the friendly names can be maintained as one entity.  Consider the file animal.inc:

MYENUM(dog)

MYENUM(cat)

MYENUM(fish)

MYENUM(bird)

And the following C++ code:

enum Animal {

#define MYENUM(e) _##e,

#include "animal.inc"

#undef MYENUM

};

 

wchar_t* AnimalDescription[] = {

#define MYENUM(e) L"_" L#e,

#include "animal.inc"

#undef MYENUM

};

 

Now editing animal.inc will update both the enum and the friendly text of the enum.  In this case I added an underscore in front of the animal names I used before to get the macro to work correctly.  The token-pasting operator ## cannot be used as the first token.  The stringizing operator # creates a string from the operator.  By adding an L right before the stringizing operator the resulting string is a wide string.

These macro generated enums can be “debugged” by using the compiler switch /EP or /P.  This will cause the compiler to output the preprocessor file:

enum Animal {

_dog,

_cat,

_fish,

_bird,

};

 

wchar_t* AnimalDescription[] = {

L"_" L"dog",

L"_" L"cat",

L"_" L"fish",

L"_" L"bird",

};

C++ allows for a comma after the last entry of the enum and the array initializer.

This macro string replacement technique can be further expanded to produce code.  Here is an example of using string replacement to create function prototypes:

#define MYENUM(e) void Order_##e();

#include "animal.inc"

#undef MYENUM

This expands to:

void Order_dog();

void Order_cat();

void Order_fish();

void Order_bird();

You may wish to do some action based on the kind of animal.  If you switch on the kind of animal, here is an example of creating case labels and function calls:

#define MYENUM(e) case _##e:\

                   Order_##e();\

                   break;

#include "animal.inc"

#undef MYENUM

 

This expands to:

case _dog: Order_dog(); break;

case _cat: Order_cat(); break;

case _fish: Order_fish(); break;

case _bird: Order_bird(); break;

 

In this example the function definitions would need to be added for each of the Order_dog(), Order_cat(), etc..  If you were to add a new animal to animal.inc, you would not need to remember that you would also need to add a new Order_ function definition for this new animal.  The linker would give you an error reminding you!

Macro string replacement is a powerful tool that can be leveraged to allow for internal data to be stored in one spot.  Keeping this data in one spot reduces the chances for errors, missed cases or missed matched cases.
  • Please see here: http://www.codeproject.com/script/Forums/View.aspx?fid=1647&msg=2574540

  • This discussion plainly shows the limitations of the current single-pass compilation paradigm.

    Look to multipass macro assemblers such as FASM; or even to the microsoft C# compiler.

    It should indeed be possible to generate code and symbols in a single source file, from a variable amount of internal and external information sources, while having correct and logical intellisense and debugging.

    It's theoretically possible, IMO.

  • I use the macro list version all the time in my own code. (not the include version).

    It is elegant IMO and standard C++. It's no more ugly than the ? : construct or templates. Before you use them they look scary, after you use them they don't look so scary anymore.

    Code generation has the problem that it adds another dependency. You try to build on a new machine and if they don't have your particlar code generator installed they are out of luck.

    I started using this because it solves the specific problem it's trying to solve, keeping lists of items in sync. Before I started using this I had cases where I spent hours debugging something only to find that some table was out of sync somewhere. Since I started using this I never have to worry about that problem again.

    I switch to code generation if I have to keep lists in since across two or more languages but otherwise it's standard and cross platform and for every case I've used it make my code FAR EASIER to maintain.

Page 2 of 2 (18 items) 12