I have to write several debugging messages in a program and I want to do it with a macro used like this:
TRAZA( "Hola" << " var=" << var );
This macro must build a character string with the file and the line where it is found in the source code and the message in parentheses, for example, if var
it is 7 , it is in the file '/src/a.cpp' and in the line 30 should build this string:
/src/a.cpp:30 - Hello var=7
And you should call the function trace(const char*) with that string.
Note that in the macro parameter I want to be able to put multiple ostream
C++-style chained objects using the operator <<
.
The macro was first implemented in the following way:
#define TRAZA( msg ) \
do { \
std::stringstream sstr; \
sstr << __FILE__ << ":" << __LINE__ << " - " << msg; \
traza( sstr.str().c_str() ); \
} while (false)
That works fine except if I have to print a variable called sstr
, which would be hidden by the one declared in the macro.
I tried to work around it by using an anonymous object so I don't have to declare a variable:
#define TRAZA( msg ) \
do { \
traza( \
dynamic_cast<std::stringstream&>( \
( std::stringstream() << __FILE__ << ":" << __LINE__ << " - " << msg)) \
.str().c_str() \
); \
} while (false)
But it doesn't write me the name of the file but it writes me the address of the pointer in hexadecimal:
0x401024:30 - Hello var=7
How do I make it so that std::stringstream
anonymous interprets that it should write the character string and not the address that points to that string?
The following program has a complete example:
#include <iostream>
#include <sstream>
#define TRAZA( msg ) \
do { \
traza( \
dynamic_cast<std::stringstream&>( \
( std::stringstream() << __FILE__ << ":" << __LINE__ << " - " << msg)) \
.str().c_str() \
); \
} while (false)
void traza( const char* texto )
{
// En realidad traza() hace otra cosa pero por simplificar
// lo imprimo en la consola.
// Yo no he implementado traza(), no la puedo cambiar y tengo que
// usarla.
std::cout << texto;
}
int main( int argc, char* argv[])
{
int sstr = 7;
TRAZA( "Hola" << " var=" << sstr );
return 0;
}
The problem occurs because the insert operator does not have a specific overload for
const char*
. Instead it has a generic entry for pointersostream& operator<< (void* val)
. If no checks are made in said function, the only thing that can be obtained from a generic pointer is the address to which it points. In later versions, at least from C++11 onwards, the problem is not reproduced.One possible option is to use the method
write
ofostream
. This method is overloaded and, unlike the insert operator, includes an overload specific toconst char*
. In this way ambiguities are avoided and the chain can be dumped without problems.Another interesting detail is that, like the insertion operator, the function
write
returns a reference to the stream and this allows calls to be concatenated.By the way, note that I have changed
dynamic_cast
tostatic_cast
. You really know that the object is of typestd::stringstream
, so a static cast is faster since it doesn't perform additional checks to see if the conversion can be done.