Skip to content

New in Qt 5.8: meta-object support for namespaces

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

12 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.

Leave a Reply

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

By continuing to use the site, you agree to the use of cookies. More information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close