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>> : std::true_type {}; 61 62 } // namespace enum_details 63 64 // The operators themselves, conditional on having been declared that they are 65 // flag-style enums. 66 67 // T operator~(T&) 68 template <typename T> 69 constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type 70 operator~(const T& l) { 71 return static_cast<T>( ~static_cast<typename std::underlying_type<T>::type>(l)); 72 } 73 74 // T operator|(T&, T&) 75 template <typename T> 76 constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type 77 operator|(const T& l, const T& r) { 78 return static_cast<T>( 79 static_cast<typename std::underlying_type<T>::type>(l) | 80 static_cast<typename std::underlying_type<T>::type>(r)); 81 } 82 83 // T operator&(T&, T&) 84 template <typename T> 85 constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type 86 operator&(const T& l, const T& r) { 87 return static_cast<T>( 88 static_cast<typename std::underlying_type<T>::type>(l) & 89 static_cast<typename std::underlying_type<T>::type>(r)); 90 } 91 92 // T operator^(T&, T&) 93 template <typename T> 94 constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type operator^( 95 const T& l, const T& r) { 96 return static_cast<T>(static_cast<typename std::underlying_type<T>::type>(l) ^ 97 static_cast<typename std::underlying_type<T>::type>(r)); 98 }; 99 100 // T operator|=(T&, T&) 101 template <typename T> 102 constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type operator|=( 103 T& l, const T& r) { 104 return l = static_cast<T>( 105 static_cast<typename std::underlying_type<T>::type>(l) | 106 static_cast<typename std::underlying_type<T>::type>(r)); 107 }; 108 109 // T operator&=(T&, T&) 110 template <typename T> 111 constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type operator&=( 112 T& l, const T& r) { 113 return l = static_cast<T>( 114 static_cast<typename std::underlying_type<T>::type>(l) & 115 static_cast<typename std::underlying_type<T>::type>(r)); 116 }; 117 118 // T operator^=(T&, T&) 119 template <typename T> 120 constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type operator^=( 121 T& l, const T& r) { 122 return l = static_cast<T>( 123 static_cast<typename std::underlying_type<T>::type>(l) ^ 124 static_cast<typename std::underlying_type<T>::type>(r)); 125 }; 126 127 #endif // LIBBRILLO_BRILLO_ENUM_FLAGS_H_ 128