[Qt-interest] QDebugStream quit working with Qt 4.8.0-rc1 and new compiler

K. Frank kfrank29.c at gmail.com
Sun Oct 30 23:09:47 CET 2011


Hi List!

I have been using QDebugStream, a utility class that I downloaded from
the list archive, and it is no longer working.

I've upgraded to Qt 4.8.0-rc1, and a new compiler, the std::thread-enabled,
64-bit g++ 4.7.0 mingw-w64 build provided by the famous Ruben.

Something broke QDebugStream along the way, and I can't figure out
what the problem is.

What do folks think the problem might be?

Does Qt 4.8.0-rc1 have a bug?

Is the new (explicitly experimental) std::thread-enabled 4.7.0 compiler
buggy?

Is there some bug (for example, some kind of undefined behavior) in
QDebugStream that happens to work in the old environment, but not
in the new?

Any ideas would be welcome!

The point of QDebugStream is to redirect output sent to a std::ostream
to a QTextEdit (e.g., a log window).

In my simple test program, below, when working (that is, for example,
when built with Qt 4.6.1 and a 32-bit tdm mingw g++ 4.4.1 compiler),
both messages show up in the QTextEdit:

   Message 1 (with '\n')...
   Message 2 (with '\n')...

But when built with 4.8.0-rc1 / 4.7.0, only the first message shows up:

   Message 1 (with '\n')...

(Some further variations on this theme are given in the lines commented
out, and should be relatively self-explanatory.)

Before I give more details and the code, I have some comments:

I have been using QDebugStream for a while now in a couple of different
Qt / compiler environments, and it has seemed to work fine.  The broken
version (4.8.0-rc1 / 4.7.0) behaves as if only the first insertion operator
for the redirected stream gets processed (and -- per QDebugStream's
design -- only if the inserted text contains a '\n' does anything get displayed
in the QTextEdit).

When I try to debug QDebugStream (with the version of gdb that came
bundled with the mingw-w32 4.7.0 compiler build), it appears that
QDebugStream::xsputn() only gets called for the first insertion operator.
But I'm somewhat confused by what the debugger shows:  Stepping
through QDebugStream::xsputn() (using next) doesn't follow the flow
of control that I would expect, and based on both stepping through
the code and on breakpoints that don't fire, QDebugStream::xsputn()
doesn't appear to exit through its return statement!

I am not an expert on iostream and std::basic_streambuf, but the
QDebugStream code looks correct to me, at least as far as I can tell.
Also, I have seen various other comments on the web from people
who have used QDebugStream (with or without modification), so it
does seem to work for people other than me.

Lastly, a real possibility is that the new compiler and/or its iostream
library is broken.  I did, however, write a test program that doesn't touch
Qt, but uses the technique of QDebugStream to redirect std::cout to
an ofstream, and that worked fine.  (However, I just did a simple
"g++ -g" build of my non-Qt test program, so maybe one of the compiler
switches set up by qmake is triggering a bug in wither the compiler or
iostream library or Qt or QDebugStream.)

Anyway, here are the details:

The qmake / mingw32-make-generated compile and linking commands
that build the broken version are:

   C:>qmake qdebugstream_test.pro

   C:>mingw32-make
   mingw32-make -f Makefile.Debug
   mingw32-make[1]: Entering directory `C:'
   g++ -c -g -frtti -fexceptions -mthreads -Wall -DUNICODE
-DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_GUI_LIB -DQT_CORE_LIB
-DQT_HAVE_MMX -DQT_HAVE_3DNOW -DQT_HAVE_SSE -DQT_HAVE_MMXEXT
-DQT_HAVE_SSE2 -DQT_THREAD_SUPPORT -DQT_NEEDS_QMAIN
-I"c:\4.8.0-rc1\include\QtCore" -I"c:\4.8.0-rc1\include\QtGui"
-I"c:\4.8.0-rc1\include" -I"c:\4.8.0-rc1\include\ActiveQt" -I"debug"
-I"c:\4.8.0-rc1\mkspecs\default" -o debug\qdebugstream_test.o
qdebugstream_test.cpp
   g++ -mthreads -Wl,-subsystem,windows -o debug\qdebugstream_test.exe
debug/qdebugstream_test.o  -L"c\4.8.0-rc1\lib" -lmingw32 -lqtmaind
-lQtGuid4 -lQtCored4
   mingw32-make[1]: Leaving directory `C:'

   C:>


