【问题标题】:How to override cout in C++?如何在 C++ 中覆盖 cout?
【发布时间】:2013-09-24 07:31:20
【问题描述】:

我有一个需求,我还需要使用printfcout 将数据显示到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(caps,上面代码的注释部分) 中完成了它。但是我想使用普通的std::cout,那么我该如何覆盖它。它应该适用于sting and variables 之类的

int i = 5;
cout << "Hello world" << i <<endl;

或者无论如何要捕获stdout数据,以便它们也可以轻松写入file and console

【问题讨论】:

  • @nijansen:确实非常邪恶。
  • @nijansen:废话,定义明确的机制:.rbduf()
  • @nijansen 您可以只替换 main 中的 cout 缓冲区。是的,这仍然不适用于在 main 之前初始化的每个对象,但也许这不是问题。
  • “错误”的评论还有效吗?它似乎与您当前的问题无关
  • printf 的代码是未定义的行为。不允许在标准库中重新定义函数。

标签: c++ windows visual-studio-2010


【解决方案1】:

如果你有另一个流缓冲区,你可以替换std::cout的:

std::cout.rdbuf(some_other_rdbuf);

http://en.cppreference.com/w/cpp/io/basic_ios/rdbuf

【讨论】:

  • 确实这么简单。在这种情况下,您只需要一个“tee”流缓冲区,它将其输出复制到两个底层流缓冲区。
【解决方案2】:

您可以交换底层缓冲区。这是通过 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 非常有用,因此我可以为现有的大型程序构建一个 ncurses 接口。谢谢!
【解决方案3】:

覆盖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 对象仍然可以在您的课程之外访问。此外,您的流不是ostream,因此不能将其传递给需要它的函数。最后,我认为您甚至不能在您的信息流中使用 std::endl
  • @MSalters 我只提供了一个最小的工作实现。当然可以从 ostream 继承,而您的第二点已经不复存在。我讨厌使用 std::endl,但复制这种行为也不难。不修改 std::cout 是我回答的重点..
  • 这里的问题是“怎么做”,而不是“我应该做”。在提供特定问题的答案后,您应该提供有用的(在某些情况下)建议
【解决方案4】:

我假设您有一些使用 std::coutprintf 的代码,您无法修改,否则解决问题的最简单方法是从 cout 写入不同的流并使用 fprintf 而不是或与printf结合使用。

通过采用这种方法,您可以定义一个新的流类,该流类实际上既可以写入标准输出,也可以写入给定文件,还可以定义一个函数,该函数结合了对printffprintf 的调用。

然而,更简单的方法是使用最初来自 UNIX 的 tee 程序,它将输入复制到输出和给定文件。有了它,您可以简单地以这种方式调用您的程序:

your_program | tee your_log_file

this question 的回答导致了一些可用于Windows 的替代实现。就我个人而言,我总是在我的 PC 上安装 cygwin 以提供 UNIX/Linux 实用程序。

【讨论】:

  • 你是对的。我混合了printfcouts 在所有这些之上我确实有第三方库g-test and g-mock 我无法将所有couts 重定向到我的用户定义的方式。那我应该怎么做呢?
  • @RasmiRanjanNayak:如果您的第三方库使用相同的 C++ 库,则它共享相同的 std::cout 对象。在这种情况下,使用 std::cout.rd_buf 的 not-rightfold 答案将起作用,因为它会更改单个 std::cout 对象。如果库使用printf,你可能会遇到更大的问题。
【解决方案5】:

如果我猜对了,您希望将输出的所有内容也记录到文件中。

你想要的是observer pattern

将代码中的所有直接登录替换为对新中继的调用。 日志中继将您的消息发送给观察者。 您的一位观察者将消息记录到屏幕上。 另一个记录到文件中。 尽可能避免让你的中继成为单例。

此建议仅在您可以编辑所有源文件时才有效。

【讨论】:

    【解决方案6】:

    std::cout 写入stdout 文件,您可以在 Linux 和 Windows 上执行以下操作

    #include <stdio.h>
    #include <iostream>
    
    
    
    int main()
    {
        freopen("test.txt", "w", stdout);
        std::cout << "Hello strange stdout\n";
    }
    

    将其改回使用取自here的以下内容

    #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");
    }
    

    注意:后者仅适用于窗口

    【讨论】:

      【解决方案7】:

      cout 通常实现为对象实例,因此您不能像重载/覆盖函数或类那样覆盖它。

      您最好不要与之抗争 - 是的,您可以构建 my_cout#define cout my_cout,但这会使您的代码变得迟钝。

      为了便于阅读,我将保留 cout 原样。这是一个标准,每个人都知道它能做什么,不能做什么。

      【讨论】:

      • -1; not-rightfold 的答案可能不是最详细的,但你就是这样做的。
      【解决方案8】:

      尝试使用宏 - 类似这样(您需要添加包含):

      #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()
      }
      

      【讨论】:

      • 正确的方法是使用输出运算符或函数(以防您需要使用流对象(不是专门为 cout)并设置自定义输出缓冲区以防您专门需要 cout )。这里没有理由使用宏。
      【解决方案9】:

      为此已经有一个 Boost 类:tee

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-06-11
        • 1970-01-01
        • 2019-08-11
        • 2015-02-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多