Использование метаобъектного компилятора (moc)
Метаобъектный компилятор, moc, — это программа, которая обрабатывает расширения Qt на C++.
Инструмент moc считывает заголовочный файл C++. Если он находит одну или несколько деклараций классов, содержащих макрос Q_OBJECT, он генерирует файл исходного кода C++ с метаобъектным кодом для этих классов. Метаобъектный код необходим, в частности, для механизма сигналов и слотов, информации о типе во время выполнения и динамической системы свойств.
Файл исходного кода C++, сгенерированный moc, необходимо скомпилировать и связать с реализацией класса.
Если вы используете qmake для создания файлов make, будут включены правила сборки, которые вызывают moc при необходимости, поэтому вам не нужно использовать moc напрямую. Дополнительную информацию о moc, см. в Почему Qt использует moc для сигналов и слотов?
Использование
moc обычно используется с входным файлом, содержащим объявления классов, например:
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass(QObject *parent = 0);
~MyClass();
signals:
void mySignal();
public slots:
void mySlot();
}; Помимо сигналов и слотов, показанных выше, moc также реализует свойства объектов, как в следующем примере. Макрос Q_PROPERTY() объявляет свойство объекта, а макрос Q_ENUM() объявляет список типов перечислений внутри класса для использования в системе свойств.
В следующем примере мы объявляем свойство типа перечисления Priority, которое также называется priority и имеет функцию получения priority() и функцию установки setPriority().
class MyClass : public QObject
{
Q_OBJECT
Q_PROPERTY(Priority priority READ priority WRITE setPriority)
Q_ENUMS(Priority)
public:
enum Priority { High, Low, VeryHigh, VeryLow };
MyClass(QObject *parent = 0);
~MyClass();
void setPriority(Priority priority) { m_priority = priority; }
Priority priority() const { return m_priority; }
private:
Priority m_priority;
}; Макрос Q_FLAGS() объявляет перечисления, которые должны использоваться как флаги, т. е. объединяются с помощью операции OR. Другой макрос, Q_CLASSINFO(), позволяет прикреплять дополнительные пары имя/значение к метаобъекту класса:
class MyClass : public QObject
{
Q_OBJECT
Q_CLASSINFO("Author", "Oscar Peterson")
Q_CLASSINFO("Status", "Active")
public:
MyClass(QObject *parent = 0);
~MyClass();
}; Выходной код, сгенерированный moc, необходимо скомпилировать и связать, как и другой код C++ в вашей программе; в противном случае сборка завершится ошибкой на финальной стадии связывания. Если вы используете qmake, это делается автоматически. Каждый раз, когда qmake выполняется, он анализирует заголовочные файлы проекта и генерирует правила make для вызова moc для файлов, содержащих макрос Q_OBJECT.
Если объявление класса находится в файле myclass.h, выходные данные moc должны быть помещены в файл с именем moc_myclass.cpp. Затем этот файл должен быть скомпилирован как обычно, в результате чего получится файл объекта, например, moc_myclass.obj в Windows. Этот объект должен быть включен в список файлов объектов, которые связываются вместе на последнем этапе сборки программы.
Написание правил make для вызова moc
Для программ, отличных от простейших тестовых программ, рекомендуется автоматизировать запуск moc. Добавив некоторые правила в файл make вашей программы, make может позаботиться о запуске moc при необходимости и обработке выходных данных moc.
Мы рекомендуем использовать инструмент генерации файлов make qmake для создания файлов make. Этот инструмент генерирует файл make, который обрабатывает все необходимые moc.
Если вы хотите создать свои файлы make, вот несколько советов по включению обработки moc.
Для деклараций классов Q_OBJECT в заголовочных файлах вот полезное правило файла make, если вы используете только GNU make:
moc_%.cpp: %.h
moc $(DEFINES) $(INCPATH) $< -o $@ Если вы хотите создать переносимый код, вы можете использовать отдельные правила следующего вида:
moc_foo.cpp: foo.h
moc $(DEFINES) $(INCPATH) $< -o $@ Также необходимо добавить moc_foo.cpp к вашей переменной SOURCES (замените на ваше любимое имя) и moc_foo.o или moc_foo.obj к вашей переменной OBJECTS.
Оба примера предполагают, что $(DEFINES) и $(INCPATH) расширяются до параметров определения и включения, которые передаются компилятору C++. Они необходимы moc для предварительной обработки исходных файлов.
Хотя мы предпочитаем называть наши файлы исходного кода C++ .cpp, вы можете использовать любые другие расширения, например, .C, .cc, .CC, .cxx, и .c++, если вам так больше нравится.
Для деклараций классов Q_OBJECT в файлах реализации (.cpp): мы предлагаем правило файла make, подобное этому:
foo.o: foo.moc
foo.moc: foo.cpp
moc $(DEFINES) $(INCPATH) -i $< -o $@ Это гарантирует, что make запустит moc перед компиляцией foo.cpp. Затем вы можете добавить
#include "foo.moc"
в конец foo.cpp, где все классы, объявленные в этом файле, полностью известны.
Параметры командной строки
Вот поддерживаемые параметры командной строки для moc:
| Параметр | Описание |
|---|---|
-o<file> |
Записать вывод в <file> вместо стандартного вывода. |
-f[<file>] |
Вынудить генерацию оператора #include в выводе. Это значение по умолчанию для заголовочных файлов, расширение которых начинается с H или h. Этот параметр полезен, если у вас есть заголовочные файлы, которые не следуют стандартным соглашениям об именовании. Часть <file> необязательна. |
-i |
Не генерировать оператор #include в выводе. Это может быть использовано для запуска moc на файле C++, содержащем одно или несколько объявлений классов. Затем вы должны #include метаобъектный код в файле .cpp. |
-nw |
Не генерировать никаких предупреждений. (Не рекомендуется.) |
-p<path> |
Заставляет moc добавлять <path>/ к имени файла в сгенерированном операторе #include. |
-I<dir> |
Добавить dir в путь включения для заголовочных файлов. |
-E |
Предварительная обработка только; метаобъектный код не генерируется. |
-D<macro>[=<def>] |
Определить макрос с необязательным определением. |
-U<macro> |
Отменить определение макроса. |
-M<key=value> |
Добавить дополнительные метаданные к плагинам. Если для класса задан Q_PLUGIN_METADATA, пара ключ-значение будет добавлена в его метаданные. Это приведет к включению JSON-объекта, который будет разрешен во время выполнения для плагина (доступен из QPluginLoader). Этот аргумент обычно используется для добавления информации, получаемой из системы сборки, к статическим плагинам. |
@<file> |
Прочитать дополнительные параметры командной строки из <file>. Каждая строка файла обрабатывается как отдельный параметр. Пустые строки игнорируются. Обратите внимание, что этот параметр не поддерживается внутри самого файла параметров (т. е. файл параметров не может "включать" другой файл). |
-h |
Отобразить справку и список параметров. |
-v |
Отобразить номер версии moc. |
-Fdir |
macOS. Добавить каталог фреймворка dir в начало списка каталогов, которые нужно искать для заголовочных файлов. Эти каталоги чередуются с теми, что указаны параметрами -I, и просматриваются слева направо (см. руководство по gcc). Обычно используйте -F /Library/Frameworks/ |
Вы можете явно указать moc, чтобы он не анализировал части заголовочного файла. moc определяет препроцессорную переменную Q_MOC_RUN. Любой код, заключенный в
#ifndef Q_MOC_RUN
...
#endif будет пропущен moc.
Диагностика
moc предупредит вас о ряде опасных или некорректных конструкций в объявлениях класса Q_OBJECT.
Если вы получите ошибки связывания на последнем этапе сборки программы, говорящие о том, что YourClass::className() не определено или что у YourClass отсутствует vtable, что-то сделано неправильно. Чаще всего вы забыли скомпилировать или #include сгенерированный moc код C++, или (в первом случае) включить этот файл объекта в команду связывания. Если вы используете qmake, попробуйте перезапустить его, чтобы обновить файл make. Это должно сработать.
Ограничения
moc не обрабатывает весь C++. Основная проблема заключается в том, что классы шаблонов не могут иметь макрос Q_OBJECT. Вот пример:
class SomeTemplate<int> : public QFrame
{
Q_OBJECT
...
signals:
void mySignal(int);
}; Следующие конструкции являются недопустимыми. Все они имеют альтернативы, которые, по нашему мнению, обычно лучше, поэтому удаление этих ограничений не является для нас первоочередной задачей.
Множественное наследование требует, чтобы QObject был первым
Если вы используете множественное наследование, moc предполагает, что первый наследуемый класс является подклассом QObject. Также убедитесь, что только первый наследуемый класс является QObject.
// correct
class SomeClass : public QObject, public OtherClass
{
...
}; Виртуальное наследование с QObject не поддерживается.
Указатели на функции не могут быть параметрами сигнала или слота
В большинстве случаев, когда вы хотели бы использовать указатели на функции в качестве параметров сигналов или слотов, мы считаем, что наследование является лучшей альтернативой. Вот пример недопустимого синтаксиса:
class SomeClass : public QObject
{
Q_OBJECT
public slots:
void apply(void (*apply)(List *, void *), char *); // WRONG
}; Вы можете обойти это ограничение следующим образом:
typedef void (*ApplyFunction)(List *, void *);
class SomeClass : public QObject
{
Q_OBJECT
public slots:
void apply(ApplyFunction, char *);
}; Иногда даже лучше заменить указатель на функцию наследованием и виртуальными функциями.
Перечисления и псевдонимы типов должны быть полностью квалифицированы для параметров сигналов и слотов
При проверке сигнатур своих аргументов, QObject::connect() сравнивает типы данных буквально. Таким образом, Alignment и Qt::Alignment обрабатываются как два разных типа. Чтобы обойти это ограничение, убедитесь, что типы данных полностью квалифицированы при объявлении сигналов и слотов и при установлении соединений. Например:
class MyClass : public QObject
{
Q_OBJECT
enum Error {
ConnectionRefused,
RemoteHostClosed,
UnknownError
};
signals:
void stateChanged(MyClass::Error error);
}; Вложенные классы не могут иметь сигналы или слоты
Вот пример нарушающей конструкции:
class A
{
public:
class B
{
Q_OBJECT
public slots: // WRONG
void b();
};
}; Типы возврата сигналов/слотов не могут быть ссылками
Сигналы и слоты могут иметь типы возврата, но сигналы или слоты, возвращающие ссылки, будут обрабатываться как возвращающие void.
Только сигналы и слоты могут появляться в разделах signals и slots класса
moc будет жаловаться, если вы попытаетесь поместить другие конструкции в секции signals или slots класса, кроме сигналов и слотов.
См. такжеСистему метаобъектов, Сигналы и слоты и Систему свойств Qt.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.9/moc.html