tags:

views:

327

answers:

2

I want to write a macro that spits out code based on the boolean value of its parameter. So say DEF_CONST(true) should be expanded into "const", and DEF_CONST(false) should be expanded into nothing.

Clearly the following doesn't work because we can't use another preprocessor inside #defines:

#define DEF_CONST(b_const) \
#if (b_const) \
  const \
#endif

Any idea about how to do it?

+12  A: 

You can simulate conditionals using macro token concatenation as follows:

#define DEF_CONST(b_const) DEF_CONST_##b_const
#define DEF_CONST_true const
#define DEF_CONST_false

Then,

/* OK */
DEF_CONST(true)  int x;  /* expands to const int x */
DEF_CONST(false) int y;  /* expands to int y */

/* NOT OK */
bool bSomeBool = true;       // technically not C :)
DEF_CONST(bSomeBool) int z;  /* error: preprocessor does not know the value
                                of bSomeBool */

Also, allowing for passing macro parameters to DEF_CONST itself (as correctly pointed out by GMan and others):

#define DEF_CONST2(b_const) DEF_CONST_##b_const
#define DEF_CONST(b_const) DEF_CONST2(b_const)
#define DEF_CONST_true const
#define DEF_CONST_false

#define b true
#define c false

/* OK */
DEF_CONST(b) int x;     /* expands to const int x */
DEF_CONST(c) int y;     /* expands to int y */
DEF_CONST(true) int z;  /* expands to const int z */

You may also consider the much simpler (though potentially less flexible):

#if b_const
# define DEF_CONST const
#else /*b_const*/
# define DEF_CONST
#endif /*b_const*/

Cheers, V.

vladr
I would add a level of indirection for `DEF_CONST`, since `#define SHOULD_CONST true` `DEF_CONST(SHOULD_CONST)` would expand incorrectly. And a bit more generically, if you have a concatenate macro lying around, that would do (Boost has one, for example): `#define CONCATENATE(x, y) x##y` `#define DEF_CONST(b_const) CONCATENATE(DEF_CONST_b_const)`
GMan
Typo in the above, just missed the edit mark. Should be `#define DEF_CONST(b_const) CONCATENATE(DEF_CONST_, b_const)`.
GMan
Yup - token pasting or stringizing ('`##`' or '`#`') preprocessor operators almost always really need a level of indirection to work as desired: http://stackoverflow.com/questions/1767683/c-programming-preprocessor-macros-as-tokens/1769037#1769037
Michael Burr
Isn't this the code for summoning el Diablo? (+1 anyway :p)
Dean Harding
A: 

Doing it as a paramterised macro is a bit odd.

Why not just do something like this:

#ifdef USE_CONST
    #define MYCONST const
#else
    #define MYCONST
#endif

Then you can write code like this:

MYCONST int x = 1;
MYCONST char* foo = "bar";

and if you compile with USE_CONST defined (e.g. typically something -DUSE_CONST in the makefile or compiler options) then it will use the consts, otherwise it won't.

Edit: Actually I see Vlad covered that option at the end of his answer, so +1 for him :)

GrahamS