Reputation: 2070
How should the Q_FLAG_NS
macro be used?
My reading of Q_FLAG_NS and KDAB's New Qt Support Namespaces suggests to me that using this macro should provide bitwise operators, but no matter what I try,
../main.cpp:11:11: error: invalid operands to binary expression
('App::ComponentRequirementState' and 'App::ComponentRequirementState')
r = r | App::ComponentRequirementState::AlwaysRequired;
~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I placed a minimum code example here.
The weird thing is, I can use bitwise operators inside the definition of the ComponentRequirementStat
enum, just not outside of it.
Am I using this macro wrong? Or does it simply not work?
When I manually define the operators, say,
auto operator|(const App::ComponentRequirementState a, App::ComponentRequirementState b) -> App::ComponentRequirementState
{
return static_cast<App::ComponentRequirementState>(static_cast<unsigned int>(a) | static_cast<unsigned int>(b));
}
Then it works.
Upvotes: 2
Views: 623
Reputation: 14634
Using Qt
Q_FLAGS_NS
does not provide bitwise operation support: it merely registers the type in the Qt metaobject system.
If you would like to register the type and provide type-safe, bitwise operator support, you should use QFlags.
An example from the documentation is:
class MyClass
{
public:
enum Option {
NoOptions = 0x0,
ShowTabs = 0x1,
ShowAll = 0x2,
SqueezeBlank = 0x4
};
Q_DECLARE_FLAGS(Options, Option)
...
};
Q_DECLARE_OPERATORS_FOR_FLAGS(MyClass::Options)
You may also just skip most of this and do:
// not type-safe, unscoped enum. You should likely define this
// in a namespace or class.
enum Option {
NoOptions = 0x0,
ShowTabs = 0x1,
ShowAll = 0x2,
SqueezeBlank = 0x4
};
// type-safe flag
using Options = QFlags<Option>;
You may also use unscoped enums for this exact purpose, or use a macro to help you provide bitwise operator support for a scoped enum:
Type-safe Bitwise Operations with Scoped Enums
Here is a macro you may freely use (public domain, my own code, although you should likely modify the macro to ensure it has a prefix to avoid any name collisions) to add bitwise operations to a scoped enum. This currently requires C++14 support, however, changing std::underlying_type_t<T>
to typename std::underlying_type<T>::type
allows the macro to work in C++11.
Use
enum class enum1_t
{
A = 1,
B,
C,
D,
E,
};
enum class enum2_t
{
F = 1,
G,
H,
I,
J,
};
ENUM_FLAG(enum1_t) // allow bitwise operations for enum1_t and enum1_t
ENUM_FLAG(enum1_t, enum2_t) // allow bitwise operations for enum1_t and enum2_t
Code
#include <type_traits>
#include <cstdint>
// HELPERS
// -------
/**
* \brief Get enum underlying type.
*/
template <typename T>
inline std::underlying_type_t<T> int_t(T t)
{
return static_cast<std::underlying_type_t<T>>(t);
}
// MACROS
// ------
/**
* \brief Macro to define enum operators between enumerations.
*
* Supports `&`, `&=`, `|`, `|=`, `^`, `^=`, `~`, and bool conversion.
*/
#define ENUM_FLAG2(lhs_t, ths_t) \
/* \brief Bitwise or operator. */ \
inline lhs_t operator|(lhs_t lhs, ths_t rhs) noexcept \
{ \
return static_cast<lhs_t>(int_t(lhs) | int_t(rhs)); \
} \
\
/* \brief Bitwise or assignment operator. */ \
inline lhs_t & operator|=(lhs_t &lhs, ths_t rhs) noexcept \
{ \
lhs = static_cast<lhs_t>(int_t(lhs) | int_t(rhs)); \
return lhs; \
} \
\
/* \brief Bitwise and operator. */ \
inline lhs_t operator&(lhs_t lhs, ths_t rhs) noexcept \
{ \
return static_cast<lhs_t>(int_t(lhs) & int_t(rhs)); \
} \
\
/* \brief Bitwise and assignment operator. */ \
inline lhs_t & operator&=(lhs_t &lhs, ths_t rhs) noexcept \
{ \
lhs = static_cast<lhs_t>(int_t(lhs) & int_t(rhs)); \
return lhs; \
} \
\
/* \brief Bitwise xor operator. */ \
inline lhs_t operator^(lhs_t lhs, ths_t rhs) noexcept \
{ \
return static_cast<lhs_t>(int_t(lhs) ^ int_t(rhs)); \
} \
\
/* \brief Bitwise xor assignment operator. */ \
inline lhs_t & operator^=(lhs_t &lhs, ths_t rhs) noexcept \
{ \
lhs = static_cast<lhs_t>(int_t(lhs) ^ int_t(rhs)); \
return lhs; \
}
/**
* \brief Set enumeration flags within the same enum.
*/
#define ENUM_FLAG1(enum_t) \
ENUM_FLAG2(enum_t, enum_t) \
\
/* \brief Bitwise negation operator. */ \
inline enum_t operator~(enum_t value) noexcept \
{ \
return static_cast<enum_t>(~int_t(value)); \
} \
\
/* \brief Negation operator. */ \
inline bool operator!(enum_t value) noexcept \
{ \
return int_t(value) == 0; \
}
/**
* \brief Macros to grab the proper bit-wise flag setter.
* `ENUM_ID` is required for MSVC compatibility, since MSVC
* has issues in expanding `__VA_ARGS__` for the dispatcher.
* Don't remove it, even if the above code works without it
* for GCC and Clang.
*/
#define ENUM_ID(x) x
#define GET_ENUM_FLAG(_1,_2,NAME,...) NAME
#define ENUM_FLAG(...) ENUM_ID(GET_ENUM_FLAG(__VA_ARGS__, ENUM_FLAG2, ENUM_FLAG1)(__VA_ARGS__))
Upvotes: 2
Reputation: 3911
Q_FLAG_NS
is not the problem. This is caused by the new C++11 scoped enumerations (enum class
). They do not implicitly convert to integral type and you cannot use them for bit-wise operations which are defined only for integral types [1][2].
You have to provide these operations yourself or use the unscoped, old, enumeration.
One issue by providing these operations is that it would require to define all possible combinations of flags as enum values or return integral type:
??? operator|(App::ComponentRequirementState lhs, App::ComponentRequirementState rhs);
Upvotes: 1