Here is my simple test program:

====================

// qdebugstream_test.cpp

// test / debug qdebugstream...
// it is no longer working with g++ 4.7.0 and qt 4.8.0-rc1

#include <iostream>

#include <QApplication>
#include <QtGui>

#include "qdebugstream.h"

int main (int argc, char *argv[]) {
  QApplication app (argc, argv);
  QTextEdit *text1 = new QTextEdit();

  QDebugStream *qout = new QDebugStream (std::cout, text1);

  if (qout) {  // to avoid unused variable warning
    // prints neither message to text1
    // std::cout << "Message 1 (with endl)..." << std::endl;
    // std::cout << "Message 2 (with endl)..." << std::endl;

    // prints only "Message 1" to text1
    std::cout << "Message 1 (with '\\n')...\n";
    std::cout << "Message 2 (with '\\n')...\n";

    // prints both messages to text1
    // std::cout << "Message 1 (with two '\\n's)...\nMessage 2 (with
two '\\n's)...\n";
  }

  text1->show();
  return app.exec();
}

====================

The associated .pro file is trivial:

====================

# qdebugstream_test.pro

HEADERS     =
SOURCES     = qdebugstream_test.cpp
FORMS       =

====================

I build the program by running:

   qmake
   mingw32-make

Lastly, QDebugStream is defined and implemented in a single header file:

====================

// qdebugstream.h          20.7.10
// version 1.0             20.7.10

// from Jochen Ulrich 6.6.2005 posting to qt-interest at trolltech.com
// "Redirecting std::cout to QTextEdit / Using QTextEdit as a Log Window"
// http://lists.trolltech.com/qt-interest/2005-06/thread00166-0.html

// example usage from same posting:

//    #include "qdebugstream.h"
//    #include "qtextedit.h"
//
//    void main( )
//    {
//       [...]
//       QTexEdit* myTextEdit = new QTextEdit(this, "myTextEdit");
//       myTextEdit->setTextFormat(Qt::LogText);
//
//       QDebugStream qout(std::cout, myTextEdit);
//       std::cout << "Send this to the Text Edit!" << endl;
//
//       [...]
//    }


//################
//# qdebugstream.h  #
//################

#ifndef Q_DEBUG_STREAM_H
#define Q_DEBUG_STREAM_H

#include <iostream>
#include <streambuf>
#include <string>

#include "qtextedit.h"

class QDebugStream : public std::basic_streambuf<char>
{
public:
 QDebugStream(std::ostream &stream, QTextEdit* text_edit) : m_stream(stream)
 {
  log_window = text_edit;
  m_old_buf = stream.rdbuf();
  stream.rdbuf(this);
 }
 ~QDebugStream()
 {
  // output anything that is left
  if (!m_string.empty())
   log_window->append(m_string.c_str());

  m_stream.rdbuf(m_old_buf);
 }

protected:
 virtual int_type overflow(int_type v)
 {
  if (v == '\n')
  {
   log_window->append(m_string.c_str());
   m_string.erase(m_string.begin(), m_string.end());
  }
  else
   m_string += v;

  return v;
 }

 virtual std::streamsize xsputn(const char *p, std::streamsize n)
 {
  m_string.append(p, p + n);

  // int pos = 0;
  unsigned pos = 0;  // avoid conversion warnings
  while (pos != std::string::npos)
  {
   pos = m_string.find('\n');
   if (pos != std::string::npos)
   {
    std::string tmp(m_string.begin(), m_string.begin() + pos);
    log_window->append(tmp.c_str());
    m_string.erase(m_string.begin(), m_string.begin() + pos + 1);
   }
  }

  return n;
 }

private:
 std::ostream &m_stream;
 std::streambuf *m_old_buf;
 std::string m_string;
 QTextEdit* log_window;
};

#endif

====================



More information about the Qt-interest-old mailing list