1 /* Copyright (C) 2015-2024 Free Software Foundation, Inc. 2 3 This file is part of GDB. 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 17 18 #ifndef GDBSUPPORT_ENUM_FLAGS_H 19 #define GDBSUPPORT_ENUM_FLAGS_H 20 21 #include "traits.h" 22 23 /* Type-safe wrapper for enum flags. enum flags are enums where the 24 values are bits that are meant to be ORed together. 25 26 This allows writing code like the below, while with raw enums this 27 would fail to compile without casts to enum type at the assignments 28 to 'f': 29 30 enum some_flag 31 { 32 flag_val1 = 1 << 1, 33 flag_val2 = 1 << 2, 34 flag_val3 = 1 << 3, 35 flag_val4 = 1 << 4, 36 }; 37 DEF_ENUM_FLAGS_TYPE(enum some_flag, some_flags); 38 39 some_flags f = flag_val1 | flag_val2; 40 f |= flag_val3; 41 42 It's also possible to assign literal zero to an enum flags variable 43 (meaning, no flags), dispensing adding an awkward explicit "no 44 value" value to the enumeration. For example: 45 46 some_flags f = 0; 47 f |= flag_val3 | flag_val4; 48 49 Note that literal integers other than zero fail to compile: 50 51 some_flags f = 1; // error 52 */ 53 54 /* Use this to mark an enum as flags enum. It defines FLAGS_TYPE as 55 enum_flags wrapper class for ENUM, and enables the global operator 56 overloads for ENUM. */ 57 #define DEF_ENUM_FLAGS_TYPE(enum_type, flags_type) \ 58 using flags_type = enum_flags<enum_type>; \ 59 void is_enum_flags_enum_type (enum_type *) 60 61 /* To enable the global enum_flags operators for enum, declare an 62 "is_enum_flags_enum_type" overload that has exactly one parameter, 63 of type a pointer to that enum class. E.g.,: 64 65 void is_enum_flags_enum_type (enum some_flag *); 66 67 The function does not need to be defined, only declared. 68 DEF_ENUM_FLAGS_TYPE declares this. 69 70 A function declaration is preferred over a traits type, because the 71 former allows calling the DEF_ENUM_FLAGS_TYPE macro inside a 72 namespace to define the corresponding enum flags type in that 73 namespace. The compiler finds the corresponding 74 is_enum_flags_enum_type function via ADL. */ 75 76 namespace enum_flags_detail 77 { 78 79 /* Private type used to support initializing flag types with zero: 80 81 foo_flags f = 0; 82 83 but not other integers: 84 85 foo_flags f = 1; 86 87 The way this works is that we define an implicit constructor that 88 takes a pointer to this private type. Since nothing can 89 instantiate an object of this type, the only possible pointer to 90 pass to the constructor is the NULL pointer, or, zero. */ 91 struct zero_type; 92 93 /* gdb::Requires trait helpers. */ 94 template <typename enum_type> 95 using EnumIsUnsigned 96 = std::is_unsigned<typename std::underlying_type<enum_type>::type>; 97 98 /* Helper to detect whether an enum has a fixed underlying type. This can be 99 achieved by using a scoped enum (in which case the type is "int") or 100 an explicit underlying type. C-style enums that are unscoped or do not 101 have an explicit underlying type have an implementation-defined underlying 102 type. 103 104 https://timsong-cpp.github.io/cppwp/n4659/dcl.enum#5 105 106 We need this trait in order to ensure that operator~ below does NOT 107 operate on old-style enums. This is because we apply operator~ on 108 the value and then cast the result to the enum_type. This is however 109 Undefined Behavior if the result does not fit in the range of possible 110 values for the enum. For enums with fixed underlying type, the entire 111 range of the integer is available. However, for old-style enums, the range 112 is only the smallest bit-field that can hold all the values of the 113 enumeration, typically much smaller than the underlying integer: 114 115 https://timsong-cpp.github.io/cppwp/n4659/expr.static.cast#10 116 https://timsong-cpp.github.io/cppwp/n4659/dcl.enum#8 117 118 To implement this, we leverage the fact that, since C++17, enums with 119 fixed underlying type can be list-initialized from an integer: 120 https://timsong-cpp.github.io/cppwp/n4659/dcl.init.list#3.7 121 122 Old-style enums cannot be initialized like that, leading to ill-formed 123 code. 124 125 We then use this together with SFINAE to create the desired trait. 126 127 */ 128 template <typename enum_type, typename = void> 129 struct EnumHasFixedUnderlyingType : std::false_type 130 { 131 static_assert(std::is_enum<enum_type>::value); 132 }; 133 134 /* Specialization that is active only if enum_type can be 135 list-initialized from an integer (0). Only enums with fixed 136 underlying type satisfy this property in C++17. */ 137 template <typename enum_type> 138 struct EnumHasFixedUnderlyingType<enum_type, std::void_t<decltype(enum_type{0})>> : std::true_type 139 { 140 static_assert(std::is_enum<enum_type>::value); 141 }; 142 143 template <typename enum_type> 144 using EnumIsSafeForBitwiseComplement = std::conjunction< 145 EnumIsUnsigned<enum_type>, 146 EnumHasFixedUnderlyingType<enum_type> 147 >; 148 149 template <typename enum_type> 150 using EnumIsUnsafeForBitwiseComplement = std::negation<EnumIsSafeForBitwiseComplement<enum_type>>; 151 152 } 153 154 template <typename E> 155 class enum_flags 156 { 157 public: 158 using enum_type = E; 159 using underlying_type = typename std::underlying_type<enum_type>::type; 160 161 /* For to_string. Maps one enumerator of E to a string. */ 162 struct string_mapping 163 { 164 E flag; 165 const char *str; 166 }; 167 168 /* Convenience for to_string implementations, to build a 169 string_mapping array. */ 170 #define MAP_ENUM_FLAG(ENUM_FLAG) { ENUM_FLAG, #ENUM_FLAG } 171 172 public: 173 /* Allow default construction. */ 174 constexpr enum_flags () 175 : m_enum_value ((enum_type) 0) 176 {} 177 178 /* The default move/copy ctor/assignment do the right thing. */ 179 180 /* If you get an error saying these two overloads are ambiguous, 181 then you tried to mix values of different enum types. */ 182 constexpr enum_flags (enum_type e) 183 : m_enum_value (e) 184 {} 185 constexpr enum_flags (enum_flags_detail::zero_type *zero) 186 : m_enum_value ((enum_type) 0) 187 {} 188 189 enum_flags &operator&= (enum_flags e) & 190 { 191 m_enum_value = (enum_type) (m_enum_value & e.m_enum_value); 192 return *this; 193 } 194 enum_flags &operator|= (enum_flags e) & 195 { 196 m_enum_value = (enum_type) (m_enum_value | e.m_enum_value); 197 return *this; 198 } 199 enum_flags &operator^= (enum_flags e) & 200 { 201 m_enum_value = (enum_type) (m_enum_value ^ e.m_enum_value); 202 return *this; 203 } 204 205 /* Delete rval versions. */ 206 void operator&= (enum_flags e) && = delete; 207 void operator|= (enum_flags e) && = delete; 208 void operator^= (enum_flags e) && = delete; 209 210 /* Like raw enums, allow conversion to the underlying type. */ 211 constexpr operator underlying_type () const 212 { 213 return m_enum_value; 214 } 215 216 /* Get the underlying value as a raw enum. */ 217 constexpr enum_type raw () const 218 { 219 return m_enum_value; 220 } 221 222 /* Binary operations involving some unrelated type (which would be a 223 bug) are implemented as non-members, and deleted. */ 224 225 /* Convert this object to a std::string, using MAPPING as 226 enumerator-to-string mapping array. This is not meant to be 227 called directly. Instead, enum_flags specializations should have 228 their own to_string function wrapping this one, thus hiding the 229 mapping array from callers. 230 231 Note: this is defined outside the template class so it can use 232 the global operators for enum_type, which are only defined after 233 the template class. */ 234 template<size_t N> 235 std::string to_string (const string_mapping (&mapping)[N]) const; 236 237 private: 238 /* Stored as enum_type because GDB knows to print the bit flags 239 neatly if the enum values look like bit flags. */ 240 enum_type m_enum_value; 241 }; 242 243 template <typename E> 244 using is_enum_flags_enum_type_t 245 = decltype (is_enum_flags_enum_type (std::declval<E *> ())); 246 247 /* Global operator overloads. */ 248 249 /* Generate binary operators. */ 250 251 #define ENUM_FLAGS_GEN_BINOP(OPERATOR_OP, OP) \ 252 \ 253 /* Raw enum on both LHS/RHS. Returns raw enum type. */ \ 254 template <typename enum_type, \ 255 typename = is_enum_flags_enum_type_t<enum_type>> \ 256 constexpr enum_type \ 257 OPERATOR_OP (enum_type e1, enum_type e2) \ 258 { \ 259 using underlying = typename enum_flags<enum_type>::underlying_type; \ 260 return (enum_type) (underlying (e1) OP underlying (e2)); \ 261 } \ 262 \ 263 /* enum_flags on the LHS. */ \ 264 template <typename enum_type, \ 265 typename = is_enum_flags_enum_type_t<enum_type>> \ 266 constexpr enum_flags<enum_type> \ 267 OPERATOR_OP (enum_flags<enum_type> e1, enum_type e2) \ 268 { return e1.raw () OP e2; } \ 269 \ 270 /* enum_flags on the RHS. */ \ 271 template <typename enum_type, \ 272 typename = is_enum_flags_enum_type_t<enum_type>> \ 273 constexpr enum_flags<enum_type> \ 274 OPERATOR_OP (enum_type e1, enum_flags<enum_type> e2) \ 275 { return e1 OP e2.raw (); } \ 276 \ 277 /* enum_flags on both LHS/RHS. */ \ 278 template <typename enum_type, \ 279 typename = is_enum_flags_enum_type_t<enum_type>> \ 280 constexpr enum_flags<enum_type> \ 281 OPERATOR_OP (enum_flags<enum_type> e1, enum_flags<enum_type> e2) \ 282 { return e1.raw () OP e2.raw (); } \ 283 \ 284 /* Delete cases involving unrelated types. */ \ 285 \ 286 template <typename enum_type, typename unrelated_type, \ 287 typename = is_enum_flags_enum_type_t<enum_type>> \ 288 constexpr enum_flags<enum_type> \ 289 OPERATOR_OP (enum_type e1, unrelated_type e2) = delete; \ 290 \ 291 template <typename enum_type, typename unrelated_type, \ 292 typename = is_enum_flags_enum_type_t<enum_type>> \ 293 constexpr enum_flags<enum_type> \ 294 OPERATOR_OP (unrelated_type e1, enum_type e2) = delete; \ 295 \ 296 template <typename enum_type, typename unrelated_type, \ 297 typename = is_enum_flags_enum_type_t<enum_type>> \ 298 constexpr enum_flags<enum_type> \ 299 OPERATOR_OP (enum_flags<enum_type> e1, unrelated_type e2) = delete; \ 300 \ 301 template <typename enum_type, typename unrelated_type, \ 302 typename = is_enum_flags_enum_type_t<enum_type>> \ 303 constexpr enum_flags<enum_type> \ 304 OPERATOR_OP (unrelated_type e1, enum_flags<enum_type> e2) = delete; 305 306 /* Generate non-member compound assignment operators. Only the raw 307 enum versions are defined here. The enum_flags versions are 308 defined as member functions, simply because it's less code that 309 way. 310 311 Note we delete operators that would allow e.g., 312 313 "enum_type | 1" or "enum_type1 | enum_type2" 314 315 because that would allow a mistake like : 316 enum flags1 { F1_FLAGS1 = 1 }; 317 enum flags2 { F2_FLAGS2 = 2 }; 318 enum flags1 val; 319 switch (val) { 320 case F1_FLAGS1 | F2_FLAGS2: 321 ... 322 323 If you really need to 'or' enumerators of different flag types, 324 cast to integer first. 325 */ 326 #define ENUM_FLAGS_GEN_COMPOUND_ASSIGN(OPERATOR_OP, OP) \ 327 /* lval reference version. */ \ 328 template <typename enum_type, \ 329 typename = is_enum_flags_enum_type_t<enum_type>> \ 330 constexpr enum_type & \ 331 OPERATOR_OP (enum_type &e1, enum_type e2) \ 332 { return e1 = e1 OP e2; } \ 333 \ 334 /* rval reference version. */ \ 335 template <typename enum_type, \ 336 typename = is_enum_flags_enum_type_t<enum_type>> \ 337 void \ 338 OPERATOR_OP (enum_type &&e1, enum_type e2) = delete; \ 339 \ 340 /* Delete compound assignment from unrelated types. */ \ 341 \ 342 template <typename enum_type, typename other_enum_type, \ 343 typename = is_enum_flags_enum_type_t<enum_type>> \ 344 constexpr enum_type & \ 345 OPERATOR_OP (enum_type &e1, other_enum_type e2) = delete; \ 346 \ 347 template <typename enum_type, typename other_enum_type, \ 348 typename = is_enum_flags_enum_type_t<enum_type>> \ 349 void \ 350 OPERATOR_OP (enum_type &&e1, other_enum_type e2) = delete; 351 352 ENUM_FLAGS_GEN_BINOP (operator|, |) 353 ENUM_FLAGS_GEN_BINOP (operator&, &) 354 ENUM_FLAGS_GEN_BINOP (operator^, ^) 355 356 ENUM_FLAGS_GEN_COMPOUND_ASSIGN (operator|=, |) 357 ENUM_FLAGS_GEN_COMPOUND_ASSIGN (operator&=, &) 358 ENUM_FLAGS_GEN_COMPOUND_ASSIGN (operator^=, ^) 359 360 /* Allow comparison with enum_flags, raw enum, and integers, only. 361 The latter case allows "== 0". As side effect, it allows comparing 362 with integer variables too, but that's not a common mistake to 363 make. It's important to disable comparison with unrelated types to 364 prevent accidentally comparing with unrelated enum values, which 365 are convertible to integer, and thus coupled with enum_flags 366 conversion to underlying type too, would trigger the built-in 'bool 367 operator==(unsigned, int)' operator. */ 368 369 #define ENUM_FLAGS_GEN_COMP(OPERATOR_OP, OP) \ 370 \ 371 /* enum_flags OP enum_flags */ \ 372 \ 373 template <typename enum_type> \ 374 constexpr bool \ 375 OPERATOR_OP (enum_flags<enum_type> lhs, enum_flags<enum_type> rhs) \ 376 { return lhs.raw () OP rhs.raw (); } \ 377 \ 378 /* enum_flags OP other */ \ 379 \ 380 template <typename enum_type> \ 381 constexpr bool \ 382 OPERATOR_OP (enum_flags<enum_type> lhs, enum_type rhs) \ 383 { return lhs.raw () OP rhs; } \ 384 \ 385 template <typename enum_type> \ 386 constexpr bool \ 387 OPERATOR_OP (enum_flags<enum_type> lhs, int rhs) \ 388 { return lhs.raw () OP rhs; } \ 389 \ 390 template <typename enum_type, typename U> \ 391 constexpr bool \ 392 OPERATOR_OP (enum_flags<enum_type> lhs, U rhs) = delete; \ 393 \ 394 /* other OP enum_flags */ \ 395 \ 396 template <typename enum_type> \ 397 constexpr bool \ 398 OPERATOR_OP (enum_type lhs, enum_flags<enum_type> rhs) \ 399 { return lhs OP rhs.raw (); } \ 400 \ 401 template <typename enum_type> \ 402 constexpr bool \ 403 OPERATOR_OP (int lhs, enum_flags<enum_type> rhs) \ 404 { return lhs OP rhs.raw (); } \ 405 \ 406 template <typename enum_type, typename U> \ 407 constexpr bool \ 408 OPERATOR_OP (U lhs, enum_flags<enum_type> rhs) = delete; 409 410 ENUM_FLAGS_GEN_COMP (operator==, ==) 411 ENUM_FLAGS_GEN_COMP (operator!=, !=) 412 413 /* Unary operators for the raw flags enum. */ 414 415 /* We require underlying type to be unsigned when using operator~ -- 416 if it were not unsigned, undefined behavior could result. However, 417 asserting this in the class itself would require too many 418 unnecessary changes to usages of otherwise OK enum types. */ 419 template <typename enum_type, 420 typename = is_enum_flags_enum_type_t<enum_type>, 421 typename 422 = gdb::Requires<enum_flags_detail::EnumIsSafeForBitwiseComplement<enum_type>>> 423 constexpr enum_type 424 operator~ (enum_type e) 425 { 426 using underlying = typename enum_flags<enum_type>::underlying_type; 427 /* Cast to ULONGEST first, to prevent integer promotions from enums 428 with fixed underlying type std::uint8_t or std::uint16_t to 429 signed int. This ensures we apply the bitwise complement on an 430 unsigned type. */ 431 return (enum_type)(underlying) ~ULONGEST (e); 432 } 433 434 template <typename enum_type, 435 typename = is_enum_flags_enum_type_t<enum_type>, 436 typename = gdb::Requires<enum_flags_detail::EnumIsUnsafeForBitwiseComplement<enum_type>>> 437 constexpr void operator~ (enum_type e) = delete; 438 439 template <typename enum_type, 440 typename = is_enum_flags_enum_type_t<enum_type>, 441 typename 442 = gdb::Requires<enum_flags_detail::EnumIsSafeForBitwiseComplement<enum_type>>> 443 constexpr enum_flags<enum_type> 444 operator~ (enum_flags<enum_type> e) 445 { 446 using underlying = typename enum_flags<enum_type>::underlying_type; 447 /* Cast to ULONGEST first, to prevent integer promotions from enums 448 with fixed underlying type std::uint8_t or std::uint16_t to 449 signed int. This ensures we apply the bitwise complement on an 450 unsigned type. */ 451 return (enum_type)(underlying) ~ULONGEST (e); 452 } 453 454 template <typename enum_type, 455 typename = is_enum_flags_enum_type_t<enum_type>, 456 typename = gdb::Requires<enum_flags_detail::EnumIsUnsafeForBitwiseComplement<enum_type>>> 457 constexpr void operator~ (enum_flags<enum_type> e) = delete; 458 459 /* Delete operator<< and operator>>. */ 460 461 template <typename enum_type, typename any_type, 462 typename = is_enum_flags_enum_type_t<enum_type>> 463 void operator<< (const enum_type &, const any_type &) = delete; 464 465 template <typename enum_type, typename any_type, 466 typename = is_enum_flags_enum_type_t<enum_type>> 467 void operator<< (const enum_flags<enum_type> &, const any_type &) = delete; 468 469 template <typename enum_type, typename any_type, 470 typename = is_enum_flags_enum_type_t<enum_type>> 471 void operator>> (const enum_type &, const any_type &) = delete; 472 473 template <typename enum_type, typename any_type, 474 typename = is_enum_flags_enum_type_t<enum_type>> 475 void operator>> (const enum_flags<enum_type> &, const any_type &) = delete; 476 477 template<typename E> 478 template<size_t N> 479 std::string 480 enum_flags<E>::to_string (const string_mapping (&mapping)[N]) const 481 { 482 enum_type flags = raw (); 483 std::string res = hex_string (flags); 484 res += " ["; 485 486 bool need_space = false; 487 for (const auto &entry : mapping) 488 { 489 if ((flags & entry.flag) != 0) 490 { 491 /* Work with an unsigned version of the underlying type, 492 because if enum_type's underlying type is signed, op~ 493 won't be defined for it, and, bitwise operations on 494 signed types are implementation defined. */ 495 using uns = typename std::make_unsigned<underlying_type>::type; 496 flags &= (enum_type) ~(uns) entry.flag; 497 498 if (need_space) 499 res += " "; 500 res += entry.str; 501 502 need_space = true; 503 } 504 } 505 506 /* If there were flags not included in the mapping, print them as 507 a hex number. */ 508 if (flags != 0) 509 { 510 if (need_space) 511 res += " "; 512 res += hex_string (flags); 513 } 514 515 res += "]"; 516 517 return res; 518 } 519 520 #endif /* GDBSUPPORT_ENUM_FLAGS_H */ 521