Qt 之实用程序 moc 学习

本文关注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

    • 控制是否包含include语句,默认是自动(对比Q_OBJECT这个宏分别出现在*.h和*.cpp时生成的文件的不同)

moc 的处理分两个阶段(共3个步骤):

  • Preprocessor
  • Moc
    • parse
    • generate

    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 下的

  • keywords.cpp
  • ppkeywords.cpp

这两个文件是由$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;
        }
    }
}

output

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


你可能感兴趣的:(object,qt,token,generator,preprocessor,enums)