The document discusses recursive macro expansion in C/C++ preprocessor. It presents a case study of defining a TRACE macro for debugging that prints the file name and line number. The key challenge is that __LINE__ is not expanded when used with stringizing operator. The solution is to use an extra level of indirection by defining another macro STRINGIZE that recursively expands its parameter before stringizing it, allowing __LINE__ to be replaced by the line number before being stringized.
1. TECHINICAL REPORT ON C/C++ PREPROCESSOR
RECURSIVE MACRO EXPANSION
Abstract
C/C++ is one of a old programming language which could not be survived without a preprocessor.
However there are some pitfalls on it’s preprocessor. They are actually not pitfalls, but it’s how that
original C/C++ preprocessor is designed to be. Anyway a newbie programmer may get get confused
due to it. I also ran into this trouble at once. At that time I was doing my final year project, and I have
asked it on http://stackoverfow.com/ and get retrieved a fix to the problem. But that time I didn’t have
precisely understood which magic does the trick.
Case Study
Think that you have to write a macro called `TRACE`. When running on __DEBUG__ defined it
should print line number and filename (ex- database.cpp line 2156) when executed through it. This is
mainly used for debugging purposes.
For a example like bellow you could use `TRACE`.
#ifdef __DEBUG__
#define TRACE
PrintErrorMsg("Trace exception in " __FILE__
" at line number " #__LINE__
);
#else
Simply the problem is `__LINE__` will expanded into a integer literal, so it is not concentrate with
the c-string literal on left hand side, so compiler emits an error.
error: cannot convert 'const char*' to 'FILE* {aka _iobuf*}' for argument '1' to
'int fprintf(FILE*, const char*, ...)'
So I do need to convert the integer literal into a c-string literal. The easiest way to do is the stringizing
operator in maco-preprocessor.
I have quoted bellow a stringizing example from MSDN website.
// stringizer.cpp
#include <stdio.h>
#define stringer( x ) printf_s( #x "n" )
int main() {
stringer( In quotes in the printf function call );
stringer( "In quotes when printed to the screen" );
stringer( "This: " prints an escaped double quote" );
}
2. So according to that we could rewrite our `TRACE` macro like bellow.
#ifdef __DEBUG__
#define TRACE
PrintErrorMsg("Trace exception in " __FILE__
" at line number " #__LINE__
);
#else
But the output is “Trace exception in database.cpp at line number __LINE__”. At that time I was
pressured to finish the project and handover the dissertation so I didn’t had time to find out what’s
going on? So the lazy minded man have fired a question on stackoverflow.
http://stackoverflow.com/questions/13301428/token-pasting-and-line
However then I fix my project codebase with following codes, By replace our `TRACE’ like bellow.
#define STRINGIZE(x) STRINGIZE_SIMPLE(x)
#define STRINGIZE_SIMPLE(x) #x
#ifdef __DEBUG__
#define TRACE
PrintErrorMsg("Trace exception in " __FILE__
" at line number " STRINGIZE(__LINE__)
);
#else
#ifdef TRACE
#undef TRACE
#endif
#endif
At that time this works like a dream to me. Now today I’m writing why the hell this is happening?
The secret lies behind how preprocessor expand it’s parameters.
Unfortunately I just fired up a very lazy duplicate question in stackoverflow , I have to apologize that
from all the contributors who read my question.
Anyway, I’m not the only one who made a duplicate. Then I have navigated into those questions and
explore why this is happening.
3. http://stackoverflow.com/questions/4284733/preprocessor-token-expansion#_=_
http://stackoverflow.com/questions/1597007/creating-c-macro-with-and-line-token-concatenation-
with-positioning-macr
So according to the accepted answer, it’s said.
the preprocessor will only expand the macros recursively if neither the stringizing
operator # nor the token-pasting operator ## are applied to it. So, you have to use
some extra layers of indirection, you can use the token-pasting operator with a
recursively expanded argument.
So now it’s much more clear.
Let me explain how this is happening….
Since `#__LINE__` is just a stringizing operator so it won’t reclusively be expanded. So the output
will be “__LINE__”.
So we need some recursive invocation, like this,
#define STRINGIZING(X) #X
This won’t work either, because `__LINE__` will be passed as X, and there is #X , so in this line
`__LINE__` would not be expanded at all.
But if we passed it to another nested macro like bellow,
#define STRINGIZING(X) STRINGIZING2(X)
Then incoming parameter `X` will be expanded to `__LINE__` and again `__LINE__` will be
expanded into whatever the line number remembered by the compiler. Say that 2326 was passed like
bellow , then
STRINGIZING2(2326)
will be called. After expansions were done.
So in the next nested macro it would be like,
#define STRINGIZING(X) #X
Where it does not hold `__LINE__` ,but integer literal `2326`. It was expanded because we don’t have
stringizing operator at STRINGIZING(X) macro definition. [ #define STRINGIZING(X)
STRINGIZING2(X) , so it get expanded ].
So that’s where the magic came from. Actually it’s not magic, it’s the preprocessors expected
behavior, only thing we need to explicitly force `__LINE__` to be expanded.
Hope you understand the document and thanks for reading.