Как переопределить 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);

См. http://en.cppreference.com/w/cpp/io/basic_ios/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 как есть. Это стандарт, и каждый знает, что он может и не может сделать.

Для этого уже есть класс Boost: tee

Попробуйте использовать макрос - что-то вроде этого (вам нужно добавить включения):

#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()
}
Другие вопросы по тегам