Volviendo stringstream temporal para c_str () en una sola declaración
-
19-09-2019 - |
Pregunta
Considere la siguiente función:
void f(const char* str);
Supongamos que quiero generar una cadena usando stringstream y pasarlo a esta función. Si quiero hacerlo en un comunicado, que puede probar:
f((std::ostringstream() << "Value: " << 5).str().c_str()); // error
Esto da un error: 'str ()' no es un miembro de 'basic_ostream'. Aceptar, por lo que el operador << regresa ostream en lugar de ostringstream -? ¿Qué hay de fundición de nuevo a un ostringstream
1) ¿Es este reparto seguro?
f(static_cast<std::ostringstream&>(std::ostringstream() << "Value: " << 5).str().c_str()); // incorrect output
Ahora, con esto, resulta que para el operador << ( "Valor:") llamada, se trata en realidad de llamar ostream operador << (void *) e imprimir una dirección hexadecimal. Esto está mal, quiero que el texto.
2) ¿Por qué operador << en la std :: temporal ostringstream () llamar al operador ostream? Sin duda, el temporal tiene un tipo de 'ostringstream' no 'ostream'?
puedo emitir el temporal para forzar la llamada del operador correcto también!
f(static_cast<std::ostringstream&>(static_cast<std::ostringstream&>(std::ostringstream()) << "Value: " << 5).str().c_str());
Esto parece trabajar y pasa "Valor: 5" para f ()
.3) ¿Estoy confiando en un comportamiento indefinido ahora? Los moldes se ven inusual.
Soy consciente de la mejor alternativa es algo como esto:
std::ostringstream ss;
ss << "Value: " << 5;
f(ss.str().c_str());
... pero estoy interesado en el comportamiento de hacerlo en una sola línea. Supongamos que alguien quería hacer una (dudosa) macro:
#define make_temporary_cstr(x) (static_cast<std::ostringstream&>(static_cast<std::ostringstream&>(std::ostringstream()) << x).str().c_str())
// ...
f(make_temporary_cstr("Value: " << 5));
¿Sería esta función como se esperaba
Solución
No se puede emitir el flujo temporal a std::ostringstream&
. Se formó-enfermo (el compilador debe decirle que es un error). Lo siguiente puede hacerlo, sin embargo:
f(static_cast<std::ostringstream&>(
std::ostringstream().seekp(0) << "Value: " << 5).str().c_str());
Eso por supuesto es feo. Pero muestra cómo se puede trabajar. seekp
es una función miembro de devolver un std::ostream&
. Probablemente mejor escribir esto generalmente
template<typename T>
struct lval { T t; T &getlval() { return t; } };
f(static_cast<std::ostringstream&>(
lval<std::ostringstream>().getlval() << "Value: " << 5).str().c_str());
La razón de que sin nada que tarda el void*
, se debe a que operator<<
es una función miembro. El operator<<
que toma un char const*
no lo es.
Otros consejos
Un temporal no se pueden pasar como una referencia no const a una función, es por eso que no encuentra el operador de streaming correcta y en su lugar toma el uno con void * argumento (que es una función miembro y por lo tanto llamándolo de forma temporal está bien).
Lo que viene a moverse por las limitaciones por fundición, tengo la sensación de que se trata, de hecho, UB, pero no puedo decir con seguridad. Otra persona seguramente citar la norma.
Esto se puede hacer usando una función lambda C ++ 11.
#include <iostream>
#include <sstream>
void f(const char * str)
{
std::cout << str << std::endl;
}
std::string str(void (*populate)(std::ostream &))
{
std::ostringstream stream;
populate(stream);
return stream.str();
}
int main(int argc, char * * args)
{
f(str([](std::ostream & ss){ ss << "Value: " << 5; }).c_str());
return 0;
}
// g++ -std=c++11 main.cpp -o main
// ./main
// Value: 5
Si te gusta declaraciones one_lined puede escribir:
// void f(const char* str);
f(static_cast<ostringstream*>(&(ostringstream() << "Value: " << 5))->str());
Sin embargo se debe preferir más fácil de mantener código como:
template <typename V>
string NumberValue(V val)
{
ostringstream ss;
ss << "Value: " << val;
return ss.str();
}
f(NumberValue(5));
utilizo algo un poco como esta para el registro.
#include <sstream>
using namespace std;
const char *log_text(ostringstream &os, ostream &theSame)
{
static string ret; // Static so it persists after the call
ret = os.str();
os.str(""); // Truncate so I can re-use the stream
return ret.c_str();
}
int main(int argc, char **argv)
{
ostringstream ss;
cout << log_text(ss, ss << "My first message") << endl;
cout << log_text(ss, ss << "Another message") << endl;
}
Salida:
Mi primer mensaje
Otro mensaje