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 claseostringstream
. (Sí, necesitamos usar.str()
. El uso del objetoostringstream
directamente terminaría invocandoios::operator void*()
, devolviendo un valor bueno / malo como un puntero y no un objeto de cadena).operator<<(...)
existe como métodos heredadosostream
y funciones globales. En todos los casos devuelve unostream&
referencia.Las opciones aquí para
ostringstream()<<"foo"
son el método heredadoostream::operator<<(void* )
y eloperator<<(ostream&,const char* )
función globaloperator<<(ostream&,const char* )
. Elostream::operator<<(void* )
heredadoostream::operator<<(void* )
gana porque no podemos convertir a una referencia de objetoostream
para invocar la función global. [¡Felicitaciones a la cocina !]
Entonces, para lograr esto, necesitamos:
-
ostringstream
unostringstream
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:
-
ostringstream() . seekp( 0, ios_base::cur )
-
ostringstream() . write( "", 0 )
-
ostringstream() . flush()
-
ostringstream() << flush
-
ostringstream() << nounitbuf
-
ostringstream() << unitbuf
-
ostringstream() << noshowpos
- O cualquier otro manipulador estándar. [
#include <iomanip>
] Referencia: consulta "Insertar datos con formato" 1/3 del camino hacia abajo en esta página web.
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
- Biblioteca IOstream
-
ostringstream
-
ostream::operator<<()
- Tipo Tutorial de Casting
- Wiki: Escribir Casting
.
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;