Поток std::cout
std::cerr
. Он похож на cout
, но выводит данные в стандартный канал ошибок, а не в стандартный канал для выходных данных.При журналировании некоторой информации могут возникать и более сложные требования. Предположим, нужно
Перенаправить выходные данные объектов потока можно. Далее мы рассмотрим, как это сделать очень простым и элегантным способом.
Как это делается
В этом примере мы реализуем вспомогательный класс, который решает задачу перенаправления потока и отмены такого перенаправления средствами конструкторов/деструкторов. А затем увидим, как это можно использовать.
1. В этот раз нужны только заголовочные файлы для потоков ввода/вывода и файлового потока. Кроме того, мы объявим об использовании пространства имен std
#include
#include
using namespace std;
2. Реализуем класс, содержащий объект файлового потока и указатель на буфер потока. cout
decltype
, чтобы узнать, какой тип возвращает конструкция cout.rdbuf()
. Данный прием подходит не для всех ситуаций, но в нашем случае мы получим тип указателя:class redirect_cout_region
{
using buftype = decltype(cout.rdbuf());
ofstream ofs;
buftype buf_backup;
3. Конструктор нашего класса принимает в качестве единственного параметра строку filename
ofs
. После этого можно передать его в cout
в качестве нового буфера потока. Та же функция, что принимает новый буфер, также возвращает указатель на старый, поэтому можно сохранить его, чтобы в будущем восстановить.public:
explicit
redirect_cout_region (const string &filename)
: ofs{filename},
buf_backup{cout.rdbuf(ofs.rdbuf())}
{}
4. Конструктор по умолчанию делает то же, что и предыдущий конструктор. Различие заключается вот в чем: он не открывает никаких файлов. Передача созданного по умолчанию буфера файлового потока в поток cout
cout
в некотором роде redirect_cout_region()
: ofs{},
buf_backup{cout.rdbuf(ofs.rdbuf())}
{}
5. Деструктор просто отменяет наше изменение. Когда объект этого класса выходит из области видимости, буфер потока cout
~redirect_cout_region() {
cout.rdbuf(buf_backup);
}
};
6. Создадим функцию,
void my_output_heavy_function()
{
cout << "some output\n";
cout << "this function does really heavy work\n";
cout << "...and lots of it...\n";
// ...
}
7. В функции main
int main()
{
cout << "Readable from normal stdout\n";
8. Теперь откроем еще одну область видимости, и первое, что мы в ней сделаем, — создадим экземпляр нового класса с параметром в виде текстового файла.
Файловые потоки открывают файлы в режиме чтения и записи по умолчанию, поэтому создадут для нас данный файл. Любые выходные данные будут перенаправлены в него, однако для вывода данных мы используем cout:
{
redirect_cout_region _ {"output.txt"};
cout << "Only visible in output.txt\n";
my_output_heavy_function();
}