sstream español ejemplo c++ format printing macros ostringstream

español - stringstream c++ ejemplo



C++ formato macro/en línea ostringstream (7)

¿Por qué no usar una función en lugar de una macro?

Intento escribir una macro que me permita hacer algo como: FORMAT(a << "b" << c << d) , y el resultado sería una cadena, lo mismo que crear un ostringstream, insertar a...d , y devolviendo .str() . Algo como:

string f(){ ostringstream o; o << a << "b" << c << d; return o.str() }

Básicamente, FORMAT(a << "b" << c << d) == f() .

Primero, lo intenté:

1: #define FORMAT(items) / ((std::ostringstream&)(std::ostringstream() << items)).str()

Si el primer elemento es una cadena C ( const char * ), imprimirá la dirección de la cadena en hexadecimal, y los siguientes elementos se imprimirán bien. Si el primer elemento es std::string , no podrá compilarse (no se asociará el operador << ).

Esta:

2: #define FORMAT(items) / ((std::ostringstream&)(std::ostringstream() << 0 << ''/b'' << items)).str()

da lo que parece ser el resultado correcto, pero el 0 y /b están presentes en la cadena, por supuesto.

Lo siguiente parece funcionar, pero compila con advertencias (tomando la dirección de temporal):

3: #define FORMAT(items) / ((std::ostringstream&)(*((std::ostream*)(&std::ostringstream())) << items)).str()

¿Alguien sabe por qué 1 imprime la dirección de la cadena c y no puede compilar con std::string ? ¿No son 1 y 3 esencialmente lo mismo?

Sospecho que las plantillas variadicas de C ++ 0x harán posible el format(a, "b", c, d) . Pero, ¿hay alguna manera de resolver esto ahora?


Aquí hay una respuesta como cadabra que no se mezcla con el estado ostream:

#define FORMAT(items) static_cast<std::ostringstream &>((std::ostringstream() << std::string() << items)).str()

Creo que el primer párrafo de la respuesta de coppro describe por qué las cosas se comportan de esta manera.


Aquí hay una solución de trabajo:

#define FORMAT(items) / ((std::ostringstream&)(std::ostringstream() << std::dec << items)).str()

No entiendo muy bien el comportamiento del primer argumento.


El problema que está teniendo está relacionado con el hecho de que el operator << (ostream&, char*) no es miembro de ostream, y su instancia temporal de ostream no puede vincularse a una referencia no const . En cambio, elige la sobrecarga void* , que es miembro de ostream, y por lo tanto no tiene esa restricción.

¡Lo mejor (pero no el más fácil ni el más elegante, por mucho de la imaginación!) Sería usar el preprocesador Boost para generar una gran cantidad de sobrecargas de funciones, cada plantilla en una gran cantidad de objetos (se han omitido y asumido using namespace std; ):

#define MAKE_OUTPUT(z, n, data) / BOOST_PP_TUPLE_ELEM(2, 0, data) << BOOST_PP_CAT(BOOST_PP_TUPLE_ELEM(2, 1, data), n); #define MAKE_FORMAT(z, n, data) / template <BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_INC(n), typename T)> / inline string format(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, BOOST_PP_INC(n), T, p)) / { / ostringstream s; / BOOST_PP_REPEAT_##z(z, n, MAKE_OUTPUT, (s, p)); / return s.str(); / }

No se garantiza que funcione exactamente (lo escribió sin pruebas), pero esa es básicamente la idea. A continuación, llama a BOOST_PP_REPEAT(N, MAKE_FORMAT, ()) para crear una serie de funciones que toman hasta N parámetros que formatearán tu cadena como desees (reemplaza N con el número entero de preferencia. Los valores más altos pueden afectar negativamente los tiempos de compilación). Esto debería ser suficiente hasta que obtenga un compilador con plantillas variadas. Debería leer la documentación del preprocesador boost, tiene funciones muy potentes para cosas como esta. (luego puede #undef las macros, después de llamar a la invocación BOOST_PP_REPEAT para generar las funciones)


Ya casi te has dado cuenta de esto. Pero es un poco difícil de seguir. Así que déjame echar un vistazo a resumir lo que has dicho ...

Las dificultades aquí son las siguientes:

  • Estamos jugando con un objeto ostringstream temporal, por lo que tomar direcciones está contraindicado.

  • Debido a que es temporal, no podemos convertir trivialmente a un objeto ostream través del casting.

  • Tanto el constructor [obviamente] como str() son métodos de clase ostringstream . (Sí, necesitamos usar .str() . El uso del objeto ostringstream directamente terminaría invocando ios::operator void*() , devolviendo un valor bueno / malo como un puntero y no un objeto de cadena).

  • operator<<(...) existe como métodos heredados ostream y funciones globales. En todos los casos devuelve un ostream& referencia.

  • Las opciones aquí para ostringstream()<<"foo" son el método heredado ostream::operator<<(void* ) y el operator<<(ostream&,const char* ) función global operator<<(ostream&,const char* ) . El ostream::operator<<(void* ) heredado ostream::operator<<(void* ) gana porque no podemos convertir a una referencia de objeto ostream para invocar la función global. [¡Felicitaciones a la cocina !]

Entonces, para lograr esto, necesitamos:

  • ostringstream un ostringstream temporal.
  • Conviértalo en un ostream .
  • Adjuntar datos.
  • Convierta nuevamente a un ostringstream .
  • E invocar a str() .

Asignación: ostringstream() .

Conversión: hay varias opciones. Otros han sugerido:

  • ostringstream() << std::string() // Kudos to *David Norman*
  • ostringstream() << std::dec // Kudos to *cadabra*

O podríamos usar:

No podemos usar:

  • operator<<( ostringstream(), "" )
  • (ostream &) ostringstream()

Anexar: Directo ahora.

Conversión de vuelta: podríamos usar (ostringstream&) . Pero un dynamic_cast sería más seguro. En el caso poco probable de que dynamic_cast devuelto NULL (no debería), el siguiente .str() activará un coredump.

Invocando a str() : Adivina.

Poniendolo todo junto.

#define FORMAT(ITEMS) / ( ( dynamic_cast<ostringstream &> ( / ostringstream() . seekp( 0, ios_base::cur ) << ITEMS ) / ) . str() )

Referencias

.


Cuando tomé la solución de mrree (la marcada como "preferida", la bellamente explicada y la que funcionaba perfectamente para G ++), tuve problemas con MSVC ++: todas las cadenas creadas con esta macro terminaron vacías.

Horas (y muchos rascándome la cabeza y haciendo una pregunta "recargada" aquí) más tarde, descubrí que la llamada a seekp () era la culpable. No estoy seguro de lo que MSVC ++ hace de manera diferente con eso, pero reemplazando

ostringstream().seekp( 0, ios_base::cur )

con cadabra

ostringstream() << std::dec

funciona para MSVC ++, también.


Esto es lo que uso. Todo encaja en una definición de clase ordenada en un archivo de encabezado.

actualización: mejora importante del código gracias a litb .

// makestring.h: class MakeString { public: std::stringstream stream; operator std::string() const { return stream.str(); } template<class T> MakeString& operator<<(T const& VAR) { stream << VAR; return *this; } };

Así es como se usa:

string myString = MakeString() << a << "b" << c << d;