2008-12-14 20 views
38

¿cómo puedo enlazar un objeto std::ostream a cualquiera std::cout o para std::ofstream, en función de una determinada condición programa? Aunque esto no es válida, por muchas razones, me gustaría lograr algo que es semánticamente equivalente a la siguiente:Obtener un std :: ostream ya sea desde std :: cout o std :: ofstream (archivo)

std::ostream out = condition ? &std::cout : std::ofstream(filename); 

He visto algunos ejemplos que no son una excepción de seguridad, tales como uno de http://www2.roguewave.com/support/docs/sourcepro/edition9/html/stdlibug/34-2.html:

int main(int argc, char *argv[]) 
{ 
    std::ostream* fp;           //1 
    if (argc > 1) 
    fp = new std::ofstream(argv[1]);       //2 
    else 
    fp = &std::cout           //3 

    *fp << "Hello world!" << std::endl;       //4 
    if (fp!=&std::cout) 
    delete fp; 
} 

¿Alguien conoce una mejor solución de excepción segura?

Respuesta

52
std::streambuf * buf; 
std::ofstream of; 

if(!condition) { 
    of.open("file.txt"); 
    buf = of.rdbuf(); 
} else { 
    buf = std::cout.rdbuf(); 
} 

std::ostream out(buf); 

Eso asocia el streambuf subyacente del flujo de archivos de salida o salida a out. Después de eso puedes escribir "salir" y terminará en el destino correcto. Si lo que desea es que todo va a std::cout entra en un archivo, puede hacer aswell

std::ofstream file("file.txt"); 
std::streambuf * old = std::cout.rdbuf(file.rdbuf()); 
// do here output to std::cout 
std::cout.rdbuf(old); // restore 

Este segundo método tiene el inconveniente de que no es seguro excepción. Posiblemente quiere escribir una clase que hace esto usando RAII:

struct opiped { 
    opiped(std::streambuf * buf, std::ostream & os) 
    :os(os), old_buf(os.rdbuf(buf)) { } 
    ~opiped() { os.rdbuf(old_buf); } 

    std::ostream& os; 
    std::streambuf * old_buf; 
}; 

int main() { 
    // or: std::filebuf of; 
    //  of.open("file.txt", std::ios_base::out); 
    std::ofstream of("file.txt"); 
    { 
     // or: opiped raii(&of, std::cout); 
     opiped raii(of.rdbuf(), std::cout); 
     std::cout << "going into file" << std::endl; 
    } 
    std::cout << "going on screen" << std::endl; 
} 

Ahora, pase lo que pase, std :: cout está en estado limpio.

+0

Ooh, me gusta std :: streambuf * mejor que std :: ostream * – Tom

+0

, pero si algo falla, se quedará con un puntero colgando en la cola de su streambuf * del ahora destruido arroyo. así que ten cuidado, mejor escribe un método RAII. –

+2

No me gusta secuestrar std :: cout. Significa que cout y printf ya no son equivalentes, algo que imagino que muchos desarrolladores dan por hecho. – Tom

22

Ésta es una excepción de seguridad:

void process(std::ostream &os); 

int main(int argc, char *argv[]) { 
    std::ostream* fp = &cout; 
    std::ofstream fout; 
    if (argc > 1) { 
     fout.open(argv[1]); 
     fp = &fout; 
    } 
    process(*fp); 
} 

Editar: Herb Sutter se ha ocupado de esto en el artículo Switching Streams (Guru of the Week).

+0

esto no parece más a excepción de fallos de el código original. – Brian

+0

Sí lo es. El código original tiene una pérdida de memoria si se lanza una excepción durante el procesamiento (* fp << "Hello World" << std :: endl). – Tom

+1

Por qué esta respuesta recibe mi voto es que la primera respuesta rompe una vieja regla mía "No te metas con las partes internas de otros objetos". Solo porque ** puedas ** reemplazar rdbuf no significa que debas hacerlo. –

10
std::ofstream of; 
std::ostream& out = condition ? std::cout : of.open(filename); 
+4

¿cómo haces .close()? –

+1

¿Cómo se puede compilar? El tipo de retorno de 'std :: ofstream :: open' es' void'. – Haoshu

0

ser un novato en C++, no sé si esto es una excepción-seguro, pero así es como normalmente lo hago:

std::ostream& output = (condition)?*(new std::ofstream(filename)):std::cout; 
Cuestiones relacionadas