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

K. Frank kfrank29.c at gmail.com
Sat Nov 5 00:18:06 CET 2011


Hello Lists!

On Sun, Oct 30, 2011 at 6:09 PM, K. Frank <kfrank29.c at gmail.com> wrote:
> Hi List!
>
> I have been using QDebugStream, a utility class that I downloaded from
> the list archive, and it is no longer working.

I believe that I have a fix for the issue:

In short, in qdebugstream.h, the declaration of "pos" should be:

   size_t pos;

rather than

   unsigned pos;

(As I understand it, under 32-bit g++, "size_t" is essentially "unsigned,"
while under 64-bit g++, "size_t" is "unsigned long," a wider integral type,
explaining why the error shows up when moving to the 64-bit compiler.)

> 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?

The error seems to be entirely independent of Qt.  I can duplicate the
problem in a test program that does not use Qt at all.

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

(I believe that the original ("unsigned pos;") code is incorrect.  However,
I do believe that there is a bug in how the 64-bit 4.7.0 compiler handles
the incorrect code.  But that's another story...)

> 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?

As indicated above, there is a bug in the original qdebustrean.h code.
(I do not believe that this bug is undefined behavior.  But that's another
story...)

For reference, I post the corrected qdebugstream.h below:

Also, I have quoted the entire original post so that the full context
shows up on the new list.

Thanks for everyone's suggestions.


K. Frank


The corrected qdebugstream.h:

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

// qdebugstream.h          20.7.10
// version 1.1             4.11.11

// version 1.1: patch ("size_t pos") to fix 64-bit error

// 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
  size_t pos = 0;  // patch to avoid 64-bit conversion error
  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

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


>
> 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 Interest mailing list