Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Token pasting and __LINE__

Tags:

c++

file

macros

I'm writing a simple macro to show TRACE information.

This is what I'm using ,

#ifdef __DEBUG__
#define TRACE  { PrintErrorMsg("Trace exception at " __FILE__  "LineNo:"##(__LINE__) "Function: " __FUNCTION__ " " );}
#else 
#define TRACE 
#endif

This is working with FILE, but it doesn't seems to work with LINE , Any idea how could I deal with this. I already tried stringing operator too.Which is as bellow.

#ifdef __DEBUG__
#define TRACE  { PrintErrorMsg("Trace exception at " __FILE__  "LineNo:"#(__LINE__) "Function: " __FUNCTION__ " " );}
#else 
#define TRACE 
#endif

and without parms and with double parms , ex - __LINE__ or ((__LINE__)) Any idea how could I deal with this problem?

And I come up with this,

#ifdef __DEBUG__
#define ERROR_MSG_BUF_SIZE 1024
#define TRACE  { char * error_msg_buffer = new char[ERROR_MSG_BUF_SIZE]; \
                 sprintf(error_msg_buffer,"Trace Exception at file: %s ,Line : %d , Function %s \n",__FILE__,__LINE__,__FUNCTION__);\
PrintErrorMsg(error_msg_buffer );\
delete[] error_msg_buffer;}
#else 
#define TRACE 

But I want to do it without using sprintf , just only by stringing and token pasting. Any idea?

#endif

--Thanks in advance--

like image 682
sandun dhammika Avatar asked Nov 30 '22 02:11

sandun dhammika


2 Answers

When you try to stringize something with #x, that x must be a macro parameter:

#define FOO #__LINE__ /* this is not okay */
#define BAR(x) #x     /* this is okay */

But you cannot simply say BAR(__LINE__), because this will pass the token __LINE__ into BAR, where it is immediately turned into a string without expansion (this is by design), giving "__LINE__". The same thing happens with the token-pasting operator ##: expansion of their operands never happens.

The solution is to add indirection. You should always have these in your codebase somewhere:

#define STRINGIZE(x) STRINGIZE_SIMPLE(x)
#define STRINGIZE_SIMPLE(x) #x

#define CONCAT(first, second) CONCAT_SIMPLE(first, second)
#define CONCAT_SIMPLE(first, second) first ## second

Now STRINGIZE(__LINE__) turns to STRINGIZE_SIMPLE(__LINE__) which gets fully expanded to (for example) #123, which results in "123". Phew! I leave STRINGIZE_SIMPLE around on the off chance I want the original behavior. So your code would be something like:

#include <iostream>

#define STRINGIZE(x) STRINGIZE_SIMPLE(x)
#define STRINGIZE_SIMPLE(x) #x

#define TRACE()                                                 \
        PrintErrorMsg("Trace exception in " __FILE__            \
                      " at line number " STRINGIZE(__LINE__)    \
                      " in function " __FUNCTION__ ".")

void PrintErrorMsg(const char* str)
{
    std::cout << str << std::endl;
}

int main()
{
    TRACE();
}
like image 81
GManNickG Avatar answered Dec 06 '22 21:12

GManNickG


You need this kind of silliness, unfortunately.

#include <stdio.h>

#define TRACE2(f,l) printf("I am at file: " f " and line: " #l "\n")
#define TRACE1(f,l) TRACE2(f,l)
#define TRACE() TRACE1(__FILE__, __LINE__)

int main(void)
{
    TRACE();
    TRACE();
}

I am at file: test.cpp and line: 9
I am at file: test.cpp and line: 10

like image 22
David Schwartz Avatar answered Dec 06 '22 21:12

David Schwartz