Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-95988

qCDebug et al are not rvalues as they look like

    XMLWordPrintable

Details

    • Bug
    • Resolution: Won't Do
    • P3: Somewhat important
    • None
    • 5.15.2, 6.1.2
    • Core: I/O
    • None
    • All

    Description

      Summary

      qDebug() and qCDebug() look very similar, but they can't be used in the same way as an rvalue.

      Description

      First, let's talk about qDebug and friends (not the absence of "c" — logging without categories). They are simple macros exanding to a call to a constructor of QMessageLogger plus a relevant method's name — without parenthesis which are supposed to be added by user. If debugging is disabled through compile-time macro definitions, then qDebug "conveniently" expands to while (false) QMessageLogger().noDebug, and this is the point when we start having problems. It's not an rvalue anymore, however still can be chained on the right side.

      Now, let's switch to categories and their macros. qCDebug expands to something even more horrific:

      #  define qCDebug(category, ...) \
          for (bool qt_category_enabled = category().isDebugEnabled(); qt_category_enabled; qt_category_enabled = false) \
              QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC, category().categoryName()).debug(__VA_ARGS__)
      

      and there is a good reason for that, which naturally flows from a bad language design.

      Changing it to the usual do {} while (false) boilerplate is not an option, because we'd lose an ability to chain right-hand-side streaming operations at all.

      And if we use if statement, it will kinda works most of the time, until you write something like if (cond) qCDebug(lcMine) << "Yahaha!"; else doSomething(); — in which case a compiler will bring a curse down on you and all your family for not using disambiguating braces (which you should always use anyway). That's pretty evident and self-explanatory case of leaking abstractions / implementation details.

      Next, I wanted to suggest inline variadic functions, but seems like GCC couldn't implement them for the last 20 years. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=10980

      Steps to reproduce

      Compile this with and without

      #define QT_NO_DEBUG_OUTPUT
      
      #include <QDebug>
      #include <QLoggingCategory>
      
      int main(int argc, char *argv[])
      {
          (qDebug() << "Hello, world").nospace() << 42 << ".";
      }
      

      Expected result

      Should compile and run regardless of the presence of QT_NO_DEBUG_OUTPUT definition.

      Actual result

      Various compiler error messages about broken syntax and semantics.

      Suggested solution

      Use trenary operator to conditionally return real logger instance or a fake noDebug one. Compiler should be able to figure out the rest.

      Besides, this while (false) in QT_NO_QDEBUG_MACRO definition looks like a micro-optimization which should be done by compiler too, since noDebug implementation just drops all its content into void.

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            kkohne Kai Köhne
            ratijas ivan tkachenko
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes