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:
Q_NAMESPACE
works in a very similar way than the Q_OBJECT
macro. It has three purposes:
- it tells moc to generate the meta-object for the given namespace;
- it tells the buildsystem (qmake) to add calls to moc for the header containing the macro;
- 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:
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:
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:
We can now expose the ColoredObject
class to QML. In C++ all we need to do is this:
And then in QML we can do:
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:
the registration would look like this:
And now again we can use the enumerations in QML:
Trusted software excellence across embedded and desktop platforms
The KDAB Group is a globally recognized provider for software consulting, development and training, specializing in embedded devices and complex cross-platform desktop applications. In addition to being leading experts in Qt, C++ and 3D technologies for over two decades, KDAB provides deep expertise across the stack, including Linux, Rust and modern UI frameworks. With 100+ employees from 20 countries and offices in Sweden, Germany, USA, France and UK, we serve clients around the world.
22 Comments
1 - Mar - 2017
Eric Lemanissier
Does it work also at global scope ?
2 - Mar - 2017
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 :)
5 - Mar - 2017
Tim
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!
11 - Mar - 2017
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.
6 - Mar - 2017
Alan Sambol
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).
11 - Mar - 2017
Giuseppe D'Angelo
Hi Alan,
yes, support for all of this in Creator is planned but not there. I've actually opened https://bugreports.qt.io/browse/QTCREATORBUG-17850 to track it.
However, apart from syntax highlighting/autocompletion, this should actually work :)
6 - Mar - 2017
Ceyk
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?
11 - Mar - 2017
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.
17 - Jul - 2017
Karl Hansen
Multiple enums can easily be distinguished and made collision-free by wrapping each enum declaration in a namespace with the same name, e.g.:
17 - Jul - 2017
Karl Hansen
--apologies for the lack of formatting, I tried wiki-markups, but apparently they don't work here.
22 - Aug - 2017
Matthias Wallnöfer
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):
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.
31 - Aug - 2017
Orient
It would be nice if
Q_INVOKABLE
also work for this feature.23 - Jan - 2018
Karsten Sperling Opdal
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.
23 - Jan - 2018
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).
3 - Feb - 2018
Bernando
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?
5 - Feb - 2018
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:
There's a bug report for tracking this feature request: https://bugreports.qt.io/browse/QTBUG-58454
Hope this helps.
5 - Feb - 2018
Bernando
Thank you.
19 - Feb - 2020
Ivan
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!
19 - Feb - 2020
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.
21 - Feb - 2020
Ivan D
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.
21 - Feb - 2020
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!
8 - Apr - 2022
matzze54
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?