![]() |
Главная · Все классы · Основные классы · Классы по группам · Модули · Функции | ![]() |
Мета-объектный компилятор, 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. Этот объект будет затем включен в список объектных файлов, которые компонуются вместе на последнем этапе сборки программы.
Для всех программ, за исключением простейших тестовых, рекомендуется автоматизировать запуск 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.
Менее значимо, что перечисленные конструкции являются недопустимыми. Все из них имеют альтернативы, которые, мы полагаем, в большинстве случаев являются более подходящими, поэтому удаление этих ограничений для нас не имеет высокого приоритета.
Если вы используете множественное наследование, то 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); };
Так как 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).
moc будет жаловаться, если вы попытаетесь поместить в разделы класса signals или slots вместо сигналов и слотов другие конструкции.
Смотрите также Мета-объектная система, Сигналы и слоты и Система свойств Qt.
Copyright © 2008 Trolltech | Торговые марки | Qt 4.3.5 |