Правильная парадигма от разработчиков Qt - MOC - мета объектный компилятор

При компиляции проекта в какой-то момент появляется утилита moc.exe - это так называемый препроцессор , который обрабатывает файлы h в которых прописаны СПЕЦИАЛЬНЫЕ макросы. Это основная парадигма Qt - создание механизма сигнал /слот. (Class QObject)

Вот первое ее появление при сборки QT из исходников:

mingw32-make.exe[2]: Entering directory `C:/QtSDK1.2.1/QtSources/4.8.1/src/corelib'
C:\QtSDK1.2.1\QtSources\4.8.1\bin\moc.exe -DQT_THREAD_SUPPORT -DUNICODE -DQT_LARGEFILE_SUPPORT -DQT_BUILD_CORE_LIB -DQT_NO_USING_NAMESPACE -DQT_ASCII_CAST_WARNINGS -DQT_MOC_COMPAT -DQT_USE_QSTRINGBUILDER -D_USE_MATH_DEFINES -DQLIBRARYINFO_EPOCROOT -DHB_EXPORT=Q_CORE_EXPORT -DQT_NO_DEBUG -DQT_NO_DYNAMIC_CAST -I"..\..\include" -I"..\..\include\QtCore" -I"tmp\rcc\release_static" -I"tmp" -I"global" -I"..\..\tools\shared" -I"..\3rdparty\zlib" -I"..\3rdparty\harfbuzz\src" -I"..\3rdparty\md5" -I"..\3rdparty\md4" -I"..\..\include\ActiveQt" -I"tmp\moc\release_static" -I"c:\QtSDK1.2.1\Desktop\Qt\4.8.1\mingw\mkspecs\win32-
g++" -D__GNUC__ -DWIN32 animation\qabstractanimation.h -o tmp\moc\release_static\moc_qabstractanimation.cpp

Что делает moc.exe (при первом рассмотрении) . Он видим что у qabstractanimation.h класс наследуется от QObject и создает на основании этого в специальном каталоге файл cpp файл tmp\moc\release_static\moc_qabstractanimation.cpp, который становится частью вашего проекта.

На самом деле , если посмотреть файл moc_qabstractanimation.cpp , то практически сразу все становится понятно :

Вот мы имеем в файле qabstractanimation.h определение класса QAbstractAnimation , унаследованного от QObject. MOC реагирует на (QObject) и создает файл moc_qabstractanimation.cpp.


class Q_CORE_EXPORT QAbstractAnimation : public QObject
{
    Q_OBJECT /// - это обязательный МАКРОС
    Q_ENUMS(State)
    Q_ENUMS(Direction)
    Q_PROPERTY(State state READ state NOTIFY stateChanged)
    Q_PROPERTY(int loopCount READ loopCount WRITE setLoopCount)
    Q_PROPERTY(int currentTime READ currentTime WRITE setCurrentTime)
    Q_PROPERTY(int currentLoop READ currentLoop NOTIFY currentLoopChanged)
    Q_PROPERTY(Direction direction READ direction WRITE setDirection NOTIFY directionChanged)
    Q_PROPERTY(int duration READ duration)

public:
    enum Direction {
        Forward,
        Backward
    };
    enum State {
        Stopped,
        Paused,
        Running
    };
    enum DeletionPolicy {
        KeepWhenStopped = 0,
        DeleteWhenStopped
    };

    QAbstractAnimation(QObject *parent = 0);
    virtual ~QAbstractAnimation();

    State state() const;

    QAnimationGroup *group() const;

    Direction direction() const;
    void setDirection(Direction direction);

    int currentTime() const;
    int currentLoopTime() const;

    int loopCount() const;
    void setLoopCount(int loopCount);
    int currentLoop() const;

    virtual int duration() const = 0;
    int totalDuration() const;

Q_SIGNALS:
    void finished();
    void stateChanged(QAbstractAnimation::State newState, QAbstractAnimation::State oldState);
    void currentLoopChanged(int currentLoop);
    void directionChanged(QAbstractAnimation::Direction);

public Q_SLOTS:
    void start(QAbstractAnimation::DeletionPolicy policy = KeepWhenStopped);
    void pause();
    void resume();
    void setPaused(bool);
    void stop();
    void setCurrentTime(int msecs);

protected:
    QAbstractAnimation(QAbstractAnimationPrivate &dd, QObject *parent = 0);
    bool event(QEvent *event);

    virtual void updateCurrentTime(int currentTime) = 0;
    virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState);
    virtual void updateDirection(QAbstractAnimation::Direction direction);

private:
    Q_DISABLE_COPY(QAbstractAnimation)
    Q_DECLARE_PRIVATE(QAbstractAnimation)
};

Cам moc_qabstractanimation.cpp при этом добавляется в конце к qabstractanimation.cpp (смотрите #include "moc_qabstractanimation.cpp").

В файл moc_qabstractanimation.cpp попросту добавляется дополнительный функционал к классу например реализацию функции qt_static_metacall :

Само определение функции qt_static_metacall определяется макросом Q_OBJECT (см .qobjectsdef.h) . Q_OBJECT всегда вначале класса, наследуемого от QObject;

Класс QObject определен в qobject.h :

class Q_CORE_EXPORT QObject
{
    Q_OBJECT /// вот он  !!!!
    Q_PROPERTY(QString objectName READ objectName WRITE setObjectName)
    Q_DECLARE_PRIVATE(QObject)

public:
    Q_INVOKABLE explicit QObject(QObject *parent=0);
    virtual ~QObject();
...............................
Q_SIGNALS:
    void destroyed(QObject * = 0);

public:
    inline QObject *parent() const { return d_ptr->parent; }

    inline bool inherits(const char *classname) const
        { return const_cast<QObject *>(this)->qt_metacast(classname) != 0; }

public Q_SLOTS:
    void deleteLater();

protected:
.......................................
private:
    Q_DISABLE_COPY(QObject)
    Q_PRIVATE_SLOT(d_func(), void _q_reregisterTimers(void *))
};

У каждого класса наследника QObject первая строка в классе это макрос Q_OBJECT


#define Q_OBJECT \
public: \
    Q_OBJECT_CHECK \
    static const QMetaObject staticMetaObject; \
    Q_OBJECT_GETSTATICMETAOBJECT \
    virtual const QMetaObject *metaObject() const; \
    virtual void *qt_metacast(const char *); \
    QT_TR_FUNCTIONS \
    virtual int qt_metacall(QMetaObject::Call, int, void **); \
private: \
    Q_DECL_HIDDEN static const QMetaObjectExtraData staticMetaObjectExtraData; \
    Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
...................

Очень похоже , что вся идея в том , чтобы для объекта (наследника QObject) пронумеровать его свойства , составить ему таблицу соответствия номер свойства = наименование. Далее в любом месте программы можно вызвать прочитать свойтсво объекта по его наименованию. Ничего не напоминает? Это реализация собственного скриптового языка.


static const char qt_meta_stringdata_QAbstractAnimation[] = {
    "QAbstractAnimation\0\0finished()\0"
    "newState,oldState\0"
    "stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)\0"
    "currentLoop\0currentLoopChanged(int)\0"
    "directionChanged(QAbstractAnimation::Direction)\0"
    "policy\0start(QAbstractAnimation::DeletionPolicy)\0"
    "start()\0pause()\0resume()\0setPaused(bool)\0"
    "stop()\0msecs\0setCurrentTime(int)\0State\0"
    "state\0int\0loopCount\0currentTime\0"
    "Direction\0direction\0duration\0Forward\0"
    "Backward\0Stopped\0Paused\0Running\0"
};

void QAbstractAnimation::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        Q_ASSERT(staticMetaObject.cast(_o));
        QAbstractAnimation *_t = static_cast<QAbstractAnimation *>(_o);
        switch (_id) {
        case 0: _t->finished(); break;
        case 1: _t->stateChanged((*reinterpret_cast< QAbstractAnimation::State(*)>(_a[1])),(*reinterpret_cast< QAbstractAnimation::State(*)>(_a[2]))); break;
        case 2: _t->currentLoopChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
        case 3: _t->directionChanged((*reinterpret_cast< QAbstractAnimation::Direction(*)>(_a[1]))); break;
        case 4: _t->start((*reinterpret_cast< QAbstractAnimation::DeletionPolicy(*)>(_a[1]))); break;
        case 5: _t->start(); break;
        case 6: _t->pause(); break;
        case 7: _t->resume(); break;
        case 8: _t->setPaused((*reinterpret_cast< bool(*)>(_a[1]))); break;
        case 9: _t->stop(); break;
        case 10: _t->setCurrentTime((*reinterpret_cast< int(*)>(_a[1]))); break;
        default: ;
        }
    }
}

У каждого класса наследника QObject есть (должен быть) в начале макрос Q_OBJECT . А значит у кажого такого класса есть :
static const QMetaObject staticMetaObject; // это структура
Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); // это функция qt_static_metacall реализацией которой занимается MOC . qt_static_metacall вызывает функцию вашего объекта по номеру _id.

Структура QMetaObject на самом деле имеет много переменных и функций.

struct Q_CORE_EXPORT QMetaObject
{
    const char *className() const;
    const QMetaObject *superClass() const;

    QObject *cast(QObject *obj) const;
    const QObject *cast(const QObject *obj) const;

#ifndef QT_NO_TRANSLATION
    // ### Qt 4: Merge overloads
    QString tr(const char *s, const char *c) const;
    QString trUtf8(const char *s, const char *c) const;
    QString tr(const char *s, const char *c, int n) const;
    QString trUtf8(const char *s, const char *c, int n) const;
#endif // QT_NO_TRANSLATION

    int methodOffset() const;
    int enumeratorOffset() const;
    int propertyOffset() const;
    int classInfoOffset() const;

    int constructorCount() const;
    int methodCount() const;
    int enumeratorCount() const;
    int propertyCount() const;
    int classInfoCount() const;

    int indexOfConstructor(const char *constructor) const;
    int indexOfMethod(const char *method) const;
    int indexOfSignal(const char *signal) const;
    int indexOfSlot(const char *slot) const;
    int indexOfEnumerator(const char *name) const;
    int indexOfProperty(const char *name) const;
    int indexOfClassInfo(const char *name) const;

    QMetaMethod constructor(int index) const;
    QMetaMethod method(int index) const;
    QMetaEnum enumerator(int index) const;
    QMetaProperty property(int index) const;
    QMetaClassInfo classInfo(int index) const;
    QMetaProperty userProperty() const;

    static bool checkConnectArgs(const char *signal, const char *method);
    static QByteArray normalizedSignature(const char *method);
    static QByteArray normalizedType(const char *type);

    // internal index-based connect
    static bool connect(const QObject *sender, int signal_index,
                        const QObject *receiver, int method_index,
                        int type = 0, int *types = 0);
    // internal index-based disconnect
    static bool disconnect(const QObject *sender, int signal_index,
                           const QObject *receiver, int method_index);
    static bool disconnectOne(const QObject *sender, int signal_index,
                              const QObject *receiver, int method_index);
    // internal slot-name based connect
    static void connectSlotsByName(QObject *o);

    // internal index-based signal activation
    static void activate(QObject *sender, int signal_index, void **argv);  //obsolete
    static void activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv); //obsolete
    static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv);
    static void activate(QObject *sender, const QMetaObject *, int from_local_signal_index, int to_local_signal_index, void **argv); //obsolete

    // internal guarded pointers
    static void addGuard(QObject **ptr);
    static void removeGuard(QObject **ptr);
    static void changeGuard(QObject **ptr, QObject *o);

    static bool invokeMethod(QObject *obj, const char *member,
                             Qt::ConnectionType,
                             QGenericReturnArgument ret,
                             QGenericArgument val0 = QGenericArgument(0),
                             QGenericArgument val1 = QGenericArgument(),
                             QGenericArgument val2 = QGenericArgument(),
                             QGenericArgument val3 = QGenericArgument(),
                             QGenericArgument val4 = QGenericArgument(),
                             QGenericArgument val5 = QGenericArgument(),
                             QGenericArgument val6 = QGenericArgument(),
                             QGenericArgument val7 = QGenericArgument(),
                             QGenericArgument val8 = QGenericArgument(),
                             QGenericArgument val9 = QGenericArgument());

    static inline bool invokeMethod(QObject *obj, const char *member,
                             QGenericReturnArgument ret,
                             QGenericArgument val0 = QGenericArgument(0),
                             QGenericArgument val1 = QGenericArgument(),
                             QGenericArgument val2 = QGenericArgument(),
                             QGenericArgument val3 = QGenericArgument(),
                             QGenericArgument val4 = QGenericArgument(),
                             QGenericArgument val5 = QGenericArgument(),
                             QGenericArgument val6 = QGenericArgument(),
                             QGenericArgument val7 = QGenericArgument(),
                             QGenericArgument val8 = QGenericArgument(),
                             QGenericArgument val9 = QGenericArgument())
    {
        return invokeMethod(obj, member, Qt::AutoConnection, ret, val0, val1, val2, val3,
                val4, val5, val6, val7, val8, val9);
    }

    static inline bool invokeMethod(QObject *obj, const char *member,
                             Qt::ConnectionType type,
                             QGenericArgument val0 = QGenericArgument(0),
                             QGenericArgument val1 = QGenericArgument(),
                             QGenericArgument val2 = QGenericArgument(),
                             QGenericArgument val3 = QGenericArgument(),
                             QGenericArgument val4 = QGenericArgument(),
                             QGenericArgument val5 = QGenericArgument(),
                             QGenericArgument val6 = QGenericArgument(),
                             QGenericArgument val7 = QGenericArgument(),
                             QGenericArgument val8 = QGenericArgument(),
                             QGenericArgument val9 = QGenericArgument())
    {
        return invokeMethod(obj, member, type, QGenericReturnArgument(), val0, val1, val2,
                                 val3, val4, val5, val6, val7, val8, val9);
    }

    static inline bool invokeMethod(QObject *obj, const char *member,
                             QGenericArgument val0 = QGenericArgument(0),
                             QGenericArgument val1 = QGenericArgument(),
                             QGenericArgument val2 = QGenericArgument(),
                             QGenericArgument val3 = QGenericArgument(),
                             QGenericArgument val4 = QGenericArgument(),
                             QGenericArgument val5 = QGenericArgument(),
                             QGenericArgument val6 = QGenericArgument(),
                             QGenericArgument val7 = QGenericArgument(),
                             QGenericArgument val8 = QGenericArgument(),
                             QGenericArgument val9 = QGenericArgument())
    {
        return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0,
                val1, val2, val3, val4, val5, val6, val7, val8, val9);
    }

    QObject *newInstance(QGenericArgument val0 = QGenericArgument(0),
                         QGenericArgument val1 = QGenericArgument(),
                         QGenericArgument val2 = QGenericArgument(),
                         QGenericArgument val3 = QGenericArgument(),
                         QGenericArgument val4 = QGenericArgument(),
                         QGenericArgument val5 = QGenericArgument(),
                         QGenericArgument val6 = QGenericArgument(),
                         QGenericArgument val7 = QGenericArgument(),
                         QGenericArgument val8 = QGenericArgument(),
                         QGenericArgument val9 = QGenericArgument()) const;

    enum Call {
        InvokeMetaMethod,
        ReadProperty,
        WriteProperty,
        ResetProperty,
        QueryPropertyDesignable,
        QueryPropertyScriptable,
        QueryPropertyStored,
        QueryPropertyEditable,
        QueryPropertyUser,
        CreateInstance
    };

    int static_metacall(Call, int, void **) const;
    static int metacall(QObject *, Call, int, void **);

#ifdef QT3_SUPPORT
    QT3_SUPPORT const char *superClassName() const;
#endif

    struct { // private data
        const QMetaObject *superdata;
        const char *stringdata;
        const uint *data;
        const void *extradata;
    } d;
};

Теперь как связать два независимых объекта ?... Один объект создан в коде ранее (и размещен уже где-то в памяти RAM ) , второй объект создается и тоже размещается в памяти RAM. Например в конструкторе можно вызать функцию connect (QMetaObject структуры ).

Мы знаем уже адреса в памяти sender и receiver объекта (мы на стадии сборки) . Все наследники класса QObject. Остается только указать индексы (номера) функций - какие мы свяжем. Понятно , что состав переменных (параметров) у обоих функций имеет значение и должен быть идентичен.

bool QMetaObject::connect(const QObject *sender, int signal_index, const QObject *receiver, int method_index, int type, int *types)
{
    signal_index = methodIndexToSignalIndex(sender->metaObject(), signal_index);
    return QMetaObjectPrivate::connect(sender, signal_index,
                                       receiver, method_index,
                                       0, //FIXME, we could speed this connection up by computing the relative index
                                       type, types);
}

По-видимому и все..