Spec-Zone.ru › Qt 5.14

Использование метаобъектного компилятора (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):

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.14/moc.html

Spec-Zone.ru

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