5

I have been thinking about how macros can be written to be safe, readable and intuitive. Proper use of them should be understood by the looks of them and when used incorrectly the compiler should tell you, and not let you introduce an obscure bug.

When writing multiple line define macros I usually find myself constructing them like this to fullfill the desired criteria:

#define macro(x)    do{                      \
                        ... some code line ; \
                        ... some code line ; \
                    }while(0)

This way you can both...

if (a)
{
    macro(a);
}

and...

if (a)
    macro(a);
else
{
    ...
}

A nice feature of this is that if you use them incorrectly you will get a compiler error. This will not compile for example:

if (a)
    macro(a)
else
    b();

However, I have seen SW developers read this kind of macro construct and getting quite perplexed by it. Can you think of alternative ways to write macros that will not deceive the user in a way where they do something other than expected, and still are quite understandable when browsing a new piece of code?

Also, can you find any problems with the do {} while(0) method? It could for example be a situation where you would expect the macro to behave a certain way and where this is not the case.

The reason why i am not completely convinced the do {} while(0) method is good practice is that the macros themselves look a bit weird and take some explaining to anyone (some at least) who has not seen the construct before.

Edit:

Here is an example from one of the links in the comments (thanks!) that i like. It's quite readable I think:

#define MYMACRO(a,b)   \
    if (xyzzy) asdf(); \
    else (void)0

Is it possible to break it?

a3f
  • 8,517
  • 1
  • 41
  • 46
Martin G
  • 17,357
  • 9
  • 82
  • 98
  • 4
    Yeah, the `do {} while(0)` approach is the much safest and recommended! – πάντα ῥεῖ May 03 '14 at 12:27
  • 11
    `do {} while(0)` is a common approach to this, specifically due to the lack of a semicolon on the last line making it look like a function. However, I'd consider macros to always be "deceiving" the user in some way, at least if they're expecting a function instead. –  May 03 '14 at 12:29
  • 5
    If you want it to be safe, why don't you just write a function? – David Heffernan May 03 '14 at 12:37
  • I use the convention of ALL UPPERCASE for a macro, to alert the user that arguments might be evaluated multiple times, or the effect of the line might not be what the user expects. – M.M May 03 '14 at 12:40
  • *All* macros "look a bit weird and take some explaining to anyone who has not seen the construct before". They're a fundamentally different language from C, and should be considered as such. – Alex Celeste May 03 '14 at 13:00
  • See http://www.parashift.com/c++-faq/macros-with-if.html, http://www.parashift.com/c++-faq/macros-with-multi-stmts.html and http://www.parashift.com/c++-faq/macros-with-token-pasting.html. – D Drmmr May 03 '14 at 13:59
  • 8
    "How to write user friendly c/c++ #define macros". Easy: You can't :) – Christian Hackl May 03 '14 at 14:26
  • @ChristianHackl The real answer is "You don't", not "You can't". As for the edit, that is not readable at all. – BЈовић May 27 '14 at 06:05
  • This question proceeds from a dodgy first premise. A programmer who sees `do{...}while(0)` and doesn't understand, can't really be described as fluent in C; this is a common enough idiom to effectively be part of the language. You could also say that `x++` is unclear because someone who's never seen it before is unlikely to guess correctly... what they should do is read the manual. – Alex Celeste Aug 15 '14 at 19:25

1 Answers1

8

A common approach when you want your macro to do nothing, and toggle this behavior with the preprocessor, is to use the void(0) operation.

#if SOMETHINGS
#define macro(x)    do{                      \
                        ... some code line ; \
                        ... some code line ; \
                    }while(0)
#else
#define macro(x) ((void)(0))
#endif

This way you won't break compilation whenever nulling out macro operations

Nikos Athanasiou
  • 29,616
  • 15
  • 87
  • 153
  • You may have to muck around with the `else` version , as various compilers issue various dead-code warnings. – M.M May 03 '14 at 12:41
  • 2
    Why not just `#define macro(x)` in the second case? – Filipe Gonçalves May 03 '14 at 16:39
  • @FilipeGonçalves because if you had code like `if (macro(x))` your code wouldn't compile every time you toggled the existence of `macro` – Nikos Athanasiou May 03 '14 at 16:41
  • `if (macro(x))` would never compile with the original macro definition anyway. `do while` constructs are not an expression, and as such cannot be used inside an `if`. I would say that if the original macro is used as a statement, then `#define macro(x)` would be enough. And though I haven't tried it, I'd guess that `if (((void)(0)))` won't compile too, since you're using a `void` value as a test condition. – Filipe Gonçalves May 03 '14 at 16:46
  • 1
    You can see [here](http://stackoverflow.com/a/2198975/2567683) an explanation on macros emulating `no ops`. I'm using the name `macro` as an example, this `macro` couldn't be in the body of an `if` clause to begin with. Another reason would be to explicitly break the compilation in places where assignment happens, to prevent state mutations from the excluded macro. – Nikos Athanasiou May 03 '14 at 16:58