1 // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef LIBBRILLO_BRILLO_ENUM_FLAGS_H_ 6 #define LIBBRILLO_BRILLO_ENUM_FLAGS_H_ 7 8 #include <type_traits> 9 10 // This is a helper for generating type-safe bitwise operators for flags that 11 // are defined by an enumeration. By default, when a bitwise operation is 12 // performed on two enumerators of an enumeration, the result is the base type 13 // (int), not a value of the enumeration: 14 // 15 // enum SomeEnumOfFlags { 16 // ONE = 1, 17 // TWO = 2, 18 // THREE = 4, 19 // // etc. 20 // }; 21 // 22 // SomeEnumOfFlags flags = static_cast<SomeEnumOfFlags>(ONE | TWO); 23 // 24 // By enabling these operators for an enum type: 25 // 26 // DECLARE_FLAGS_ENUM(SomeEnumOfFlags); 27 // 28 // The syntax is simplified to: 29 // 30 // SomeEnumOfFlags flags = ONE | TWO; 31 // 32 // But the following still does not compile without using a cast (as is 33 // expected): 34 // 35 // SomeEnumOfFlags flags = ONE | 2; 36 37 // This is the macro used to declare that an enum type |ENUM| should have bit- 38 // wise operators defined for it. 39 #define DECLARE_FLAGS_ENUM(ENUM) \ 40 template <typename> struct EnumFlagTraitType; \ 41 template <> struct EnumFlagTraitType<ENUM> { using EnumFlagType = ENUM; }; \ 42 EnumFlagTraitType<ENUM> GetEnumFlagTraitType(ENUM) __attribute__((used)); 43 44 45 // Setup the templates used to declare that the operators should exist for a 46 // given type T. 47 48 namespace enum_details { 49 50 template <typename T> 51 using FlagEnumTraits = decltype(GetEnumFlagTraitType(std::declval<T>())); 52 53 template <typename T> 54 using Void = void; 55 56 template <typename T, typename = void> 57 struct IsFlagEnum : std::false_type {}; 58 59 template <typename T> 60 struct IsFlagEnum<T, Void<typename FlagEnumTraits<T>::EnumFlagType>> 61 : std::true_type {}; 62 63 } // namespace enum_details 64 65 // The operators themselves, conditional on having been declared that they are 66 // flag-style enums. 67 68 // T operator~(T&) 69 template <typename T> 70 constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type 71 operator~(const T& l) { 72 return static_cast<T>( 73 ~static_cast<typename std::underlying_type<T>::type>(l)); 74 } 75 76 // T operator|(T&, T&) 77 template <typename T> 78 constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type 79 operator|(const T& l, const T& r) { 80 return static_cast<T>( 81 static_cast<typename std::underlying_type<T>::type>(l) | 82 static_cast<typename std::underlying_type<T>::type>(r)); 83 } 84 85 // T operator&(T&, T&) 86 template <typename T> 87 constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type 88 operator&(const T& l, const T& r) { 89 return static_cast<T>( 90 static_cast<typename std::underlying_type<T>::type>(l) & 91 static_cast<typename std::underlying_type<T>::type>(r)); 92 } 93 94 // T operator^(T&, T&) 95 template <typename T> 96 constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type 97 operator^(const T& l, const T& r) { 98 return static_cast<T>(static_cast<typename std::underlying_type<T>::type>(l) ^ 99 static_cast<typename std::underlying_type<T>::type>(r)); 100 } 101 102 // T operator|=(T&, T&) 103 template <typename T> 104 constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type 105 operator|=(T& l, const T& r) { 106 return l = static_cast<T>( 107 static_cast<typename std::underlying_type<T>::type>(l) | 108 static_cast<typename std::underlying_type<T>::type>(r)); 109 } 110 111 // T operator&=(T&, T&) 112 template <typename T> 113 constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type 114 operator&=(T& l, const T& r) { 115 return l = static_cast<T>( 116 static_cast<typename std::underlying_type<T>::type>(l) & 117 static_cast<typename std::underlying_type<T>::type>(r)); 118 } 119 120 // T operator^=(T&, T&) 121 template <typename T> 122 constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type 123 operator^=(T& l, const T& r) { 124 return l = static_cast<T>( 125 static_cast<typename std::underlying_type<T>::type>(l) ^ 126 static_cast<typename std::underlying_type<T>::type>(r)); 127 } 128 129 #endif // LIBBRILLO_BRILLO_ENUM_FLAGS_H_ 130