Qt 메타데이터 시스템 (2) [moc 파일에 따라 Q OBJECT 분석]

20043 단어 Qt
우리는 신호와 슬롯의 작은 예제 헤더 파일을 보았다.
#ifndef MYCLASS
#define MYCLASS
#include 
class myClass : public QObject
{
    Q_OBJECT
public:
    explicit myClass(QObject *parent = 0 );
     ~myClass();
    void triggerOne();
    void triggerTwo();
signals:
    void signalOne(QString);     // one
    void signalTwo(int);         // two
private slots:                   // 
protected slots:                 // 
public slots:                    // 
    QString slotOne(QString msg);
    int slotTwo(int a);

};

#endif // MYCLASS

구현 파일:
#include 
#include 
#include "myclass.h"
#include 
using std::cout;
using std::endl;
myClass::myClass(QObject *parent):
    QObject(parent)
{
    //Qt4 
//    connect(this , SIGNAL(signalOne(QString)) , this ,SLOT(slotOne(QString) , Qt::AutoConnection);
//    connect(this , SIGNAL(signalTwo(int)) , this ,SLOT(slotTwo(int) , Qt::AutoConnection);

    //Qt5 , 。 
    connect(this , &myClass::signalOne , this , &myClass::slotOne , Qt::AutoConnection);
    connect(this , &myClass::signalTwo , this , &myClass::slotTwo ,  Qt::AutoConnection);

    triggerOne();
    triggerTwo();
}
myClass::~myClass()
{

}
void myClass::triggerOne()
{
    emit signalOne("SignalOne");
}
void myClass::triggerTwo()
{
    emit signalTwo(88);
}
QString myClass::slotOne(QString msg)
{
    cout<<"this is slot one::"+msg.toStdString()<return msg;
}
int myClass::slotTwo(int a)
{
    cout<<"this is slot two::"<return a;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
myClass test;
return a.exec();
}

이 작은 예를 쓰는 과정에서, 흰둥이 뇌장애는 본래 cpp 파일에 쓰려고 했다.결국 안되더라고요.자료를 찾아보니 moc 도구 식별 QOBJECT 매크로 정의는 헤더 파일에서 식별됩니다. 즉 cpp 파일에 이 매크로를 포함하는 것은 소용없습니다.이 게시물 자세히 보기http://blog.chinaunix.net/uid-20801802-id-1839159.html도움말 안의 "read a C++ source file"도 이미지에 대한 표현일 뿐입니다.
다음은 차근차근 QOBJEC이 어떻게 모크를 실현했는지...myclass.cpp 파일의1、QQt 라이브러리 파일(5.5 버전)에서 OBJECT의 정의
#define Q_OBJECT \
public: \
    Q_OBJECT_CHECK \
    QT_WARNING_PUSH \
    Q_OBJECT_NO_OVERRIDE_WARNING \
    static const QMetaObject staticMetaObject; \
    virtual const QMetaObject *metaObject() const; \
    virtual void *qt_metacast(const char *); \
    virtual int qt_metacall(QMetaObject::Call, int, void **); \
    QT_WARNING_POP \
    QT_TR_FUNCTIONS \
private: \
    Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
    struct QPrivateSignal {};

moc의 첫 번째 작업은 개발자가 쓴 이 종류에 어떤 신호조와 각자의 매개 변수 상황을 통계 분석하는 것이다. 여기는 moc생성mocmycalss.cpp 파일 신호 슬롯 수량 데이터 코드:
struct qt_meta_stringdata_myClass_t {
    QByteArrayData data[8];
    char stringdata0[51];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
    Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
    qptrdiff(offsetof(qt_meta_stringdata_myClass_t, stringdata0) + ofs \
        - idx * sizeof(QByteArrayData)) \
    )
static const qt_meta_stringdata_myClass_t qt_meta_stringdata_myClass = {
    {
QT_MOC_LITERAL(0, 0, 7), // "myClass"
QT_MOC_LITERAL(1, 8, 9), // "signalOne"
QT_MOC_LITERAL(2, 18, 0), // ""
QT_MOC_LITERAL(3, 19, 9), // "signalTwo"
QT_MOC_LITERAL(4, 29, 7), // "slotOne"
QT_MOC_LITERAL(5, 37, 3), // "msg"
QT_MOC_LITERAL(6, 41, 7), // "slotTwo"
QT_MOC_LITERAL(7, 49, 1) // "a"

    },
    "myClass\0signalOne\0\0signalTwo\0slotOne\0"
    "msg\0slotTwo\0a"
};
#undef QT_MOC_LITERAL

