do {…} while(false)

前端 未结 25 2451
小鲜肉
小鲜肉 2020-11-28 03:29

I was looking at some code by an individual and noticed he seems to have a pattern in his functions:

 function()
{
 

        
25条回答
  •  渐次进展
    2020-11-28 03:52

    Several explanations. The first one is general, the second one is specific to C preprocessor macros with parameters:

    Flow control

    I've seen this used in plain C code. Basically, it's a safer version of goto, as you can break out of it and all memory gets cleaned up properly.

    Why would something goto-like be good? Well, if you have code where pretty much every line can return an error, but you need to react to all of them the same way (e.g. by handing the error to your caller after cleaning up), it's usually more readable to avoid an if( error ) { /* cleanup and error string generation and return here */ } as it avoids duplication of clean-up code.

    However, in C++ you have exceptions + RAII for exactly this purpose, so I would consider it bad coding style.

    Semicolon checking

    If you forget the semicolon after a function-like macro invocation, arguments might contract in an undesired way and compile into valid syntax. Imagine the macro

    #define PRINT_IF_DEBUGMODE_ON(msg) if( gDebugModeOn ) printf("foo");
    

    That is accidentally called as

    if( foo )
        PRINT_IF_DEBUGMODE_ON("Hullo\n")
    else
        doSomethingElse();
    

    The "else" will be considered to be associated with the gDebugModeOn, so when foo is false, the exact reverse of what was intended will happen.

    Providing a scope for temporary variables.

    Since the do/while has curly braces, temporary variables have a clearly defined scope they can't escape.

    Avoiding "possibly unwanted semicolon" warnings

    Some macros are only activated in debug builds. You define them like:

    #if DEBUG
    #define DBG_PRINT_NUM(n) printf("%d\n",n);
    #else
    #define DBG_PRINT_NUM(n) 
    #endif
    

    Now if you use this in a release build inside a conditional, it compiles to

    if( foo )
        ;
    

    Many compilers see this as the same as

    if( foo );
    

    Which is often written accidentally. So you get a warning. The do{}while(false) hides this from the compiler, and is accepted by it as an indication that you really want to do nothing here.

    Avoiding capturing of lines by conditionals

    Macro from previous example:

    if( foo )
        DBG_PRINT_NUM(42)
    doSomething();
    

    Now, in a debug build, since we also habitually included the semicolon, this compiles just fine. However, in the release build this suddenly turns into:

    if( foo )
    
    doSomething();
    

    Or more clearly formatted

    if( foo )
        doSomething();
    

    Which is not at all what was intended. Adding a do{ ... }while(false) around the macro turns the missing semicolon into a compile error.

    What's that mean for the OP?

    In general, you want to use exceptions in C++ for error handling, and templates instead of macros. However, in the very rare case where you still need macros (e.g. when generating class names using token pasting) or are restricted to plain C, this is a useful pattern.

提交回复
热议问题