The Qt toolkit has often been criticized for extending C++ and requiring a non-standard code generator (moc
) to provide introspection.
Now, the C++ standardization committee is looking at how to extend C++ with introspection and reflection. As the current maintainer of Qt's moc I thought I could write a bit about the need of Qt, and even experiment a bit.
In this blog post, I will comment on the current proposal draft, and try to analyze what one would need to be able to get rid of moc.
Current draft proposal
Here is the draft proposal: N3951: C++ type reflection via variadic template expansion. It is a clever way to add compile time introspection to C++. It gives new meaning to typedef
and typename
such that it would work like this:
We can use that to go over the member of a class at compile time and do stuff like generating a QMetaObject with a normal compiler.
With the help of some more traits that is a very good start to be able to implement moc features in pure C++.
The experiment
I have been managing to re-implement most of the moc features such as signals and slots and properties using the proposals, without the need of moc. Of course since the compiler obviously doesn't have support for that proposal yet, I have been manually expanding the typedef...
and typename...
in the prototype.
This signal does nothing, by itself; it must be connected to a slot, which is an object that acts as a recipient for a signal and, given one, acts on it. Connecting Built-In PySide/PyQt Signals. Qt widgets have a number of signals built in. For example, when a QPushButton is clicked, it emits its clicked signal.
- Square6 Nokia offers a seamless integration of Qt development tools for the Eclipse platform. Square6 The integration allows developers to use this standard development environment without needing to take care of any Qt-related build steps or tools. Qt Eclipse Integration (1/2).
- The Signal/Slot Editor. The signal and slot used in a connection can be changed after it has been set up. When a connection is configured, it becomes visible in Qt Designer's signal and slot editor where it can be further edited. You can also edit signal/slot connections by double-clicking on the connection path or one of its labels to display.
The code does a lot of template tricks to handle strings and array at compile time and generates a QMetaObject
that is even binary compatible to the one generated by moc
Signals And Slots Qt
About Qt and moc
Qt is a cross platform C++ toolkit specialized for developing applications with user interfaces.Qt code is purely standard C++ code, however it needs a code generator to provide introspection data: the Meta Object Compiler (moc). That little utility parses the C++ headers and generates additional C++ code that is compiled alongside the program. The generated code contains the implementations of the Qt signals, and builds the QMetaObject
(which embeds string tables with the names of all methods and properties).
Historically, the first mission of the moc was to enable signals and slots using a nice syntax. It is also used for the property system. The first use of the properties was for the property editor in Qt designer, then it became used for integration with a scripting language (QtScript), and is now widely used to access C++ objects from QML.
(For an explanation of the inner working of the signals and slots, read one of my previous articles:How Qt signals and slots work.)
Generating the QMetaObject at compile time
We could ask the programmer to add a macro in the .cpp such as Q_OBJECT_IMPL(MyObject)
which would be expanded to that code:
The implementation of createMetaObject uses the reflection capabilities to find out all the slots, signals and properties in order to build the metaobject at compile time. The functionqt_metacall_impland qt_static_metacall_impl are generic implementations that use the same data to call the right function. Click on the function name if you are interested in the implementation.
Annotating signals and slots
We could perhaps use C++11 attributes for that. In that case, it would be convenient if attributes could be placed next to the access specifiers.(There is already a proposal to add group access specifiers, but it does not cover the attributes.)
Then we would need compile time traits such as has_attribute<&MyObject::myFunction>('qt::signal')
Function traits
I just mentioned has_attribute
. Another trait will be needed to determine if the function is public, protected or private.
The proposal also mentioned we could use typename<&MyObject::myFunction>...
to get the parameter names. We indeed need them as they are used when you connect to a signal in QML to access the parameters.
And currently we are able to call a function without specifying all the parameters if there are default parameters. So we need to know the default parameters at compile time to create them at run time.
However, there is a problem with functions traits in that form: non-type template parameters of function type need to be function literals. (See this stackoverflow question.) Best explained with this code:
But as we are introspecting, we get, at best, the functions in constexpr
form. So this restriction would need to be removed.
The properties
We have not yet solved the Q_PROPERTY
feature.
I'm afraid we will have to introduce a new macro because it is most likely not possible to keep the source compatibility with Q_PROPERTY
. A way to do it would be to add static constexpr members of a recognizable type. For example, this is my prototype implementation:
To be used like this
We can find the properties by looking for the QProperty<...>
members and removing the 'qt_property_'
part of the name. Then all the information about the getter, setter and others are available.
And if we want to keep the old Q_PROPERTY
?
I was wondering if it is possible to even keep the source compatibility using the same macro: I almost managed:
This basically creates a function with two arguments. The name of the first argument is the name of the property, which we can get via reflection. Its type is the type of the property. The second argument is of the type QPropertyHolder<...>::Property<...>
, which contains pointers to the member functions for the different attributes of the property. Introspection would allow us to dig into this type.
But the problem here is that it needs to do a typedef ThisType
. It would be nice if there was something like deltype(*this)
that would be working in the class scope without any members, then we would put this typedef within the Q_OBJECT
macro.
Re-implementing the signals
This is going to be the big problem as I have no idea how to possibly do that. We need, for each signal, to generateits code. Something that could look like this made-up syntax:
The implementation of SignalImplementation::impl is then easy.
Summary: What would we need
In summary, this is what would be needed in the standard to implement Qt like features without the need of moc:
- The N3951 proposal: C++ type reflection via variadic template expansion would be a really good start.
- Allow attributes within the access specifier (
public [[qt::slot]]:
) - Traits to get the attributes (
constexpr std::has_attribute<&MyClass::mySignal>('qt::signal');
- Traits to get the access of a function (public, private, protected) (for
QMetaMethod::access
) - A way to declare functions.
- Getting default value of arguments.
- Accessing function traits via
constexpr
expression. - Listing the constructors. (for
Q_INVOKABLE
constructors.)
What would then still be missing
Q_PLUGIN_METADATA
which allows to load a JSON file, and put the information in the binary:
I'm afraid we will still need a tool for that. (Because I hardly see the c++ compiler opening a file and parsing JSON.) This does not really belong inmoc
anyway and is only there because moc was already existing.- Whatever else I missed or forgot. :-)
Conclusion: will finally moc disappear?
Until Qt6, we have to maintain source and binary compatibility. Therefore moc
is not going to disappear, but may very well be optional for new classes. We could have a Q_OBJECT2
which does not need moc, but would use only standard C++.
In general, while it would be nice to avoid the moc, there is also no hurry to get rid of it. It is generally working fine and serving its purpose quite well. A pure template C++ implementation is not necessarily easier to maintain. Template meta-programming should not be abused too much.
For a related experiment, have a look at my attempt to reimplement moc using libclang
Update:Verdigris, a library with macrosto create a QMetaObject without moc, was based on this work.
Qt5 Tutorial Signals and Slots - 2018
In this tutorial, we will learn QtGUI project with signal and slot mechanism.
File->New File or Project...
Applications->Qt Gui Application->Choose...
We keep the class as MainWindow as given by default.
Qt Signal Slot Parameter
Next->Finish
Let's open up Forms by double-clicking the mainwindow.ui to put gui components:
From the Widgets, drag Horizontal Slider and Progress Bar, and place them into the main window. Then,
Run the code. Now, if we move the slider, the progress will reflect the changes in the slider:
We did it via gui, but we can do it via direct programming.
Let's delete the signal and slot, and write the code for the signal and slot mechanism in the constructor of the MainWindow class as shown below:
Signals and slots are used for communication between objects. The signals and slots mechanism is a central feature of Qt and probably the part that differs most from the features provided by other frameworks.
In GUI programming, when we change one widget, we often want another widget to be notified. More generally, we want objects of any kind to be able to communicate with one another. For example, if a user clicks a Close button, we probably want the window's close() function to be called.Older toolkits achieve this kind of communication using callbacks. A callback is a pointer to a function, so if you want a processing function to notify you about some event you pass a pointer to another function (the callback) to the processing function. The processing function then calls the callback when appropriate. Callbacks have two fundamental flaws: Firstly, they are not type-safe. We can never be certain that the processing function will call the callback with the correct arguments. Secondly, the callback is strongly coupled to the processing function since the processing function must know which callback to call.
In Qt, we have an alternative to the callback technique: We use signals and slots. A signal is emitted when a particular event occurs. Qt's widgets have many predefined signals, but we can always subclass widgets to add our own signals to them. A slot is a function that is called in response to a particular signal. Qt's widgets have many pre-defined slots, but it is common practice to subclass widgets and add your own slots so that you can handle the signals that you are interested in.
The signals and slots mechanism is type safe: The signature of a signal must match the signature of the receiving slot. (In fact a slot may have a shorter signature than the signal it receives because it can ignore extra arguments.) Since the signatures are compatible, the compiler can help us detect type mismatches. Signals and slots are loosely coupled: A class which emits a signal neither knows nor cares which slots receive the signal. Qt's signals and slots mechanism ensures that if you connect a signal to a slot, the slot will be called with the signal's parameters at the right time. Signals and slots can take any number of arguments of any type. They are completely type safe.
All classes that inherit from QObject or one of its subclasses (e.g., QWidget) can contain signals and slots. Signals are emitted by objects when they change their state in a way that may be interesting to other objects. This is all the object does to communicate. It does not know or care whether anything is receiving the signals it emits. This is true information encapsulation, and ensures that the object can be used as a software component.
Slots can be used for receiving signals, but they are also normal member functions. Just as an object does not know if anything receives its signals, a slot does not know if it has any signals connected to it. This ensures that truly independent components can be created with Qt.You can connect as many signals as you want to a single slot, and a signal can be connected to as many slots as you need. It is even possible to connect a signal directly to another signal. (This will emit the second signal immediately whenever the first is emitted.)
- from Signals & Slots