QT元数据

QT作为一个流行的C++开发框架,保留了C++代码执行效率高的同时,也提供了类似动态语言的反射机制,能够实现很多标准c++做不到的事情,这些要归功于QT moc出来的元数据。那么,QT元数据都有哪些信息呢?下面通过一个例子详细说明(基于QT6.5)。

TodoModelView这个类的声明以及moc出来的源文件如下:

cpp 复制代码
#ifndef TODOMODELVIEWMODEL_H
#define TODOMODELVIEWMODEL_H

#include <QObject>
#include <QString>


class TodoModel;
class TodoModelViewModel : public QObject
{
    Q_OBJECT
    Q_PROPERTY(TodoModel* TodM READ todoModel CONSTANT)
    Q_PROPERTY(QString Itext READ inputText WRITE setInputText NOTIFY inputTextChanged)
    Q_PROPERTY(bool bCad READ canAdd NOTIFY inputTextChanged)
    Q_PROPERTY(int cCount READ completedCount NOTIFY todoModelChanged)
    Q_PROPERTY(int tCount READ totalCount NOTIFY todoModelChanged)

public:
    explicit TodoModelViewModel(QObject *parent = nullptr);

    TodoModel* todoModel() const { return m_todoModel; }

    QString inputText() const { return m_inputText; }
    void setInputText(const QString &text);

    bool canAdd() const { return !m_inputText.trimmed().isEmpty(); }

    int completedCount() const;
    int totalCount() const { return m_todoModel->rowCount(); }

    Q_INVOKABLE void addTodo();
    Q_INVOKABLE void removeTodo(int index);
    Q_INVOKABLE void toggleTodo(int index);
    Q_INVOKABLE void clearInput();

signals:
    void inputTextChanged();
    void todoModelChanged();

private:
    TodoModel *m_todoModel;
    QString m_inputText;
};

#endif // TODOMODELVIEWMODEL_H
cpp 复制代码
/****************************************************************************
** Meta object code from reading C++ file 'todomodelviewmodel.h'
**
** Created by: The Qt Meta Object Compiler version 68 (Qt 6.5.3)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/

#include "../../../todomodelviewmodel.h"
#include <QtCore/qmetatype.h>

#if __has_include(<QtCore/qtmochelpers.h>)
#include <QtCore/qtmochelpers.h>
#else
QT_BEGIN_MOC_NAMESPACE
#endif


#include <memory>

#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'todomodelviewmodel.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 68
#error "This file was generated using the moc from 6.5.3. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endif

#ifndef Q_CONSTINIT
#define Q_CONSTINIT
#endif

QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
QT_WARNING_DISABLE_GCC("-Wuseless-cast")
namespace {

#ifdef QT_MOC_HAS_STRINGDATA
struct qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS_t {};
static constexpr auto qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS = QtMocHelpers::stringData(
    "TodoModelViewModel",
    "inputTextChanged",
    "",
    "todoModelChanged",
    "addTodo",
    "removeTodo",
    "index",
    "toggleTodo",
    "clearInput",
    "TodM",
    "TodoModel*",
    "Itext",
    "bCad",
    "cCount",
    "tCount"
);
#else  // !QT_MOC_HAS_STRING_DATA
struct qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS_t {
    uint offsetsAndSizes[30];
    char stringdata0[19];
    char stringdata1[17];
    char stringdata2[1];
    char stringdata3[17];
    char stringdata4[8];
    char stringdata5[11];
    char stringdata6[6];
    char stringdata7[11];
    char stringdata8[11];
    char stringdata9[5];
    char stringdata10[11];
    char stringdata11[6];
    char stringdata12[5];
    char stringdata13[7];
    char stringdata14[7];
};
#define QT_MOC_LITERAL(ofs, len) \
    uint(sizeof(qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS_t::offsetsAndSizes) + ofs), len 
Q_CONSTINIT static const qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS_t qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS = {
    {
        QT_MOC_LITERAL(0, 18),  // "TodoModelViewModel"
        QT_MOC_LITERAL(19, 16),  // "inputTextChanged"
        QT_MOC_LITERAL(36, 0),  // ""
        QT_MOC_LITERAL(37, 16),  // "todoModelChanged"
        QT_MOC_LITERAL(54, 7),  // "addTodo"
        QT_MOC_LITERAL(62, 10),  // "removeTodo"
        QT_MOC_LITERAL(73, 5),  // "index"
        QT_MOC_LITERAL(79, 10),  // "toggleTodo"
        QT_MOC_LITERAL(90, 10),  // "clearInput"
        QT_MOC_LITERAL(101, 4),  // "TodM"
        QT_MOC_LITERAL(106, 10),  // "TodoModel*"
        QT_MOC_LITERAL(117, 5),  // "Itext"
        QT_MOC_LITERAL(123, 4),  // "bCad"
        QT_MOC_LITERAL(128, 6),  // "cCount"
        QT_MOC_LITERAL(135, 6)   // "tCount"
    },
    "TodoModelViewModel",
    "inputTextChanged",
    "",
    "todoModelChanged",
    "addTodo",
    "removeTodo",
    "index",
    "toggleTodo",
    "clearInput",
    "TodM",
    "TodoModel*",
    "Itext",
    "bCad",
    "cCount",
    "tCount"
};
#undef QT_MOC_LITERAL
#endif // !QT_MOC_HAS_STRING_DATA
} // unnamed namespace

Q_CONSTINIT static const uint qt_meta_data_CLASSTodoModelViewModelENDCLASS[] = {

 // content:
      11,       // revision
       0,       // classname
       0,    0, // classinfo
       6,   14, // methods
       5,   60, // properties
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       2,       // signalCount

 // signals: name, argc, parameters, tag, flags, initial metatype offsets
       1,    0,   50,    2, 0x06,    6 /* Public */,
       3,    0,   51,    2, 0x06,    7 /* Public */,

 // methods: name, argc, parameters, tag, flags, initial metatype offsets
       4,    0,   52,    2, 0x02,    8 /* Public */,
       5,    1,   53,    2, 0x02,    9 /* Public */,
       7,    1,   56,    2, 0x02,   11 /* Public */,
       8,    0,   59,    2, 0x02,   13 /* Public */,

 // signals: parameters
    QMetaType::Void,
    QMetaType::Void,

 // methods: parameters
    QMetaType::Void,
    QMetaType::Void, QMetaType::Int,    6,
    QMetaType::Void, QMetaType::Int,    6,
    QMetaType::Void,

 // properties: name, type, flags, notify, revision
       9, 0x80000000 | 10, 0x00015409, uint(-1), 0,
      11, QMetaType::QString, 0x00015003, uint(0), 0,
      12, QMetaType::Bool, 0x00015001, uint(0), 0,
      13, QMetaType::Int, 0x00015001, uint(1), 0,
      14, QMetaType::Int, 0x00015001, uint(1), 0,

       0        // eod
};

Q_CONSTINIT const QMetaObject TodoModelViewModel::staticMetaObject = { {
    QMetaObject::SuperData::link<QObject::staticMetaObject>(),
    qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS.offsetsAndSizes,
    qt_meta_data_CLASSTodoModelViewModelENDCLASS,
    qt_static_metacall,
    nullptr,
    qt_incomplete_metaTypeArray<qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS_t,
        // property 'TodM'
        QtPrivate::TypeAndForceComplete<TodoModel*, std::true_type>,
        // property 'Itext'
        QtPrivate::TypeAndForceComplete<QString, std::true_type>,
        // property 'bCad'
        QtPrivate::TypeAndForceComplete<bool, std::true_type>,
        // property 'cCount'
        QtPrivate::TypeAndForceComplete<int, std::true_type>,
        // property 'tCount'
        QtPrivate::TypeAndForceComplete<int, std::true_type>,
        // Q_OBJECT / Q_GADGET
        QtPrivate::TypeAndForceComplete<TodoModelViewModel, std::true_type>,
        // method 'inputTextChanged'
        QtPrivate::TypeAndForceComplete<void, std::false_type>,
        // method 'todoModelChanged'
        QtPrivate::TypeAndForceComplete<void, std::false_type>,
        // method 'addTodo'
        QtPrivate::TypeAndForceComplete<void, std::false_type>,
        // method 'removeTodo'
        QtPrivate::TypeAndForceComplete<void, std::false_type>,
        QtPrivate::TypeAndForceComplete<int, std::false_type>,
        // method 'toggleTodo'
        QtPrivate::TypeAndForceComplete<void, std::false_type>,
        QtPrivate::TypeAndForceComplete<int, std::false_type>,
        // method 'clearInput'
        QtPrivate::TypeAndForceComplete<void, std::false_type>
    >,
    nullptr
} };

void TodoModelViewModel::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        auto *_t = static_cast<TodoModelViewModel *>(_o);
        (void)_t;
        switch (_id) {
        case 0: _t->inputTextChanged(); break;
        case 1: _t->todoModelChanged(); break;
        case 2: _t->addTodo(); break;
        case 3: _t->removeTodo((*reinterpret_cast< std::add_pointer_t<int>>(_a[1]))); break;
        case 4: _t->toggleTodo((*reinterpret_cast< std::add_pointer_t<int>>(_a[1]))); break;
        case 5: _t->clearInput(); break;
        default: ;
        }
    } else if (_c == QMetaObject::IndexOfMethod) {
        int *result = reinterpret_cast<int *>(_a[0]);
        {
            using _t = void (TodoModelViewModel::*)();
            if (_t _q_method = &TodoModelViewModel::inputTextChanged; *reinterpret_cast<_t *>(_a[1]) == _q_method) {
                *result = 0;
                return;
            }
        }
        {
            using _t = void (TodoModelViewModel::*)();
            if (_t _q_method = &TodoModelViewModel::todoModelChanged; *reinterpret_cast<_t *>(_a[1]) == _q_method) {
                *result = 1;
                return;
            }
        }
    } else if (_c == QMetaObject::RegisterPropertyMetaType) {
        switch (_id) {
        default: *reinterpret_cast<int*>(_a[0]) = -1; break;
        case 0:
            *reinterpret_cast<int*>(_a[0]) = qRegisterMetaType< TodoModel* >(); break;
        }
    } else if (_c == QMetaObject::ReadProperty) {
        auto *_t = static_cast<TodoModelViewModel *>(_o);
        (void)_t;
        void *_v = _a[0];
        switch (_id) {
        case 0: *reinterpret_cast< TodoModel**>(_v) = _t->todoModel(); break;
        case 1: *reinterpret_cast< QString*>(_v) = _t->inputText(); break;
        case 2: *reinterpret_cast< bool*>(_v) = _t->canAdd(); break;
        case 3: *reinterpret_cast< int*>(_v) = _t->completedCount(); break;
        case 4: *reinterpret_cast< int*>(_v) = _t->totalCount(); break;
        default: break;
        }
    } else if (_c == QMetaObject::WriteProperty) {
        auto *_t = static_cast<TodoModelViewModel *>(_o);
        (void)_t;
        void *_v = _a[0];
        switch (_id) {
        case 1: _t->setInputText(*reinterpret_cast< QString*>(_v)); break;
        default: break;
        }
    } else if (_c == QMetaObject::ResetProperty) {
    } else if (_c == QMetaObject::BindableProperty) {
    }
}

const QMetaObject *TodoModelViewModel::metaObject() const
{
    return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}

void *TodoModelViewModel::qt_metacast(const char *_clname)
{
    if (!_clname) return nullptr;
    if (!strcmp(_clname, qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS.stringdata0))
        return static_cast<void*>(this);
    return QObject::qt_metacast(_clname);
}

int TodoModelViewModel::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
    _id = QObject::qt_metacall(_c, _id, _a);
    if (_id < 0)
        return _id;
    if (_c == QMetaObject::InvokeMetaMethod) {
        if (_id < 6)
            qt_static_metacall(this, _c, _id, _a);
        _id -= 6;
    } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
        if (_id < 6)
            *reinterpret_cast<QMetaType *>(_a[0]) = QMetaType();
        _id -= 6;
    }else if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty
            || _c == QMetaObject::ResetProperty || _c == QMetaObject::BindableProperty
            || _c == QMetaObject::RegisterPropertyMetaType) {
        qt_static_metacall(this, _c, _id, _a);
        _id -= 5;
    }
    return _id;
}

// SIGNAL 0
void TodoModelViewModel::inputTextChanged()
{
    QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
}

// SIGNAL 1
void TodoModelViewModel::todoModelChanged()
{
    QMetaObject::activate(this, &staticMetaObject, 1, nullptr);
}
QT_WARNING_POP

在moc_todomodelviewmodel.cpp中qt_meta_stringdata_CLASSTodoModelViewModelENDCLASS(太长了,记作①),qt_meta_data_CLASSTodoModelViewModelENDCLASS(记作②)以及staticMetaObject(③)是存放元数据的具体结构。其中①是moc元数据时注册的字符串资源,是一个字符串数组。除了第一个元素offsetsAndSizes是uint类型,用于存放字符串资源索引头(每个字符串的偏移+长度),其余元素都是char类型,存放与该类相关的字符串信息。②是一个uint数组,信息密度很高:

