Hi. You're watching Programming with Qt and QML.
We're down in the machine room at the C++ level. In this video, which is likely going to be
very short, I'm going to show you what you will experience if you forget the Q_OBJECTS. And then
I'll explain to you what is really going on there. The code that we had in just the example before
looks like this. It's the StopWatch that just counts up the numbers here. The way that it
works, let's just go to our main, just create an instance of our StopWatch.
The StopWatch is this Q_OBJECT macro. Here's a subclass from QObject. So, it has
this Q_OBJECT macro. It has one slot, and whenever this slot is being
connected to from this timer in my constructor. Okay. Let's imagine that I forgot this Q_OBJECT macro here. Let's see what it says when I compile it. So first of all, it's perfectly
okay to forget this Q_OBJECT macro here because, well it's not. It says Q_OBJECT...."Class declaration lacks Q_OBJECT macro." And why is that? Well, it is because
QMake's actually been executed and the make system thinks
that it should be doing this. So let me just run QMake again...Builds...Run QMake...and compile again.
And this time, it doesn't complain. It just compiles fine and everything's cool. Now, let's try the
other way around. Let's try that I actually need this Q_OBJECT macro, but I forgot it. Let's imagine that I
added it, but I forgot to run QMake. So, we add it back here. This is the most common
situation after this error. So, now we build. And, hallelujah -- a beautiful error message that
says something that is hard to understand. Let's read the error message and see if we
can get anything out of it. It says, "unresolved external symbol." First of all, this is
from the linker. So, the linker is trying to take all the object files that it compiles from the C++ files and link them together into one binary.
"public virtual struct QMetaObject const pointer _cdecl StopWatch::
meta -- just stop me, right? What does it say? It says you promised me
that you would implement this method that is called MetaObject that doesn't
take any parameter and that is const. "I did? I'm sorry, I have never ever promised you
anything like that. How can you say that to me?" Well, I'm sorry, you did because you added this
Q_OBJECT macro. Let me just go and show you the definition of Q_OBJECT. This is
what Q_OBJECT looks like in your code. It is one scary beast. Fortunately,
you do not need to understand it. But please see here it says, "virtual const QMetaObject pointer metaObject," and that is your void here, const. So at the second
when I added Q_OBJECT, I actually did promise to do this. So, why doesn't it
work? Let me show you on the whiteboard. So, what I've drawn up here is the process of
running QMake and of running moc. So, we have our make file over here. The makefile is of course the input
for make. That's what will compile our software. The makefile is generated from
QMake by reading this Ex_Stop.Watch.pro file that we created in our project. Of course, we created that
behind the scenes by using Qt Creator, or if we used another ID, likely also created behind the
scenes. But that's beside the point here. So, in my profile when I run the QMake step,
QMake reads the profile. It will read which are the headers in this project here.
And each and every single header it will open the header file and it will look for this Q_OBJECT macro. If it doesn't find the Q_OBJECT macro, it'll continue
on. If it finds it, it will say, "Okay. Then..." and it will write a target into the make file, saying,
"I need a moc_StopWatch.cpp," every time this StopWatch.h file changes. So there's going to
be a target up in your make file for that. But as I said, if there is no Q_OBJECT macro, we are
not going to have this. And then when I run make, I will compile my steps. My StopWatch.h will promise
that there is this meta object method because there is the Q_OBJECT macro that we
added. But given that it wasn't there at the time when I ran QMake, this stage will not be there and
therefore we will get this error message when we link our application. So, the cure is simple. All I
need to do is go up here, build, run QMake, and now that it is run QMake I will press build again. It will compile it. Press run and my application
is back in a working state. In my regular scheduled
trainings or on-site trainings with this QML show here, I, at this stage, ask my
students (well, we have a reflection section after the section and in that reflection section
I ask them), "When should you call QMake? And at that stage, I hope that the student will tell
me, "Well, whenever you add your first signal or your first slot, or really whenever you add Q_OBJECT to your class." But at some point, one of the students raised his hand said, "Well,
Jesper, you should run QMake whenever the compiler gives you an error message and you have no clue
what it is." I actually like that answer because, rather than remembering exactly when just
whenever you see this compiler error about missing whatever, whatever it was -- and it might
be different on your system -- at that stage just remember run QMake or if you're in a CMake
build setup it has the same issue, just run CMake. And in this stage, it's pretty certain that every
single time somebody will raise their hand and say, "Why doesn't it run QMake for me automatically?"
But think about it. What it would need to do is that every time you change your header file
there would need to be a dependency for every single one of your header files on your make file -- well, the other
way around. The make file needs to be regenerated with QMake every time a header file is changed.
That would mean a lot of QMake re-running. And even if QMake only takes a second or two, it would
be a second or two for every recompilation step, something that at least the people in Trolltech
back in the day decided, "Hey, we're not gonna waste a second or two for every single step."
And just imagine this was a complex CMake setup. Those setups can easily take, what do I know,
10 seconds to run the whole CMake setup. You definitely do not want 10 seconds for every
compilation or recompilation of your software, just to avoid having to do it that very seldom
time when you add a new Q_OBJECT macro. That's it for this video. In
the next video, we're gonna go really deep into the machine room and
we're gonna watch one single class called... well I'm not gonna tell you. It's gonna be
a surprise. But please do stay tuned and please join us in the next video and please do
subscribe to our channel and for my daughter's sake, please give me a thumbs up. Will you? See
you next time. Until then, have a great day!