Stringification - how does it work?
Solution 1
The relevant steps of macro expansion are (per C 2011 [n1570] 6.10.3.1 and C++ 1998 16.3.1):
- Process tokens that are preceded by
#
or##
. - Apply macro replacement to each argument.
- Replace each parameter with the corresponding result of the above macro replacement.
- Rescan for more macros.
Thus, with xstr(foo)
, we have:
- The replacement text,
str(s)
, contains no#
or##
, so nothing happens. - The argument
foo
is replaced with4
, so it is as ifxstr(4)
had been used. - In the replacement text
str(s)
, the parameters
is replaced with4
, producingstr(4)
. str(4)
is rescanned. (The resulting steps produce”4”
.)
Note that the problem with str(foo)
is that step 2, which would replace foo
with 4
, comes after step 1, which changes the argument to a string. In step 1, foo
is still foo
; it has not been replaced with 4
, so the result is ”foo”
.
This is why a helper macro is used. It allows us to get a step 2 performed, then use another macro to perform step 1.
Solution 2
First case
- Evaluate
str(foo)
: Substitutestr(foo)
with#foo
, ie"foo"
Second case
- Evaluate
xstr(foo)
: Substitutexstr(foo)
withstr(<foo-value>)
, iestr(4)
- Evaluate
str(4)
: Substitutestr(4)
with#4
, ie"4"
Generally,
preprocessor evaluates macro-functions expanding macro-variables, until it is nothing to evaluate:
If you define
#define xstr(s) str(s) + 1
#define str(s) s + 1
in the following code
#define foo 4
int main()
{
std::cout << str(foo) << '\n'
<< xstr(foo) << '\n' ;
}
it would evaluate like
First string
- Substitute
str(foo)
with<foo-value> + 1
, ie4 + 1
- Nothing more to substitute. Finishing.
And result is 4 + 1
Second string
- Substitute
xstr(foo)
withstr(<foo-value>) + 1
, iestr(4) + 1
- Substitute
str(4)
with<4-value> + 1
, ie4 + 1
- Nothing more to substitute.
And result is 4 + 1 + 1
Marco A.
Former NVIDIA and AWS engineer. Don't take anything I say for granted: I'm always learning. Gold badge in the c++ tag Some of my favorite answers and questions gcc and clang implicitly instantiate template arguments during operator overload resolution In overload resolution, does selection of a function that uses the ambiguous conversion sequence necessarily result in the call being ill-formed? clang fails replacing a statement if it contains a macro c++11 constexpr flatten list of std::array into array Is a program compiled with -g gcc flag slower than the same program compiled without -g? How is clang able to steer C/C++ code optimization? Newton Raphson with SSE2 - can someone explain me these 3 lines Clang compilation works while gcc doesn't for diamond inheritance
Updated on January 29, 2020Comments
-
Marco A. over 4 years
I know that:
#define foo 4 #define str(s) #s
with
str(foo)
writes out:"foo"
, because stringify is executed first of text expansion, but this:#define xstr(s) str(s) #define str(s) #s #define foo 4
with
xstr(foo)
writes out:"4"
.Why? What are the steps involved in the process?
-
Marco A. almost 11 yearsI still don't understand.. First case: 1) Stringify foo 2) Expand, I now have foo. Second case: 1) Stringify, there's nothing to stringify.. 2) expand, I now have str(s) 3) stringify, now I have str(s) foo 4) Expand, now I have foo. Why is it different??
-
Lol4t0 almost 11 years@DavidKernin, Well, do you understand, that 'stringify' operation is
#
? It is only one step. -
Lol4t0 almost 11 years@DavidKernin, this just not so smart, as you believe. It is pretty dumb. It just expands everything.
-
Marco A. almost 11 yearsI got it, it's like a stack of functions.. it just keeps substituting until it encounters a # then stops the substitution. Returning from these "functions" substitutions continue if there is no other #.
-
Tomasz Gandor over 10 yearsAt last! I finally understood why this works like this, after reading this answer. I even was using it correctly by intuition, but couldn't come to the rule. The
stringify
macro comes up in many places (e.g. appending__LINE__
to identifier - to force it to be expanded and not left as__LINE__
, in that case you'll use##
instead of#
), or - more recently needed to pass arguments to GCC's_Pragma()
macro (intrinsic?), which accepts a quoted string, so you may need:#define my_pragma(arg) _Pragma(stringify("blah " #arg " blah2"))
. -
Joniale almost 4 years#define PE(BZL,YY) printf("ERROR - " #BZL ": result: 0x%08x!", YY); Doing:int variable1=1; PE(Test1,variable1); --> Results printing: ERROR - Test1: result: 0x1!