Как переопределить Cout в C++?
У меня есть требование, мне нужно использовать printf
а также cout
отображать данные в console and file
также. За printf
Я сделал это, но для cout
Я борюсь, как это сделать?
#ifdef _MSC_VER
#define GWEN_FNULL "NUL"
#define va_copy(d,s) ((d) = (s))
#else
#define GWEN_FNULL "/dev/null"
#endif
#include <iostream>
#include <fstream>
using namespace std;
void printf (FILE * outfile, const char * format, ...)
{
va_list ap1, ap2;
int i = 5;
va_start(ap1, format);
va_copy(ap2, ap1);
vprintf(format, ap1);
vfprintf(outfile, format, ap2);
va_end(ap2);
va_end(ap1);
}
/* void COUT(const char* fmt, ...)
{
ofstream out("output-file.txt");
std::cout << "Cout to file";
out << "Cout to file";
}*/
int main (int argc, char *argv[]) {
FILE *outfile;
char *mode = "a+";
char outputFilename[] = "PRINT.log";
outfile = fopen(outputFilename, mode);
char bigfoot[] = "Hello
World!\n";
int howbad = 10;
printf(outfile, "\n--------\n");
//myout();
/* then i realized that i can't send the arguments to fn:PRINTs */
printf(outfile, "%s %i",bigfoot, howbad); /* error here! I can't send bigfoot and howbad*/
system("pause");
return 0;
}
Я сделал это в COUT
(заглавные буквы, закомментированная часть кода выше) . Но я хочу использовать нормальный std::cout
так как я могу переопределить это. И это должно работать для обоих sting and variables
лайк
int i = 5;
cout << "Hello world" << i <<endl;
Или есть в любом случае, чтобы захватить stdout
данные, так что они могут быть легко записаны в file and console
также.
9 ответов
Переопределение поведения std::cout
это действительно плохая идея, так как другим разработчикам будет трудно понять, что использование std::cout
не ведет себя как обычно.
Проясните свое намерение с помощью простого класса
#include <fstream>
#include <iostream>
class DualStream
{
std::ofstream file_stream;
bool valid_state;
public:
DualStream(const char* filename) // the ofstream needs a path
:
file_stream(filename), // open the file stream
valid_state(file_stream) // set the state of the DualStream according to the state of the ofstream
{
}
explicit operator bool() const
{
return valid_state;
}
template <typename T>
DualStream& operator<<(T&& t) // provide a generic operator<<
{
if ( !valid_state ) // if it previously was in a bad state, don't try anything
{
return *this;
}
if ( !(std::cout << t) ) // to console!
{
valid_state = false;
return *this;
}
if ( !(file_stream << t) ) // to file!
{
valid_state = false;
return *this;
}
return *this;
}
};
// let's test it:
int main()
{
DualStream ds("testfile");
if ( (ds << 1 << "\n" << 2 << "\n") )
{
std::cerr << "all went fine\n";
}
else
{
std::cerr << "bad bad stream\n";
}
}
Это обеспечивает чистый интерфейс и выводит то же самое и для консоли, и для файла. Вы можете добавить метод очистки или открыть файл в режиме добавления.
Если у вас есть другой буфер потока, вы можете просто заменить std::cout
"S:
std::cout.rdbuf(some_other_rdbuf);
Вы можете поменять местами основные буферы. Вот что сделано через RAII.
#include <streambuf>
class buffer_restore
{
std::ostream& os;
std::streambuf* buf;
public:
buffer_restore(std::ostream& os) : os(os), buf(os.rdbuf())
{ }
~buffer_restore()
{
os.rdbuf(buf);
}
};
int main()
{
buffer_restore b(std::cout);
std::ofstream file("file.txt");
std::cout.rdbuf(file.rdbuf());
// ...
}
Я полагаю, у вас есть код, использующий std::cout
а также printf
который вы не можете изменить, иначе самым простым способом решения вашей проблемы будет запись в другой поток из cout
и использовать fprintf
вместо или в сочетании с printf
,
Следуя этому подходу, вы можете определить как новый класс потока, который фактически записывает как в стандартный вывод, так и в заданный файл, а также функцию, которая объединяет вызовы обоих printf
а также fprintf
,
Однако гораздо более простой подход заключается в использовании tee
Программа, изначально из UNIX, которая копирует свои входные данные как для вывода, так и для данного файла. При этом вы можете просто назвать свою программу следующим образом:
your_program | tee your_log_file
Ответы на этот вопрос приводят к нескольким альтернативным реализациям, доступным для Windows. Лично я всегда устанавливаю cygwin на свои ПК, чтобы были доступны утилиты UNIX/Linux.
Если я правильно угадал, вы хотите записать все, что идет на вывод, также в файл.
То, что вы хотите, это шаблон наблюдателя.
Замените всю прямую регистрацию в вашем коде вызовами на новый ретранслятор. Реле регистрации отправляет ваши сообщения наблюдателям. Один из ваших наблюдателей записывает сообщение на экран. Другой записывает в файл. Старайтесь не делать свое реле одиночным, если это возможно.
Это предложение работает, только если вы можете редактировать все свои исходные файлы.
std::cout
пишет stdout
файл вы можете сделать следующее на Linux и Windows
#include <stdio.h>
#include <iostream>
int main()
{
freopen("test.txt", "w", stdout);
std::cout << "Hello strange stdout\n";
}
чтобы вернуть его обратно используйте следующее, взятое отсюда
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
FILE *stream ;
if((stream = freopen("file.txt", "w", stdout)) == NULL)
exit(-1);
printf("this is stdout output\n");
stream = freopen("CON", "w", stdout);
printf("And now back to the console once again\n");
}
Примечание: последний только для окон
cout
обычно реализуется как экземпляр объекта, поэтому вы не можете переопределить его так, как если бы вы перегрузили / переопределили функцию или класс.
Лучше всего не бороться с этим - да, вы могли бы построить my_cout
а также #define cout my_cout
но это сделало бы ваш код тупым.
Для удобства чтения я бы оставил cout
как есть. Это стандарт, и каждый знает, что он может и не может сделать.
Попробуйте использовать макрос - что-то вроде этого (вам нужно добавить включения):
#define MY_COUT(theos,printThis) { cout << printThis ; theos << printThis; }
void test()
{
ofstream myos;
myos.open("testfile", ios::trunc|ios::out);
int i = 7;
MY_COUT(myos, "try this numbers" << i << i + 1 << endl);
myos.close()
}