打算看看Qt的源码了,毕竟也好几次被问到Qt的问题,现在想看看Qt里面的原理。查了一下说是从最初的版本看比较容易懂。在官网找了找,最低的版本都是1.4.2 了,但是更新时间确实2019年?还在更新不成?具体也不是很懂,咱也不知道去哪儿问,只能先把坑挖这儿有空了再填……

然后我在百度脑图上写了一下目录结构,如果想起了更新的话,应该会更新的……
image.png
html目录更像是文档,doc有点看不懂。重点还是src。我的目标是这样的,当看不懂src源码的时候,我再去看文档。主要Qt文档向来是只有用法,没有实现。所以还是得自己看源代码……
然后我发现src/moc文件夹有点意思,知道qt会将signalsQ_OBJECT这些宏处理一下。处理的指令就是moc,而ui文件则是使用uic预处理。很好奇它怎么做到的。看看目录

image.png
.l .t 和.y文件就是啥格式啊……
点击进去vscode让我找找插件,然后就出现了谜底:
image.png
这时候我们应该怎么样?诶,对了,大呼VSCODE天下第一!?
继续利用我们的互联网思维,google一下YACC,来到一个神奇的网站,当然不是58同城,而是很NB的IBM网站
image.png
看这意思,就是专门处理c语言文件的 语言,然后会形成新的文件。想了想在Qt里面可不是有中间的moc_xx.cc的文件嘛。
看看这个moc.y文件

obj_member_area:      qt_access_specifier    { BEGIN QT_DEF; }
              slot_area
            | SIGNALS        { BEGIN QT_DEF; }
              ':'  opt_signal_declarations
            | Q_OBJECT        { if ( tmpAccessPerm )
                moc_warn("Q_OBJECT is not in the private"
                    " section of the class.\n"
                    "Q_OBJECT is a macro that resets"
                    " access permission to \"private\".");
                          Q_OBJECTdetected = TRUE; }

然后就看到一个神奇的函数

