Использование компилятора метаобъектов (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.13/moc.html