Spec-Zone.ru › Qt 5.6

Почему Qt использует moc для сигналов и слотов?

Шаблоны — встроенный механизм в C++, позволяющий компилятору генерировать код на лету в зависимости от типа передаваемых аргументов. Поэтому шаблоны представляют большой интерес для создателей фреймворков, и мы используем продвинутые шаблоны во многих частях Qt. Однако есть ограничения: существуют вещи, которые легко выразить с помощью шаблонов, и вещи, которые невозможно выразить с помощью шаблонов. Класс-контейнер обобщенного вектора легко выражается, даже с частичной специализацией для типов указателей, в то время как функция, которая настраивает графический интерфейс пользователя на основе описания XML, заданного как строка, не может быть выражена как шаблон. И затем есть серая зона между ними. Вещи, которые можно реализовать с помощью шаблонов ценой увеличения размера кода, снижения читабельности, портируемости, удобства использования, расширяемости, надежности и, в конечном итоге, красоты дизайна. Как шаблоны, так и препроцессор C можно использовать для выполнения невероятно умных и поражающих воображение вещей. Но просто потому, что эти вещи можно сделать, это не обязательно означает, что их реализация — правильный выбор дизайна. К сожалению, код не предназначен для публикации в книгах, а для компиляции с реальными компиляторами на реальных операционных системах.

Вот несколько причин, по которым Qt использует moc:

Синтаксис имеет значение

Синтаксис — это не просто удобный синтаксис: синтаксис, который мы используем для выражения наших алгоритмов, может существенно повлиять на читабельность и поддерживаемость нашего кода. Синтаксис, используемый для сигналов и слотов Qt, доказал свою эффективность на практике. Синтаксис интуитивно понятен, прост в использовании и легко читается. Люди, изучающие Qt, находят, что синтаксис помогает им понять и использовать концепцию сигналов и слотов — несмотря на ее высокую степень абстрактности и общности. Это помогает программистам правильно проектировать с самого начала, даже не задумываясь о шаблонах проектирования.

Генераторы кода — это хорошо

Qt's moc (Meta Object Compiler) предоставляет чистый способ выйти за пределы возможностей языка компиляции. Это делается путем генерации дополнительного кода C++, который может быть скомпилирован любым стандартным компилятором C++. moc читает файлы исходного кода C++. Если он обнаруживает одно или несколько объявлений классов, содержащих макрос Q_OBJECT, он создает другой файл исходного кода C++, который содержит метаобъектный код для этих классов. Файл исходного кода C++, сгенерированный moc, должен быть скомпилирован и связан с реализацией класса (или он может быть #included в файл исходного кода класса). Как правило, moc вызывается не вручную, а автоматически системой сборки, поэтому программисту не требуется никаких дополнительных усилий.

moc — не единственный генератор кода, используемый Qt. Другим ярким примером является uic (Компилятор пользовательского интерфейса). Он принимает описание пользовательского интерфейса на XML и создает код C++, который настраивает форму. Вне Qt генераторы кода также распространены. Взять, например, rpc и idl, которые позволяют программам или объектам взаимодействовать через границы процессов или машин. Или огромное разнообразие генераторов сканеров и парсеров, где lex и yacc являются наиболее известными. Они принимают грамматическое описание в качестве входных данных и генерируют код, реализующий конечный автомат. Альтернативами генераторам кода являются взломанные компиляторы, проприетарные языки или графические инструменты разработки с диалогами или мастерами в одном направлении, которые генерируют неясную кодовую базу во время проектирования, а не компиляции. Вместо того, чтобы заставлять наших клиентов использовать проприетарный компилятор C++ или определенную интегрированную среду разработки, мы предоставляем им возможность использовать инструменты по своему выбору. Вместо того, чтобы заставлять программистов добавлять сгенерированный код в хранилища исходного кода, мы поощряем их добавлять наши инструменты в свою систему сборки: чище, безопаснее и более в духе UNIX.

GUI динамичны

C++ — это стандартизированный, мощный и сложный язык общего назначения. Это единственный язык, который используется во столь широком спектре программных проектов, охватывая все типы приложений, от целых операционных систем, серверов баз данных и высокопроизводительных графических приложений до обычных настольных приложений. Одним из ключей к успеху C++ является его масштабируемая конструкция языка, ориентированная на максимальную производительность и минимальное потребление памяти при одновременном сохранении совместимости с ANSI C.

При всех этих преимуществах есть и некоторые недостатки. Для C++ статическая модель объектов — явный недостаток по сравнению с динамическим подходом к обмену сообщениями Objective-C, когда речь идет о программировании графического пользовательского интерфейса на основе компонентов. То, что хорошо подходит для высокопроизводительного сервера базы данных или операционной системы, не обязательно является правильным выбором дизайна для GUI-фронта. С помощью moc, мы превратили этот недостаток в преимущество и добавили гибкость, необходимую для решения задач безопасного и эффективного программирования графического пользовательского интерфейса.

