Matt
Matt

Reputation: 2070

Q_FLAG_NS in Qt 5.9 not providing bitwise operators

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

Answers (2)

Alex Huszagh
Alex Huszagh

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

Mihayl
Mihayl

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

Related Questions