首页 > 技术文章 > Qt之QMetaMethod

shHome 2021-03-12 10:03 原文

利用Qt QMetaMethod 元数据实现调用安全调用对象的成员方法

参考链接: 
https://doc.qt.io/qt-5/qmetamethod.html
https://blog.csdn.net/wangpengk7788/article/details/56291180
https://www.cnblogs.com/findumars/p/11161019.html

文章目录

1. 介绍 QMetaMethod

QMetaMethod 和 QMetaMethodPrivate 是用来管理QMetaObject包含信息中的函数节的。MOC生成的函数结构图如下

2. QMetaMethod方法介绍

// 这个枚举描述了一个方法的访问级别,遵循C++中使用的约定。
enum Access { Private, Protected, Public };
//
enum MethodType 
{ 
    Method,     ///> 方法
    Signal,     ///> 信号
    Slot,       ///> 插槽
    Constructor ///> 构造函数
};
// 方法的属性枚举类型
enum Attributes { Compatibility = 0x1, Cloned = 0x2, Scriptable = 0x4 };

// 返回此方法(private、protected或public)的访问规范。
QMetaMethod::Access access() const;

// 对对象调用对象使用此方法。如果可以调用成员,则返回true。如果没有这样的成员或参数不匹配,则返回false。
// 调用可以是同步调用,也可以是异步调用,具体取决于连接类型:
// Qt::DirectConnection 立即调用此方法
// Qt::QueuedConnection 应用程序进入主事件循环时将发布QEvent并调用该成员。
// Qt::AutoConnection   如果对象与调用方位于同一线程中,则会同步调用该成员;否则,它将异步调用该成员。
// 此方法调用的返回值放在returnValue中。如果调用是异步的,则无法计算返回值。最多可以向此方法调用传递十个参数\
// (val0、val1、val2、val3、val4、val5、val6、val7、val8和val9)。
// QGenericArgument和QGenericReturnArgument是内部帮助程序类。
// 因为可以动态调用信号和插槽,所以必须使用Q_ARG()和Q_RETURN_ARG()宏将参数括起来。Q_ARG()接受类型名和\
// 该类型的常量引用;Q_RETURN_ARG()接受类型名和非常量引用。
bool 
invoke(QObject *object, // 对象句柄
Qt::ConnectionType connectionType,  
QGenericReturnArgument returnValue,
QGenericArgument val0 = QGenericArgument(nullptr), 
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;


// 在Q_GADGET上调用此方法。如果可以调用成员,则返回true。如果没有这样的成员或参数不匹配,则返回false。
// 调用总是同步的。
// Q_GADGET是Q_OBJECT宏的较轻版本,适用于不从QObject继承但仍希望使用QMetaObject提供的某些反射功能的类。它必须出现在类定义的私有部分。
bool 
invokeOnGadget(void *gadget, // 指针小工具必须指向小工具类的实例。
QGenericReturnArgument returnValue,
QGenericArgument val0 = QGenericArgument(nullptr), 
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;

// 如果此方法有效(可以内省和调用),则返回true,否则返回false。
bool QMetaMethod::isValid() const;
// 返回此方法的索引。
int QMetaMethod::methodIndex() const;
// 返回此方法的签名(例如,setValue(double))。
QByteArray QMetaMethod::methodSignature() const;
// 返回此方法的类型(信号、插槽或方法)。
QMetaMethod::MethodType QMetaMethod::methodType() const;
// 返回此方法的名称。
QByteArray QMetaMethod::name() const;
// 返回此方法的参数数。
int QMetaMethod::parameterCount() const;
// 返回参数名列表。
QList<QByteArray> QMetaMethod::parameterNames() const;
// 返回给定索引处参数的类型。
// 返回值是使用QMetaType注册的类型之一,如果未注册类型,则返回值为QMetaType::UnknownType。
int QMetaMethod::parameterType(int index) const;
// 返回参数类型的列表。
QList<QByteArray> QMetaMethod::parameterTypes() const;
// 返回此方法的返回类型。
// 返回值是使用QMetaType注册的类型之一,如果未注册类型,则返回值为QMetaType::UnknownType。
int QMetaMethod::returnType() const
// 如果方法修订版是由Q_revision指定的,则返回该方法修订版,否则返回0。
int QMetaMethod::revision() const;
// 返回此方法的返回类型名称。
const char *QMetaMethod::typeName() const;

// [static]
// 返回与给定信号对应的元方法,如果信号不是类的信号,则返回无效的QMetaMethod。
[static] QMetaMethod QMetaMethod::fromSignal(PointerToMemberFunction signal);

3. QMetaMethod使用方法

// header file.
// 我在这里创建的是Qt控制台程序。
// 习惯使用 C/C++ 标准输出方法,就没有使用 Qt 标注输出方法。
#pragma once
#if !defined(_QTOBJECT_H_)
#define _QTOBJECT_H_

#include <QObject>

class MyObject : public QObject
{
    Q_OBJECT
public:
    explicit MyObject(QObject* parent = 0){}

public slots:
    int add(int a, int b) {

        return 1;
    }

    void func()const{
        
        printf("%s\n", __FUNCSIG__);
    }

    QString compute(QString str, int n, double f) {

        printf(" %s : \n \t\t%s >> %d >> %.3f\n", __FUNCSIG__, str.toStdString().c_str(), n, f);
        return __FUNCSIG__;
    }
};


#endif // _QTOBJECT_H_

// main.cpp
MyObject obj;
QByteArray normalizedSignature = QMetaObject::normalizedSignature("add(int, int)");
int methodIndex = obj.metaObject()->indexOfMethod(normalizedSignature);
// 获取下标对应的元方法
//QMetaMethod metaMethod = obj.metaObject()->method(methodIndex);
// 返回与给定信号对应的元方法,如果信号不是类的信号,则返回无效的QMetaMethod。
QMetaMethod metaMethod = QMetaMethod::fromSignal(&QObject::destroyed);  

// isValid
if (metaMethod.isValid()) {

    qDebug() << QString::fromLocal8Bit("方法有效");
}
else {

    qDebug() << QString::fromLocal8Bit("方法无效");
}

// function name string.
QString funcNameString;
funcNameString = metaMethod.typeName() + QString(" ") + metaMethod.name();
funcNameString += "(";

for (auto it = 0; it < metaMethod.parameterCount(); ++it) {

    funcNameString += metaMethod.parameterTypes()[it] + QString(" ") + metaMethod.parameterNames()[it] + ",";
}

funcNameString += ");";
qDebug() << funcNameString;


// function access.
switch (metaMethod.access())
{
case QMetaMethod::Access::Private:
    qDebug() << QString::fromLocal8Bit("访问级别:   私有访问级别.");
    break;
case QMetaMethod::Access::Protected:
    qDebug() << QString::fromLocal8Bit("访问级别:   保护访问级别.");
    break;
case QMetaMethod::Access::Public:
    qDebug() << QString::fromLocal8Bit("访问级别:   公有访问级别.");
    break;
default:
    qDebug() << QString::fromLocal8Bit("访问级别:   错误访问级别.");
    break;
}
qDebug() << "Index: " << metaMethod.methodIndex();
qDebug() << "Signature: " << metaMethod.methodSignature();
qDebug() << "Type: " << metaMethod.methodType();
qDebug() << "Name: " << metaMethod.name();
qDebug() << "Parameter names: " << metaMethod.parameterNames();
qDebug() << "Return type: " << metaMethod.returnType();
qDebug() << "Type name: " << metaMethod.typeName();

int retVar =0;
if (metaMethod.invoke(&obj, Qt::AutoConnection, Q_RETURN_ARG(int, retVar), Q_ARG(int, 99), Q_ARG(int, -100)))
{
    qDebug() << "call invoke sucess!";
}
else {

    qDebug() << "call invoke faild.";
}

qDebug() << "call function return val: " << retVar;

输出信息

4. 总结

这个模块提供有关成员函数的元数据。能让我们灵活的,安全的调用对象方法,以及该方法的属性等接口。

推荐阅读