print_once, how blockwise it is working?

2020-04-12 09:38发布

问题:

I am trying to understand implement a print once function, this code is working fine; But I am not able to understand some things: 1) How blockwise this is working? Maybe I am not able to clear it properly, but just want to know, what property of C make this happen?

  1 #include <stdio.h>
  2 
  3 #define _TRACE_
  4 
  5 #ifdef _TRACE_
  6 #define print_once(fmt, ...)            \
  7 ({                                      \
  8      static bool __print_once=false;           \
  9      if(!__print_once)    {               \
 10          __print_once = true;           \
 11          do  {                               \
 12             printf(fmt, ##__VA_ARGS__);   \
 13         }while(0);                          \
 14      }                                  \
 15 })
 16 #else
 17 #define print_once(fmt, ...)    \
 18 printf("\n");                   
 19 #endif
 20 
 21 void func()
 22 {
 23     print_once("func\n");
 24 }
 25 int main(int argc, char* argv[])
 26 {
 27     print_once("test1\n");
 28     //:w
 29     print_once("test22\n");
 30     for(int i =0; i < 2; i++)
 31         print_once("loop\n");
 32     func();
 33     func();
 34     return 0;
 35 }

回答1:

That's not standard C, it uses a GCC extension called statement expressions (the funny ({...}) syntax). (It's not thread-safe either.)

It works by adding a static boolean at each print_once call site (each call site gets its own static flag), initially false, which is set to true the first time that piece of code is run (the code only prints if it was indeed false). So subsequent executions of the same block of code won't print anything.

Note that the use of the GCC extension is not necessary, and the construction of the macro is strange - it was probably extended from something simpler. This should be C99-compatible (C99 or above required for the variadic macro), and has the do {...} while(0) "trick" in the right place:

#define print_once(fmt, ...) \
do { static bool flag = false;  \
     if (!flag) { \
       flag = true; \
       printf(fmt, ##__VA_ARGS__); \
     } \
} while (0)