c++ – How to write a const char * to an anonymous std :: stringstream?

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.

Scroll to Top