static const uint qt_meta_data_myClass[] = {

 // content:
       7,       // revision
       0,       // classname
       0,    0, // classinfocount,classinfodata
       4,   14, // methodscount,methoddata
       0,    0, // propertiescount,propertiesdata
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       2,       // signalCount

 // signals: name, argc, parameters, tag, flags
       1,    1,   34,    2, 0x06 /* Public */,
       3,    1,   37,    2, 0x06 /* Public */,

 // slots: name, argc, parameters, tag, flags
       4,    1,   40,    2, 0x0a /* Public */,
       6,    1,   43,    2, 0x0a /* Public */,

 // signals: parameters
    QMetaType::Void, QMetaType::QString,    2,
    QMetaType::Void, QMetaType::Int,    2,

 // slots: parameters
    QMetaType::QString, QMetaType::QString,    5,
    QMetaType::Int, QMetaType::Int,    7,

       0        // eod
};

여기 앞의 숫자 주석은 모두 명확하게 말했는데 기본 데이터의 통계이다.signals와 slots 안의: 1, 3, 4, 6은 자신이 문자열에 있다는 것을 나타낸다. myClass\0signalOne\0\0signalTwo\0slotOne\0"msg\0slotTwo\0a에서 몇 번째"\0"부터 자신의 정보를 표시한다.여기서 Signal의 첫 번째 "\0"오프셋부터 시작하여 신호 이름을 나타냅니다.두 번째'\0'은 반환 값을 나타낸다. (학생이 묻는다. 신호 함수 실현에 있어서 어느 정도의 반환 값도 없다. 비동기 호출에 대한 반환 값은 영원히 기본 구조 함수에서 나온 것이다. 호출된 함수가 모두 실행되지 않았기 때문에 하나의 반환 값만 기본으로 할 수 있다. 동기화 호출에 대해서는 마지막으로 연결된 함수의 집행 반환 값이다. 만약에 중간에 일치하지 않는 상황이 있으면 Qt가 해결해 줄 것이다. 기본값이다).같은 이치 뒤에 있는 슬로츠가 더 잘 이해했어.(1)static const QMetaObject staticMetaObject; 해당 moc 파일 섹션(set):
const QMetaObject myClass::staticMetaObject = {
    { &QObject::staticMetaObject, qt_meta_stringdata_myClass.data,
      qt_meta_data_myClass,  qt_static_metacall, Q_NULLPTR, Q_NULLPTR}
};
//staticMetaObject Q_OBJECT , ,QMetaObject , ,moc , moc_myclass.cpp :
struct { // private data
        const QMetaObject *superdata;         // 
        const QByteArrayData *stringdata;
        const uint *data;
        typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);
        StaticMetacallFunction static_metacall;
        const QMetaObject * const *relatedMetaObjects;// 
        void *extradata; //reserved for future use
    };

다른 사람의 블로그를 보면 typedef void(Static Metacall Function)(QObject, QMeta Object:::Call, int,void **);StaticMetacallFunction static_metacall; const QMetaObject * const *relatedMetaObjects; 이 세 가지는 다른 사람이 사용하는 버전이 어떤 버전인지 모르겠다.이치에 따르면 여기는 여러 가지 계승이 있는 상황에서 관련 정보를 기록하지만 코드를 보면 하나밖에 없다.그러나 여러 클래스가 계승될 때 QObject의 속성을 가지려면 QObject 속성을 가진 부류가 첫자리에 있어야 한다.큰 소는 moc가 다중 계승을 규정하는 상황에서 moc는 첫 번째 계승의 종류를 QObject라고 가정하고 다중 계승 중 유일한 종류만 QObject에서 계승된다고 보증한다.이렇게 보면 여분의 QObject가 계승한 것이고 두 번째 QObject는 식별할 수 없다.여기 흰둥이는 첫 번째만 놓고 모크의 구체적인 작업 절차를 잘 몰라요.get 섹션:
const QMetaObject *myClass::metaObject() const
{
    return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}

