【问题标题】:How does QDebug() << stuff; add a newline automatically?QDebug() << 东西;自动添加换行符?
【发布时间】:2011-01-11 21:14:45
【问题描述】:

我正在尝试实现我自己的qDebug() 风格的调试输出流,这基本上是我目前所拥有的:

struct debug
{
#if defined(DEBUG)
    template<typename T>
    std::ostream& operator<<(T const& a) const
    {
        std::cout << a;
        return std::cout;
    }
#else
    template<typename T>
    debug const& operator<<(T const&) const
    {
        return *this;
    }

    /* must handle manipulators (endl) separately:
     * manipulators are functions that take a stream& as argument and return a
     * stream&
     */
    debug const& operator<<(std::ostream& (*manip)(std::ostream&)) const
    {
        // do nothing with the manipulator
        return *this;
    }
#endif
};

典型用法:

debug() << "stuff" << "more stuff" << std::endl;

但我不想添加 std::endl;

我的问题基本上是,我如何判断operator&lt;&lt; 的返回类型何时不会被另一个operator&lt;&lt; 使用(因此追加endl)?

我能想到的唯一方法是创建一个与qDebug() 创建的每个临时对象关联的要打印的内容列表,然后打印所有内容以及尾随换行符(我可以在~debug() 中做一些聪明的事情,比如插入空格),但显然这并不理想,因为我不能保证临时对象会在作用域结束之前被销毁(或者我会这样做吗?)。

【问题讨论】:

  • qDebug() 只是创建临时对象。按照符号,你会发现这样的东西:QDebug qDebug() { return QDebug(QtDebugMsg); }.

标签: c++ qt stream


【解决方案1】:

流插入 (&lt;&lt;) 和提取 (&gt;&gt;) 应该是非成员。

我的问题基本上是,我该怎么做 告诉什么时候返回类型 operator

你不能。创建一个成员函数来专门附加它或在完成这些链接调用后附加一个endl。很好地记录您的课程,以便客户知道如何使用它。这是你最好的选择。

【讨论】:

  • 我将成为这个的主要用户;我正在尝试一遍又一遍地节省写 std::endl 。
  • endl 做了两件事——1)添加换行符——这是你无法控制的,因为字符串可以嵌入换行符;2)刷新输出流——你可以控制而不需要将它们放入 dtor 中。您的问题不是很清楚您要达到的目标。
【解决方案2】:

当你写这是典型的用法时:

debug() << "stuff" << "more stuff" << std::endl;

您是否确实打算在每次使用时都构造一个调试对象?如果是这样,您应该能够通过让调试析构函数添加换行符来获得所需的行为:

~debug()
{
    *this << std::endl;

    ... the rest of your destructor ...
}

这确实意味着你不能做这样的事情:

// this won't output "line1" and "line2" on separate lines
debug d;
d << "line1";
d << "line2";

【讨论】:

  • @last 部分:如果你用大括号包围它,它将起作用。当调试对象超出范围时,将调用其析构函数。
  • @jmucchiello:析构函数将在语句末尾被调用,这正是你想要插入换行符的时候。
  • @jmucchiello - 我所说的“行不通”是 line1 和 line2 不会像你写 debug() &lt;&lt; "line1"; debug() &lt;&lt; "line2()"; 时那样位于不同的行上
  • 是的,我肯定打算这样做,我可能会禁用复制/复制分配构造函数以在某种程度上强制执行它。
【解决方案3】:

这样就可以了:

struct debug {
    debug() {
    }

    ~debug() {
        std::cerr << m_SS.str() << std::endl;
    }

public:
    // accepts just about anything
    template<class T>
    debug &operator<<(const T &x) {
        m_SS << x;
        return *this;
    }
private:
    std::ostringstream m_SS;
};

应该让你做这样的事情:

debug() << "hello world";

我使用了这样的模式与锁相结合来提供类似流的日志系统,它可以保证日志条目是原子写入的。

注意:未经测试的代码,但应该可以工作:-)

【讨论】:

  • 你有一个错误:析构函数应该叫做“debug”。另外,为什么要使用中间 ostringstream?当它们到达您的运营商时,您不能将它们输出到 cerr
  • 我修复了析构函数名称(复制/粘贴错误)。但字符串流是有目的的。这样做是为了在调用析构函数之前不会写出输出(从而可以防止线程问题)。
【解决方案4】:

Qt 使用类似于@Evan 的方法。有关实现细节,请参阅a version of qdebug.h,但它们将所有内容流式传输到底层文本流,然后在销毁 qDebug() 返回的临时 QDebug 对象时刷新流和结束行。

【讨论】:

  • 很酷,谢谢,这就是我想知道的,并确认临时文件大部分时间都会被立即销毁:)
  • @Autopulated:我忘记了确切的措辞,但我相信标准要求一个未命名的对象在创建它的表达式之后立即被破坏。
  • 啊哈!找到它: 12.2/4 说“在两种情况下,临时对象在与完整表达式结尾不同的点被销毁。”这基本上意味着除了即将列出的两个异常之外,临时对象将在表达式结束时立即销毁。
猜你喜欢
  • 2016-03-25
  • 1970-01-01
  • 1970-01-01
  • 2014-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多