本文关注Qt的工具程序 moc 本身。
moc : 元对象编译器(Meta-Object Compiler)
moc -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtGui -I/usr/include/qt4 -I. -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED hled.h -o moc_hled.cpp
运行 moc -h 或者查看Manual关注几个选项:
-I<dir> |
add dir to the include path for header files |
-E |
preprocess only; do not generate meta object code |
-D<macro>[=<def>] |
define macro, with optional definition |
-U<macro> |
undefine macro |
-i |
do not generate an #include statement |
-p<path> |
path prefix for included file |
-f[<file>] |
force #include, optional file name |
其中:
-D -U
定义和反定义宏, (注:内部默认定义Q_MOC_RUN和__cplusplus两个宏)
-i -f
moc 的处理分两个阶段(共3个步骤):
Preprocessor pp; Moc moc; pp.macros["Q_MOC_RUN"]; pp.macros["__cplusplus"]; ... // 1. preprocess moc.symbols = pp.preprocessed(moc.filename, in); fclose(in); if (!pp.preprocessOnly) { // 2. parse moc.parse(); } // 3. and output meta object code if (pp.preprocessOnly) { fprintf(out, "%s\n", composePreprocessorOutput(moc.symbols).constData()); } else { moc.generate(out); }
lexical analysis
moc 将输入文件解析成一个 Token (即symbols) 的列表
moc.symbols = pp.preprocessed(moc.filename, in);
词法分析的基础是一个有限状态机(fix me?),源码位于 $QTDIR/src/tools/moc 下的
这两个文件是由$QTDIR/src/tools/moc/util下的工具程序生成的,generate_keywords.cpp 文件中是原始的(可读的) 关键词和Token的对应关系
... { "while", "WHILE" }, { "do", "DO" }, { "for", "FOR" }, { "break", "BREAK" }, { "continue", "CONTINUE" }, { "goto", "GOTO" }, { "return", "RETURN" }, { "Q_OBJECT", "Q_OBJECT_TOKEN" }, { "Q_GADGET", "Q_GADGET_TOKEN" }, { "Q_PROPERTY", "Q_PROPERTY_TOKEN" }, { "Q_ENUMS", "Q_ENUMS_TOKEN" }, { "Q_FLAGS", "Q_FLAGS_TOKEN" }, { "Q_DECLARE_FLAGS", "Q_DECLARE_FLAGS_TOKEN" }, { "Q_DECLARE_INTERFACE", "Q_DECLARE_INTERFACE_TOKEN" }, ...
在 Tokenization 之后,需要parse某些我们关注的Token,此时需要处理Token之间的关系。
moc.parse();
比如:对于 Q_DECLARE_METATYPE
void Moc::parseDeclareMetatype() { next(LPAREN); QByteArray typeName = lexemUntil(RPAREN); typeName.remove(0, 1); typeName.chop(1); metaTypes.append(typeName); }
对于 signals
void Moc::parseSignals(ClassDef *def) { next(COLON); while (inClass(def) && hasNext()) { ... FunctionDef funcDef; funcDef.access = FunctionDef::Protected; parseFunction(&funcDef); if (funcDef.isVirtual) warning("Signals cannot be declared virtual"); if (funcDef.inlineCode) error("Not a signal declaration"); def->signalList += funcDef; while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) { funcDef.wasCloned = true; funcDef.arguments.removeLast(); def->signalList += funcDef; } } }
moc.generate(out);
将metatype信息转换成代码并输出是通过另一个类来完成的:
for (i = 0; i < classList.size(); ++i) { Generator generator(&classList[i], metaTypes, out); generator.generateCode(); }
从类定义大致可以猜出它做了什么
class Generator { FILE *out; ClassDef *cdef; QVector<uint> meta_data; public: Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes, FILE *outfile = 0); void generateCode(); QMetaObject *generateMetaObject(bool ignoreProperties); private: void generateClassInfos(); void generateFunctions(QList<FunctionDef> &list, const char *functype, int type); void generateEnums(int index); void generateProperties(); void generateMetacall(); void generateStaticMetacall(const QByteArray &prefix); void generateSignal(FunctionDef *def, int index); ...
http://doc.qt.nokia.com/4.7/moc.html