2

I want to invoke multi-line macro in circulation to break/continue it.

If I use "do{...}while(0)" in multi-line macro definition, break/continue is only effect on "do {...}while(0)", not the circulation who invoke this macro. So I consider using "if(1){...}" in multi-macro definition.

#define EXIT_CIRCULATION() \
        if(1){ \
           break; \
        }

void func(){
    while(1){
       ...
       EXIT_CIRCULATION();
       ...
    }
}

But I doubt wheather it is a good way to use "if(1){...}" in macro definition, because I can not find any example in internet.

Thanks!

cnnbcza
  • 121
  • 1
  • 7
  • 2
    http://stackoverflow.com/questions/1067226/c-multi-line-macro-do-while0-vs-scope-block The first example itself is a reason not to. – Karthik T Nov 26 '12 at 06:36

3 Answers3

10

if you code something like

 if (somecondition)
    EXIT_CIRCULATION();
 else
    break;

then the expansion of your macro won't behave as you intuitively expect. The else would apply to your if (1) and will never happen.

Klas Lindbäck
  • 32,669
  • 4
  • 56
  • 80
Basile Starynkevitch
  • 216,767
  • 17
  • 275
  • 509
  • Won't it resolve to what you intuitively expect anyway because of the semicolon after the curly braces? If he wrote `#define EXIT_CIRCULATION() if(1) break`, I could see your point... (didn't try though, maybe I'm wrong) – Amadan Nov 26 '12 at 06:37
  • But then my example won't even compile (mismatched `else`). – Basile Starynkevitch Nov 26 '12 at 06:38
  • Oh, I see. Yup, I thought something else. Do ignore please. – Amadan Nov 26 '12 at 06:43
  • But is there any way to resolve that exit circulation by invoke multi-line macro who use "do{...}while(0)". Thanks! – cnnbcza Nov 26 '12 at 07:00
  • The explanation given in the answer is actually inaccurate. If you write something like that, the `;` after `EXIT_CIRCULATION()` will be seen as a standalone empty statement. Because of that it will *terminate* both `if`s. The `else` branch will be seen as an "orphaned" `else` and will produce a syntax error. It won't be attached to the inner "hidden" `if`, as this answer incorrectly claims. It will, again, produce a syntax error. – AnT Nov 26 '12 at 07:07
  • In fact, if the problem were with incorrect `else` association, then this definition would solve it `if (0); else { ... }`. This version does not suffer from the problem described in this answer. However, this "solution" will not work properly either. Only `do/while` does. – AnT Nov 26 '12 at 07:19
2

The whole idea behind this trick is to find a way to create a multi-line (i.e compound) statement that also incorporates a terminating ; as its integral part. That will give you the opportunity to use ; after your macro invocation without inadvertently introducing an empty statement.

The ordinary compound statement in { ... } doesn't work because it does not end in ;. The only multi-line statement in C/C++ that ends in ; is do/while. There's no other statements in C/C++ grammar that would satisfy this requirement. (This is an inaccurate claim, see my "P.S." below)

In all other cases (including your if (1) {...}) the ; after the macro invocation will be seen as an additional independent standalone empty statement. This will make it impossible to write a ; after your macro invocation when it is used in contexts requiring exactly one statement (like true branch of if-else or the body of do/while cycle).

For example, if you define

#define A() if (1) {}

then this code will not compile

do
  A();
while (1);

because it will be replaced with

do
  if (1) {}; /* <- two statements, not one */
while (1);

and that is actually two statements between do and while. Specifying two statements between do and while without wrapping them into {} is syntax error.

P.S. Correction: The claim I make above about do/while being the only viable variant is incorrect . In @Michael Burr's answer you can see another suitable variant, which is using the else ((void) 0) trick for the same purpose. However, the main principle remains the same.

AnT
  • 302,239
  • 39
  • 506
  • 752
2

Here's a macro that I believe will safely do what you want:

#define EXIT_CIRCULATION()  \
            if (1) {        \
                /* some statements */   \
                break;                  \
            }                           \
            else                        \
                do {} while (0)

The fact that the if here is matched with an else means that the macro is safe to use inside another if, and since the else clause is a do-nothing do/while statement that provides the similar properties to wrapping a multi-line macro in a do/while. Such as the macro will need to be terminated by a semicolon as if it were a normal statement; forgetting the semicolon will result in a syntax error. And it plays nice inside another if or else clause.

And most importantly for you (I think), the break statement isn't swallowed by the macro - it will break out the loop the macro is used in.

Whether that's a good idea or not is something else entirely. Many programmers don't like flow-control statements to be hidden within a macro (unless the flow of control is entirely within the macro unit).

Here it is in action:

#include<stdio.h>
#include<stdlib.h>

#define EXIT_CIRCULATION()  \
            if (1) {        \
                puts("done.");          \
                break;                  \
            }                           \
            else                        \
                do {} while (0)

int main()
{
    int i = 0;

    for (i = 0; i < 10; ++i) {
        if (i  > 4)
            EXIT_CIRCULATION();
        else
            puts("working...");
    }

    printf("i == %d\n", i);

    return 0;
}

output:

working...
working...
working...
working...
working...
done.
i == 5
Michael Burr
  • 321,763
  • 49
  • 514
  • 739