Qt中QDebug输出枚举转字符串(源码解析)

我意外的发现在Qt中,用qDebug()函数输出枚举值的时候,输出结果不是int类型,而是一个字符串,比如下面的代码

int main(int argc, char *argv[])
{
    //QApplication a(argc, argv);

    qDebug() << QLocale().country();

    //return a.exec();
}

输出结果是

QLocale::Country(China)

据我以前了解的知识,qDebug之所以能够输出各种类型,是因为重载了 << 运算符,而枚举是多种多样的,不可能每一个都用<< 进行重载,我猜想可能是用了模板。

出于好奇,我于是跟入了源码调试,首先进入了下面的函数

template<typename T>
typename std::enable_if<QtPrivate::IsQEnumHelper<T>::Value, QDebug>::type
operator<<(QDebug dbg, T value)
{
    const QMetaObject *obj = qt_getEnumMetaObject(value);
    const char *name = qt_getEnumName(value);
    return qt_QMetaEnum_debugOperator(dbg, typename QFlags<T>::Int(value), obj, name);
}

此时value的值是枚举 QLocale::China (44),name的值是字符串 Country, qt_QMetaEnum_debugOperator 函数定义如下

QDebug qt_QMetaEnum_debugOperator(QDebug &dbg, int value, const QMetaObject *meta, const char *name)
{
    QDebugStateSaver saver(dbg);
    QMetaEnum me = meta->enumerator(meta->indexOfEnumerator(name));
    const char *key = me.valueToKey(value);
    dbg.nospace() << meta->className() << "::" << name << '(';
    if (key)
        dbg << key;
    else
        dbg << value;
    dbg << ')';
    return dbg;
}

由上面的代码我们可以知道,QDebug用模板重载了枚举类型的 << 运算符,然后利用Qt的metaObject 进行反射,获取枚举的信息,当然,要使用QMetaObject,必须要在类中用宏Q_ENUM声明,以上面的QLocale类为例

class QLocale 
{
public:
    enum Country {...}
    Q_ENUM(Country)
    ...
}

下面说明以下QMetaObject中的 QMetaEnum 关于枚举的接口简单说明

int main(int argc, char *argv[])
{
    QMetaEnum metaEnum = QMetaEnum::fromType<QLocale::Country>();
    qDebug() << metaEnum.valueToKey(QLocale::China);      // “China”
    qDebug() << metaEnum.keyToValue("China");             // 44
    qDebug() << metaEnum.valueToKey(44);                  // “China”
}

而QMetaEnum::fromType 的函数定义如下:

template<typename T> static QMetaEnum fromType() {
        Q_STATIC_ASSERT_X(QtPrivate::IsQEnumHelper<T>::Value,
                          "QMetaEnum::fromType only works with enums declared as Q_ENUM or Q_FLAG");
        const QMetaObject *metaObject = qt_getEnumMetaObject(T());
        const char *name = qt_getEnumName(T());
        return metaObject->enumerator(metaObject->indexOfEnumerator(name));
    }

我们可以看到,QMetaEnum::fromType 中的代码与最前面QDebug中加红的代码是一样的。

随便提说一下,QDebug中用了标准库的std::enable_if, 经过查看源码发现声明如下:

// Primary template.
  /// Define a member typedef @c type only if a boolean constant is true.
  template<bool, typename _Tp = void>
    struct enable_if 
    { };

  // Partial specialization for true.
  template<typename _Tp>
    struct enable_if<true, _Tp>
    { typedef _Tp type; };

第一个是基本的模板,模板类型的两个参数分别是bool和类型 _Tp,  enable_if 是空的结构体。

第二个是一个特例的模板,即当bool取true时的情况,一般这样使用

enable_if<true, YourType>::type  
其中type等价与YourType,这里的模板中的true也可以换成一个表达式,但结果一定要是true,否则会直接报错,举例说明
int main(int argc, char *argv[])
{
    using Type1 = std::enable_if<true, float>::type;
    typedef std::enable_if<(8 > 6), int>::type Type2;
    using Type3 = std::enable_if<true, QString>::type;

    Type1 a = 5.8;
    Type2 b = 5.8;
    Type3 c;

    c += "hello TCB";
    auto d = a + b;
    qDebug() << "a = " << a;    // 5.8
    qDebug() << "b = " << b;    // 5
    qDebug() << "c = " << c;    // hello TCB
    qDebug() << "d = " << d;    // 10.8

    qDebug() << typeid (a).name() << "\t" << typeid (float).name();     // f    f
    qDebug() << typeid (b).name() << "\t" << typeid (int).name();       // i    i
    qDebug() << typeid (c).name() << "\t" << typeid (QString).name();   // 7QString    7QString
    qDebug() << typeid (d).name() << "\t" << typeid (float).name();     // f    f
}









  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值