Est. 2011

Moc myths debunked

I have often read, on various places, criticisms about Qt because of its use of moc. As the maintainer of moc I thought it would be good to write an article debunking some of the myths.

Introduction

moc is a developer tool and is part of the Qt library. Its role is to handle Qt's extension within the C++ code to offer introspection and enable reflection for Qt Signals and Slots, and for QML. For a more detailed explanation, read my previous article How Qt Signals and Slots work.

The use of moc is often one of the criticisms given to Qt. It even led to forks of Qt, such as CopperSpice. However, most of the so-called drawbacks are not completely founded.

Myths

Moc rewrites your code before passing it to the compiler

This is often misunderstood, moc does not modify or rewrite your code. It simply parses part of your code to generate additional C++ files which are then usually compiled independently.
This does not make a big difference overall, but is still a technical misconception.

The moc is just generating some boilerplate code that would be fastidious to write otherwise. If you were masochist, you would write all the introspection tables and implementation of signals by hand. It is so much more convenient to have this auto generated.

When writing Qt code, you are not writing real C++ ¹

I have read this many times, but this is simply false. The macros understood by moc to annotate the code are simply standard C++ macros defined in a header. They should be understood by any tool that can understand C++. When you write Q_OBJECT, it is a standard C++ macro that expands to some function declarations. When you write signals: it is just a macro that expands to public:. Many other Qt macros expand to nothing. The moc will then locate these macros and generate the code of the signal emitter functions, together with some additional introspection tables.

The fact that your code is also read by another tool than the compiler does not make it less C++. I've never heard that you are not using vanilla C++ if you use tools like gettext or doxygen, which will also parse your code to extract some information.

Moc makes the build process much more complicated ²

If you are using any mainstream build system, such as CMake or qmake, they will have a native integration of Qt. Even with a custom build system, we are just talking about invoking one additional command onto your header files. All the build systems allow this because many projects have some sort of generated code as part of the build. (Things like yacc/bison, gperf, llvm has TableGen)

It makes the debugging experience harder

Since the moc generated code is pure C++, debuggers or other tools have no problems with it. We try to keep the generated code without warnings or any issues that would trigger static or dynamic code analyzers.
You will sometimes see backtraces containing frames within the moc generated code. In some rare case you can have errors within the moc generated code, but it is usually straightforward to find their cause. The moc generated code is human readable for most part. It will also probably be easier to debug than the infamous compiler error messages you can get while using advanced template code.

Removing moc improves run time performance ³

This is a quote from the CopperSpice home page, and is probably their biggest lie. The moc generated code is carefully crafted to avoid dynamic allocation and reduce relocations.
All the moc generated tables go in const arrays that are stored in the shareable read-only data segment. CopperSpice, on the other hand, registers the QMetaObject data (information about signals, slots and properties) at run time.

Milian Wolff did some measurements to compare Qt and CopperSpice for his CppCon2015 talk. Here is a screenshot from one of his slides (smaller is better):

It is also worth mentioning that Qt code with moc compiles faster than CopperSpice code.

Outdated Myths

Some criticisms used to be true, but are long outdated.

A macro cannot be used to declare a signal, a slot, the base class of an object, or ...

Before Qt5, moc did not expand macros. But since Qt 5.0 moc fully expands macros, and this is no longer an issue at all.

Enums and typedefs must be fully qualified for signal and slot parameters

This is only an issue if you want to use the string-based connection syntax (as this is implemented with a string comparison). With the Qt5 function pointer syntax, this is not an issue.

Q_PROPERTY does not allow commas in its type

Q_PROPERTY is a macro with one argument that expands to nothing and is only understood by moc. But since it is a macro, the comma in QMap<Foo, Bar> separating macro arguments is causing a compilation error. When I saw that CopperSpice used this as an argument against Qt, I spent five minutes to fix it using C++11 variadic macros.

Other criticisms

Template, nested, or multiple inherited classes cannot be QObjects

While true, those are just missing features of QObject, which could be implemented in moc if we wanted them. The Qt project does not think these features are important.

For example, I implemented support for templated QObjects in moc, but this was not merged because it did not raise enough interest within the Qt project.

As a side note, moc-ng supports template and nested classes.

Multiple inheritance is also something that is in itself controversial. Often considered bad design, it has been left out of many languages. You can have multiple inheritance with Qt as long as QObject comes first as base class. This small restriction allows us to make useful optimization. Ever wondered why qobject_cast is so much faster than dynamic_cast?

Conclusion

I believe moc is not a problem. The API and usability of the Qt meta object macro helps. Compare them to CopperSpice's to see the excessive boilerplate and user unfriendly macros (not even talking about the loss in performance). The Qt signals and slots syntax which exists since the 90s is among the things that made Qt so successful.

You might also be interested to learn about some research projects around moc, like moc-ng: a re-implementation of moc using the clang libraries; or this blog research if moc can be replaced by C++ reflection.
Update: And also Verdigris, a library with macros to create a QMetaObject without moc.

Footnotes

¹ StackOverflow question
² StackOverflow answer
³ CopperSpice home page
CopperSpice documentation
CopperSpice documentation

Woboq is a software company that specializes in development and consulting around Qt and C++. Hire us!

If you like this blog and want to read similar articles, consider subscribing via our RSS feed (Via Google Feedburner, Privacy Policy), by e-mail (Via Google Feedburner, Privacy Policy) or follow us on twitter or add us on G+.

Submit on reddit Submit on reddit Tweet about it Share on Facebook Post on Google+

Article posted by Olivier Goffart on 22 February 2016

Load Comments...
Loading comments embeds an external widget from disqus.com.
Check disqus privacy policy for more information.
Get notified when we post a new interesting article!

Click to subscribe via RSS or e-mail on Google Feedburner. (external service).

Click for the privacy policy of Google Feedburner.
© 2011-2023 Woboq GmbH
Google Analytics Tracking Opt-Out