Implementing formatted print with the possibility to do nothing when it gets no arguments

自作多情 提交于 2020-12-09 06:51:14

问题


I want to implement a macro named PRINT which gets zero or more parameters, and does the following:

  1. If it gets zero parameters - do nothing.
  2. If it gets one or more arguments - act like printf.

I succeed in implementing it as you can see in my code below, but only at the cost of calling to printf with an empty string in the case we get zero arguments.

Is there a way I can handle the zero arguments case without calling to printf (it's not efficient to print something when you just want to do nothing)?

#include <stdio.h>

#define PRINT(...) printf("" __VA_ARGS__);

int main(){
    PRINT();
    PRINT("print\n");
    PRINT("print number: %d\n", 7);
    return 0;
}   

output:

print
print number: 7  

回答1:


Both gcc and clang will completely eliminate the call to printf in the case that it is passed an empty format string. Probably that optimisation is reasonably common.

See here and here for disassembly on gcc.godbolt.org.

In short, don't worry about it.




回答2:


If you do want to outsource the check of empty argument list to the macro, then perhaps something like this:

#define HAS_ARGS(...) (sizeof( (char[]){#__VA_ARGS__} ) > 1)

#define PRINT(...) (HAS_ARGS(__VA_ARGS__) ? printf("" __VA_ARGS__) : (void)0)

This relies on the compound literal getting size 1 in case of an empty string = null terminator only. Example:

#include <stdio.h>

#define HAS_ARGS(...) (sizeof( (char[]){#__VA_ARGS__} ) > 1)

#define PRINT(...) (HAS_ARGS(__VA_ARGS__) ? printf("" __VA_ARGS__) : (void)0)

int main (void)
{
  int i = 5;
  PRINT("%d\n", i);
  PRINT();
}



回答3:


A solution is:

#define Argument1(a,...)    Argument2(a
#define Argument2(a, b,...) b
#define TestEmpty()         ,
#define PRINT(...)          Argument1(TestEmpty __VA_ARGS__ (),), printf(__VA_ARGS__);,)

For this source text:

Test 0: PRINT()
Test 1: PRINT("Hello, world.\n")
Test 2: PRINT("Result is %d.\n", result)
Test 3: PRINT("%d = %g.\n", 3, 3.)

the result of preprocessing is:

Test 0:
Test 1: printf("Hello, world.\n");
Test 2: printf("Result is %d.\n", result);
Test 3: printf("%d = %g.\n", 3, 3.);

This requires that the arguments to PRINT not start with a parenthesized item, which seems acceptable for this particular question. The code here is not a general solution for detecting an empty parameter when parentheses may be present.

Explanation:

  • In preparing to replace PRINT, __VA_ARGS__ is replaced. TestEmpty is not replaced at this time because it is not followed by parentheses.
  • Then PRINT is replaced, and the result is rescanned.
  • Argument1 is identified for replacement. In preparation for that, its arguments are processed.
  • At this point, if __VA_ARGS__ is empty, we have have the tokens TestEmpty (), which are replaced by ,. Otherwise, TestEmpty <some tokens> () remains. Note that, if TestEmpty () is present, the , it expands to is the first argument to Argument1. It is not an argument separator, because the arguments to Argument1 have already been identified.
  • Then Argument1 is replaced, producing either Argument2(, (if __VA_ARGS was empty) or Argument2(TestEmpty possibly followed by additional tokens (otherwise).
  • The result is either Argument2(,, printf();,) or Argument2(TestEmpty <some tokens>, printf(<some tokens>);,), according to whether __VA_ARGS__ was empty or not.
  • The tokens are now rescanned for further replacement, so the , will now be recognized as an argument separator.
  • Finally, Argument2 is replaced with an empty token list or with printf(<some tokens>);.


来源:https://stackoverflow.com/questions/53297695/implementing-formatted-print-with-the-possibility-to-do-nothing-when-it-gets-no

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!