Q_ENUM Q_ENUMS Q_ENUM_NS Q_FLAG Q_FLAGS Q_FLAG_NS
使用qt5.12.0
qt中有一套meta系统,用于方便做类型检测、反射等等操作。
Q_ENUMS是最早的将enum类型纳入meta系统的宏。在qt5.5版(网上看到的)之后,主要使用Q_ENUM宏。
Q_ENUM、Q_ENUMS、Q_ENUM_NS、Q_FLAG、Q_FLAGS、Q_FLAG_NS这些宏在qt中有两重意义,一重意义是直观的作为C++的宏定义,另一重意义是作为Qt的关键字。
Q_ENUMS宏定义如下:
//QtInstallDir\Src\qtbase\src\corelib\kernel\qobjectdefs.h
#ifndef QT_ANNOTATE_CLASS
# ifndef Q_COMPILER_VARIADIC_MACROS
# define QT_ANNOTATE_CLASS(type, x)
# else
# define QT_ANNOTATE_CLASS(type, ...)
# endif
........
#define Q_ENUMS(x) QT_ANNOTATE_CLASS(qt_enums, x)
作为宏定义,Q_ENUMS基本没有意义。
而作为Qt的关键字(参考:Q_PLUGIN_METADATA_丘上人的博客-CSDN博客 ),在moc.exe中经过Preprocessor对象的识别和Moc对象的处理并在Moc对象中记录下相关信息,然后在Generator对象中根据相关信息生成moc_*.cpp中enum相关的必要的代码。
//QtInstallDir\Src\qtbase\src\tools\moc\util\generate_keywords.cpp
static const Keyword keywords[] = {
............
{ "Q_ENUMS", "Q_ENUMS_TOKEN" },
{ "Q_ENUM", "Q_ENUM_TOKEN" },
{ "Q_ENUM_NS", "Q_ENUM_NS_TOKEN" },
{ "Q_FLAGS", "Q_FLAGS_TOKEN" },
{ "Q_FLAG", "Q_FLAG_TOKEN" },
{ "Q_FLAG_NS", "Q_FLAG_NS_TOKEN" },
............
}
----------------------
//QtInstallDir\Src\qtbase\src\tools\moc\preprocessor.cpp
Symbols Preprocessor::tokenize(const QByteArray& input, int lineNum, Preprocessor::TokenizeMode mode)
{
..........
while (*data) {
if (mode == TokenizeCpp || mode == TokenizeDefine) {
int column = 0;
const char *lexem = data;
int state = 0;
Token token = NOTOKEN;
for (;;) {
if (static_cast<signed char>(*data) < 0) {
++data;
continue;
}
int nextindex = keywords[state].next;
int next = 0;
if (*data == keywords[state].defchar)
next = keywords[state].defnext;
else if (!state || nextindex)
next = keyword_trans[nextindex][(int)*data];
if (!next)
break;
state = next;
token = keywords[state].token;
++data;
}
// suboptimal, is_ident_char should use a table
if (keywords[state].ident && is_ident_char(*data))
token = keywords[state].ident;
if (token == NOTOKEN) {
if (*data)
++data;
// an error really, but let's ignore this input
// to not confuse moc later. However in pre-processor
// only mode let's continue.
if (!Preprocessor::preprocessOnly)
continue;
}
..........
}
----------------------
//QtInstallDir\Src\qtbase\src\tools\moc\moc.cpp
void Moc::parse(){
.........
case Q_ENUMS_TOKEN:
case Q_ENUM_TOKEN:
parseEnumOrFlag(&def, false);
break;
case Q_ENUM_NS_TOKEN:
error("Q_ENUM_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_ENUM instead");
break;
case Q_FLAGS_TOKEN:
case Q_FLAG_TOKEN:
parseEnumOrFlag(&def, true);
........
}
void Moc::parseEnumOrFlag(BaseDef *def, bool isFlag)
{
next(LPAREN);
QByteArray identifier;
while (test(IDENTIFIER)) {
identifier = lexem();
while (test(SCOPE) && test(IDENTIFIER)) {
identifier += "::";
identifier += lexem();
}
def->enumDeclarations[identifier] = isFlag;
}
next(RPAREN);
}
----------------------
//QtInstallDir\Src\qtbase\src\tools\moc\generator.cpp
void Generator::generateEnums(int index)
{
if (cdef->enumDeclarations.isEmpty())
return;
fprintf(out, "\n // enums: name, alias, flags, count, data\n");
index += 5 * cdef->enumList.count();
int i;
for (i = 0; i < cdef->enumList.count(); ++i) {
const EnumDef &e = cdef->enumList.at(i);
int flags = 0;
if (cdef->enumDeclarations.value(e.name))
flags |= EnumIsFlag;
if (e.isEnumClass)
flags |= EnumIsScoped;
fprintf(out, " %4d, %4d, 0x%.1x, %4d, %4d,\n",
stridx(e.name),
e.enumName.isNull() ? stridx(e.name) : stridx(e.enumName),
flags,
e.values.count(),
index);
index += e.values.count() * 2;
}
fprintf(out, "\n // enum data: key, value\n");
for (i = 0; i < cdef->enumList.count(); ++i) {
const EnumDef &e = cdef->enumList.at(i);
for (int j = 0; j < e.values.count(); ++j) {
const QByteArray &val = e.values.at(j);
QByteArray code = cdef->qualified.constData();
if (e.isEnumClass)
code += "::" + (e.enumName.isNull() ? e.name : e.enumName);
code += "::" + val;
fprintf(out, " %4d, uint(%s),\n",
stridx(val), code.constData());
}
}
}
主要来关注一下Q_ENUMS的使用和moc_*.cpp中生成的enum相关的代码:
//MyProjectPath/myenum.h
class MyEnum : public QObject
{
Q_OBJECT
public:
explicit MyEnum(QObject *parent = nullptr);
enum class Orientation
{
Up = 1,
Down = 2,
Left = 4,
Right = 8,
};
Q_ENUMS(Orientation) //如不使用Orientation,可省略
};
------------------
//moc_myenum.cpp
struct qt_meta_stringdata_MyEnum_t {
QByteArrayData data[6];
char stringdata0[38];
};
static const qt_meta_stringdata_MyEnum_t qt_meta_stringdata_MyEnum = {
{
QT_MOC_LITERAL(0, 0, 6), // "MyEnum"
QT_MOC_LITERAL(1, 7, 11), // "Orientation"
QT_MOC_LITERAL(2, 19, 2), // "Up"
QT_MOC_LITERAL(3, 22, 4), // "Down"
QT_MOC_LITERAL(4, 27, 4), // "Left"
QT_MOC_LITERAL(5, 32, 5) // "Right"
},
"MyEnum\0Orientation\0Up\0Down\0Left\0Right"
};
static const uint qt_meta_data_MyEnum[] = {
// content:
8, // revision
0, // classname
0, 0, // classinfo
0, 0, // methods
0, 0, // properties
1, 14, // enums/sets
0, 0, // constructors
0, // flags
0, // signalCount
// enums: name, alias, flags, count, data
1, 1, 0x2, 4, 19,
// enum data: key, value
2, uint(MyEnum::Orientation::Up),
3, uint(MyEnum::Orientation::Down),
4, uint(MyEnum::Orientation::Left),
5, uint(MyEnum::Orientation::Right),
0 // eod
};
QT_INIT_METAOBJECT const QMetaObject MyEnum::staticMetaObject = { {
&QObject::staticMetaObject,
qt_meta_stringdata_MyEnum.data,
qt_meta_data_MyEnum,
qt_static_metacall,
nullptr,
nullptr
} };
可以看到所有的enum class Orientation的信息都存入到了qt_meta_stringdata_MyEnum和qt_meta_data_MyEnum中了 。qt_meta_stringdata_MyEnum主要记录名字,最关键的还是看qt_meta_data_MyEnum,记录了enum的数量和enum的具体类型信息。其实到此为止,meta系统就已经完成了初始化了,并将初始化信息作为这几个数组中记录的数据固定存储下来了。
首先要知道,qt程序构建和编译过程是先构建,构建过程用moc生成moc_*.cpp文件,然后再是预编译(宏替换),然后再是编译。也就是说moc生成moc_*.cpp代码是在预编译之前!
程序运行起来后,先将moc_*.cpp中的几个static类型的数据初始化了,也就是说MyEnum类相关的qt_meta_stringdata_MyEnum和qt_meta_data_MyEnum、QMetaObject MyEnum::staticMetaObject三个static类型的变量在main函数运行之前就是被初始化了,后续所有的对该类的meta系统的调用都是基于这几个static类型数据进行运算的。
而具体的调用过程可以参看下面这篇文章:QMetaObjectPrivate meta Q_INVOKABLE
所以总结下:Q_ENUMS宏具体意义就是将enum class Orientation{...}的所有信息包括具体名字在moc.exe的操作下存入meta系统中。
//QtInstallDir\Src\qtbase\src\corelib\kernel\qmetaobject.h
class Q_CORE_EXPORT QMetaEnum //将枚举类型中的名字和值进行转换的工具类。
{
public:
Q_DECL_CONSTEXPR inline QMetaEnum() : mobj(nullptr), handle(0) {}
const char *name() const;
const char *enumName() const;
bool isFlag() const;
bool isScoped() const;
int keyCount() const;
const char *key(int index) const;
int value(int index) const;
const char *scope() const;
int keyToValue(const char *key, bool *ok = nullptr) const;
const char* valueToKey(int value) const;
int keysToValue(const char * keys, bool *ok = nullptr) const;
QByteArray valueToKeys(int value) const;
inline const QMetaObject *enclosingMetaObject() const { return mobj; }
inline bool isValid() const { return name() != nullptr; }
.....
}
----------------------------
//projectPath//main.cpp
static QString getStringByID(MyEnum::Orientation id)
{
if(id<MyEnum::Orientation::Up||id>MyEnum::Orientation::Right)
{
return QString();
}
const QMetaObject obj=MyEnum::staticMetaObject;//第二步
int index=obj.indexOfEnumerator("Orientation");//第三步
if(index<0)
{
return QString();
}
QMetaEnum en= obj.enumerator(index);//第四步,拿到MyEnum中的Orientation的QMetaEnum对象
return QString(en.valueToKey(static_cast<int>(id)));//返回枚举的字符串
}
int main()
{
qDebug()<<getStringByID(MyEnum::Orientation::Up)<<endl;//输出:Up
return 0;
}
Q_ENUM与Q_ENUMS相比,多了一个宏定义,但是使用时省去了一些操作:
//QtInstallDir\Src\qtbase\src\corelib\kernel\qobjectdefs.h
#define Q_ENUM_IMPL(ENUM) \
friend Q_DECL_CONSTEXPR const QMetaObject *qt_getEnumMetaObject(ENUM) Q_DECL_NOEXCEPT { return &staticMetaObject; } \
friend Q_DECL_CONSTEXPR const char *qt_getEnumName(ENUM) Q_DECL_NOEXCEPT { return #ENUM; }
#define Q_ENUM(x) Q_ENUMS(x) Q_ENUM_IMPL(x)
------------------------------------
//QtInstallDir\Src\qtbase\src\corelib\kernel\qmetaobject.h
class Q_CORE_EXPORT QMetaEnum //将枚举类型中的名字和值进行转换的工具类。
{
...........
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));
}
.......
}
-----------------------------------
//myProjectPath/main.cpp
int main()
{
qDebug()<<getStringByID(MyEnum::Orientation::Up)<<endl;//输出:Up
return 0;
}
Q_ENUM_NS 是用于结合Q_NAMESPACE来使用的。作为C++宏定义与Q_ENUM是一致的。作为Qt关键字稍微有点差别。具体使用可以参考Qt中的枚举变量,Q_ENUM,Q_FLAG,Q_NAMESPACE,Q_ENUM_NS,Q_FLAG_NS以及其他_荆楚闲人的博客-CSDN博客_qt 定义枚举
//QtInstallDir\Src\qtbase\src\corelib\kernel\qobjectdefs.h
#define Q_ENUM_NS_IMPL(ENUM) \
inline Q_DECL_CONSTEXPR const QMetaObject *qt_getEnumMetaObject(ENUM) Q_DECL_NOEXCEPT { return &staticMetaObject; } \
inline Q_DECL_CONSTEXPR const char *qt_getEnumName(ENUM) Q_DECL_NOEXCEPT { return #ENUM; }
#define Q_ENUM_NS(x) Q_ENUMS(x) Q_ENUM_NS_IMPL(x)
#define Q_NAMESPACE \
extern const QMetaObject staticMetaObject; \
QT_ANNOTATE_CLASS(qt_qnamespace, "") \
/*end*/
QMetaObjectPrivate meta_constractors Q_INVOKABLE_丘上人的博客-CSDN博客 Q_FLAGS,其意义包含了Q_ENUMS的意义,也就是Q_ENUMS能做的Q_FLAGS也能做。在moc.exe中Q_FLAGS与Q_ENUMS的操作是一致的,两者作为C++宏定义的意义也是一样的。Q_FLAGS和Q_FLAG的差异 与 Q_ENUMS和Q_ENUM的差异是一致的,为了方便下面就讲Q_FLAG
Q_FLAG通过Q_DECLARE_FLAGS宏引入了QFlags<T>模板类,它将enum 转换成QFlags类,从而进行使enum类型数据进行一些位相关的运算并能成功返回其名称。
//projectPath/myEnum.h
class MyEnum : public QObject
{
Q_OBJECT
public:
explicit MyEnum(QObject *parent = nullptr);
enum class Orientation
{
Up = 1,
Down = 2,
Left = 4,
Right = 8,
};
Q_FLAGS(Orientation) //如不使用Orientation,可省略
Q_DECLARE_FLAGS(OrientationFlags, Orientation)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(MyEnum::OrientationFlags)
-----------------------------
//moc_myenum.cpp
........
static const qt_meta_stringdata_MyEnum_t qt_meta_stringdata_MyEnum = {
{
QT_MOC_LITERAL(0, 0, 6), // "MyEnum"
QT_MOC_LITERAL(1, 7, 11), // "Orientation"
QT_MOC_LITERAL(2, 19, 2), // "Up"
QT_MOC_LITERAL(3, 22, 4), // "Down"
QT_MOC_LITERAL(4, 27, 4), // "Left"
QT_MOC_LITERAL(5, 32, 5) // "Right"
},
"MyEnum\0Orientation\0Up\0Down\0Left\0Right"
};
.......
static const uint qt_meta_data_MyEnum[] = {
// content:
8, // revision
0, // classname
0, 0, // classinfo
0, 0, // methods
0, 0, // properties
1, 14, // enums/sets
0, 0, // constructors
0, // flags
0, // signalCount
// enums: name, alias, flags, count, data
1, 1, 0x3, 4, 19,
// enum data: key, value
2, uint(MyEnum::Orientation::Up),
3, uint(MyEnum::Orientation::Down),
4, uint(MyEnum::Orientation::Left),
5, uint(MyEnum::Orientation::Right),
0 // eod
};
......
-----------------------------
//QtInstallPath\Src\qtbase\src\corelib\global\qflags.h
template<typename Enum>
class QFlags
{
Q_STATIC_ASSERT_X((sizeof(Enum) <= sizeof(int)),
"QFlags uses an int as storage, so an enum with underlying "
"long long will overflow.");
Q_STATIC_ASSERT_X((std::is_enum<Enum>::value), "QFlags is only usable on enumeration types.");
struct Private;
typedef int (Private::*Zero);
template <typename E> friend QDataStream &operator>>(QDataStream &, QFlags<E> &);
template <typename E> friend QDataStream &operator<<(QDataStream &, QFlags<E>);
public:
#if defined(Q_CC_MSVC) || defined(Q_CLANG_QDOC)
// see above for MSVC
// the definition below is too complex for qdoc
typedef int Int;
#else
typedef typename std::conditional<
std::is_unsigned<typename std::underlying_type<Enum>::type>::value,
unsigned int,
signed int
>::type Int;
#endif
typedef Enum enum_type;
// compiler-generated copy/move ctor/assignment operators are fine!
#ifdef Q_CLANG_QDOC
Q_DECL_CONSTEXPR inline QFlags(const QFlags &other);
Q_DECL_CONSTEXPR inline QFlags &operator=(const QFlags &other);
#endif
Q_DECL_CONSTEXPR inline QFlags(Enum flags) Q_DECL_NOTHROW : i(Int(flags)) {}
Q_DECL_CONSTEXPR inline QFlags(Zero = Q_NULLPTR) Q_DECL_NOTHROW : i(0) {}
Q_DECL_CONSTEXPR inline QFlags(QFlag flag) Q_DECL_NOTHROW : i(flag) {}
#ifdef Q_COMPILER_INITIALIZER_LISTS
Q_DECL_CONSTEXPR inline QFlags(std::initializer_list<Enum> flags) Q_DECL_NOTHROW
: i(initializer_list_helper(flags.begin(), flags.end())) {}
#endif
Q_DECL_RELAXED_CONSTEXPR inline QFlags &operator&=(int mask) Q_DECL_NOTHROW { i &= mask; return *this; }
Q_DECL_RELAXED_CONSTEXPR inline QFlags &operator&=(uint mask) Q_DECL_NOTHROW { i &= mask; return *this; }
Q_DECL_RELAXED_CONSTEXPR inline QFlags &operator&=(Enum mask) Q_DECL_NOTHROW { i &= Int(mask); return *this; }
Q_DECL_RELAXED_CONSTEXPR inline QFlags &operator|=(QFlags other) Q_DECL_NOTHROW { i |= other.i; return *this; }
Q_DECL_RELAXED_CONSTEXPR inline QFlags &operator|=(Enum other) Q_DECL_NOTHROW { i |= Int(other); return *this; }
Q_DECL_RELAXED_CONSTEXPR inline QFlags &operator^=(QFlags other) Q_DECL_NOTHROW { i ^= other.i; return *this; }
Q_DECL_RELAXED_CONSTEXPR inline QFlags &operator^=(Enum other) Q_DECL_NOTHROW { i ^= Int(other); return *this; }
Q_DECL_CONSTEXPR inline operator Int() const Q_DECL_NOTHROW { return i; }
Q_DECL_CONSTEXPR inline QFlags operator|(QFlags other) const Q_DECL_NOTHROW { return QFlags(QFlag(i | other.i)); }
Q_DECL_CONSTEXPR inline QFlags operator|(Enum other) const Q_DECL_NOTHROW { return QFlags(QFlag(i | Int(other))); }
Q_DECL_CONSTEXPR inline QFlags operator^(QFlags other) const Q_DECL_NOTHROW { return QFlags(QFlag(i ^ other.i)); }
Q_DECL_CONSTEXPR inline QFlags operator^(Enum other) const Q_DECL_NOTHROW { return QFlags(QFlag(i ^ Int(other))); }
Q_DECL_CONSTEXPR inline QFlags operator&(int mask) const Q_DECL_NOTHROW { return QFlags(QFlag(i & mask)); }
Q_DECL_CONSTEXPR inline QFlags operator&(uint mask) const Q_DECL_NOTHROW { return QFlags(QFlag(i & mask)); }
Q_DECL_CONSTEXPR inline QFlags operator&(Enum other) const Q_DECL_NOTHROW { return QFlags(QFlag(i & Int(other))); }
Q_DECL_CONSTEXPR inline QFlags operator~() const Q_DECL_NOTHROW { return QFlags(QFlag(~i)); }
Q_DECL_CONSTEXPR inline bool operator!() const Q_DECL_NOTHROW { return !i; }
Q_DECL_CONSTEXPR inline bool testFlag(Enum flag) const Q_DECL_NOTHROW { return (i & Int(flag)) == Int(flag) && (Int(flag) != 0 || i == Int(flag) ); }
Q_DECL_RELAXED_CONSTEXPR inline QFlags &setFlag(Enum flag, bool on = true) Q_DECL_NOTHROW
{
return on ? (*this |= flag) : (*this &= ~Int(flag));
}
另外还有一个宏:Q_DECLARE_OPERATORS_FOR_FLAGS
其意义是让"|"操作更方便。
//QtInstallPath\Src\qtbase\src\corelib\global\qflags.h
#define Q_DECLARE_OPERATORS_FOR_FLAGS(Flags) \
Q_DECL_CONSTEXPR inline QFlags<Flags::enum_type> operator|(Flags::enum_type f1, Flags::enum_type f2) Q_DECL_NOTHROW \
{ return QFlags<Flags::enum_type>(f1) | f2; } \
Q_DECL_CONSTEXPR inline QFlags<Flags::enum_type> operator|(Flags::enum_type f1, QFlags<Flags::enum_type> f2) Q_DECL_NOTHROW \
{ return f2 | f1; } Q_DECLARE_INCOMPATIBLE_FLAGS(Flags)
---------------------------------
int main()
{
MyEnum::Orientation o = MyEnum::Orientation::Up;
MyEnum::OrientationFlags s = o;
MyEnum::OrientationFlags s1 = s|o;
//如果没有声明Q_DECLARE_OPERATORS_FOR_FLAGS(MyEnum::OrientationFlags),下面语句将会报错。
MyEnum::OrientationFlags s2 = o|s;
return 0;
}