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
QtWS25 Last Chance

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 13 Dec 2022, 02:40 last edited by
    #1

    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.

    J C 2 Replies Last reply 13 Dec 2022, 09:18
    0
    • C ComixHe
      14 Dec 2022, 02:32

      @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~

      C Offline
      C Offline
      Chris Kawa
      Lifetime Qt Champion
      wrote on 14 Dec 2022, 02:51 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 14 Dec 2022, 03:01
      3
      • C ComixHe
        13 Dec 2022, 02:40

        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.

        J Offline
        J Offline
        jsulm
        Lifetime Qt Champion
        wrote on 13 Dec 2022, 09:18 last edited by
        #2

        @ComixHe Are you trying to implement templated classes based on QObject?

        https://forum.qt.io/topic/113070/qt-code-of-conduct

        1 Reply Last reply
        0
        • C Offline
          C Offline
          ComixHe
          wrote on 13 Dec 2022, 10:42 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
          • C Online
            C Online
            Christian Ehrlicher
            Lifetime Qt Champion
            wrote on 13 Dec 2022, 15:36 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 13 Dec 2022, 19:38
            0
            • C Christian Ehrlicher
              13 Dec 2022, 15:36

              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 13 Dec 2022, 19:38 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 13 Dec 2022, 19:45 last edited by
                #6

                any reason why you return something in a slot func?

                1 Reply Last reply
                1
                • C Online
                  C Online
                  Christian Ehrlicher
                  Lifetime Qt Champion
                  wrote on 13 Dec 2022, 21:36 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
                    13 Dec 2022, 02:40

                    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.

                    C Offline
                    C Offline
                    Chris Kawa
                    Lifetime Qt Champion
                    wrote on 14 Dec 2022, 01:23 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 14 Dec 2022, 02:32
                    3
                    • C Chris Kawa
                      14 Dec 2022, 01:23

                      @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 14 Dec 2022, 02:32 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~

                      C 1 Reply Last reply 14 Dec 2022, 02:51
                      0
                      • C ComixHe
                        14 Dec 2022, 02:32

                        @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~

                        C Offline
                        C Offline
                        Chris Kawa
                        Lifetime Qt Champion
                        wrote on 14 Dec 2022, 02:51 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 14 Dec 2022, 03:01
                        3
                        • C Chris Kawa
                          14 Dec 2022, 02:51

                          @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 14 Dec 2022, 03:01 last edited by
                          #11

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

                          C 1 Reply Last reply 14 Dec 2022, 03:16
                          1
                          • C ComixHe
                            14 Dec 2022, 03:01

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

                            C Offline
                            C Offline
                            Chris Kawa
                            Lifetime Qt Champion
                            wrote on 14 Dec 2022, 03:16 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

                            2/12

                            13 Dec 2022, 09:18

                            10 unread
                            • Login

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