Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Undocumented automatic metatype registration in Qt6?
QtWS25 Last Chance

Undocumented automatic metatype registration in Qt6?

Scheduled Pinned Locked Moved Unsolved General and Desktop
metatypesignals & slotsqvariantqt6
6 Posts 3 Posters 1.8k 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.
  • oblivioncthO Offline
    oblivioncthO Offline
    oblivioncth
    wrote on last edited by oblivioncth
    #1

    In an effort to dig deeper in this, I came across a situation that I do not understand given the documentation for QMetaType.

    MyStruct.h

    #ifndef MYSTRUCT_H
    #define MYSTRUCT_H
    
    #include <QMetaType>
    
    struct MyStruct
    {
        int val;
    };
    #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
        Q_DECLARE_METATYPE(MyStruct);
    #endif
    
    
    #endif // MYSTRUCT_H
    

    MyObject.h

    #ifndef MYOBJECT_H
    #define MYOBJECT_H
    
    #include <QObject>
    #include <QDebug>
    #include "mystruct.h"
    
    class MyObject : public QObject
    {
        Q_OBJECT
    
        Q_PROPERTY(MyStruct value READ value WRITE setValue NOTIFY valueChanged)
    
    private:
        MyStruct mValue;
    
    public:
        MyObject() {}
        MyStruct value() { return mValue; }
    
    signals:
        void valueChanged(MyStruct newValue);
    
    public slots:
        void setValue(MyStruct newValue)
        {
            mValue = newValue;
            emit valueChanged(mValue);
            qDebug() << qint64(this) << "Update: " << mValue.val;
        }
    };
    #endif // MYOBJECT_H
    

    main.cpp

    #include <QCoreApplication>
    #include <QTimer>
    #include <QVariant>
    #include "myobject.h"
    #include "mystruct.h"
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        // Test Signal/Slot connection
        MyObject oA = MyObject();
        MyObject oB = MyObject();
        bool connected = static_cast<bool>(QObject::connect(&oA,
                                                            &MyObject::valueChanged,
                                                            &oB,
                                                            &MyObject::setValue,
                                                            Qt::QueuedConnection
        ));
        qDebug() << "Connection?: " << connected;
        oA.setValue(MyStruct{777});
    
        // Test Variant
        MyStruct sA{4};
        QVariant var;
        var.setValue(sA);
        MyStruct sB = var.value<MyStruct>();
        assert(sA.val == sB.val);
    
        // Automatically end
        QTimer::singleShot(1000, &a, &QCoreApplication::quit);
    
        return a.exec();
    }
    
    

    Example Output with Qt 5.12.3 and 6.2.4.

    06:23:02: Starting MetaTypeIssue.exe...
    Connection?:  true
    88140151592 Update:  777
    88140151640 Update:  777
    06:23:03: MetaTypeIssue.exe exited with code 0
    

    I'm confused as to why this code compiles and runs correctly with Qt6. It's as if the requirement to use Q_DECLARE_METATYPE was removed, even though the docs certainly do not reflect that.

    As an aside, I'm also confused as to why this compiles/runs in either case without a call to qRegisterMetaType<MyStruct>(); since the connection type is queued. If I compile with Qt5 and remove the Q_DECLARE_METATYPE() call and QVariant code, the code runs and gives the appropriate runtime warning that MyStruct needs to be registered with qRegisterMetaType<>(), but if I just put back Q_DECLARE_METATYPE() without it then the warning goes away and the connection is created successfully.

    With Qt6 neither is required.

    kshegunovK 1 Reply Last reply
    0
    • oblivioncthO oblivioncth

      In an effort to dig deeper in this, I came across a situation that I do not understand given the documentation for QMetaType.

      MyStruct.h

      #ifndef MYSTRUCT_H
      #define MYSTRUCT_H
      
      #include <QMetaType>
      
      struct MyStruct
      {
          int val;
      };
      #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
          Q_DECLARE_METATYPE(MyStruct);
      #endif
      
      
      #endif // MYSTRUCT_H
      

      MyObject.h

      #ifndef MYOBJECT_H
      #define MYOBJECT_H
      
      #include <QObject>
      #include <QDebug>
      #include "mystruct.h"
      
      class MyObject : public QObject
      {
          Q_OBJECT
      
          Q_PROPERTY(MyStruct value READ value WRITE setValue NOTIFY valueChanged)
      
      private:
          MyStruct mValue;
      
      public:
          MyObject() {}
          MyStruct value() { return mValue; }
      
      signals:
          void valueChanged(MyStruct newValue);
      
      public slots:
          void setValue(MyStruct newValue)
          {
              mValue = newValue;
              emit valueChanged(mValue);
              qDebug() << qint64(this) << "Update: " << mValue.val;
          }
      };
      #endif // MYOBJECT_H
      

      main.cpp

      #include <QCoreApplication>
      #include <QTimer>
      #include <QVariant>
      #include "myobject.h"
      #include "mystruct.h"
      
      int main(int argc, char *argv[])
      {
          QCoreApplication a(argc, argv);
      
          // Test Signal/Slot connection
          MyObject oA = MyObject();
          MyObject oB = MyObject();
          bool connected = static_cast<bool>(QObject::connect(&oA,
                                                              &MyObject::valueChanged,
                                                              &oB,
                                                              &MyObject::setValue,
                                                              Qt::QueuedConnection
          ));
          qDebug() << "Connection?: " << connected;
          oA.setValue(MyStruct{777});
      
          // Test Variant
          MyStruct sA{4};
          QVariant var;
          var.setValue(sA);
          MyStruct sB = var.value<MyStruct>();
          assert(sA.val == sB.val);
      
          // Automatically end
          QTimer::singleShot(1000, &a, &QCoreApplication::quit);
      
          return a.exec();
      }
      
      

      Example Output with Qt 5.12.3 and 6.2.4.

      06:23:02: Starting MetaTypeIssue.exe...
      Connection?:  true
      88140151592 Update:  777
      88140151640 Update:  777
      06:23:03: MetaTypeIssue.exe exited with code 0
      

      I'm confused as to why this code compiles and runs correctly with Qt6. It's as if the requirement to use Q_DECLARE_METATYPE was removed, even though the docs certainly do not reflect that.

      As an aside, I'm also confused as to why this compiles/runs in either case without a call to qRegisterMetaType<MyStruct>(); since the connection type is queued. If I compile with Qt5 and remove the Q_DECLARE_METATYPE() call and QVariant code, the code runs and gives the appropriate runtime warning that MyStruct needs to be registered with qRegisterMetaType<>(), but if I just put back Q_DECLARE_METATYPE() without it then the warning goes away and the connection is created successfully.

      With Qt6 neither is required.

      kshegunovK Offline
      kshegunovK Offline
      kshegunov
      Moderators
      wrote on last edited by
      #2

      @oblivioncth said in Undocumented automatic metatype registration in Qt6?:

      I'm confused as to why this code compiles and runs correctly with Qt6. Its as if the requirement to use Q_DECLARE_METATYPE was removed, even though the docs certainly do not reflect that.

      Your original code (in the top-mentioned topic) assumes std::unique_ptr is copyable, which it isn't, so it can not ever be a metatype. Although I'm not sure what you mean by "correctly" as you haven't provided the qDebug() log.

      As an aside, I'm also confused as to why this compiles/runs in either case without a call to qRegisterMetaType<MyStruct>(); since the connection type is queued.

      It should compile and run with warnings at runtime.

      If I compile with Qt5 and remove the Q_DECLARE_METATYPE() call and QVariant code, the code runs and gives the appropriate runtime warning that MyStruct needs to be registered with qRegisterMetaType<>(), but if I just put back Q_DECLARE_METATYPE() without it then the warning goes away and the connection is created successfully.

      The connection statement is going to be compiled successfully either way if the signatures match, this doesn't mean the connection is correctly established, or that it can be processed at runtime. QObject::connect actually returns an object, which can also be tested for validity.

      Read and abide by the Qt Code of Conduct

      1 Reply Last reply
      0
      • oblivioncthO Offline
        oblivioncthO Offline
        oblivioncth
        wrote on last edited by oblivioncth
        #3

        @kshegunov said in Undocumented automatic metatype registration in Qt6?:

        Your original code (in the top-mentioned topic) assumes std::unique_ptr is copyable, which it isn't, so it can not ever be a metatype. Although I'm not sure what you mean by "correctly" as you haven't provided the qDebug() log.

        My apologies, that was a leftover from experimentation. The metatype declaration was supposed to be for std::shared_ptr<int>, as is used everywhere else in the code posted there. The attached example has the correct statement and exhibits the issue. I've corrected that post. As for the output of the code in this post, see below.

        It should compile and run with warnings at runtime.

        Yes, as I said in the section you quoted next, it does give a runtime warning specifying that qRegisterMetaType needs to be used, but this is only present if Q_DECLARE_METATYPE is missing as well. If I use the latter, the warning also disappears, even though Q_DECLARE_METATYPE doesn't imply the former (i.e. they're distinct and in theory both should be required here).

        To clarify, without the QVariant section (since that won't compile with Qt5 in some of these cases):

        Qt5
        Without Q_DECLARE_METATYPE and without qRegisterMetaType: Runtime warning, slot isn't called.
        With Q_DECLARE_METATYPE and without qRegisterMetaType: No warning, slot is called
        With Q_DECLARE_METATYPE and with qRegisterMetaType: No warning, slot is called

        Qt6
        Without Q_DECLARE_METATYPE and without qRegisterMetaType: No warning, slot is called
        With Q_DECLARE_METATYPE and without qRegisterMetaType: No warning, slot is called
        With Q_DECLARE_METATYPE and with qRegisterMetaType: No warning, slot is called

        So in both cases qRegisterMetaType isn't required for the slot to be called and the custom type to be accessible within the slot (i.e. no unexpected garbage data or the like).

        The connection statement is going to be compiled successfully either way if the signatures match, this doesn't mean the connection is correctly established, or that it can be processed at runtime. QObject::connect actually returns an object, which can also be tested for validity.

        In this case I truly meant correctly. I've modified the example slightly to leverage connect's return and show example output. Unfortunately it just cements my bewilderment that everything is working "as expected" with no call to qRegisterMetaType in either version, and no call to Q_DECLARE_METATYPE in Qt6. I'd say I must be misunderstanding something with the documentation, but it seems fairly explicit to me.

        kshegunovK 1 Reply Last reply
        0
        • oblivioncthO oblivioncth

          @kshegunov said in Undocumented automatic metatype registration in Qt6?:

          Your original code (in the top-mentioned topic) assumes std::unique_ptr is copyable, which it isn't, so it can not ever be a metatype. Although I'm not sure what you mean by "correctly" as you haven't provided the qDebug() log.

          My apologies, that was a leftover from experimentation. The metatype declaration was supposed to be for std::shared_ptr<int>, as is used everywhere else in the code posted there. The attached example has the correct statement and exhibits the issue. I've corrected that post. As for the output of the code in this post, see below.

          It should compile and run with warnings at runtime.

          Yes, as I said in the section you quoted next, it does give a runtime warning specifying that qRegisterMetaType needs to be used, but this is only present if Q_DECLARE_METATYPE is missing as well. If I use the latter, the warning also disappears, even though Q_DECLARE_METATYPE doesn't imply the former (i.e. they're distinct and in theory both should be required here).

          To clarify, without the QVariant section (since that won't compile with Qt5 in some of these cases):

          Qt5
          Without Q_DECLARE_METATYPE and without qRegisterMetaType: Runtime warning, slot isn't called.
          With Q_DECLARE_METATYPE and without qRegisterMetaType: No warning, slot is called
          With Q_DECLARE_METATYPE and with qRegisterMetaType: No warning, slot is called

          Qt6
          Without Q_DECLARE_METATYPE and without qRegisterMetaType: No warning, slot is called
          With Q_DECLARE_METATYPE and without qRegisterMetaType: No warning, slot is called
          With Q_DECLARE_METATYPE and with qRegisterMetaType: No warning, slot is called

          So in both cases qRegisterMetaType isn't required for the slot to be called and the custom type to be accessible within the slot (i.e. no unexpected garbage data or the like).

          The connection statement is going to be compiled successfully either way if the signatures match, this doesn't mean the connection is correctly established, or that it can be processed at runtime. QObject::connect actually returns an object, which can also be tested for validity.

          In this case I truly meant correctly. I've modified the example slightly to leverage connect's return and show example output. Unfortunately it just cements my bewilderment that everything is working "as expected" with no call to qRegisterMetaType in either version, and no call to Q_DECLARE_METATYPE in Qt6. I'd say I must be misunderstanding something with the documentation, but it seems fairly explicit to me.

          kshegunovK Offline
          kshegunovK Offline
          kshegunov
          Moderators
          wrote on last edited by
          #4

          I suspect the struct being public and relying on the default constructor(s)/destructor may be getting you a 'free pass'. I'd suggest you do your experiments with a class/struct that has out-of-line (copy) constructor implementation (i.e. in a separate translation unit) to prevent the compiler from accessing and inlining it whenever it needs/feels/can.

          Read and abide by the Qt Code of Conduct

          1 Reply Last reply
          0
          • oblivioncthO Offline
            oblivioncthO Offline
            oblivioncth
            wrote on last edited by oblivioncth
            #5

            @kshegunov
            Spiced it up a bit in accordance with your suggestion.

            CMakeLists.txt

            cmake_minimum_required(VERSION 3.19)
            
            project(MetaTypeIssue VERSION 0.1 LANGUAGES CXX)
            
            set(CMAKE_CXX_STANDARD 17)
            set(CMAKE_CXX_STANDARD_REQUIRED ON)
            
            set(CMAKE_AUTOMOC ON)
            
            find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
            find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)
            
            set(PROJECT_SOURCES
                    main.cpp
                    myclass.h
                    myobject.h
                    myclass.cpp
                    myobject.cpp
            )
            if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
                qt_add_executable(${PROJECT_NAME} ${PROJECT_SOURCES})
            else()
                add_executable(${PROJECT_NAME} ${PROJECT_SOURCES})
            endif()
            
            target_link_libraries(MetaTypeIssue PRIVATE Qt${QT_VERSION_MAJOR}::Core)
            

            myclass.h

            #ifndef MYCLASS_H
            #define MYCLASS_H
            
            #include <QMetaType>
            
            #define HEX_ADDR(ptr) QString("0x%1").arg(quint64(ptr), 0, 16)
            
            class MyClass
            {
            private:
                int mValue;
            
            public:
                MyClass();
                MyClass(int value);
                MyClass(const MyClass& other);
                ~MyClass();
            
                int value();
            };
            #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
                Q_DECLARE_METATYPE(MyClass);
            #endif
            
            
            #endif // MYCLASS_H
            

            myclass.cpp

            #include "myclass.h"
            #include <QDebug>
            
            MyClass::MyClass() {}
            MyClass::MyClass(int value) { mValue = value; }
            MyClass::~MyClass()
            {
                qDebug() << HEX_ADDR(this) << "dead";
            }
            MyClass::MyClass(const MyClass& other)
            {
                mValue = other.mValue;
                qDebug() << HEX_ADDR(this) << "Copied from: " << HEX_ADDR(&other);
            }
            int MyClass::value() { return mValue; }
            

            myobject.h

            #ifndef MYOBJECT_H
            #define MYOBJECT_H
            
            #include <QObject>
            #include <QDebug>
            #include "myclass.h"
            
            class MyObject : public QObject
            {
                Q_OBJECT
            
                Q_PROPERTY(MyClass value READ value WRITE setValue NOTIFY valueChanged)
            
            private:
                MyClass mValue;
            
            public:
                MyObject();
                MyClass value();
            
            signals:
                void valueChanged(MyClass newValue);
            
            public slots:
                void setValue(MyClass newValue);
            };
            
            #endif // MYOBJECT_H
            

            myobject.cpp

            #include "myobject.h"
            
            MyObject::MyObject() {}
            
            MyClass MyObject::value() { return mValue; }
            
            void MyObject::setValue(MyClass newValue)
            {
                mValue = newValue;
                emit valueChanged(mValue);
                qDebug() << HEX_ADDR(this) << "Update: " << mValue.value();
            }
            

            main.cpp

            #include <QCoreApplication>
            #include <QTimer>
            #include <QVariant>
            #include "myobject.h"
            #include "myclass.h"
            
            int main(int argc, char *argv[])
            {
                QCoreApplication a(argc, argv);
            
                // Test Signal/Slot connection
                qDebug() << "Setup Signal & Slot test";
                MyObject oA;
                MyObject oB;
                bool connected = static_cast<bool>(QObject::connect(&oA,
                                                                    &MyObject::valueChanged,
                                                                    &oB,
                                                                    &MyObject::setValue,
                                                                    Qt::QueuedConnection
                ));
                qDebug() << "Connection ?: " << connected;
                oA.setValue(MyClass(777));
            
                // Test Variant
                qDebug() << "Test Variant";
                MyClass sA(4);
                QVariant var;
                var.setValue(sA);
                MyClass sB = var.value<MyClass>();
                assert(sA.value() == sB.value());
            
                // Automatically end
                qDebug() << "Setup quit";
                QTimer::singleShot(1000, &a, &QCoreApplication::quit);
            
                return a.exec();
            }
            

            Sample Output:

            Setup Signal & Slot test
            Connection ?:  true
            "0xfed5cff460" Copied from:  "0xfed5cff5e8"
            "0x21aaecfe8a0" Copied from:  "0xfed5cff460"
            "0xfed5cff460" dead
            "0xfed5cff5d8" Update:  777
            "0xfed5cff708" dead
            Test Variant
            "0x21aaecfc358" Copied from:  "0xfed5cff634"
            "0xfed5cff694" Copied from:  "0x21aaecfc358"
            Setup quit
            "0xfed5cfb2d0" Copied from:  "0x21aaecfe8a0"
            "0xfed5cfb1a0" Copied from:  "0xfed5cff618"
            "0xfed5cfb1a0" dead
            "0xfed5cff608" Update:  777
            "0xfed5cfb2d0" dead
            "0x21aaecfe8a0" dead
            "0xfed5cff694" dead
            "0x21aaecfc358" dead
            "0xfed5cff634" dead
            "0xfed5cff618" dead
            "0xfed5cff5e8" dead
            

            Still magical with both versions of Qt :)

            Will try to pry some more.

            EDIT:
            Ensured that /Ob0 (MSVC) was set to completely disable inlining and tried changing the data member of MyClass from an int to a QString. Situation is still the same.

            1 Reply Last reply
            0
            • L Offline
              L Offline
              LL L
              wrote on last edited by
              #6

              @oblivioncth same here. Neither Macro nor register function needed. no related documents.

              1 Reply Last reply
              0
              • Pl45m4P Pl45m4 referenced this topic on

              • Login

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