123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427 |
- /* Copyright (C) 2015-2022 Free Software Foundation, Inc.
- This file is part of GDB.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
- #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.
- This allows writing code like the below, while with raw enums this
- would fail to compile without casts to enum type at the assignments
- to 'f':
- enum some_flag
- {
- flag_val1 = 1 << 1,
- flag_val2 = 1 << 2,
- flag_val3 = 1 << 3,
- flag_val4 = 1 << 4,
- };
- DEF_ENUM_FLAGS_TYPE(enum some_flag, some_flags);
- some_flags f = flag_val1 | flag_val2;
- f |= flag_val3;
- It's also possible to assign literal zero to an enum flags variable
- (meaning, no flags), dispensing adding an awkward explicit "no
- value" value to the enumeration. For example:
- some_flags f = 0;
- f |= flag_val3 | flag_val4;
- Note that literal integers other than zero fail to compile:
- some_flags f = 1; // error
- */
- #ifdef __cplusplus
- /* 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; \
- 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. */
- /* 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; };
- template<> struct integer_for_size<4, 0> { typedef uint32_t type; };
- template<> struct integer_for_size<8, 0> { typedef uint64_t type; };
- template<> struct integer_for_size<1, 1> { typedef int8_t type; };
- template<> struct integer_for_size<2, 1> { typedef int16_t type; };
- template<> struct integer_for_size<4, 1> { typedef int32_t type; };
- template<> struct integer_for_size<8, 1> { typedef int64_t type; };
- template<typename T>
- struct enum_underlying_type
- {
- typedef typename
- integer_for_size<sizeof (T), static_cast<bool>(T (-1) < T (0))>::type
- type;
- };
- namespace enum_flags_detail
- {
- /* Private type used to support initializing flag types with zero:
- foo_flags f = 0;
- but not other integers:
- 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;
- /* 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. */
- 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. */
- constexpr enum_flags (enum_type e)
- : m_enum_value (e)
- {}
- constexpr enum_flags (enum_flags_detail::zero_type *zero)
- : m_enum_value ((enum_type) 0)
- {}
- enum_flags &operator&= (enum_flags e) &
- {
- m_enum_value = (enum_type) (m_enum_value & e.m_enum_value);
- return *this;
- }
- enum_flags &operator|= (enum_flags e) &
- {
- m_enum_value = (enum_type) (m_enum_value | e.m_enum_value);
- return *this;
- }
- enum_flags &operator^= (enum_flags e) &
- {
- m_enum_value = (enum_type) (m_enum_value ^ e.m_enum_value);
- return *this;
- }
- /* 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;
- }
- /* Get the underlying value as a raw enum. */
- constexpr enum_type raw () const
- {
- 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. */
- /* 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)
- {
- using underlying = typename enum_flags<enum_type>::underlying_type;
- return (enum_type) ~underlying (e);
- }
- 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)
- {
- using underlying = typename enum_flags<enum_type>::underlying_type;
- return (enum_type) ~underlying (e);
- }
- 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;
- /* 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 */
- /* In C, the flags type is just a typedef for the enum type. */
- #define DEF_ENUM_FLAGS_TYPE(enum_type, flags_type) \
- typedef enum_type flags_type
- #endif /* __cplusplus */
- #endif /* COMMON_ENUM_FLAGS_H */
|