여기 dptr는 Qt에 있는 템플릿 클래스 포인터입니다. QObject의 정적 구성원이 아닙니다. 여기 도움말 문서에서dynamicMetaObject () 구성원 함수를 찾지 못했습니다. 보안 처리로 추정되고staticMetaObject로 되돌아갑니다.(2)virtual void qt_metacast(const char );
void *myClass::qt_metacast(const char *_clname)
{
    if (!_clname) return Q_NULLPTR;
    if (!strcmp(_clname, qt_meta_stringdata_myClass.stringdata0))
        return static_cast<void*>(const_cast< myClass*>(this));
    return QObject::qt_metacast(_clname);
}

전송된 문자열 데이터가 현재 클래스라면this지침을void지침으로 변환하여 외부에 전송합니다. 이 조작은 상당히 위험합니다.
현재 클래스가 아니면 부모 클래스의 qt 를 호출합니다metacast에서 계속 검색합니다.
여기서 나의 부류는 QObject이기 때문에 자연히 QObject:::qt 를 호출합니다메타케이스야.(3)virtual int qt_metacall(QMetaObject::Call, int, void );**
void myClass::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        myClass *_t = static_cast(_o);
        Q_UNUSED(_t)
        switch (_id) {
        case 0: _t->signalOne((*reinterpret_cast< QString(*)>(_a[1]))); break;
        case 1: _t->signalTwo((*reinterpret_cast< int(*)>(_a[1]))); break;
        case 2: { QString _r = _t->slotOne((*reinterpret_cast< QString(*)>(_a[1])));
            if (_a[0]) *reinterpret_cast< QString*>(_a[0]) = _r; }  break;
        case 3: { int _r = _t->slotTwo((*reinterpret_cast< int(*)>(_a[1])));
            if (_a[0]) *reinterpret_cast< int*>(_a[0]) = _r; }  break;
        default: ;
        }
    } else if (_c == QMetaObject::IndexOfMethod) {
        int *result = reinterpret_cast<int *>(_a[0]);
        void **func = reinterpret_cast<void **>(_a[1]);
        {
            typedef void (myClass::*_t)(QString );
            if (*reinterpret_cast<_t>(func) == static_cast<_t>(&myClass::signalOne)) {
                *result = 0;
            }
        }
        {
            typedef void (myClass::*_t)(int );
            if (*reinterpret_cast<_t>(func) == static_cast<_t>(&myClass::signalTwo)) {
                *result = 1;
            }
        }
    }
}

이름도 알다시피 이 종류의 모든 신호 처리 함수는 우리가 자주 사용하는 slots가 아니다.우리emit에 신호가 있을 때도 이 함수를 호출해서 진정한 신호 함수를 호출하고 다른 관련 신호가 오면 진정한 홈 함수를 호출한다.
진정한 신호 함수:
// SIGNAL 0
void myClass::signalOne(QString _t1)
{
    void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    QMetaObject::activate(this, &staticMetaObject, 0, _a);
}

// SIGNAL 1
void myClass::signalTwo(int _t1)
{
    void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    QMetaObject::activate(this, &staticMetaObject, 1, _a);
}

도움말에서 activate 함수를 찾지 못했습니다. 소들이 말하는 것을 보십시오. 실행 프로세스가 activate에 들어가면 이 signal에 대응하는 connection list를 먼저 꺼냅니다. 이 전체 연결표는 connect () 함수로 완성됩니다. 다음 장에서 설명하겠습니다.상기 두 함수의 세 번째 매개 변수를 보면 이 종류의 몇 번째 신호의 인덱스를 대표하고 종류의 신호 슬롯 인덱스는 함께 배열된 것이다.
많은 곳에서 흰둥이도 반은 알고 반은 알아, 분명히 말했는지 모르겠다.여기까지moc는 Qt의 신호와 슬롯의 그 부분의 관련 코드를 C++ 코드로 바꾸는 것입니다.만약 큰 소가 있다면, 교육을 구해라...

좋은 웹페이지 즐겨찾기