使用qt5.12.0
注意:qtcreator对qt程序的编译过程是先qmake然后构建(等价于make)。从项目->build->构建步骤可以看出,构建过程中先用qmake生成makefile,make过程用jom和前面产生的makefile生成目标程序的exe。jom按makefile规则的运行过程中根据依赖会先调用moc.exe生成moc_*.cpp文件,然后是按正常C++编译过程进行C++预编译(宏替换),再然后是C++编译。也就是说moc生成moc_*.cpp代码是在C++编译器进行预编译之前!jom具体如何调用moc的过程请参考:
qt 工程构建过程 默认构建路径设置 通过Dos窗口运行命令编译qt工程_丘上人的博客-CSDN博客_qt执行dos命令
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系统就已经完成了初始化了,并将初始化信息作为这几个数组中记录的数据固定存储下来了。
程序运行起来后,先将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
- //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
&); - 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;
- }