427 lines
14 KiB
C++
427 lines
14 KiB
C++
/* 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 */
|