问题
I want to implement a macro named PRINT
which gets zero or more parameters, and does the following:
- If it gets zero parameters - do nothing.
- 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 tokensTestEmpty ()
, which are replaced by,
. Otherwise,TestEmpty <some tokens> ()
remains. Note that, ifTestEmpty ()
is present, the,
it expands to is the first argument toArgument1
. It is not an argument separator, because the arguments toArgument1
have already been identified. - Then
Argument1
is replaced, producing eitherArgument2(,
(if__VA_ARGS
was empty) orArgument2(TestEmpty
possibly followed by additional tokens (otherwise). - The result is either
Argument2(,, printf();,)
orArgument2(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 withprintf(<some tokens>);
.
来源:https://stackoverflow.com/questions/53297695/implementing-formatted-print-with-the-possibility-to-do-nothing-when-it-gets-no