简体   繁体   中英

C multi-line macro issue: why not use if(1){…} instead of do{…}while(0) in multi-line macro definition

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!

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.

The whole idea behind this trick is to find a way to create a multi-line (ie 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 "PS" 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.

PS 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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM