Question:
I have to write several debug 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 string of characters with the file and the line in the source code and the message in parentheses, for example, if var
is 7, it is in the file '/src/a.cpp' and in line 30 should construct this string:
/src/a.cpp:30 – Hello var = 7
And you should call the trace function (const char *) with that string.
Note that in the macro parameter I want to be able to put several chained objects in the style of the c ++ ostream
using the <<
operator.
The macro I first implemented it as follows:
#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.
Tried to fix it 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 does not write me the name of the file but it writes the address of the pointer in hexadecimal:
0x401024: 30 – Hello var = 7
How do I get the anonymous std::stringstream
interpret that it should write the character string and not the address pointing 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;
}
Answer:
The problem occurs because the insert operator does not have a specific overload for const char*
. Instead it has a generic entry for pointers ostream& operator<< (void* val)
. If no checks are made in this function, the only thing that can be obtained from a generic pointer is the address it points to. In later versions, at least from C ++ 11 onwards, the problem is not reproduced.
One possible option is to use the write
method of ostream
. This method is overloaded and, unlike the insert operator, includes a specific overload for const char*
. This avoids ambiguities and the chain can be tipped over without problems.
Another interesting detail is that, like the insertion operator, the write
function returns a reference to the stream and this allows calls to be concatenated.
#define TRAZA( msg ) \
do { \
traza( \
static_cast<std::stringstream&>( \
( std::stringstream().write(__FILE__,strlen(__FILE__)) << ":" << __LINE__ << " - " << msg)) \
.str().c_str() \
); \
} while (false)
By the way, note that I have changed dynamic_cast
to static_cast
. You really know that the object is of type std::stringstream
, so a static cast is faster since it does not perform additional checks to verify if the conversion can be performed.