3

I have a C++ class called MyClass. This class is registered as an uncreatable type so it can be referred by name in QML.

MyClass.h:

#pragma once

#include <QObject>

class MyClass : public QObject {
Q_OBJECT

public:
    static void initQML();

    MyClass();
    MyClass(const MyClass &myClass);
    ~MyClass();

    Q_INVOKABLE void printText();
};

MyClass.cpp:

#include "MyClass.h"

#include <QtQml>
#include <QDebug>
#include <QMetaType>

/*static*/ void MyClass::initQML() {
    qmlRegisterUncreatableType<const MyClass>("Test", 1, 0, "MyClass", "Don't create it");

    qRegisterMetaType<const MyClass*>("const MyClass*");
}

MyClass::MyClass() {
    qDebug() << "Constructor called.";
}

MyClass::MyClass(const MyClass &myClass) {
    qDebug() << "Copy constructor called.";
}

MyClass::~MyClass() {
    qDebug() << "Destructor called.";
}

void MyClass::printText() {
    qDebug() << "This a sample text.";
}

I have another class which is called MyClassFactory. This class creates MyClass objects. It is registered as a singleton type to QML.

MyClassFactory.h:

#pragma once

#include "MyClass.h"

#include <QObject>

class MyClassFactory : public QObject {
Q_OBJECT

public:
    static void initQML();

    MyClassFactory();

public slots:
    static const MyClass *makeDefaultMyClass();
};

MyClassFactory.cpp:

#include "MyClassFactory.h"

#include <QtQml>
#include <QQmlEngine>
#include <QJSEngine>

QObject *simpleQmlRegisterSingletonTypeCallback(QQmlEngine *, QJSEngine *) {
    return new MyClassFactory();
}

/*static*/ void MyClassFactory::initQML() {
    qmlRegisterSingletonType<MyClassFactory>("Test", 1, 0, "MyClassFactory",
                                             simpleQmlRegisterSingletonTypeCallback);
}

MyClassFactory::MyClassFactory() {}

/*static*/ const MyClass* MyClassFactory::makeDefaultMyClass() {
    return new MyClass();
}

In my main.qml I create a property of MyClass type and bind a value to it via MyClassFactory. Here is my main.qml:

import QtQuick 2.9
import QtQuick.Controls 2.2

import Test 1.0

ApplicationWindow {
    visible: true

    width: 480
    height: 272

    property MyClass myClass: MyClassFactory.makeDefaultMyClass()

    Button {
        anchors.centerIn: parent

        text: "Create MyClass"

        onClicked: myClass.printText()
    }
}

When I build and run my code, I get the following error in the console:

qrc:/main.qml:12: Error: Unknown method return type: const MyClass*

If I register const MyClass* as a meta type, then this error disappears, but I get another one:

qrc:/main.qml:12:31: Unable to assign const MyClass* to MyClass*

First of all why do I have to register const MyClass* as a meta type, if I have already registered const MyClass as a QML (uncreatable) type?

Main question is how do I pass a const pointer to QML?

For a full running example, here is my main.cpp:

#include <QGuiApplication>
#include <QQmlApplicationEngine>

#include "MyClass.h"
#include "MyClassFactory.h"

int main(int argc, char *argv[])
{
    QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    // Init

    MyClass::initQML();
    MyClassFactory::initQML();

    // Main loop

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

I use Qt 5.9.5.

Silex
  • 2,583
  • 3
  • 35
  • 59
  • why not just pass `MyClass` instead of `const MyClass`? – folibis Oct 12 '18 at 18:22
  • 1
    I could pass `MyClass` instead of `const MyClass` and that is what I am doing currently, but I would like to learn how to do it with `const` or if it is not possible to do it with `const`, then why? – Silex Oct 12 '18 at 21:04

1 Answers1

4

The errors you are getting are making it clear that it wouldn't work with const anyway you twist it, because that's not how the API is designed, because while you can pass non-const objects to const functions, you cannot do the opposite for reasons that should be obvious. And it doesn't make a lot of sense that this particular API will have a const-correct side, at least in the version of Qt you are using.

Up until recently, JS didn't really have a notion of constants. It was introduced in ECMA 2015, but that specification is currently still not supported in QML anyway. Qt 5.12 will implement it, but even then, I doubt it will be integrated with the C++ side of things from the start. Properly propagating const correctness would be a nice thing to have, but then again, seeing how null pointer variants still pass if checks in QML, I wouldn't be holding my breath...

There exists the notion of read only properties, but that's really not the same as constants, it is just properties that cannot be assigned a different value, if the value happens to be an object, it can still be modified.

So the easiest thing to do if you want it to be a constant is to simply not expose an interface to make changes to that object.

Also, there shouldn't be any need to register QObject derived type pointers if you are using qmlRegisterUncreatableType(), qmlRegisterType(), qmlRegisterSingletonType() or similar for those types.

dtech
  • 47,916
  • 17
  • 112
  • 190
  • Thanks a lot @dtech for your explanation. I had the feeling that this is the case, but I wanted to have an additional input on it. – Silex Oct 16 '18 at 12:47
  • @dtech, can you look at [this question](https://stackoverflow.com/questions/22270456/is-it-possible-to-modify-qt-qmake-variable-in-the-parent-subdirs-project#comment92670865_22270456) I'm wondering if you've ever solved it a different way. – Ross Rogers Oct 19 '18 at 16:00