Raymii.org
Quis custodiet ipsos custodes?Home | About | All pages | Cluster Status | RSS Feed
Add moc includes to speed up Qt compilation
Published: 12-12-2022 | Author: Remy van Elst | Text only version of this article
❗ This post is over one years old. It may no longer be up to date. Opinions may have changed.
Table of Contents
The Meta-Object Compiler, moc
, handles Qt's C++ extensions and it is required for signals and slots and properties in Qt. moc
reads C++ header files and if the Q_OBJECT
macro is used, it generates an extra .cpp
file named moc_filename.cpp
containing extra (meta-object) code. This post has a bit of background information and a shell script to automatically include moc_*.cpp
files in your code whenever Q_OBJECT
is used. If you use qmake
, this will probably speed up your build and if you use cmake
, this will probably speed up incremental builds (when CMAKE_AUTOMOC
is on
).
Recently I removed all Google Ads from this site due to their invasive tracking, as well as Google Analytics. Please, if you found this content useful, consider a small donation using any of the options below:
I'm developing an open source monitoring app called Leaf Node Monitoring, for windows, linux & android. Go check it out!
Consider sponsoring me on Github. It means the world to me if you show your appreciation and you'll help pay the server costs.
You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $200 credit for 60 days. Spend $25 after your credit expires and I'll get $25!
Below you'll find a shell script that scans your code for missing moc_*.cpp
includes and if you pass the addinclude
flag, the script will add them for you.
Summarized heavily, by including your moc_*.cpp
files you save the compiler
and linker some work allowing for faster builds and even some optimizations.
Together with something like ccache
, precompiled headers and different
qrc
files (one for assets and one for .qml
code and scripts), you can
save quite a bit of time of your builds.
Shell script to include moc_*.cpp
files
I wrote this script back in 2020, but decided to post it now because I recently got an email regarding it and had an interesting conversation with the person (which requested to remain anonymous).
Save the following file as fixMocInclude.sh
in your project folder:
#!/bin/bash
# Copyright (c) 2022 Remy van Elst
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3.
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
for filename in $(grep --recursive --files-with-matches --extended-regexp 'Q_OBJECT|Q_GADGET|Q_NAMESPACE'); do
cppfile="${filename%.*}.cpp";
if [[ -f "${cppfile}" ]]; then
if grep --quiet "#include \"moc_$(basename ${cppfile})\"" "${cppfile}"; then
echo "OK: ${cppfile} HAS moc include";
else
echo -n "FAIL: ${cppfile} MISSES moc include. "
if [[ ${1} == "addinclude" ]]; then
echo "Addding include...";
echo "" >> ${cppfile}
echo "#include \"moc_$(basename ${cppfile})\"" >> ${cppfile}
else
echo "Add this line to the end of the file: ";
echo -e "\t#include \"moc_$(basename ${cppfile})\""
fi
echo
fi;
fi;
done
if [[ "${1}" != "addinclude" ]]; then
echo "Run this script (./${0}) with the flag 'addinclude' to automatically add the moc includes";
echo "Warning: make sure you have a backup before running this script with that flag."
fi
Example output, running on the Leaf Node Monitoring code base:
OK: lib/src/versioncheck/VersionCheck.cpp HAS moc include
OK: lib/src/traymenu/TrayMenu.cpp HAS moc include
OK: lib/src/model/MainModel.cpp HAS moc include
FAIL: lib/src/viewmodel/AboutViewModel.cpp MISSES moc include. Add this line to the end of the file:
#include "moc_AboutViewModel.cpp"
The next part of the article bundles background information on moc
and
this specific optimization.
Background information regarding moc
Because the Qt documentation currently does not have clear and concise
instructions on this and most documentation on speeding up Qt builds is
spread all over the internet (forum, mailing lists, git repositories, YouTube
video's, etc), I have quoted (with sources) the most important statements
regarding including your moc_*.cpp
files in one place.
Quoting the Qt documentation:
The Meta-Object Compiler,
moc
, is the program that handles Qt's C++ extensions.
moc
reads a C++ header file, if it finds one or more class declarations that contain theQ_OBJECT
macro, it produces a C++ source file containing the meta-object code for those classes. Among other things, meta-object code is required for the signals and slots mechanism, the run-time type information, and the dynamic property system.The C++ source file generated by
moc
must be compiled and linked with the implementation of the class (or it can be#included
into the class's source file).If you use
qmake
to create yourMakefiles
, build rules will be included that call themoc
when required, so you will not need to use the moc directly. For more background information onmoc
, see Why Does Qt Use Moc for Signals and Slots?
Interestingly, the line (or it can be #included into the class's source
file)
only appears on this page and not in the overview.
KDAB, a large Qt consulting firm, have a python script that does the same including a git hook, but that only was published 5 months ago and required python. My script is older and only needs bash. They have also published a video on the subject with benchmarks.
From the KDAB python repository, another quote:
If you are using CMake
, you will probably have a line like this in your
CMakeLists.txt
file:
set(CMAKE_AUTOMOC ON)
This results in all moc_*.cpp
files being included from one file, namely
mocs_compilation.cpp
. The advantage of this is that it speeds up the initial
build, but the disadvantage is that incremental builds (when you
for example touch just a single header file) will be much slower.
Including the moc
files in the source files, gives us the best of both
worlds, both fast initial build and fast incremental builds.
Via Sze Howe Koh
on the Qt forum, regarding how the moc_*.cpp
files are
created and included in the build:
qmake
is responsible for this. It scans your code and your *.pro
file to
generate a Makefile
. Your compiler and linker follow the "recipe" contained
in the Makefile
. When you don't #include "moc_myclass.cpp"
, your
QObject-based class spans 2 separate *.o
files:
SOURCES = ../src/main.cpp \
../src/myclass.cpp moc_myclass.cpp
OBJECTS = main.o \
myclass.o \
moc_myclass.o
When you #include
your moc file, qmake
sees this and adjusts the Makefile
accordingly, so your class is fully contained within 1 *.o
file:
SOURCES = ../src/main.cpp \
../src/myclass.cpp
OBJECTS = main.o \
myclass.o
- KDE code and internal Qt code prefer the tactic of
#include
themoc_file.cpp
in the corresponding.cpp.
- This results in fewer
*.o
files fed into the linker. This has been shown to speed up builds. - This can at times enable the reduction of the header-to-header include graph, per this email, which is a tactic I had mentioned on this other forum thread as being my main weapon overall against long rebuild times (in any C/C++ project, Qt or not).
- Per the famous Thiago Macieira (see here), the
#include-your-moc
will also allow Clang to provide better diagnostics regarding your class.
Quoting the Qt mailing list, regarding the fact that both KDE
and Qt do this (including moc_
files):
On Thursday, 28 May 2020 02:06:01 PDT Shawn Rutledge wrote:
> > On 2020 May 27, at 17:50, Thiago Macieira <thiago.macieira@intel.com>
> > wrote:>
> > On Wednesday, 27 May 2020 03:42:19 PDT Oswald Buddenhagen wrote:
> >>> this is not something we can subject our users to.
> >>
> >> orly? kde had been doing that for quite a while.
> >
> > And I fixed QtCore to do the same.
> >
> > The only reason not to include the moc output in your .cpp is if you don't
> > have one (a header-only class whose only non-inline methods are the moc-
> > generated ones). Otherwise, #include your mocs.
>
> The reason is to speed up compilation, right? Is there another reason?
Aside from that benefit and the reason for this thread, it enables some
warnings in Clang that aren't otherwise. If it can see all members of a class,
including non-inline, it can tell if you forgot to use or initialise some of
them.
Plus the benefit of more inlining, as there's more the compiler can see.
In that same thread, another reason is given to include your moc_
files:
Shawn Rutledge (28 May 2020 11:06)
> The reason is to speed up compilation, right? Is there another reason?
Yes, see earlier in this thread: if you #include your .moc in your .cpp,
your .h can get away with forward-declaring some classes whose headers
it doesn't #include; the .moc may need to see the actual definition,
rather than a forward declaration, and the .cpp shall do the needed
#include, hence make the definition visible, which the .moc then
benefits from by being #included in the .cpp.
Eddy.
Tags: bash
, blog
, c++
, cmake
, cpp
, kde
, moc
, performance
, qmake
, qml
, qt
, qt5
, qt6