Наш подход выходит далеко за рамки того, что можно сделать с помощью шаблонов. Например, мы можем иметь свойства объектов. И мы можем иметь перегруженные сигналы и слоты, что кажется естественным при программировании на языке, где перегрузка является ключевой концепцией. Наши сигналы не добавляют ни одного байта к размеру экземпляра класса, что означает, что мы можем добавлять новые сигналы без нарушения бинарной совместимости.

Другим преимуществом является то, что мы можем исследовать сигналы и слоты объекта во время выполнения. Мы можем устанавливать соединения с помощью безопасного вызова по имени, не зная точных типов объектов, которые мы подключаем. Это невозможно с помощью шаблонов. Этот вид интроспекции во время выполнения открывает новые возможности, например, GUI, которые генерируются и подключаются из файлов XML пользовательского интерфейса Qt Designer.

Производительность вызовов — не всё

Реализация сигналов и слотов Qt не так быстра, как решение на основе шаблонов. В то время как отправка сигнала примерно стоит четыре обычных вызова функции с общими реализациями шаблонов, Qt требует усилий, сопоставимых примерно с десятью вызовами функций. Это не удивительно, так как механизм Qt включает в себя универсальный маршализующий элемент, интроспекцию, вызовы в очереди между различными потоками и, в конечном итоге, возможность выполнения скриптов. Он не полагается на чрезмерную вставку и расширение кода и обеспечивает беспрецедентную безопасность во время выполнения. Итераторы Qt безопасны, в то время как итераторы более быстрых систем на основе шаблонов — нет. Даже во время процесса отправки сигнала нескольким получателям эти получатели могут быть удалены безопасно, не вызывая сбоя вашей программы. Без этой безопасности ваша программа в конечном итоге может завершиться сбоем из-за трудно отлаживаемой ошибки чтения или записи освобожденной памяти.

Тем не менее, разве решение на основе шаблонов не может улучшить производительность приложения, использующего сигналы и слоты? Хотя верно, что Qt добавляет небольшую нагрузку к затратам на вызов слота через сигнал, стоимость вызова составляет лишь небольшую часть общей стоимости слота. Измерение производительности системы сигналов и слотов Qt обычно выполняется с пустыми слотами. Как только вы делаете что-то полезное в своих слотах, например, несколько простых строковых операций, накладные расходы вызовов становятся незначительными. Система Qt настолько оптимизирована, что все, что требует operator new или delete (например, строковые операции или вставка/удаление чего-либо из контейнера шаблона), значительно дороже, чем отправка сигнала.

Примечание: Если у вас есть соединение сигналов и слотов в узком внутреннем цикле задачи с высокой производительностью, и вы определили это соединение как узкое место, подумайте о использовании стандартного шаблона интерфейса слушателя вместо сигналов и слотов. В случаях, когда это происходит, вам, вероятно, понадобится только подключение 1:1. Например, если у вас есть объект, который загружает данные из сети, вполне разумно использовать сигнал для указания того, что запрошенные данные поступили. Но если вам нужно отправлять каждый байт по одному в потребительский объект, используйте интерфейс слушателя, а не сигналы и слоты.

Без ограничений

Поскольку у нас был moc для сигналов и слотов, мы могли добавить к нему другие полезные вещи, которые нельзя было сделать с помощью шаблонов. Среди них — отформатированные переводы с помощью сгенерированной функции tr() и расширенная система свойств с интроспекцией и расширенной информацией о типе во время выполнения. Сама система свойств — это огромное преимущество: мощный и универсальный инструмент проектирования пользовательского интерфейса, такой как Qt Designer, было бы намного труднее написать — если не невозможно — без мощной и интроспективной системы свойств. Но на этом не всё. Мы также предоставляем динамический механизм qobject_cast<T>(), который не зависит от RTTI системы и, следовательно, не имеет ее ограничений. Мы используем его для безопасного запроса интерфейсов из динамически загружаемых компонентов. Другой областью применения являются динамические метаобъекты. Мы можем, например, взять компоненты ActiveX и во время выполнения создать вокруг них метаобъект. Или мы можем экспортировать компоненты Qt как компоненты ActiveX, экспортируя их метаобъект. Всё это невозможно сделать с помощью шаблонов.

C++ с moc в значительной степени предоставляет нам гибкость Objective-C или среды выполнения Java, сохраняя при этом уникальные преимущества производительности и масштабируемости C++. Именно это делает Qt гибким и удобным инструментом, которым он является сегодня.

© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/archives/qt-5.6/why-moc.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API