aboutsummaryrefslogtreecommitdiff
path: root/gdbsupport/enum-flags.h
diff options
context:
space:
mode:
Diffstat (limited to 'gdbsupport/enum-flags.h')
-rw-r--r--gdbsupport/enum-flags.h370
1 files changed, 288 insertions, 82 deletions
diff --git a/gdbsupport/enum-flags.h b/gdbsupport/enum-flags.h
index 825ff4f..d30d90b 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,324 @@ 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
+ /* Delete rval versions. */
+ void operator&= (enum_flags e) && = delete;
+ void operator|= (enum_flags e) && = delete;
+ void operator^= (enum_flags e) && = delete;
+
+ /* 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>> \
+ void \
+ OPERATOR_OP (enum_type &&e1, enum_type e2) = delete; \
+ \
+ /* 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>> \
+ void \
+ 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 */