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.

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 topublic:. 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.

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.

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

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.