Skip to content

New in Qt 5.8: meta-object support for namespaces Using namespaces in Qt for enumeration introspection

A small new feature that was added to Qt 5.8 is the ability for moc (Qt’s meta-object compiler) to extract meta-object information at the namespace level. This means, for instance, that you can have introspection support for enumerations defined in a namespace (not just in QObject subclasses, or classes tagged with Q_GADGET), and use such enumerations in QtScript, QtDBus and QML.

Introducing Q_NAMESPACE

The support for all of this involves a few new macros. The most important one is Q_NAMESPACE, which is a marker that tells moc to generate meta-object information for the enclosing namespace:

namespace MyNamespace 
{
    Q_NAMESPACE

    // ...
}

Q_NAMESPACE works in a very similar way than the Q_OBJECT macro. It has three purposes:

  1. it tells moc to generate the meta-object for the given namespace;
  2. it tells the buildsystem (qmake) to add calls to moc for the header containing the macro;
  3. in plain C++, it expands to a few declarations.

In particular, in C++, we can now access the namespace’s meta-object and extract data out of it. For instance:

namespace MyNamespace
{
    Q_NAMESPACE
    Q_CLASSINFO("author", "Uncle Scrooge")
    Q_CLASSINFO("revision", "3.14")
}

for (int i = 0; i < MyNamespace::staticMetaObject.classInfoCount(); ++i) {
    const auto &metaClassInfo = MyNamespace::staticMetaObject.classInfo(i);
    qDebug() << metaClassInfo.name() << metaClassInfo.value();
}

Support for enumerations and flags

A direct use case for Q_NAMESPACE is the ability to register enumerations and flags declared at the namespace level. This is now possible with the brand new Q_ENUM_NS and Q_FLAG_NS macros, which are the namespace counterparts of the existing Q_ENUM and Q_FLAG macros for classes:

namespace MyNamespace
{
    Q_NAMESPACE

    enum class SomeEnum {
        Foo,
        Bar
    };
    Q_ENUM_NS(SomeEnum)
}

In the above example, thanks to the Q_ENUM_NS macro, we can now benefit from all the meta-object goodies for enumerations: programmatically enumerate Mynamespace::SomeEnum enumerators, use QMetaEnum::fromType<T>(), have the enumeration automatically registered as a QMetaType, get automatic QDebug support and so on.

Using namespace-level enumerations in QML

If you’re familiar with QML you know that it’s possible to expose QObject subclasses to the QML engine, and use in QML (amongst other things) the enumerations defined in the class and marked with the Q_ENUM macro. For example, let’s build such a class:

class ColoredObject : public QObject {
    Q_OBJECT
    Q_PROPERTY(Color color MEMBER m_color)
public:
    enum class Color {
        Red, Blue, Green
    };
    Q_ENUM(Color)

private:
    Color m_color;
};

We can now expose the ColoredObject class to QML. In C++ all we need to do is this:

qmlRegisterType<ColoredObject>(    // C++ datatype
    "com.kdab.components",         // import statement
    42, 0,                         // major and minor version of the import
    "ColorThing");                 // name in QML

And then in QML we can do:

import QtQuick 2.0
import com.kdab.components 42.0

Item {
    ColorThing {
        color: ColorThing.Red    // enum defined in C++
    }
}

We can now do the same with enumerations defined at the namespace level, that is, outside QObject subclasses. The API for this is slightly different, because for namespaces we don’t have a C++ type to register, but only its meta-object. So for this namespace:

namespace MyNamespace
{
    Q_NAMESPACE

    enum class SomeEnum {
        Foo,
        Bar
    };
    Q_ENUM_NS(SomeEnum)
}

the registration would look like this:

qmlRegisterUncreatableMetaObject(MyNamespace::staticMetaObject, // static meta object
                                 "com.kdab.namespace",          // import statement
                                 3, 14,                         // major and minor version of the import
                                 "MyNamespace",                 // name in QML
                                 "Error: only enums");          // error in case someone tries to create a MyNamespace object

And now again we can use the enumerations in QML:

import QtQuick 2.0
import com.kdab.namespace 3.14

Item {
    Component.onCompleted: console.log(MyNamespace.Foo)
}

