Stringification - how does it work?

33,493

Solution 1

The relevant steps of macro expansion are (per C 2011 [n1570] 6.10.3.1 and C++ 1998 16.3.1):

  1. Process tokens that are preceded by # or ##.
  2. Apply macro replacement to each argument.
  3. Replace each parameter with the corresponding result of the above macro replacement.
  4. Rescan for more macros.

Thus, with xstr(foo), we have:

  1. The replacement text, str(s), contains no # or ##, so nothing happens.
  2. The argument foo is replaced with 4, so it is as if xstr(4) had been used.
  3. In the replacement text str(s), the parameter s is replaced with 4, producing str(4).
  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

  1. Evaluate str(foo): Substitute str(foo) with #foo, ie "foo"

Second case

  1. Evaluate xstr(foo): Substitute xstr(foo) with str(<foo-value>), ie str(4)
  2. Evaluate str(4): Substitute str(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

  1. Substitute str(foo) with <foo-value> + 1, ie 4 + 1
  2. Nothing more to substitute. Finishing.

And result is 4 + 1

Second string

  1. Substitute xstr(foo) with str(<foo-value>) + 1, ie str(4) + 1
  2. Substitute str(4) with <4-value> + 1, ie 4 + 1
  3. Nothing more to substitute.

And result is 4 + 1 + 1

Share:
33,493
Marco A.
Author by

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, 2020

Comments

  • Marco A.
    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.
    Marco A. almost 11 years
    I 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
    Lol4t0 almost 11 years
    @DavidKernin, Well, do you understand, that 'stringify' operation is #? It is only one step.
  • Lol4t0
    Lol4t0 almost 11 years
    @DavidKernin, this just not so smart, as you believe. It is pretty dumb. It just expands everything.
  • Marco A.
    Marco A. almost 11 years
    I 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
    Tomasz Gandor over 10 years
    At 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
    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!