I was playing with a macro to enable/disable traces when I came out with the following code when the macro is disabled:
int main()
{
("Hello world");
}
This code is valid and I got the desired effect (nothing happens when the macro is disabled) but I couldn't figure out what exactly is happening. Is the compiler seeing the parenthesis as a "nameless" method declaration?
To make it clearer the code is :
#ifdef TRACE
#define trace printf("%s %d -> ",__FILE__, __LINE__);printf
else
#define trace
#endif
int main()
{
trace("Hello world");
}
Thanks in advance.
If the function name is missing, as in your first example, then it is not a "parenthesis operator". It is simply a syntactic element of an expression that alters the association between operators and operands. In this case it simply does nothing. What you have is just an expression
"Hello world";
which evaluates to a value of char *
type, and that value is ignored. You can surround that expression in a redundant pair of ()
("Hello world");
which will not change anything.
In exactly the same way you can write
(5 + 3);
in the middle of your code and get an expression that evaluates to value 8
, which is immediately discarded.
Usually compilers generate no code for expression statements that have no side effects. In fact, in C language the result of every expression statement is discarded, so the only expression statements that "make sense" are expression statements with side effects. Compilers are normally fairly good at detecting effectless statements and discarding them (sometimes with a warning).
The warning could be annoying, so writing effectless expression statements like
"Hello world";
might not be a good idea. Typically compilers recognize a cast to void
as a request not to generate this warning
(void) "Hello world";
So you might consider redefining your macro accordingly.
Of course, using the above trace
technique, you have to remember that if you put something that does have a side effect as an argument for your macro
trace("%d\n", i++);
then in "disabled" form it will look as follows
("%d\n", i++);
(two subexpressions, chained by a comma operator into one expression). The side effect of incrementing i
persists in this case, it does not get disabled. The whole thing is equivalent to plain
i++;
Also if you use a function call as an argument
trace(get_trace_name());
the "disabled" form will look as
(get_trace_name());
and the compiler might not be smart enough to realize that the call to get_trace_name()
should be discarded. So, be careful when using your macro. Avoid arguments with side effects, avoid arguments with function calls, unless, of course, it is your intent to preserve the side effects when disabling the actual tracing.
Whether it works or not may depend on exactly what you pass as the arguments to the macro (see the side-effects issue mentioned by AndreyT). In this case it is benign. However the following is probably safer since it will result in no text being inserted when the macro is processed and TRACE is not defined:
#ifdef TRACE
#define trace printf("%s %d -> ",__FILE__, __LINE__);printf
#else
#define trace( ... )
#endif
assuming your compiler supports variadic macros. If it does the following would be a better definition perhaps:
#ifdef TRACE
#define trace( fmt, ...) printf("%s %d -> " fmt, __FILE__, __LINE__, __VA_ARGS__ ) ;
#else
#define trace( ... )
#endif
Note that the lack of a comma between "%s %d -> "
and fmt
is deliberate and required. Note also that the fmt
argument must be a literal string constant in order for adjacent string literal concatenation to occur - a variable of any kind would generate an error, but it is bad practice to use a variable for a format specifier in any case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With