I often meet criticism of the Qt framework, in which he is accused of using the meta-object compiler (the moc utility). As one of the moc developers, I decided to write this article in order to debunk some of the associated myths.
Introduction
Moc is one of the developer tools and part of
the Qt library . Its task is to support the extension of the C ++ language, which is necessary for introspection and reflection in Qt (this includes signals, slots, and QML). For a more detailed explanation, you can read about how
signals and slots in Qt work .
The need to use moc is one of the main targets of Qt's criticism. This even led to the appearance of forks in Qt, which fundamentally refused moc (for example, CopperSpice). But still, most of the so-called weaknesses attributed to moc are not justified.
Myths
Moc rewrites your code before passing it to the compiler.
This is a common misconception. Moc does not modify or rewrite your code. It simply parses a piece of code in order to generate additional C ++ files, which will then be compiled independently. This is not a very big difference, but still an important technical misunderstanding.
')
Moc simply automatically generates a template code that could be long and tedious to write manually if moc did not exist. If you are a masochist, it is still possible to take and write all the tables for introspection and realization of signals. Well, or rely on a reliable tool that will do it for you.
Using Qt, you do not write in real C ++
I have heard this argument many times, but it is simply incorrect. The macros used by moc for code annotation are the standard C ++ macros. They must be correctly recognized by any tool that can analyze C ++ code. When you add Q_OBJECT to your code, you simply append the declaration of
several functions . When you write “signals:”, you simply add a macro, which
will turn into “public:”. Many other Qt macros do not expand at all. Moc simply finds them and generates a code of signal emitters and introspection tables.
The fact that your code can now be read by another tool does not make it “less conforming to the C ++ standard”. You do not think the code written with the expectation of using gettext or doxygen is any kind of “less correct C ++”?
Moc complicates code building process
If you use any industrial code building system, like CMake or qmake, then you get native support for Qt. Even with some kind of proprietary build system, we are talking about just one additional start of the header file processing command. All the build systems I know allow you to add steps to run additional commands before running the compiler, since many projects in one form or another use code generation when building a project. Recall, for example, tools like yacc / bison,
gperf ,
llvm / TableGen .
Moc makes debugging harder
Since moc generates code in pure C ++, debuggers should not have any problems with it. We try to keep the generated code in such a state that it does not trigger compiler warnings or static or dynamic code analysis tools. Yes, sometimes when debugging, you will see traces of the generated moc code in the Cluster. In some rare cases, you may get an error related to the code created by moc, but usually the reasons are fairly easy to detect. The code generated by moc is quite human readable. It is also likely to be understood and debugged even easier than the infamous errors of some libraries built on advanced use of templates.
Rejecting moc improves performance during code execution.
This is a direct quote from the CopperSpice home page, and is probably their biggest lie. The code generated by moc tries to avoid dynamic allocations and reduce the number of reallocations of memory. The generated moc tables are constant arrays and are stored in the read-only data segment. CopperSpice registers its QMetaObject (information about signals, slots and properties) at runtime.
Milian Wolff did some performance comparisons of Qt and CopperSpice in his
report on CppCon2015. Here is a screenshot of one of his slides (less is better).

It should also be noted that the code on Qt, even with the launch of the moc compiles faster than the code on CopperSpice.
Outdated myths
Some criticism was once fair, but more is not.
The macro cannot be used when declaring a signal, slot, base object class, and also ...
Before the release of Qt5, the moc utility didn’t open macros. But since Qt 5.0, moc fully supports macros in all the places listed above, so this is no longer a problem at all.
Enumerations (enums) and type overrides (typedefs) must strictly conform when used as parameters of signals and slots
This is only a problem if you still want to use string-based connection syntax (since it does use direct comparison of type names). With the release of Qt5 and the
new syntax, this is no longer an obstacle.
Q_PROPERTY does not allow commas in types
Q_PROPERTY is a macro with one argument that is not expanded and serves only to help moc. But since this is still a macro, a comma in, for example, QMap <Foo, Bar> separates the macro arguments and causes a compilation error. When I
saw CopperSpice using this argument against Qt, it took 5 minutes to
fix it using the variadic macros from the C ++ 11 standard.
Other criticism
Templates, nested classes, or classes used by multiple inheritance cannot be QObjects
Although this is true, these features are not yet supported by QObject, although they can be fully implemented in moc if we want to. The Qt project does not currently consider these features as priorities.
I once
added support for template QObjects in moc, but this change was not included in the main development branch, since no one else expressed interest in this functionality.
You also need to note the support for template and nested classes in
moc-ng .
Multiple inheritance is in itself very ambiguous. Most often, it indicates problems with the architecture of the application and many modern languages ​​directly prohibit it. You can still use multiple inheritance with Qt if the QObject is the first base class in the inheritance chain. This small limitation allows us to apply useful optimizations. Ever wondered why qobject_cast is so much faster than dynamic_cast?
findings
I don't think moc is the problem. Qt macros really help to implement the necessary Qt functionality. Comparing them with the CopperSpice approach, we can notice in the latter a significant redundancy of the service code, as well as the unfriendly syntax of macros (not to mention the performance loss at runtime). The syntax of signals and slots that has existed in Qt since the 90s is one of the fundamental things that ensured the success of the framework.
You may also be interested in exploring some experiments related to moc, like
moc-ng (this is moc, rewritten using clang libraries). There is also this
study of replacing moc using the C ++ reflection tools. Well,
Verdigris library, with macros that create QMetaObject without moc.