Upload
sandun-perera
View
330
Download
1
Embed Size (px)
Citation preview
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" );
}
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.
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.