views:

90

answers:

2

I have the following 2 macros:

#define SCOPED_ENUM_HEADER(NAME) struct NAME{ enum _NAME{
#define SCOPED_ENUM_FOOTER(NAME) };}; typedef NAME::_NAME NAMEtype;

Only the first instance of NAME get replaced by the passed NAME. What's wrong with it?

Is is to be used in such a way:

 SCOPED_ENUM_HEADER(LOGLEVEL)
   UNSET,
   FILE,
   SCREEN
 SCOPED_ENUM_FOOTER(LOGLEVEL)

Thanks you

+7  A: 

The problem is that NAME is not the same as _NAME; they are two totally separate identifiers.

If you want to add an underscore to the front of whatever the parameter NAME is, use the concatenation (##) operator:

_##NAME

You do need to be really careful with prepending an underscore, though. All identifiers beginning with an underscore followed by a capital letter are reserved to the implementation.

James McNellis
Hey, that's the answer I was typing. Oh well, +1.
Fred Larson
Thanks a lot, I though it would just replace any match...
JP
@JP If it replaces any occurrence indiscriminately it would replace FOONAMEBAR as well, which is probably not what you want
Michael Mrozek
@JP: Note that if the argument passed to `SCOPED_ENUM_HEADER` is itself a macro, you'll need to use the indirection trick as described by David Thornley in his answer, otherwise you won't get the intended result. This is required because the replacement is not rescanned until after the concatenation. If you don't pass a macro to `SCOPED_ENUM_HEADER`, you won't have a problem, but for completeness, it's probably a good idea to use the extra layer of indirection anyway, just in case (it certainly doesn't hurt).
James McNellis
+1  A: 

You only have one instance of NAME in each macro. You also have an example of _NAME in each macro, but that's highly questionable: names beginning with an underscore and having a capital letter following are reserved for the implementation, and so you could have a problem there.

However, you want to take the value of NAME and concatenate something to it, let's say putting a E_ in front. This is a little tricky, since _##NAME will normally get you _NAME, no matter what you put in.

This is explained on the C++ FAQ Lite, but what you really have to do is something like:

#define REALLY_CONCATENATE(a,b) a ## b
#define CONCATENATE(a,b) REALLY_CONCATENATE(a,b)
#define SCOPED_ENUM_HEADER(NAME) struct NAME{ enum CONCATENATE("E_", NAME) {

(sorry, can't test this right now).

On the other hand, there has got to be a better way to do what you're doing. Seriously.

David Thornley
Thanks you :) you were just a bit too late :)
JP