Главная · Все классы · Основные классы · Классы по группам · Модули · Функции

Использование мета-объектного компилятора (Meta-Object Compiler, moc)

Мета-объектный компилятор, moc, - программа, которая обрабатывает расширения C++ от Qt.

Инструмент moc читает заголовочный файл C++. Если он находит одно или более объявлений классов, которые содержат макрос Q_OBJECT, то он порождает файл исходного кода C++, содержащий мета-объектный код для этих классов. Кроме всего прочего, мета-объектный код требуется механизму сигналов и слотов, информации о типе времени выполнения и системы динамических свойств.

Файл исходного кода C++, сгенерированный moc, должен компилироваться и компоноваться с помощью реализации класса.

Если вы используете qmake для создания своих make-файлов, в правила сборки будет включен вызов moc когда это необходимо, поэтому вам не нужно использовать moc непосредственно. Дополнительную информацию по истории moc смотрите в статье Почему Qt не использует шаблон для сигналов и слотов?

Использование

Обычно moc используется с входным файлом, содержащим такое объявление класса:

 class MyClass : public QObject
 {
     Q_OBJECT

 public:
     MyClass(QObject *parent = 0);
     ~MyClass();

 signals:
     void mySignal();

 public slots:
     void mySlot();
 };

В дополнение к сигналам и слотам, показанным выше, moc также реализует свойства объекта как в следующем примере. Макрос Q_PROPERTY() объявляет свойство объекта, в то время как Q_ENUMS() объявляет список перечислимых типов внутри класса используемый внутри системы свойств.

В следующем примере мы объявляем свойство перечислимого типа 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);
     Priority priority() const;
 };

Макрос Q_FLAGS() объявляет перечисления, которые используются как флаги, т.е. соединены через ИЛИ. Другой макрос, 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. Этот файл можно затем компилировать обычным образом, получив в результате объектный файл, например в Windows - moc_myclass.obj. Этот объект будет затем включен в список объектных файлов, которые компонуются вместе на последнем этапе сборки программы.

Написание make-правил для вызывания moc

Для всех программ, за исключением простейших тестовых, рекомендуется автоматизировать запуск moc. Добавляя некоторые правила к make-файлу вашей программы, make может позаботиться о запуске moc, если нужно, и обработке вывода moc.

Мы рекомендуем использовать для создания ваших make-файлов инструмент генерации make-файлов qmake от Trolltech. Этот инструмент генерирует 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>Добавляет каталог к пути поиска заголовочных файлов.
-EТолько для предварительной обработки; не генерировать мета-объектный код.
-D<macro>[=<def>]Задает макрос с необязательным определением.
-U<macro>Отменяет определение макроса.
-hВыводит на экран использование и список опций.
-vВыводит на экран номер версии moc'а.

Вы можете явно указать 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++. Главная проблема - что классы-шаблоны не могут содержать сигналов или слотов. Вот пример:

 class SomeTemplate<int> : public QFrame
 {
     Q_OBJECT
     ...

 signals:
     void mySignal(int);
 };

Другое ограничение заключается в том, что moc не расширяет макросы, поэтому вы не можете, например, использовать макрос для объявления сигнала/слота или использовать его для объявления базового класса для QObject.

Менее значимо, что перечисленные конструкции являются недопустимыми. Все из них имеют альтернативы, которые, мы полагаем, в большинстве случаев являются более подходящими, поэтому удаление этих ограничений для нас не имеет высокого приоритета.

Множественное наследование требует указания 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 *);
 };

Иногда будет даже лучше заменить указатель на функцию наследованием и виртуальными функциями.

Перечисления и переименования типов (Typedefs) должны быть полностью уточнены для параметров сигналов и слотов

Когда проверяются сигнатуры аргументов, QObject::connect() сравнивает типы данных посимвольно. Соответственно, Alignment и Qt::Alignment будут рассматриваться как два отдельных типа. Чтобы обойти это ограничение убедитесь, что полностью уточнили типы данных когда объявляли сигналы и слоты и когда создавали соединения. Например:

 class MyClass : public QObject
 {
     Q_OBJECT

     enum Error {
         ConnectionRefused,
         RemoteHostClosed,
         UnknownError
     };

 signals:
     void stateChanged(MyClass::Error error);
 };

Тип макросов не может использоваться в качестве параметров для сигналов и слотов

Так как moc не расширяет директивы #define, тип макросов, который получает аргумент, не будет работать в сигналах и слотах. Вот некорректный пример:

 #ifdef ultrix
 #define SIGNEDNESS(a) unsigned a
 #else
 #define SIGNEDNESS(a) a
 #endif

 class Whatever : public QObject
 {
     Q_OBJECT

 signals:
     void someSignal(SIGNEDNESS(int));
 };

Макрос без параметров будет работать.

Вложенные классы не могут содержать сигналы или слоты

Вот пример проблемной конструкции:

 class A
 {
 public:
     class B
     {
         Q_OBJECT

     public slots:   // WRONG
         void b();
     };
 };

Возвращаемыми типами сигналов/слотов не могут быть ссылки

Сигналы и слоты могут возвращать типы, но возвращаемые ссылки на сигналы или на слоты будут трактоваться как возвращаемое пустое значение (void).

Только сигналы и слоты могут появляться в разделах класса signals и slots

moc будет жаловаться, если вы попытаетесь поместить в разделы класса signals или slots вместо сигналов и слотов другие конструкции.

Смотрите также Мета-объектная система, Сигналы и слоты и Система свойств Qt.


Copyright © 2008 Trolltech Торговые марки
Qt 4.3.5