Spec-Zone.ru › Qt 6.1

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

Spec-Zone.ru

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