Qt Preparation
https://doc.qt.io/qt-5/qobject.html#Q_OBJECT
For important Qt related questions.
Signal & Slot Mechanism:-
The Meta-Object System
Using the Meta-Object Compiler (moc)
The Meta-Object Compiler,
moc
, is the program that handles Qt's C++ extensions.
The
moc
tool reads a C++ header file. If it finds one or more class declarations that contain the Q_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.
If you use qmake to create your makefiles, build rules will be included that call the moc when required, so you will not need to use the moc directly. For more background information on
moc
, see Why Does Qt Use Moc for Signals and Slots?Multiple Inheritance Requires QObject to Be First
If you are using multiple inheritance,
moc
assumes that the first inherited class is a subclass of QObject. Also, be sure that only the first inherited class is a QObject.// correct class SomeClass : public QObject, public OtherClass { ... };
Virtual inheritance with QObject is not supported.
Function Pointers Cannot Be Signal or Slot Parameters
In most cases where you would consider using function pointers as signal or slot parameters, we think inheritance is a better alternative. Here is an example of illegal syntax:
class SomeClass : public QObject { Q_OBJECT public slots: void apply(void (*apply)(List *, void *), char *); // WRONG };
You can work around this restriction like this:
typedef void (*ApplyFunction)(List *, void *); class SomeClass : public QObject { Q_OBJECT public slots: void apply(ApplyFunction, char *); };
It may sometimes be even better to replace the function pointer with inheritance and virtual functions.
Enums and Typedefs Must Be Fully Qualified for Signal and Slot Parameters
When checking the signatures of its arguments, QObject::connect() compares the data types literally. Thus, Alignment and Qt::Alignment are treated as two distinct types. To work around this limitation, make sure to fully qualify the data types when declaring signals and slots, and when establishing connections. For example:
class MyClass : public QObject { Q_OBJECT enum Error { ConnectionRefused, RemoteHostClosed, UnknownError }; signals: void stateChanged(MyClass::Error error); };
Nested Classes Cannot Have Signals or Slots
Here's an example of the offending construct:
class A { public: class B { Q_OBJECT public slots: // WRONG void b(); }; };
Signal/Slot return types cannot be references
Signals and slots can have return types, but signals or slots returning references will be treated as returning void.
Only Signals and Slots May Appear in the signals
and slots
Sections of a Class
moc
will complain if you try to put other constructs in the signals
or slots
sections of a class than signals and slots.Why Does Qt Use Moc for Signals and Slots?
Templates are a builtin mechanism in C++ that allows the compiler to generate code on the fly, depending on the type of the arguments passed. As such, templates are highly interesting to framework creators, and we do use advanced templates in many places in Qt. However, there are limitations: There are things that you can easily express with templates, and there are things that are impossible to express with templates. A generic vector container class is easily expressible, even with partial specialisation for pointer types, while a function that sets up a graphical user interface based on an XML description given as a string is not expressible as a template. And then there is a gray area in between. Things that you can hack with templates at the cost of code size, readability, portability, usability, extensability, robustness and ultimately design beauty. Both templates and the C preprocessor can be stretched to do incredibility smart and mind boggling things. But just because those things can be done, it does not necessarily mean doing them is the right design choice. Code, unfortunately, is not meant to be published in books, but compiled with real-world compilers on real-world operating systems.
Here are some reasons why Qt uses the moc:
Syntax Matters
Syntax isn't just sugar: the syntax we use to express our algorithms can significantly affect the readability and maintainability of our code. The syntax used for Qt's signals and slots has proved very successful in practice. The syntax is intuitive, simple to use and easy to read. People learning Qt find the syntax helps them understand and utilize the signals and slots concept -- despite its highly abstract and generic nature. This helps programmers get their design right from the very beginning, without even having to think about design patterns.
Code Generators are Good
Qt's
moc
(Meta Object Compiler) provides a clean way to go beyond the compiled language's facilities. It does so by generating additional C++ code which can be compiled by any standard C++ compiler. The moc
reads C++ source files. If it finds one or more class declarations that contain the Q_OBJECT macro, it produces another C++ source file which contains the meta object code for those classes. The C++ source file generated by the moc
must be compiled and linked with the implementation of the class (or it can be #included
into the class's source file). Typically moc
is not called manually, but automatically by the build system, so it requires no additional effort by the programmer.
The
moc
is not the only code generator Qt is using. Another prominent example is the uic
(User Interface Compiler). It takes a user interface description in XML and creates C++ code that sets up the form. Outside Qt, code generators are common as well. Take for example rpc
and idl
, that enable programs or objects to communicate over process or machine boundaries. Or the vast variety of scanner and parser generators, with lex
and yacc
being the most well-known ones. They take a grammar specification as input and generate code that implements a state machine. The alternatives to code generators are hacked compilers, proprietary languages or graphical programming tools with one-way dialogs or wizards that generate obscure code during design time rather than compile time. Rather than locking our customers into a proprietary C++ compiler or into a particular Integrated Development Environment, we enable them to use whatever tools they prefer. Instead of forcing programmers to add generated code into source repositories, we encourage them to add our tools to their build system: cleaner, safer and more in the spirit of UNIX.GUIs are Dynamic
C++ is a standarized, powerful and elaborate general-purpose language. It's the only language that is exploited on such a wide range of software projects, spanning every kind of application from entire operating systems, database servers and high end graphics applications to common desktop applications. One of the keys to C++'s success is its scalable language design that focuses on maximum performance and minimal memory consumption whilst still maintaining ANSI C compatibility.
For all these advantages, there are some downsides. For C++, the static object model is a clear disadvantage over the dynamic messaging approach of Objective C when it comes to component-based graphical user interface programming. What's good for a high end database server or an operating system isn't necessarily the right design choice for a GUI frontend. With
moc
, we have turned this disadvantage into an advantage, and added the flexibility required to meet the challenge of safe and efficient graphical user interface programming.
Our approach goes far beyond anything you can do with templates. For example, we can have object properties. And we can have overloaded signals and slots, which feels natural when programming in a language where overloads are a key concept. Our signals add zero bytes to the size of a class instance, which means we can add new signals without breaking binary compatibility.
Another benefit is that we can explore an object's signals and slots at runtime. We can establish connections using type-safe call-by-name, without having to know the exact types of the objects we are connecting. This is impossible with a template based solution. This kind of runtime introspection opens up new possibilities, for example GUIs that are generated and connected from Qt Designer's XML UI files.
Calling Performance is Not Everything
Qt's signals and slots implementation is not as fast as a template-based solution. While emitting a signal is approximately the cost of four ordinary function calls with common template implementations, Qt requires effort comparable to about ten function calls. This is not surprising since the Qt mechanism includes a generic marshaller, introspection, queued calls between different threads, and ultimately scriptability. It does not rely on excessive inlining and code expansion and it provides unmatched runtime safety. Qt's iterators are safe while those of faster template-based systems are not. Even during the process of emitting a signal to several receivers, those receivers can be deleted safely without your program crashing. Without this safety, your application would eventually crash with a difficult to debug free'd memory read or write error.
Nonetheless, couldn't a template-based solution improve the performance of an application using signals and slots? While it is true that Qt adds a small overhead to the cost of calling a slot through a signal, the cost of the call is only a small proportion of the entire cost of a slot. Benchmarking against Qt's signals and slots system is typically done with empty slots. As soon as you do anything useful in your slots, for example a few simple string operations, the calling overhead becomes negligible. Qt's system is so optimized that anything that requires operator new or delete (for example, string operations or inserting/removing something from a template container) is significantly more expensive than emitting a signal.
Aside: If you have a signals and slots connection in a tight inner loop of a performance critical task and you identify this connection as the bottleneck, think about using the standard listener-interface pattern rather than signals and slots. In cases where this occurs, you probably only require a 1:1 connection anyway. For example, if you have an object that downloads data from the network, it's a perfectly sensible design to use a signal to indicate that the requested data arrived. But if you need to send out every single byte one by one to a consumer, use a listener interface rather than signals and slots.
No Limits
Because we had the
moc
for signals and slots, we could add other useful things to it that could not be done with templates. Among these are scoped translations via a generated tr()
function, and an advanced property system with introspection and extended runtime type information. The property system alone is a great advantage: a powerful and generic user interface design tool like Qt Designer would be a lot harder to write - if not impossible - without a powerful and introspective property system. But it does not end here. We also provide a dynamic qobject_cast<T>() mechanism that does not rely on the system's RTTI and thus does not share its limitations. We use it to safely query interfaces from dynamically loaded components. Another application domain are dynamic meta objects. We can e.g. take ActiveX components and at runtime create a meta object around it. Or we can export Qt components as ActiveX components by exporting its meta object. You cannot do either of these things with templates.
C++ with the
moc
essentially gives us the flexibility of Objective-C or of a Java Runtime Environment, while maintaining C++'s unique performance and scalability advantages. It is what makes Qt the flexible and comfortable tool we have today.Q_OBJECT
The Q_OBJECT macro must appear in the private section of a class definition that declares its own signals and slots or that uses other services provided by Qt's meta-object system.
For example:
#include <QObject> class Counter : public QObject { Q_OBJECT public: Counter() { m_value = 0; } int value() const { return m_value; } public slots: void setValue(int value); signals: void valueChanged(int newValue); private: int m_value; };
Note: This macro requires the class to be a subclass of QObject. Use Q_GADGET instead of Q_OBJECT to enable the meta object system's support for enums in a class that is not a QObject subclass.
Q_PROPERTY(...)
This macro is used for declaring properties in classes that inherit QObject. Properties behave like class data members, but they have additional features accessible through the Meta-Object System.
Q_PROPERTY(type name (READ getFunction [WRITE setFunction] | MEMBER memberName [(READ getFunction | WRITE setFunction)]) [RESET resetFunction] [NOTIFY notifySignal] [REVISION int] [DESIGNABLE bool] [SCRIPTABLE bool] [STORED bool] [USER bool] [CONSTANT] [FINAL])
The property name and type and the
READ
function are required. The type can be any type supported by QVariant, or it can be a user-defined type. The other items are optional, but a WRITE
function is common. The attributes default to true except USER
, which defaults to false.
For example:
Q_PROPERTY(QString title READ title WRITE setTitle USER true)
For more details about how to use this macro, and a more detailed example of its use, see the discussion on Qt's Property System.
See also Qt's Property System.
Q_REVISION
Apply this macro to declarations of member functions to tag them with a revision number in the meta-object system. The macro is written before the return type, as shown in the following example:
class Window : public QWidget { Q_OBJECT Q_PROPERTY(int normalProperty READ normalProperty) Q_PROPERTY(int newProperty READ newProperty REVISION 1) public: Window(); int normalProperty(); int newProperty(); public slots: void normalMethod(); Q_REVISION(1) void newMethod(); };
This is useful when using the meta-object system to dynamically expose objects to another API, as you can match the version expected by multiple versions of the other API. Consider the following simplified example:
Window window; int expectedRevision = 0; const QMetaObject *windowMetaObject = window.metaObject(); for (int i=0; i < windowMetaObject->methodCount(); i++) if (windowMetaObject->method(i).revision() <= expectedRevision) exposeMethod(windowMetaObject->method(i)); for (int i=0; i < windowMetaObject->propertyCount(); i++) if (windowMetaObject->property(i).revision() <= expectedRevision) exposeProperty(windowMetaObject->property(i));
Using the same Window class as the previous example, the newProperty and newMethod would only be exposed in this code when the expected version is 1 or greater.
Since all methods are considered to be in revision 0 if untagged, a tag of Q_REVISION(0) is invalid and ignored.
This tag is not used by the meta-object system itself. Currently this is only used by the QtQml module.
Q_SIGNAL
This is an additional macro that allows you to mark a single function as a signal. It can be quite useful, especially when you use a 3rd-party source code parser which doesn't understand a
signals
or Q_SIGNALS
groups.
Use this macro to replace the
signals
keyword in class declarations, when you want to use Qt Signals and Slots with a 3rd party signal/slot mechanism.
The macro is normally used when
no_keywords
is specified with the CONFIG
variable in the .pro
file, but it can be used even when no_keywords
is not specified.Q_SIGNALS
Use this macro to replace the
signals
keyword in class declarations, when you want to use Qt Signals and Slots with a 3rd party signal/slot mechanism.
The macro is normally used when
no_keywords
is specified with the CONFIG
variable in the .pro
file, but it can be used even when no_keywords
is not specified.Q_SLOT
This is an additional macro that allows you to mark a single function as a slot. It can be quite useful, especially when you use a 3rd-party source code parser which doesn't understand a
slots
or Q_SLOTS
groups.
Use this macro to replace the
slots
keyword in class declarations, when you want to use Qt Signals and Slots with a 3rd party signal/slot mechanism.
The macro is normally used when
no_keywords
is specified with the CONFIG
variable in the .pro
file, but it can be used even when no_keywords
is not specified.Q_SLOTS
Use this macro to replace the
slots
keyword in class declarations, when you want to use Qt Signals and Slots with a 3rd party signal/slot mechanism.
The macro is normally used when
no_keywords
is specified with the CONFIG
variable in the .pro
file, but it can be used even when no_keywords
is not specified.
Comments
Post a Comment