Spec-Zone.ru › Qt 5.11

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

Spec-Zone.ru

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