void generateClass()              // generate C++ source code for a class
{
    static int gen_count = 0;
    char *hdr1 = "/****************************************************************************\n"
         "** %s meta object code from reading C++ file '%s'\n**\n";
    char *hdr2 = "** Created: %s\n"
         "**      by: The Qt Meta Object Compiler ($Revision: 2.25.2.11 $)\n**\n";
    char *hdr3 = "** WARNING! All changes made in this file will be lost!\n";
    char *hdr4 = "*****************************************************************************/\n\n";
    int   i;

    if ( skipClass )                // don't generate for class
    return;
    if ( !Q_OBJECTdetected ) {
    if ( signals.count() == 0 && slots.count() == 0 )
        return;
    generatedCode = TRUE;
    if ( displayWarnings )
        moc_err("The declaration of the class \"%s\" contains slots "
            "and/or signals\n\t but no Q_OBJECT macro!", className.data());
    } else {
    if ( superclassName.isEmpty() )
        moc_err("The declaration of the class \"%s\" contains the\n"
            "\tQ_OBJECT macro but does not inherit from any class!\n"
            "\tInherit from QObject or one of its descendants"
            " or remove Q_OBJECT. ", className.data() );
    }
    if ( templateClass ) {            // don't generate for class
    moc_err( "Sorry, Qt does not support templates that contain\n"
         "signals, slots or Q_OBJECT. This will be supported soon." );
    return;
    }
    generatedCode = TRUE;
    if ( gen_count++ == 0 ) {            // first class to be generated
    QDateTime dt = QDateTime::currentDateTime();
    QString dstr = dt.toString();
    QString fn = fileName;
    i = fileName.length()-1;
    while ( i>0 && fileName[i-1] != '/' && fileName[i-1] != '\\' )
        i--;                // skip path
    if ( i >= 0 )
        fn = &fileName[i];
    fprintf( out, hdr1, (const char*)className, (const char*)fn );
    fprintf( out, hdr2, (const char*)dstr );
    fprintf( out, hdr3 );
    fprintf( out, hdr4 );
    fprintf( out, "#if !defined(Q_MOC_OUTPUT_REVISION)\n" );
    fprintf( out, "#define Q_MOC_OUTPUT_REVISION %d\n", formatRevision );
    fprintf( out, "#elif Q_MOC_OUTPUT_REVISION != %d\n", formatRevision );
    fprintf( out, "#error \"Moc format conflict - "
         "please regenerate all moc files\"\n" );
    fprintf( out, "#endif\n\n" );
    if ( !noInclude )
        fprintf( out, "#include \"%s\"\n", (const char*)includeFile );
    fprintf( out, "#include <%sqmetaobject.h>\n", (const char*)qtPath );
    fprintf( out, "\n\n" );
    } else {
    fprintf( out, "\n\n" );
    }

//
// Generate virtual function className()
//
    fprintf( out, "const char *%s::className() const\n{\n    ",
         (const char*)className );
    fprintf( out, "return \"%s\";\n}\n\n", (const char*)className );

//
// Generate static metaObj variable
//
    fprintf( out, "QMetaObject *%s::metaObj = 0;\n\n", (const char*)className);

//
// Generate static meta-object constructor-object (we don't rely on
// it, except for QBuilder).
//
    fprintf( out, "\n#if QT_VERSION >= 200\n" );
    fprintf( out, "static QMetaObjectInit init_%s(&%s::staticMetaObject);\n\n",
    (const char*)className, (const char*)className );
    fprintf( out, "#endif\n\n" );

//
// Generate initMetaObject member function
//
    fprintf( out, "void %s::initMetaObject()\n{\n", (const char*)className );
    fprintf( out, "    if ( metaObj )\n\treturn;\n" );
    fprintf( out, "    if ( strcmp(%s::className(), \"%s\") != 0 )\n"
              "\tbadSuperclassWarning(\"%s\",\"%s\");\n",
             (const char*)superclassName, (const char*)superclassName,
             (const char*)className, (const char*)superclassName );
    fprintf( out, "\n#if QT_VERSION >= 200\n" );
    fprintf( out, "    staticMetaObject();\n");
    fprintf( out, "}\n\n");

//
// Generate staticMetaObject member function
//
    fprintf( out, "void %s::staticMetaObject()\n{\n", (const char*)className );
    fprintf( out, "    if ( metaObj )\n\treturn;\n" );
    fprintf( out, "    %s::staticMetaObject();\n", (const char*)superclassName );
    fprintf( out, "#else\n\n" );
    fprintf( out, "    %s::initMetaObject();\n", (const char*)superclassName );
    fprintf( out, "#endif\n\n" );
//
// Build slots array in staticMetaObject()
//
    generateFuncs( &slots, "slot", Slot_Num );

//
// Build signals array in staticMetaObject()
//
    generateFuncs( &signals, "signal", Signal_Num );

//
// Finally code to create and return meta object
//
    fprintf( out, "    metaObj = new QMetaObject( \"%s\", \"%s\",\n",
         (const char*)className, (const char*)superclassName );
    if ( slots.count() )
    fprintf( out, "\tslot_tbl, %d,\n", slots.count() );
    else
    fprintf( out, "\t0, 0,\n" );
    if ( signals.count() )
    fprintf( out, "\tsignal_tbl, %d );\n", signals.count());
    else
    fprintf( out, "\t0, 0 );\n" );

    fprintf( out, "}\n" );

//
// End of function initMetaObject()
//

//
// Generate internal signal functions
//
    Function *f;
    f = signals.first();            // make internal signal methods
    static bool included_list_stuff = FALSE;
    while ( f ) {
    QString typstr = "";            // type string
    QString valstr = "";            // value string
    QString argstr = "";            // argument string (type+value)
    char    buf[12];
    Argument *a = f->args->first();
    QString typvec[32], valvec[32], argvec[32];
    typvec[0] = "";
    valvec[0] = "";
    argvec[0] = "";

    i = 0;
    while ( a ) {                // argument list
        if ( !a->leftType.isEmpty() || !a->rightType.isEmpty() ) {
        if ( i ) {
            typstr += ",";
            valstr += ", ";
            argstr += ", ";
        }
        typstr += a->leftType;
        typstr += a->rightType;
        argstr += a->leftType;
        argstr += " ";
        sprintf( buf, "t%d", i );
        valstr += buf;
        argstr += buf;
        argstr += a->rightType;
        ++i;
        typvec[i] = typstr.copy();
        valvec[i] = valstr.copy();
        argvec[i] = argstr.copy();
        }
        a = f->args->next();
    }

    bool predef_call = FALSE;
    if ( typstr.isEmpty() || typstr == "short" || typstr == "int" ||
         typstr == "long" || typstr == "char*" || typstr == "const char*"){
        predef_call = TRUE;
    }
    if ( !predef_call && !included_list_stuff ) {
        // yes we need it, because otherwise QT_VERSION may not be defined
        fprintf( out, "\n#include <%sqobjectdefs.h>\n", (const char*)qtPath );
        fprintf( out, "#if QT_VERSION >= 141\n" );
        fprintf( out, "/" "/ newer implementation\n" );
        fprintf( out, "#include <%sqsignalslotimp.h>\n", (const char*)qtPath );
        fprintf( out, "#else\n" );
        fprintf( out, "/" "/ for late-model 1.x header files\n" );
        fprintf( out, "#if !defined(Q_MOC_CONNECTIONLIST_DECLARED)\n" );
        fprintf( out, "#define Q_MOC_CONNECTIONLIST_DECLARED\n" );
        fprintf( out, "#include <%sqlist.h>\n", (const char*)qtPath );
        fprintf( out, "Q_DECLARE(QListM,QConnection);\n" );
        fprintf( out, "Q_DECLARE(QListIteratorM,QConnection);\n" );
        fprintf( out, "#endif\n" );
        fprintf( out, "#endif\n" );
        included_list_stuff = TRUE;
    }

    fprintf( out, "\n/" /* c++ */ "/ SIGNAL %s\n", (const char*)f->name );
    fprintf( out, "void %s::%s(", (const char*)className,
         (const char*)f->name );

    if ( argstr.isEmpty() )
        fprintf( out, ")\n{\n" );
    else
        fprintf( out, " %s )\n{\n", (const char*)argstr );

    if ( predef_call ) {
        fprintf( out, "    activate_signal( \"%s(%s)\"",
             (const char*)f->name, (const char*)typstr );
        if ( !valstr.isEmpty() )
        fprintf( out, ", %s", (const char*)valstr );
        fprintf( out, " );\n}\n" );
        f = signals.next();
        continue;
    }

    int nargs = f->args->count();
    fprintf( out, "    QConnectionList *clist = receivers(\"%s(%s)\");\n",
         (const char*)f->name, (const char*)typstr );
    fprintf( out, "    if ( !clist || signalsBlocked() )\n\treturn;\n" );
    if ( nargs ) {
        for ( i=0; i<=nargs; i++ ) {
        fprintf( out, "    typedef void (QObject::*RT%d)(%s);\n",
             i, (const char*)typvec[i] );
        fprintf( out, "    typedef RT%d *PRT%d;\n", i, i );
        }
    } else {
        fprintf( out, "    typedef void (QObject::*RT)(%s);\n",
             (const char*)typstr);
        fprintf( out, "    typedef RT *PRT;\n" );    
    }
    if ( nargs ) {
        for ( i=0; i<=nargs; i++ )
        fprintf( out, "    RT%d r%d;\n", i, i );
    } else {
        fprintf( out, "    RT r;\n" );
    }
    fprintf( out, "    QConnectionListIt it(*clist);\n" );
    fprintf( out, "    QConnection   *c;\n" );
    fprintf( out, "    QSenderObject *object;\n" );
    fprintf( out, "    while ( (c=it.current()) ) {\n" );
    fprintf( out, "\t++it;\n" );
    fprintf( out, "\tobject = (QSenderObject*)c->object();\n" );
    fprintf( out, "\tobject->setSender( this );\n" );
    if ( nargs ) {
        fprintf( out, "\tswitch ( c->numArgs() ) {\n" );
        for ( i=0; i<=nargs; i++ ) {
        fprintf( out, "\t    case %d:\n", i );
        fprintf( out, "\t\tr%d = *((PRT%d)(c->member()));\n", i, i );
        fprintf( out, "\t\t(object->*r%d)(%s);\n",
             i, (const char*)valvec[i] );
        fprintf( out, "\t\tbreak;\n" );
        }
        fprintf( out, "\t}\n" );
    } else {
        fprintf( out, "\tr = *((PRT)(c->member()));\n" );
        fprintf( out, "\t(object->*r)(%s);\n", (const char*)valstr );
    }
    fprintf( out, "    }\n}\n" );
    f = signals.next();
    }

}

但是呢,这种预处理的确是有点超越我的维度知识,所以现在就只是看一遍作数,并不深究。然后就是看到一个qgloab.h的文件,为什么注意到它呢?因为我对QString 揭开头文件套娃的时候,发现它是最后一层。然后就发现了它可以通过宏来判断平台,编译器!
更妙的是它判断bool值的办法:
image.png
意思是早起的bool 可能只是一个宏?还是说是否定义了某个类型,真的可以通过#if defined(type)来判断?
顺手查了下#ifdef 和#if defined 的区别……
image.png
就一个括号的区别,连结尾都是#endif
那么也就从另一个方面证明了,那里的bool 就是一个宏,早起的C++也是厉害。毕竟我一个半路出家的,通过代码来考古还是挺有必要的。先写这么多吧,继续挖坑


程某有一计
28 声望7 粉丝

本科机械,半路转行C++,在掉头发的边缘疯狂试探。