【问题标题】:Standard no-op output stream标准无操作输出流
【发布时间】:2012-08-03 07:43:53
【问题描述】:

有没有办法创建一个基本上什么都不做的 ostream 实例?

例如:

std::ostream dummyStream(...);
dummyStream << "Nothing will be printed";

我可以只创建一个 ostringstream,但数据将被缓冲(我真的不想用它们做任何事情,所以它增加了无用的开销)。

有什么想法吗?

[编辑] 找到适合我需要的 related question。但是,我认为有一个答案说明如何使用标准 c++ 创建 valid(无坏位)输出流可能会很有用。

【问题讨论】:

  • 我被指向this solution
  • Boost.Iostreams 是一个选项吗?

标签: c++ iostream


【解决方案1】:

您需要一个自定义的流缓冲区。

class NullBuffer : public std::streambuf
{
public:
  int overflow(int c) { return c; }
};

然后您可以在任何 ostream 类中使用此缓冲区

NullBuffer null_buffer;
std::ostream null_stream(&null_buffer);
null_stream << "Nothing will be printed";

streambuf::overflow 是当缓冲区必须将数据输出到流的实际目的地时调用的函数。上面的NullBuffer 类在调用溢出时什么都不做,所以任何使用它的流都不会产生任何输出。

【讨论】:

  • 可以创建一个便利类class NullStream : public std::ostream { public: NullStream() : std::ostream(&amp;m_sb) {} private: NullBuffer m_sb; };,将使用简化为NullStream null_stream; null_stream &lt;&lt; ...
  • 这很棒,我建议添加@Sjoerd 的建议。我实现了一些与他实际上相同的东西,直到我回来投票时才看到他的评论。
  • 只是一点点:该函数可能会导致失败,从而将流变为失败状态(尽管大多数人不会在意)。为避免这种情况,您希望在假定的不太可能的路径上返回 not_eof(). Also, buffering characters is way more effective than calling a virtual` 函数的结果,即,我还建议添加设置一个被忽略的缓冲区。覆盖将变为int overflow(int c) { return this-&gt;setp(std::begin(d_buffer), std::end(this-&gt;d_buffer); std::char_traits&lt;char&gt;::not_eof(c); }。同样,覆盖xsputn() 而不做任何事情可能是合理的。
  • @DietmarKühl:您介意将其编辑到答案中,还是自己编写?
【解决方案2】:

如果这是为了禁用日志输出,您的dummyStream 仍会导致对参数进行评估。如果您想在禁用日志记录时尽量减少影响,您可以依赖条件,例如:

#define debugStream \
    if (debug_disabled) {} \
    else std::cerr

所以如果你有这样的代码:

debugStream << "debugging output: " << foo() << std::endl;

如果debug_disabled 为真,则不会评估任何参数。

【讨论】:

  • 我很抱歉取消了这个问题,但我真的需要知道这一点:这个答案不是比选择的答案性能更好吗?如果 debug_disabled 是一个常量(或者更合适的是一个宏),编译器可能(会?)优化 else 子句,而使用空缓冲区仍然会导致流输入被处理,只是被放入空设备中。真的吗?或不?如果有人能为我阐明这一点,那就太棒了。
  • @bobismijnnaam:事实上,有人在我发布它的当天晚些时候提出的问题中扯掉了这个答案:-)。 Link.
  • 嗯,我还是同意了你的回答。整个 NullStream 的事情看起来工作量太大了。
  • 这是一个很好的解决方案,但是否可以在不必包含 iostream 或定义一次性全局变量的情况下做类似的事情?
  • @Paul:问题是关于使用ostream,我只是选择了一个已经可用的。要禁用日志记录,日志行必须落入else 一侧。因此,如果目标是始终禁用,只需使用 true 而不是变量。
【解决方案3】:

新流类的基本方法是:

  1. std::streambuf派生一个类;
  2. 覆盖该类中的虚函数。这是真正的工作完成的地方。在您的情况下,空实现应该就足够了。
  3. std::ostream 派生一个类,其中一个成员是您的 streambuf 类。
  4. 您的流类的构造函数应该将指向该成员的指针转发给 std::ostream 的基本构造函数。

不过,恐怕你不会摆脱格式化步骤。

希望这能给你一些指导;抱歉,我没有时间将其扩展为完整的答案。

更新:详见john's answer

【讨论】:

    【解决方案4】:

    对于日志消息的运行时可控重定向,结合了 john 和 Sjoerd 思想的独立解决方案:

    class DebugStream {
    private:
        class NullStream : public std::ostream {
        private:
            class NullBuffer : public std::streambuf {
            public:
                int overflow(int c) override { return c; }
            } buffer_;
        public:
            NullStream() : std::ostream(&buffer_) {}
        } null_;
    
        std::ostream &output_;
        bool enabled_;
    
    public:
        DebugStream(std::ostream &output = std::cout) : output_(output), enabled_(false) {}
        void enable(const bool enable) { enabled_ = enable; }
    
        template <typename T> std::ostream& operator<<(const T &arg) {
            if (enabled_) return output_ << arg;
            else return null_ << arg;
        }
    };
    
    extern DebugStream debug_stream;
    #define TRACE_ENABLE(x) debug_stream.enable(x)
    #define TRACELN(x) debug_stream << x << std::endl
    #define TRACE(x) debug_stream << x
    

    然后您可以执行以下操作:

    TRACELN("The value of x is " << x " and the value of y is " << y);
    

    使用#define 将跟踪宏完全从发布版本中删除到空语句也很容易。

    不过,您仍然需要在全球某处定义 debug_stream

    【讨论】:

      【解决方案5】:

      如果您担心调试器的开销,那么您可以编写一个非常简单的代码来消除编译时的调试消息。这是我用于我的 c++ 程序的。

      #include <iostream>
      #define DEBUGGING // Define this in your config.h or not.
      #ifdef DEBUGGING
      /*
       * replace std::cout with your stream , you don't need to
       * worry about the context since macros are simply search
       * and replace on compilation.
       */
      #define LOG_START std::cout <<
      #define LOG_REDIR <<
      #define LOG_END   << std::endl;
      #else
      #define LOG_START if(0){(void)
      #define LOG_REDIR ;(void)
      #define LOG_END   ;}
      #endif // DEBUGGING
      
      int main(){
      LOG_START "This is a log message " LOG_REDIR "Still a log message." LOG_END;
      return 0;
      }
      

      现在在制作项目时,检查用户是否要禁用日志记录,如果是,只需取消定义 DEBUGGING 宏或您选择检查的任何宏。

      现在您的代码将由编译器进行优化,因为当任何内容无效时,它都不会包含在生成的二进制文件中(大部分时间),从而使二进制文件准备就绪。

      【讨论】:

      • 编译器不会优化函数调用。您需要将 LOG_START 定义为 if(0){(void),并将 LOG_END 定义为 ;}。即使禁用了优化,这也会被优化 - 至少 gcc 在使用 -O0 编译时可以这样做。
      • @DanielFrużyński 感谢您的提示。我已经进行了更改。
      【解决方案6】:

      我需要一个 ostream 类型的空流,所以我做了这样的事情:

      struct NullStream: public stringstream {
         NullStream(): stringstream() {}
      };
      
      template<typename T>
      void operator<<(const NullStream&, const T&) {}
      

      应用代码:

      NullStream ns;
      ostream &os = ns;
      os << "foo";
      

      真正的问题是我继承但不关心的所有公共方法,所以我只是懒得重写它们。

      【讨论】:

        猜你喜欢
        • 2021-11-18
        • 1970-01-01
        • 1970-01-01
        • 2018-07-11
        • 2011-07-17
        • 1970-01-01
        • 1970-01-01
        • 2012-08-22
        • 1970-01-01
        相关资源
        最近更新 更多