diff options
Diffstat (limited to 'gdbsupport/enum-flags.h')
-rw-r--r-- | gdbsupport/enum-flags.h | 366 |
1 files changed, 284 insertions, 82 deletions
diff --git a/gdbsupport/enum-flags.h b/gdbsupport/enum-flags.h index 825ff4f..b3e317e 100644 --- a/gdbsupport/enum-flags.h +++ b/gdbsupport/enum-flags.h @@ -18,6 +18,8 @@ #ifndef COMMON_ENUM_FLAGS_H #define COMMON_ENUM_FLAGS_H +#include "traits.h" + /* Type-safe wrapper for enum flags. enum flags are enums where the values are bits that are meant to be ORed together. @@ -51,23 +53,31 @@ #ifdef __cplusplus -/* Traits type used to prevent the global operator overloads from - instantiating for non-flag enums. */ -template<typename T> struct enum_flags_type {}; - -/* Use this to mark an enum as flags enum. It defines FLAGS as +/* Use this to mark an enum as flags enum. It defines FLAGS_TYPE as enum_flags wrapper class for ENUM, and enables the global operator overloads for ENUM. */ #define DEF_ENUM_FLAGS_TYPE(enum_type, flags_type) \ typedef enum_flags<enum_type> flags_type; \ - template<> \ - struct enum_flags_type<enum_type> \ - { \ - typedef enum_flags<enum_type> type; \ - } + void is_enum_flags_enum_type (enum_type *) + +/* To enable the global enum_flags operators for enum, declare an + "is_enum_flags_enum_type" overload that has exactly one parameter, + of type a pointer to that enum class. E.g.,: + + void is_enum_flags_enum_type (enum some_flag *); + + The function does not need to be defined, only declared. + DEF_ENUM_FLAGS_TYPE declares this. + + A function declaration is preferred over a traits type, because the + former allows calling the DEF_ENUM_FLAGS_TYPE macro inside a + namespace to define the corresponding enum flags type in that + namespace. The compiler finds the corresponding + is_enum_flags_enum_type function via ADL. */ -/* Until we can rely on std::underlying type being universally - available (C++11), roll our own for enums. */ +/* Note that std::underlying_type<enum_type> is not what we want here, + since that returns unsigned int even when the enum decays to signed + int. */ template<int size, bool sign> class integer_for_size { typedef void type; }; template<> struct integer_for_size<1, 0> { typedef uint8_t type; }; template<> struct integer_for_size<2, 0> { typedef uint16_t type; }; @@ -86,128 +96,320 @@ struct enum_underlying_type type; }; -template <typename E> -class enum_flags +namespace enum_flags_detail { -public: - typedef E enum_type; - typedef typename enum_underlying_type<enum_type>::type underlying_type; -private: - /* Private type used to support initializing flag types with zero: +/* Private type used to support initializing flag types with zero: - foo_flags f = 0; + foo_flags f = 0; - but not other integers: + but not other integers: - foo_flags f = 1; + foo_flags f = 1; - The way this works is that we define an implicit constructor that - takes a pointer to this private type. Since nothing can - instantiate an object of this type, the only possible pointer to - pass to the constructor is the NULL pointer, or, zero. */ - struct zero_type; + The way this works is that we define an implicit constructor that + takes a pointer to this private type. Since nothing can + instantiate an object of this type, the only possible pointer to + pass to the constructor is the NULL pointer, or, zero. */ +struct zero_type; - underlying_type - underlying_value () const - { - return m_enum_value; - } +/* gdb::Requires trait helpers. */ +template <typename enum_type> +using EnumIsUnsigned + = std::is_unsigned<typename enum_underlying_type<enum_type>::type>; +template <typename enum_type> +using EnumIsSigned + = std::is_signed<typename enum_underlying_type<enum_type>::type>; + +} + +template <typename E> +class enum_flags +{ +public: + typedef E enum_type; + typedef typename enum_underlying_type<enum_type>::type underlying_type; public: /* Allow default construction. */ - enum_flags () + constexpr enum_flags () : m_enum_value ((enum_type) 0) {} + /* The default move/copy ctor/assignment do the right thing. */ + /* If you get an error saying these two overloads are ambiguous, then you tried to mix values of different enum types. */ - enum_flags (enum_type e) + constexpr enum_flags (enum_type e) : m_enum_value (e) {} - enum_flags (struct enum_flags::zero_type *zero) + constexpr enum_flags (enum_flags_detail::zero_type *zero) : m_enum_value ((enum_type) 0) {} - enum_flags &operator&= (enum_type e) + enum_flags &operator&= (enum_flags e) { - m_enum_value = (enum_type) (underlying_value () & e); + m_enum_value = (enum_type) (m_enum_value & e.m_enum_value); return *this; } - enum_flags &operator|= (enum_type e) + enum_flags &operator|= (enum_flags e) { - m_enum_value = (enum_type) (underlying_value () | e); + m_enum_value = (enum_type) (m_enum_value | e.m_enum_value); return *this; } - enum_flags &operator^= (enum_type e) + enum_flags &operator^= (enum_flags e) { - m_enum_value = (enum_type) (underlying_value () ^ e); + m_enum_value = (enum_type) (m_enum_value ^ e.m_enum_value); return *this; } - operator enum_type () const + /* Like raw enums, allow conversion to the underlying type. */ + constexpr operator underlying_type () const { return m_enum_value; } - enum_flags operator& (enum_type e) const - { - return (enum_type) (underlying_value () & e); - } - enum_flags operator| (enum_type e) const - { - return (enum_type) (underlying_value () | e); - } - enum_flags operator^ (enum_type e) const - { - return (enum_type) (underlying_value () ^ e); - } - enum_flags operator~ () const + /* Get the underlying value as a raw enum. */ + constexpr enum_type raw () const { - // We only the underlying type to be unsigned when actually using - // operator~ -- if it were not unsigned, undefined behavior could - // result. However, asserting this in the class itself would - // require too many unnecessary changes to otherwise ok enum - // types. - gdb_static_assert (std::is_unsigned<underlying_type>::value); - return (enum_type) ~underlying_value (); + return m_enum_value; } + /* Binary operations involving some unrelated type (which would be a + bug) are implemented as non-members, and deleted. */ + private: /* Stored as enum_type because GDB knows to print the bit flags neatly if the enum values look like bit flags. */ enum_type m_enum_value; }; +template <typename E> +using is_enum_flags_enum_type_t + = decltype (is_enum_flags_enum_type (std::declval<E *> ())); + /* Global operator overloads. */ -template <typename enum_type> -typename enum_flags_type<enum_type>::type -operator& (enum_type e1, enum_type e2) +/* Generate binary operators. */ + +#define ENUM_FLAGS_GEN_BINOP(OPERATOR_OP, OP) \ + \ + /* Raw enum on both LHS/RHS. Returns raw enum type. */ \ + template <typename enum_type, \ + typename = is_enum_flags_enum_type_t<enum_type>> \ + constexpr enum_type \ + OPERATOR_OP (enum_type e1, enum_type e2) \ + { \ + using underlying = typename enum_flags<enum_type>::underlying_type; \ + return (enum_type) (underlying (e1) OP underlying (e2)); \ + } \ + \ + /* enum_flags on the LHS. */ \ + template <typename enum_type, \ + typename = is_enum_flags_enum_type_t<enum_type>> \ + constexpr enum_flags<enum_type> \ + OPERATOR_OP (enum_flags<enum_type> e1, enum_type e2) \ + { return e1.raw () OP e2; } \ + \ + /* enum_flags on the RHS. */ \ + template <typename enum_type, \ + typename = is_enum_flags_enum_type_t<enum_type>> \ + constexpr enum_flags<enum_type> \ + OPERATOR_OP (enum_type e1, enum_flags<enum_type> e2) \ + { return e1 OP e2.raw (); } \ + \ + /* enum_flags on both LHS/RHS. */ \ + template <typename enum_type, \ + typename = is_enum_flags_enum_type_t<enum_type>> \ + constexpr enum_flags<enum_type> \ + OPERATOR_OP (enum_flags<enum_type> e1, enum_flags<enum_type> e2) \ + { return e1.raw () OP e2.raw (); } \ + \ + /* Delete cases involving unrelated types. */ \ + \ + template <typename enum_type, typename unrelated_type, \ + typename = is_enum_flags_enum_type_t<enum_type>> \ + constexpr enum_flags<enum_type> \ + OPERATOR_OP (enum_type e1, unrelated_type e2) = delete; \ + \ + template <typename enum_type, typename unrelated_type, \ + typename = is_enum_flags_enum_type_t<enum_type>> \ + constexpr enum_flags<enum_type> \ + OPERATOR_OP (unrelated_type e1, enum_type e2) = delete; \ + \ + template <typename enum_type, typename unrelated_type, \ + typename = is_enum_flags_enum_type_t<enum_type>> \ + constexpr enum_flags<enum_type> \ + OPERATOR_OP (enum_flags<enum_type> e1, unrelated_type e2) = delete; \ + \ + template <typename enum_type, typename unrelated_type, \ + typename = is_enum_flags_enum_type_t<enum_type>> \ + constexpr enum_flags<enum_type> \ + OPERATOR_OP (unrelated_type e1, enum_flags<enum_type> e2) = delete; + +/* Generate non-member compound assignment operators. Only the raw + enum versions are defined here. The enum_flags versions are + defined as member functions, simply because it's less code that + way. + + Note we delete operators that would allow e.g., + + "enum_type | 1" or "enum_type1 | enum_type2" + + because that would allow a mistake like : + enum flags1 { F1_FLAGS1 = 1 }; + enum flags2 { F2_FLAGS2 = 2 }; + enum flags1 val; + switch (val) { + case F1_FLAGS1 | F2_FLAGS2: + ... + + If you really need to 'or' enumerators of different flag types, + cast to integer first. +*/ +#define ENUM_FLAGS_GEN_COMPOUND_ASSIGN(OPERATOR_OP, OP) \ + /* lval reference version. */ \ + template <typename enum_type, \ + typename = is_enum_flags_enum_type_t<enum_type>> \ + constexpr enum_type & \ + OPERATOR_OP (enum_type &e1, enum_type e2) \ + { return e1 = e1 OP e2; } \ + \ + /* rval reference version. */ \ + template <typename enum_type, \ + typename = is_enum_flags_enum_type_t<enum_type>> \ + constexpr enum_type & \ + OPERATOR_OP (enum_type &&e1, enum_type e2) \ + { return e1 = e1 OP e2; } \ + \ + /* Delete compound assignment from unrelated types. */ \ + \ + template <typename enum_type, typename other_enum_type, \ + typename = is_enum_flags_enum_type_t<enum_type>> \ + constexpr enum_type & \ + OPERATOR_OP (enum_type &e1, other_enum_type e2) = delete; \ + \ + template <typename enum_type, typename other_enum_type, \ + typename = is_enum_flags_enum_type_t<enum_type>> \ + constexpr enum_type & \ + OPERATOR_OP (enum_type &&e1, other_enum_type e2) = delete; + +ENUM_FLAGS_GEN_BINOP (operator|, |) +ENUM_FLAGS_GEN_BINOP (operator&, &) +ENUM_FLAGS_GEN_BINOP (operator^, ^) + +ENUM_FLAGS_GEN_COMPOUND_ASSIGN (operator|=, |) +ENUM_FLAGS_GEN_COMPOUND_ASSIGN (operator&=, &) +ENUM_FLAGS_GEN_COMPOUND_ASSIGN (operator^=, ^) + +/* Allow comparison with enum_flags, raw enum, and integers, only. + The latter case allows "== 0". As side effect, it allows comparing + with integer variables too, but that's not a common mistake to + make. It's important to disable comparison with unrelated types to + prevent accidentally comparing with unrelated enum values, which + are convertible to integer, and thus coupled with enum_flags + convertion to underlying type too, would trigger the built-in 'bool + operator==(unsigned, int)' operator. */ + +#define ENUM_FLAGS_GEN_COMP(OPERATOR_OP, OP) \ + \ + /* enum_flags OP enum_flags */ \ + \ + template <typename enum_type> \ + constexpr bool \ + OPERATOR_OP (enum_flags<enum_type> lhs, enum_flags<enum_type> rhs) \ + { return lhs.raw () OP rhs.raw (); } \ + \ + /* enum_flags OP other */ \ + \ + template <typename enum_type> \ + constexpr bool \ + OPERATOR_OP (enum_flags<enum_type> lhs, enum_type rhs) \ + { return lhs.raw () OP rhs; } \ + \ + template <typename enum_type> \ + constexpr bool \ + OPERATOR_OP (enum_flags<enum_type> lhs, int rhs) \ + { return lhs.raw () OP rhs; } \ + \ + template <typename enum_type, typename U> \ + constexpr bool \ + OPERATOR_OP (enum_flags<enum_type> lhs, U rhs) = delete; \ + \ + /* other OP enum_flags */ \ + \ + template <typename enum_type> \ + constexpr bool \ + OPERATOR_OP (enum_type lhs, enum_flags<enum_type> rhs) \ + { return lhs OP rhs.raw (); } \ + \ + template <typename enum_type> \ + constexpr bool \ + OPERATOR_OP (int lhs, enum_flags<enum_type> rhs) \ + { return lhs OP rhs.raw (); } \ + \ + template <typename enum_type, typename U> \ + constexpr bool \ + OPERATOR_OP (U lhs, enum_flags<enum_type> rhs) = delete; + +ENUM_FLAGS_GEN_COMP (operator==, ==) +ENUM_FLAGS_GEN_COMP (operator!=, !=) + +/* Unary operators for the raw flags enum. */ + +/* We require underlying type to be unsigned when using operator~ -- + if it were not unsigned, undefined behavior could result. However, + asserting this in the class itself would require too many + unnecessary changes to usages of otherwise OK enum types. */ +template <typename enum_type, + typename = is_enum_flags_enum_type_t<enum_type>, + typename + = gdb::Requires<enum_flags_detail::EnumIsUnsigned<enum_type>>> +constexpr enum_type +operator~ (enum_type e) { - return enum_flags<enum_type> (e1) & e2; + using underlying = typename enum_flags<enum_type>::underlying_type; + return (enum_type) ~underlying (e); } -template <typename enum_type> -typename enum_flags_type<enum_type>::type -operator| (enum_type e1, enum_type e2) +template <typename enum_type, + typename = is_enum_flags_enum_type_t<enum_type>, + typename = gdb::Requires<enum_flags_detail::EnumIsSigned<enum_type>>> +constexpr void operator~ (enum_type e) = delete; + +template <typename enum_type, + typename = is_enum_flags_enum_type_t<enum_type>, + typename + = gdb::Requires<enum_flags_detail::EnumIsUnsigned<enum_type>>> +constexpr enum_flags<enum_type> +operator~ (enum_flags<enum_type> e) { - return enum_flags<enum_type> (e1) | e2; + using underlying = typename enum_flags<enum_type>::underlying_type; + return (enum_type) ~underlying (e); } -template <typename enum_type> -typename enum_flags_type<enum_type>::type -operator^ (enum_type e1, enum_type e2) -{ - return enum_flags<enum_type> (e1) ^ e2; -} +template <typename enum_type, + typename = is_enum_flags_enum_type_t<enum_type>, + typename = gdb::Requires<enum_flags_detail::EnumIsSigned<enum_type>>> +constexpr void operator~ (enum_flags<enum_type> e) = delete; -template <typename enum_type> -typename enum_flags_type<enum_type>::type -operator~ (enum_type e) -{ - return ~enum_flags<enum_type> (e); -} +/* Delete operator<< and operator>>. */ + +template <typename enum_type, typename any_type, + typename = is_enum_flags_enum_type_t<enum_type>> +void operator<< (const enum_type &, const any_type &) = delete; + +template <typename enum_type, typename any_type, + typename = is_enum_flags_enum_type_t<enum_type>> +void operator<< (const enum_flags<enum_type> &, const any_type &) = delete; + +template <typename enum_type, typename any_type, + typename = is_enum_flags_enum_type_t<enum_type>> +void operator>> (const enum_type &, const any_type &) = delete; + +template <typename enum_type, typename any_type, + typename = is_enum_flags_enum_type_t<enum_type>> +void operator>> (const enum_flags<enum_type> &, const any_type &) = delete; #else /* __cplusplus */ |