Successfully reported this slideshow.

Macro expansion techinical_report


Published on

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Macro expansion techinical_report

  1. 1. TECHINICAL REPORT ON C/C++ PREPROCESSORRECURSIVE MACRO EXPANSIONAbstractC/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 thatoriginal C/C++ preprocessor is designed to be. Anyway a newbie programmer may get get confuseddue to it. I also ran into this trouble at once. At that time I was doing my final year project, and I haveasked it on and get retrieved a fix to the problem. But that time I didn’t haveprecisely understood which magic does the trick.Case StudyThink that you have to write a macro called `TRACE`. When running on __DEBUG__ defined itshould print line number and filename (ex- database.cpp line 2156) when executed through it. This ismainly 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__ );#elseSimply the problem is `__LINE__` will expanded into a integer literal, so it is not concentrate withthe c-string literal on left hand side, so compiler emits an error.error: cannot convert const char* to FILE* {aka _iobuf*} for argument 1 toint 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 stringizingoperator 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. 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__ );#elseBut the output is “Trace exception in database.cpp at line number __LINE__”. At that time I waspressured to finish the project and handover the dissertation so I didn’t had time to find out what’sgoing on? So the lazy minded man have fired a question on stackoverflow. 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#endifAt 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 thatfrom 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 andexplore why this is happening.
  3. 3. according to the accepted answer, it’s said.the preprocessor will only expand the macros recursively if neither the stringizingoperator # nor the token-pasting operator ## are applied to it. So, you have to usesome extra layers of indirection, you can use the token-pasting operator with arecursively 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 outputwill be “__LINE__”.So we need some recursive invocation, like this,#define STRINGIZING(X) #XThis 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 beexpanded into whatever the line number remembered by the compiler. Say that 2326 was passed likebellow , thenSTRINGIZING2(2326)will be called. After expansions were done.So in the next nested macro it would be like,#define STRINGIZING(X) #XWhere it does not hold `__LINE__` ,but integer literal `2326`. It was expanded because we don’t havestringizing 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 expectedbehavior, only thing we need to explicitly force `__LINE__` to be expanded.Hope you understand the document and thanks for reading.