c++qtqmetatype

How Could I Register Types With A Deleted Copy Constructor To QMetaType?


The Problem:

I want to create an object instance at runtime using QMetaType by the type name. But I couldn't register the type because it's a QObject and I don't have access to the implementation to change it.

Duplicate Question(s):

Reproducible Example Of The Problem:

The CMake: CMakeLists.txt

#include <QMetaType>
#include <QObject>

class MyObject : public QObject {
  Q_OBJECT
};

#include "main.moc"

int main()
{
  qRegisterMetaType<MyObject>("MyObject");
  int const type_id = QMetaType::type("MyObject");
}

It will fail:

/tmp/untitled1/main.cc:12:3: note: in instantiation of function template specialization 'qRegisterMetaType<MyObject>' requested here
  qRegisterMetaType<MyObject>("MyObject");
  ^
/tmp/untitled1/main.cc:4:18: note: copy constructor of 'MyObject' is implicitly deleted because base class 'QObject' has a deleted copy constructor
class MyObject : public QObject {
                 ^
/opt/Qt/5.15.2/gcc_64/include/QtCore/qobject.h:467:20: note: 'QObject' has been explicitly marked deleted here
    Q_DISABLE_COPY(QObject)
                   ^

Solution

  • A Non-portable And Non-documented Solution:

    Specialize the QtMetaTypePrivate::QMetaTypeFunctionHelper for your type:

    namespace QtMetaTypePrivate
    {
    template <>
    struct QMetaTypeFunctionHelper<MyObject, true> {
      static void Destruct(void *address)
      {
        static_cast<MyObject *>(address)->~MyObject();
      }
    
      static void *Construct(void *where, void const *address)
      {
        if (address != nullptr) {
          delete static_cast<MyObject const *>(address);
        }
        return where != nullptr ? new (where) MyObject : new MyObject;
      }
    };
    }  // namespace QtMetaTypePrivate
    
    

    Using QMetaObject, But Requires A Q_INVOKABLE Constructor:

    By not providing a Public Copy Constructor one solution is to register a pointer type:

    qRegisterMetaType<MyObject*>();
    

    And then retrieve the type ID using "MyObject*" string and the QMetaObject using QMetaType::metaObjectForType:

    int const type_id = QMetaType::type("MyObject*");
    QMetaObject const* metaObject = QMetaType::metaObjectForType(type_id);
    

    And now we could create an instance of the class:

    QObject* instance = metaObject->newInstance();
    

    But still, we need to have an invokable public constructor:

    class MyObject : public QObject {
      Q_OBJECT
    public:
      Q_INVOKABLE MyObject()
      {}
    };
    

    https://interest.qt-project.narkive.com/zP6wxOZf/how-to-create-qobject-derived-class-instance-from-class-name#post7