Enjoy!

About KDAB

KDAB is a consulting company offering a wide variety of expert services in Qt, C++ and 3D/OpenGL and providing training courses in:

KDAB believes that it is critical for our business to contribute to the Qt framework and C++ thinking, to keep pushing these technologies forward to ensure they remain competitive.

Categories: C++ / KDAB Blogs / KDAB on Qt / Qt

22 thoughts on “New in Qt 5.8: meta-object support for namespaces”

    1. Giuseppe D'Angelo

      Hi,
      no, at the moment it does not. Besides a technical limitation, making this work at global scope likely means opening the door for all sorts of clashes (two Q_NAMESPACE in different files would violate ODR…), so it’s probably better to ask the user to put it in a namespace explicitly. Last, it’s called Q_NAMESPACE because it wants to be used in a namespace 🙂

  1. What is the reason that KDAB does not provide the whole content of its articles in the RSS feed any longer? It makes reading the interesting KDAB articles from within my RSS reader quite complicated. Could this be fixed? Thanks and keep up the great blog posts!

    1. Giuseppe D'Angelo

      Hi Tim,
      the idea is to try to drive a bit more traffic to the website, in order to get some traffic statistics. We’d like figure out which blog posts or areas are more popular and therefore provide more content along those lines. I hope you don’t mind — we don’t make money through our website (using ads or something like that), it’s really for knowing our users a little more.

  2. Sadly, QtCreator support for this is still not that good.

    Q_ENUM_NS(MyNamespace) gets underlined – “expected a declaration”.
    The imports in QML are also not recognised and therefore there is no autocomplete support (enums inside QObjects work ok).

  3. Hi,

    is it possible to define mutliple Q_ENUM_NS in the same namespace? If so, how to register and how to use them from QML?

    1. Giuseppe D'Angelo

      Hi,

      Yes, it’s possible. They will all get registered automatically. However, notice that QML enums are like C++98 enums — they’re not themselves namespaced, but they will leak in the enclosing scope. In other words: all the enumerators names must be unique, even across different enumerations.

  4. Multiple enums can easily be distinguished and made collision-free by wrapping each enum declaration in a namespace with the same name, e.g.:


    // SysEnums.h
    namespace SysEnums
    {
    Q_NAMESPACE
    namespace MyEnum1
    {
    Q_NAMESPACE
    enum class MyEnum1
    {
    V1,
    V2,
    }
    Q_ENUM_NS(MyEnum1)
    } // ns MyEnum1
    namespace MyEnum2
    {
    Q_NAMESPACE
    enum class MyEnum2
    {
    V1,
    V2,
    V3,
    }
    Q_ENUM_NS(MyEnum2)
    } // ns MyEnum2
    } // ns SysEnums

    —-

    #include "enums.h"

    // somewhere in startup code

    qmlRegisterUncreatableMetaObject(Enums::staticMetaObject, // static meta object
    "com.medtronic.enums", // import statement
    1,0, // major and minor version of the import
    "Enums", // name in QML
    "Error: only enums");
    qmlRegisterUncreatableMetaObject(Enums::MyEnum1::staticMetaObject, // static meta object
    "com.medtronic.enums.MyEnum1", // import statement
    1,0, // major and minor version of the import
    "MyEnum1", // name in QML
    "Error: only enums");
    qmlRegisterUncreatableMetaObject(Enums::MyEnum2::staticMetaObject, // static meta object
    "com.medtronic.enums.MyEnum2", // import statement
    1,0, // major and minor version of the import
    "MyEnum2", // name in QML
    "Error: only enums");

  5. –apologies for the lack of formatting, I tried wiki-markups, but apparently they don’t work here.

  6. For the sake of completeness I report an interesting observation which I made with QML. Please consider this code (obviously with the appropriate qmlRegisterUncreatableMetaObject() call):

    namespace MyNamespace
    {
        Q_NAMESPACE
     
        enum class SomeEnum {
            Foo,
            Bar
        };
        Q_ENUM_NS(SomeEnum)
    
        class SomeQObject : public QObject
        {
             Q_OBJECT
             Q_PROPERTY(SomeEnum value MEMBER value)
    
             SomeEnum value;
        public:
             explicit SomeQObject() { }
        };
    }
    

    This compiles but QML is not able to recognise the property’s type of “value” and prints a run-time error message like “Unrecognised data type ‘SomeEnum’. What works is to prefix SomeEnum with its namespace, in other words “Q_PROPERTY(MyNamespace::SomeEnum value MEMBER value)” is the instruction you need.

    It would be nice if this could be mentioned as well.

  7. Unfortunaly it only works for one file, if you have multiple headers with same namespace and uses Q_NAMESPACE and Q_ENUMS_NS() the linker will fail with multiple instances of same object.

    1. Giuseppe D'Angelo

      Hi, true. That’s an unfortunate consequence of moc working only on one file at a time (so it can’t generate a complete metaobject if the namespace is split across multiple headers, with different enums in them).

  8. C++: Q_INVOKABLE bool startBackUp(int i); // works
    QML: Model.startBackUp( MyNamespace.Foo) // works

    C++: Q_INVOKABLE bool startBackUp(SomeEnum i);
    QML: Model.startBackUp( MyNamespace.Foo) // Error: Unknown method parameter type: MyNamespace.Foo

    Is there a solution for own data enums?

    1. Giuseppe D'Angelo

      Hello,

      I think what you’re facing here is a limitation of the QML language, which unfortunately does not allow for this yet. See here https://doc.qt.io/qt-5/qtqml-cppintegration-data.html#enumeration-types-as-signal-and-method-parameters when it says:

      C++ signals and methods with enumeration-type parameters can be used from QML provided that the enumeration and the signal or method are both declared within the same class, or that the enumeration value is one of those declared in the Qt Namespace.

      There’s a bug report for tracking this feature request: https://bugreports.qt.io/browse/QTBUG-58454

      Hope this helps.

  9. I’m trying to use Q_NAMESPACE + Q_ENUM_NS, but getting “undefined reference to `MyNamespace::staticMetaObject'” error.

    See sections under ‘#ifdef TRY_Q_NAMESPACE’ here:
    https://pastebin.com/CdJDBRv5

    Anything obvious I’m missing? Basically it’s just standard Qt example with copy-pasted code from this post.

    Thank you!

    1. Giuseppe D'Angelo

      Hi,

      The bulk of your problem is that you’re trying to use Qt meta-object features (Q_NAMESPACE ; but this also applies to Q_GADGET or Q_OBJECT) for a namespace defined inside a .cpp file. moc is not normally run on .cpp files, therefore it won’t generate the code providing the “staticMetaObject” definition that you’re missing. The solution is either to move the namespace definition with Q_NAMESPACE into its own header file, or if you really need to keep it into a .cpp file, add a line like #include “main.moc” at the end of your main.cpp.

      1. Thank you, Giuseppe!

        I failed to make it work with this dummy example though, even after moving code to header file.

        But in the real project, the problem turned out to be a missing Q_OBJECT macro in QObject-derived class (declared in the same header file).

        Class is completely unrelated to namespace, confusing error message, but just adding Q_OBJECT got rid of the linking error.

        1. Giuseppe D'Angelo

          You shouldn’t have had to do that… the symptom seems to be that moc was not being run on your header (but I have no idea as of the why). Glad that it worked, anyhow!

  10. Hi!

    I had some sort of need for doing such stuff, and this site was quite helpful.
    But I still facing an issue with it.

    I’ve made a namespace where I defined three state enums, which partially use the same identifiers.
    That is why I needed to add a Q_CLASSINFO(“RegisterEnumClassesUnscoped”, “false”).

    I’m using these enums for interprocess communication over DBus and for strict comparison in QML on the other hand.

    The issue I have is that, when I use one of these enums as type for a Q_PROPERTY, I can not use the strict comparison operators, since the type of the Q_PROPERTY is of type ‘object’ whereas the type of the enumeration value is of type ‘number’ on QML side, so the strict comparison fails even when they have the same value.

    Does anyone have an idea how to solve this?
    May it be possible that the Q_CLASSINFO macro causes these problems?

Leave a Reply

Your email address will not be published. Required fields are marked *