Calling qmlRegisterType() in the registered class

2019-07-15 07:32发布

I want to use qmlRegiterType() in registered class itself. I tried to use method from this topic but whenever I try to run application in debug mode it crashes the application with error

read access violation at 0x0

Crashes on macro QML_GETTYPENAMES inside qqml.h (235 line).

TestClass.h:

class RegisterQmlTest : public QObject
{
    Q_OBJECT
public:
    explicit RegisterQmlTest(QObject *parent = 0);
};

TestClass.cpp:

QML_REGISTER(RegisterQmlTest);

RegisterQmlTest::RegisterQmlTest(QObject *parent) : QObject(parent)
{ }

void RegisterQmlTest::foo()
{
    qDebug() << "Foo test";
}

I tried to compile application on MSVC2013x64 on Qt 5.6.2, on Windows.

标签: qt qml qtquick2
2条回答
▲ chillily
2楼-- · 2019-07-15 08:20

You are not the only one that it is happening to : https://github.com/benlau/quickflux/issues/7, and I believe it is likely due to the static initialization order fiasco.

One solution could be to use Q_COREAPP_STARTUP_FUNCTION to ensure the call to qmlRegisterType is not done too early.

You can use this macro in a .cpp file like so :

static void registerMyQmlTypes() {
    qmlRegisterType<MyType>("MyImortUri", 1, 0, "MyType");
}
Q_COREAPP_STARTUP_FUNCTION(registerMyQmlTypes)
查看更多
女痞
3楼-- · 2019-07-15 08:20

I had the exact same issue. The problem is that the staticMetaObject associated to your class is not initialized at the moment the macro invokes the call to qmlRegisterType. As already stated in this answer (from the same topic), you're more flexible without macros. I solved this by introducing one static type per custom class.

appQmlRegister.hpp

#include <functional>

#include <QtQml>
#include <QList>

namespace app {
    namespace Qml {
        namespace Register {

            auto Init() -> void;

            static auto GetList()->QList<std::function<void(void)>>&;

            template <class T>
            struct Type {
                Type() {
                    auto initializer = []() {
                        qmlRegisterType<T>();
                    };

                    GetList().append(initializer);
                }
            };
        }
    }
}

appQmlRegister.cpp

#include "appQmlRegister.hpp"

namespace app {
    namespace Qml {
        namespace Register {

            auto Init() -> void {
                for (auto registerFunc : GetList()) {
                    registerFunc();
                }
            }

            auto GetList()->QList<std::function<void(void)>>& {
                static QList<std::function<void(void)>> List;
                return List;
            }
        }
    }
}

The type app::Qml::Register::Type takes a template argument (the type of your custom class) and wraps the call to qmlRegisterType in a lambda. And that's the basic concept. Instead of an immediate call you now have full control of when to register all your custom types via app::Qml::Register::Init(). By calling that function at runtime but before starting the QML engine you can ensure that the staticMetaObjects are initialized properly and you're safe to register them.

This requires a bit of typing on a per-custom-class level though. You'd have to declare a static member in the header of the class you want to register in QML:

MyCustomClass.hpp

#include "appQmlRegister.hpp"

namespace app {

    class MyCustomClass : public QObject {
        Q_OBJECT
    private:
        static Qml::Register::Type<MyCustomClass> Register;
        // stuff...
    }
}

and then define it in the .cpp file like this:

MyCustomClass.cpp

#include "MyCustomClass.hpp"

namespace app {
    Qml::Register::Type<MyCustomClass> MyCustomClass::Register;
}

This can of course be extended to support other sorts of type registration like registering uncreatable types, custom versions/names etc. I implemented this in a QML showcase/template project on GitHub

Hope this helps!

查看更多
登录 后发表回答