Получите std:: ostream либо из std:: cout, либо из std:: ofstream (file)
как мне привязать std::ostream
к std::cout
или к объекту std::ofstream
, в зависимости от определенного условия программы? Хотя это недействительно по многим причинам, я хотел бы достичь того, что семантически эквивалентно следующему:
std::ostream out = condition ? &std::cout : std::ofstream(filename);
Я видел несколько примеров, которые не являются безопасными для исключений, например, один из 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;
}
Кто-нибудь знает лучшее безопасное решение?
Ответы
Ответ 1
std::streambuf * buf;
std::ofstream of;
if(!condition) {
of.open("file.txt");
buf = of.rdbuf();
} else {
buf = std::cout.rdbuf();
}
std::ostream out(buf);
Это связывает базовый streambuf либо cout, либо поток выходных файлов. После этого вы можете написать "вне", и он окажется в нужном месте. Если вы просто хотите, чтобы все, что происходит в std::cout
, попало в файл, вы также можете сделать
std::ofstream file("file.txt");
std::streambuf * old = std::cout.rdbuf(file.rdbuf());
// do here output to std::cout
std::cout.rdbuf(old); // restore
Этот второй метод имеет тот недостаток, что он не является безопасным. Возможно, вы захотите написать класс, который делает это с помощью 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;
}
Теперь, что бы ни случилось, std:: cout находится в чистом состоянии.
Ответ 2
Это безопасно для исключений:
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);
}
Изменить: Herb Sutter обратился к этому в статье "Переключение потоков" (Guru of the Week).
Ответ 3
std::ofstream of;
std::ostream& out = condition ? std::cout : of.open(filename);
Ответ 4
Будучи новичком на С++, я не знаю, безопасно ли это, но вот как я обычно это делаю:
std::ostream& output = (condition)?*(new std::ofstream(filename)):std::cout;
Ответ 5
Следующий простой код работает для меня:
int main(int argc, char const *argv[]){
std::ofstream outF;
if (argc > 1)
{
outF = std::ofstream(argv[1], std::ofstream::out);
}
std::ostream& os = (argc > 1)? outF : std::cout;
}