C preprocessing gating weirdness

I am defining a macro that computes a constant line, storing the file name and line number, for logging purposes.

This works great, but I just can’t understand why 2 additional macros are STRINGIFY - STRINGIFY and TOSTRING , when my intuition just offers __FILE__ ":" #__LINE__ .

 #include <stdio.h> #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) #define THIS_ORIGIN (__FILE__ ":" TOSTRING(__LINE__)) int main (void) { /* correctly prints "test.c:9" */ printf("%s", THIS_ORIGIN); return 0; } 

It just seems like an ugly hack to me.

Can someone explain in detail what is happening in stages, so that __LINE__ correctly gated and why none of __FILE__ ":" STRINGIFY(__LINE__) and __FILE__ ":" #__LINE__ does not work?

+4
source share
2 answers

Due to the order of expansion. The GCC documentation states:

Macro arguments are fully macro expanded before they are replaced in the macro body, unless they are stiffened or inserted by other tokens. After substituting, the entire macro, including substituted arguments, is scanned again to expand the macros. As a result, the arguments are double checked to expand the macros in them.

So, if the argument is stiffened, it does not expand at first. You get literal text in brackets. But if it is passed to another macro, it expands. Therefore, if you want to expand it, you need two levels of macros.

This is because there are times when you do not want to expand the argument before the line most commonly used by the assert() macro. If you write:

 assert(MIN(width, height) >= 240); 

you want the message to be:

 Assertion MIN(width, height) >= 240 failed 

and not some crazy thing that MIN extends to (in gcc it uses several gcc-specific extensions and a rather long IIRC).

+6
source

You cannot just use __FILE__":"#__LINE__ because the stringify # operator can only be applied to a macro parameter.

__FILE__ ":" STRINGIFY(__LINE__) will work fine with other text (for example, __FILE__ ":" STRINGIFY(foo) , but will not work when used with another macro (which is all __LINE__ really) as a parameter, otherwise the macro will not replaced by.

+2
source

All Articles