Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Special Interest Groups
  3. C++ Gurus
  4. why qt6 add more compiled-time check and caused some unexpected errors

why qt6 add more compiled-time check and caused some unexpected errors

Scheduled Pinned Locked Moved Solved C++ Gurus
mocqt6.2template
12 Posts 5 Posters 2.0k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • C Offline
    C Offline
    ComixHe
    wrote on last edited by
    #3

    @jsulm no, i want to implement a wrapper template class from some custom type

    example:

    struct NewConnection{
        quint64 connId;
        QString ConnInfo;
    };
    
    template<typename T> class A{
    public:
        ...
        // some functions
    
        template <typename E>
        friend bool operator==(const A &_x, const A<E> &_e)
        {
            return bool(_x.value()  == _e.value());
        }
    
       // some functions
         ...
    }; 
    
    class B : public QObject{
    Q_OBJECT:
    public:
         A<NewConnection> func() {};
    }
    

    Then moc will generate some template code for compile-time checking

    qt_incomplete_metaTypeArray<qt_meta_stringdata_xxx__xxxx__classxxx_t
    , QtPrivate::TypeAndForceComplete<A<NewConnection>, std::true_type>,...>
    
    1 Reply Last reply
    0
    • Christian EhrlicherC Online
      Christian EhrlicherC Online
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on last edited by
      #4

      Your (slightly modified code) compile fine for me:

      #include <QtCore>
      
      struct NewConnection {
          quint64 connId;
          QString ConnInfo;
      };
      
      template<typename T> class A {
      public:
          template <typename E>
          friend bool operator==(const A& _x, const A<E>& _e)
          {
              return bool(_x.value() == _e.value());
          }
      };
      
      class B : public QObject {
          Q_OBJECT
      public:
          A<NewConnection> func() {};
      };
      
      int main(int argc, char** argv)
      {
          QCoreApplication app(argc, argv);
          B b;
          return 0;
      }
      
      #include "main.moc"
      

      So please provide a minimal, compilable example to reproduce the error.

      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
      Visit the Qt Academy at https://academy.qt.io/catalog

      C 1 Reply Last reply
      0
      • Christian EhrlicherC Christian Ehrlicher

        Your (slightly modified code) compile fine for me:

        #include <QtCore>
        
        struct NewConnection {
            quint64 connId;
            QString ConnInfo;
        };
        
        template<typename T> class A {
        public:
            template <typename E>
            friend bool operator==(const A& _x, const A<E>& _e)
            {
                return bool(_x.value() == _e.value());
            }
        };
        
        class B : public QObject {
            Q_OBJECT
        public:
            A<NewConnection> func() {};
        };
        
        int main(int argc, char** argv)
        {
            QCoreApplication app(argc, argv);
            B b;
            return 0;
        }
        
        #include "main.moc"
        

        So please provide a minimal, compilable example to reproduce the error.

        C Offline
        C Offline
        ComixHe
        wrote on last edited by
        #5

        @Christian-Ehrlicher
        code:

        #include <QObject>
        #include <QCoreApplication>
        struct NewConnection{
            quint64 connId;
            double connTmp;
        };
        
        template <typename T> class A{
        public:
            A() = default;
        
            template <typename E>
            friend bool operator==(const A& _x, const A<E>& _e)
            {
                return bool(_x.value() == _e.value());
            }
        
        private:
                T value;
        };
        
        class Test:public QObject{
            Q_OBJECT
        public:
            explicit Test(QObject *parent = nullptr);
        
        public slots:
            A<NewConnection> func() {return {};};
        };
        
        int main(int argc, char *argv[])
        {
            QCoreApplication a(argc, argv);
            Test t;
            auto ret = t.func();
            return a.exec();
        }
        
        #include "main.moc"
        

        gcc version:12.2.0;
        Qt version: 6.4.1

        compiler outputs:

        [3/4] Building CXX object CMakeFiles/untitled.dir/main.cpp.o
        FAILED: CMakeFiles/untitled.dir/main.cpp.o 
        /usr/bin/c++ -DQT_CORE_LIB -DQT_NO_DEBUG -I/home/comix/Qt/untitled/build/untitled_autogen/include -isystem /usr/include/qt6/QtCore -isystem /usr/include/qt6 -isystem /usr/lib/qt6/mkspecs/linux-g++ -O3 -DNDEBUG -fPIC -std=gnu++17 -MD -MT CMakeFiles/untitled.dir/main.cpp.o -MF CMakeFiles/untitled.dir/main.cpp.o.d -o CMakeFiles/untitled.dir/main.cpp.o -c /home/comix/Qt/untitled/main.cpp
        /home/comix/Qt/untitled/main.cpp: In instantiation of ‘bool operator==(const A<T>&, const A<E>&) [with E = NewConnection; T = NewConnection]’:
        /usr/include/qt6/QtCore/qmetatype.h:2224:46:   required from ‘static bool QtPrivate::QEqualityOperatorForType<T, <anonymous> >::equals(const QtPrivate::QMetaTypeInterface*, const void*, const void*) [with T = A<NewConnection>; bool <anonymous> = true]’
        /usr/include/qt6/QtCore/qmetatype.h:2359:54:   required from ‘constexpr const QtPrivate::QMetaTypeInterface QtPrivate::QMetaTypeInterfaceWrapper<A<NewConnection> >::metaType’
        /usr/include/qt6/QtCore/qmetatype.h:2486:16:   required from ‘constexpr const QtPrivate::QMetaTypeInterface* QtPrivate::qTryMetaTypeInterfaceForType() [with Unique = {anonymous}::qt_meta_stringdata_Test_t; TypeCompletePair = TypeAndForceComplete<A<NewConnection>, std::integral_constant<bool, false> >]’
        /usr/include/qt6/QtCore/qmetatype.h:2537:55:   required from ‘constexpr const QtPrivate::QMetaTypeInterface* const qt_incomplete_metaTypeArray [2]<{anonymous}::qt_meta_stringdata_Test_t, QtPrivate::TypeAndForceComplete<Test, std::integral_constant<bool, true> >, QtPrivate::TypeAndForceComplete<A<NewConnection>, std::integral_constant<bool, false> > >’
        /home/comix/Qt/untitled/build/untitled_autogen/include/main.moc:79:5:   required from here
        /home/comix/Qt/untitled/main.cpp:15:29: error: no match for call to ‘(const NewConnection) ()’
           15 |         return bool(_x.value() == _e.value());
              |                     ~~~~~~~~^~
        /home/comix/Qt/untitled/main.cpp:15:43: error: no match for call to ‘(const NewConnection) ()’
           15 |         return bool(_x.value() == _e.value());
              |                                   ~~~~~~~~^~
        ninja: build stopped: subcommand failed.
        
        

        moc generates code:

        Q_CONSTINIT const QMetaObject Test::staticMetaObject = { {
            QMetaObject::SuperData::link<QObject::staticMetaObject>(),
            qt_meta_stringdata_Test.offsetsAndSizes,
            qt_meta_data_Test,
            qt_static_metacall,
            nullptr,
            qt_incomplete_metaTypeArray<qt_meta_stringdata_Test_t,
                // Q_OBJECT / Q_GADGET
                QtPrivate::TypeAndForceComplete<Test, std::true_type>,
                // method 'func'
                QtPrivate::TypeAndForceComplete<A<NewConnection>, std::false_type>
            >,
            nullptr
        } };
        
        1 Reply Last reply
        0
        • JoeCFDJ Offline
          JoeCFDJ Offline
          JoeCFD
          wrote on last edited by
          #6

          any reason why you return something in a slot func?

          1 Reply Last reply
          1
          • Christian EhrlicherC Online
            Christian EhrlicherC Online
            Christian Ehrlicher
            Lifetime Qt Champion
            wrote on last edited by Christian Ehrlicher
            #7

            As @JoeCFD said - returning something in a slot is not something a slot should do.
            Maybe you can ask at the mailing list because there are the ones who know much more about templates and the moc stuff than here but I would guess you will get the same answer - don't return something in a slot, esp. not a template.

            Works fine for me with msvc when the compare operator is corrected - value is not a function.

            Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
            Visit the Qt Academy at https://academy.qt.io/catalog

            1 Reply Last reply
            0
            • C ComixHe

              If a template class A

              template<typename T> class A;
              

              it has a friend template operator ==

              template <typename E> friend bool operator ==(const A&,const A<E>&){…}
              

              But in my project I haven't called this function in the code, So this code will not be generated by the compiler in the end. Even though underlying type T or E isn’t overloading operator ==, this code should pass compiler check.
              But Qt6 will generate some code by moc to check these types have some function or not, It seems that moc can't determine whether the code is finally generated by the compiler.

              Chris KawaC Offline
              Chris KawaC Offline
              Chris Kawa
              Lifetime Qt Champion
              wrote on last edited by Chris Kawa
              #8

              @ComixHe said:

              But in my project I haven't called this function in the code, So this code will not be generated by the compiler in the end

              Your code doesn't, but Qt will. When you mark a class as Q_OBJECT Qt's moc will generate code that builds a QMetaObject for that class, which is a way to have runtime reflection of a type. The Test::func is indexed in the meta object as a QMetaMethod of type QMetaMethod::Slot. Part of that indexing is also recognizing function parameters and return type as QMetaType. See QMetaMethod::returnMetaType(). Moving further the type is categorized and all relevant traits of it are checked. One of those traits is if the type is comparable i.e. if it has a comparison operator. See QMetaType::isEqualityComparable().

              So long story short, as part of that meta class generation an address of the comparison operator of a return type of a method of a Q_OBJECT class is instantiated, which is

              bool A<NewConnection>::operator==<NewConnection>(const A<NewConnection>& _x, const A<NewConnection>& _e)
              

              So both _x and _e are of type A<NewConnection>, which does not have a value() method, so compilation fails.

              Couple ways to fix this:

              • like @Christian-Ehrlicher said, value is a member variable and not a function. If it's just a mistake then remove () from the comparison and it will compile.
              • Don't unconditionally assume arguments have value() method. You can check it with enable_if, or, if you're on C++20, with a concept.
              • If A<NewConnection> should be comparable create a template specialization for the operator == that takes A<NewConnection> as _e parameter and implement comparison using only the contents of that class.
              C 1 Reply Last reply
              3
              • Chris KawaC Chris Kawa

                @ComixHe said:

                But in my project I haven't called this function in the code, So this code will not be generated by the compiler in the end

                Your code doesn't, but Qt will. When you mark a class as Q_OBJECT Qt's moc will generate code that builds a QMetaObject for that class, which is a way to have runtime reflection of a type. The Test::func is indexed in the meta object as a QMetaMethod of type QMetaMethod::Slot. Part of that indexing is also recognizing function parameters and return type as QMetaType. See QMetaMethod::returnMetaType(). Moving further the type is categorized and all relevant traits of it are checked. One of those traits is if the type is comparable i.e. if it has a comparison operator. See QMetaType::isEqualityComparable().

                So long story short, as part of that meta class generation an address of the comparison operator of a return type of a method of a Q_OBJECT class is instantiated, which is

                bool A<NewConnection>::operator==<NewConnection>(const A<NewConnection>& _x, const A<NewConnection>& _e)
                

                So both _x and _e are of type A<NewConnection>, which does not have a value() method, so compilation fails.

                Couple ways to fix this:

                • like @Christian-Ehrlicher said, value is a member variable and not a function. If it's just a mistake then remove () from the comparison and it will compile.
                • Don't unconditionally assume arguments have value() method. You can check it with enable_if, or, if you're on C++20, with a concept.
                • If A<NewConnection> should be comparable create a template specialization for the operator == that takes A<NewConnection> as _e parameter and implement comparison using only the contents of that class.
                C Offline
                C Offline
                ComixHe
                wrote on last edited by
                #9

                @Chris-Kawa
                In fact, I know the reason for the moc error, and I also know how to fix this problem.
                I'm sorry I forgot before that there should be a value method in this template class

                template <typename T>
                class A {
                 public:
                  A() = default;
                
                  template <typename E>
                  friend bool operator==(const A& _x, const A<E>& _e) {
                    return bool(_x.value() == _e.value());
                  }
                
                  const T& value() const { return m_value; }
                
                 private:
                  T m_value;
                };
                

                and now

                /home/heyuming/workspace/demo/demo.cpp:16:28: error:no match for ‘operator==’ (operand types are ‘const NewConnection’ and ‘const NewConnection’)
                   16 |     return bool(_x.value() == _e.value());
                

                For some reason, I can only use C++17 and such functions must be placed under slots. I'm just curious why qt6 added type checking for slot functions and qt5 didn't.

                I don't think moc should generate some check code for template functions that are not generated at the end. Is this because moc is not a compiler and therefore cannot make a determination?

                I am a newbie who just learned Qt, I don't understand much about moc, maybe there are some misunderstandings in it...

                Thanks~

                Chris KawaC 1 Reply Last reply
                0
                • C ComixHe

                  @Chris-Kawa
                  In fact, I know the reason for the moc error, and I also know how to fix this problem.
                  I'm sorry I forgot before that there should be a value method in this template class

                  template <typename T>
                  class A {
                   public:
                    A() = default;
                  
                    template <typename E>
                    friend bool operator==(const A& _x, const A<E>& _e) {
                      return bool(_x.value() == _e.value());
                    }
                  
                    const T& value() const { return m_value; }
                  
                   private:
                    T m_value;
                  };
                  

                  and now

                  /home/heyuming/workspace/demo/demo.cpp:16:28: error:no match for ‘operator==’ (operand types are ‘const NewConnection’ and ‘const NewConnection’)
                     16 |     return bool(_x.value() == _e.value());
                  

                  For some reason, I can only use C++17 and such functions must be placed under slots. I'm just curious why qt6 added type checking for slot functions and qt5 didn't.

                  I don't think moc should generate some check code for template functions that are not generated at the end. Is this because moc is not a compiler and therefore cannot make a determination?

                  I am a newbie who just learned Qt, I don't understand much about moc, maybe there are some misunderstandings in it...

                  Thanks~

                  Chris KawaC Offline
                  Chris KawaC Offline
                  Chris Kawa
                  Lifetime Qt Champion
                  wrote on last edited by Chris Kawa
                  #10

                  @ComixHe said:

                  I don't think moc should generate some check code

                  It's not a check code. It generates code that builds a meta object, which is a runtime reflection system. See here. It needs to do that, otherwise the runtime introspection wouldn't work, and that's one of main features of QObjects.

                  for template functions that are not generated at the end

                  They are, just not by your code. Meta system stores function pointers to these methods and thus instantiates them. It's similar to standard type traits and rtti mechanisms combined, just richer.

                  Is this because moc is not a compiler and therefore cannot make a determination?

                  Moc is just a boilerplate code generator. It doesn't do much logic on its own, just sees macros like Q_OBJECT and generates some method implementations for that class. There's no magic here, it's all standard C++. It saves it to a file in your build dir, so you can look at the code it generates if you want (although it can be a bit hard to read due to its complexity).

                  I'm just curious why qt6 added type checking for slot functions and qt5 didn't

                  Because the functionality of meta system was expanded in Qt6. One of the addition was possibility to compare meta types. See QMetaType::equals.

                  There's nothing wrong with Qt here. It's just that your code was ambiguous and has paths that lead to compilation errors. Nothing in Qt5 relied upon it being specific so it worked. Qt6 asks more specific questions and it exposed a bug. Simply be more specific. Define the operator correctly or delete/enable_if it out, so that Qt can't find it if it's not supposed to. Think of it like this - if Qt can cause an instantiation of your template fail to compile then anyone can. It's a bug you need to fix. Qt6 is just the messenger here, so don't shoot it.

                  C 1 Reply Last reply
                  3
                  • Chris KawaC Chris Kawa

                    @ComixHe said:

                    I don't think moc should generate some check code

                    It's not a check code. It generates code that builds a meta object, which is a runtime reflection system. See here. It needs to do that, otherwise the runtime introspection wouldn't work, and that's one of main features of QObjects.

                    for template functions that are not generated at the end

                    They are, just not by your code. Meta system stores function pointers to these methods and thus instantiates them. It's similar to standard type traits and rtti mechanisms combined, just richer.

                    Is this because moc is not a compiler and therefore cannot make a determination?

                    Moc is just a boilerplate code generator. It doesn't do much logic on its own, just sees macros like Q_OBJECT and generates some method implementations for that class. There's no magic here, it's all standard C++. It saves it to a file in your build dir, so you can look at the code it generates if you want (although it can be a bit hard to read due to its complexity).

                    I'm just curious why qt6 added type checking for slot functions and qt5 didn't

                    Because the functionality of meta system was expanded in Qt6. One of the addition was possibility to compare meta types. See QMetaType::equals.

                    There's nothing wrong with Qt here. It's just that your code was ambiguous and has paths that lead to compilation errors. Nothing in Qt5 relied upon it being specific so it worked. Qt6 asks more specific questions and it exposed a bug. Simply be more specific. Define the operator correctly or delete/enable_if it out, so that Qt can't find it if it's not supposed to. Think of it like this - if Qt can cause an instantiation of your template fail to compile then anyone can. It's a bug you need to fix. Qt6 is just the messenger here, so don't shoot it.

                    C Offline
                    C Offline
                    ComixHe
                    wrote on last edited by
                    #11

                    @Chris-Kawa Thank you very much for your detailed answer

                    Chris KawaC 1 Reply Last reply
                    1
                    • C ComixHe

                      @Chris-Kawa Thank you very much for your detailed answer

                      Chris KawaC Offline
                      Chris KawaC Offline
                      Chris Kawa
                      Lifetime Qt Champion
                      wrote on last edited by
                      #12

                      @ComixHe No problem. Oh, you also said that you now need C++17. Yes, the minimum language version requirement in Qt6 was bumped from C++11 to C++17. It makes a lot stuff easier, the implementation can be cleaner and it is supported by all major compilers anyway.

                      1 Reply Last reply
                      0

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved