3
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" ); }

Macro expansion techinical_report

Embed Size (px)

Citation preview

Page 1: Macro expansion techinical_report

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" );

}

Page 2: Macro expansion techinical_report

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.

Page 3: Macro expansion techinical_report

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.