staticMetaObject中有一个字段存放了该类所有property属性和invokable方法,涉及到的类型的基本信息表qt_incomplete_metaTypeArray,每种类型都提供了这些信息:

cpp 复制代码
  
template<typename T>
struct QMetaTypeInterfaceWrapper
{
    // if the type ID for T is known at compile-time, then we can declare
    // the QMetaTypeInterface object const; otherwise, we declare it as
    // non-const and the .typeId is updated by QMetaType::idHelper().
    static constexpr bool IsConstMetaTypeInterface = !!BuiltinMetaType<T>::value;
    using InterfaceType = std::conditional_t<IsConstMetaTypeInterface, const QMetaTypeInterface, NonConstMetaTypeInterface>;

    static inline InterfaceType metaType = {
        /*.revision=*/ QMetaTypeInterface::CurrentRevision,//与QT版本相关的版本号
        /*.alignment=*/ alignof(T),
        /*.size=*/ sizeof(T),//类型的size
        /*.flags=*/ QMetaTypeForType<T>::Flags,
        /*.typeId=*/ BuiltinMetaType<T>::value,
        /*.metaObjectFn=*/ MetaObjectForType<T>::metaObjectFunction,//该函数返回类型的staticMetaObject地址
        /*.name=*/ QMetaTypeForType<T>::getName(),//类型名称
        /*.defaultCtr=*/ QMetaTypeForType<T>::getDefaultCtr(),//类型的默认构造函数
        /*.copyCtr=*/ QMetaTypeForType<T>::getCopyCtr(),//类型的拷贝构造函数
        /*.moveCtr=*/ QMetaTypeForType<T>::getMoveCtr(),//类型的移动构造函数
        /*.dtor=*/ QMetaTypeForType<T>::getDtor(),//类型的析构函数
        /*.equals=*/ QEqualityOperatorForType<T>::equals,
        /*.lessThan=*/ QLessThanOperatorForType<T>::lessThan,
        /*.debugStream=*/ QDebugStreamOperatorForType<T>::debugStream,
        /*.dataStreamOut=*/ QDataStreamOperatorForType<T>::dataStreamOut,
        /*.dataStreamIn=*/ QDataStreamOperatorForType<T>::dataStreamIn,
        /*.legacyRegisterOp=*/ QMetaTypeForType<T>::getLegacyRegister()
    };
};

开发者通常不需要也不应该直接手动调用 qt_metacall。它是 Qt 内部机制的底层接口,由以下高级 API 间接触发:

触发场景 调用的高层 API 底层行为映射
信号发射 emit mySignal(); 调用QMetaObject::activate -> 触发 qt_metacall(InvokeMetaMethod, signalIndex, ...)。
反射调用方法 QMetaObject::invokeMethod(obj, "addTodo") 查找方法 ID -> 调用 obj->qt_metacall(InvokeMetaMethod, methodIndex, args)。
动态属性读取 obj->property("Itext").toString() 查找属性 ID -> 调用 obj->qt_metacall(ReadProperty, propertyIndex, &value)。
QML 交互 QML 引擎访问 C++ 对象
相关推荐
D e re k ZH1 小时前
C++ 系统学习手记:从语法细节到工程实践
开发语言·数据结构·c++·算法
灿飞屋1 小时前
Kotlin与Java虚拟机三:控制流
android·java·开发语言·后端·python·kotlin·安卓
xiaoye-duck2 小时前
《C++ 项目:手写大模型接入 SDK》Ollama接口设计(上):初始化、全量响应实现和测试
开发语言·网络·c++·语言模型
总要学点什么3 小时前
第98篇:三维特效-材质线效果——给一条线换十种材质,虚线的图案只填了一半
开发语言·前端·javascript·ecmascript·gis·webgl·材质
田甲3 小时前
做一款 Type-C 母座读卡器:GL3224 双卡方案实测
c语言·开发语言
严肃小白兔3 小时前
MATLAB 绘图与科研论文插图制作:图形设计、统计表达与矢量导出
开发语言·matlab·科研绘图
Funlang5 小时前
给机器写的 JavaScript 造个笼子
开发语言·javascript·ecmascript
普通人65 小时前
PHP文件读写完全指南
开发语言·php·1024程序员节
坤坤子吖6 小时前
Linux网络编程:TCP服务器的多进程与多线程实现
linux·服务器·开发语言·网络·c++·笔记·学习