enum_ops.hpp (12841B)
1 /* 2 Copyright 2013 Adobe 3 Distributed under the Boost Software License, Version 1.0. 4 (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 */ 6 /**************************************************************************************************/ 7 8 #ifndef STLAB_ENUM_OPS_HPP 9 #define STLAB_ENUM_OPS_HPP 10 11 /**************************************************************************************************/ 12 13 #include <type_traits> 14 15 /**************************************************************************************************/ 16 17 /*! 18 \defgroup enum_ops Typesafe Integers and Bit Fields (enums) 19 \ingroup utility 20 21 \section Description Description 22 23 \c enum_ops provides optional typesafe bitset and arithmetic operations for enumeration types. 24 Without these typesafe operations, the compiler will promote the operand(s) to the appropriate 25 integral type, and the result will be an integral type. When the typesafe operations have been 26 defined for an enumeration type, \c E, the result will be of type \c E exactly when the 27 operand(s) are of type \c E. 28 29 \c auto stlab_enable_bitmask_enum(E) -> std::true_type; 30 enables the bitset operations <code>~, |, &, ^, |=, &=, ^= </code> 31 for enumeration type \c E. 32 33 \c auto stlab_enable_arithmetic_enum(E) -> std::true_type; 34 enables the typesafe arithmetic operations <code>+, -, *, /, 35 %, +=, *=, -=, /=, \%=</code> for enumeration type \c E. 36 37 \section Definition Definition 38 39 Defined in \link enum_ops.hpp <code>stlab/enum_ops.hpp</code> \endlink 40 41 \section Example Example 42 43 The following is an example of code that will compile: 44 \dontinclude enum_ops_example.cpp 45 \skip start_of_example 46 \until end_of_example 47 48 The following is contains an example of code that will not compile 49 since the typesafe operators have not been defined. 50 51 \dontinclude enum_ops_example_fail.cpp 52 \skip start_of_example 53 \until end_of_example 54 */ 55 56 /**************************************************************************************************/ 57 58 namespace stlab { 59 60 /**************************************************************************************************/ 61 62 auto stlab_enable_bitmask_enum(...) -> std::false_type; 63 auto stlab_enable_arithmetic_enum(...) -> std::false_type; 64 65 /**************************************************************************************************/ 66 67 namespace implementation { 68 69 /**************************************************************************************************/ 70 71 template <class T> 72 using has_enabled_bitmask_t = decltype(stlab_enable_bitmask_enum(std::declval<T>())); 73 74 template <class T> 75 constexpr bool has_enabled_bitmask = has_enabled_bitmask_t<T>::value; 76 77 template <class T> 78 using has_enabled_arithmetic_t = decltype(stlab_enable_arithmetic_enum(std::declval<T>())); 79 80 template <class T> 81 constexpr bool has_enabled_arithmetic = has_enabled_arithmetic_t<T>::value; 82 83 template <class T, class U> 84 using enable_if_bitmask_or_arithmetic = 85 std::enable_if_t<std::disjunction_v<stlab::implementation::has_enabled_bitmask_t<T>, 86 stlab::implementation::has_enabled_arithmetic_t<T>>, 87 U>; 88 89 template <class, bool> 90 struct safe_underlying_type; 91 92 template <class T> 93 struct safe_underlying_type<T, true> { 94 using type = std::underlying_type_t<T>; 95 }; 96 97 template <class T> 98 struct safe_underlying_type<T, false> { 99 using type = void; 100 }; 101 102 template <class T> 103 using safe_underlying_type_t = typename safe_underlying_type<T, std::is_enum<T>::value>::type; 104 105 template <class U, class T> 106 using is_convertible_to_underlying = 107 std::is_convertible<U, stlab::implementation::safe_underlying_type_t<T>>; 108 109 /**************************************************************************************************/ 110 111 } // namespace implementation 112 113 /**************************************************************************************************/ 114 115 } // namespace stlab 116 117 /**************************************************************************************************/ 118 119 template <class T> 120 constexpr auto operator&(T lhs, T rhs) 121 -> std::enable_if_t<stlab::implementation::has_enabled_bitmask<T>, T> { 122 using underlying = std::underlying_type_t<T>; 123 return static_cast<T>(static_cast<underlying>(lhs) & static_cast<underlying>(rhs)); 124 } 125 126 template <class T> 127 constexpr auto operator~(T a) 128 -> std::enable_if_t<stlab::implementation::has_enabled_bitmask<T>, T> { 129 using underlying = std::underlying_type_t<T>; 130 return static_cast<T>(~static_cast<underlying>(a)); 131 } 132 133 template <class T> 134 constexpr auto operator|(T lhs, T rhs) 135 -> std::enable_if_t<stlab::implementation::has_enabled_bitmask<T>, T> { 136 using underlying = std::underlying_type_t<T>; 137 return static_cast<T>(static_cast<underlying>(lhs) | static_cast<underlying>(rhs)); 138 } 139 140 template <class T> 141 constexpr auto operator^(T lhs, T rhs) 142 -> std::enable_if_t<stlab::implementation::has_enabled_bitmask<T>, T> { 143 using underlying = std::underlying_type_t<T>; 144 return static_cast<T>(static_cast<underlying>(lhs) ^ static_cast<underlying>(rhs)); 145 } 146 147 template <class T> 148 constexpr auto operator<<(T lhs, std::size_t rhs) 149 -> std::enable_if_t<stlab::implementation::has_enabled_bitmask<T>, T> { 150 using underlying = std::make_unsigned<std::underlying_type_t<T>>; 151 152 return lhs = static_cast<underlying>(lhs) << static_cast<underlying>(rhs); 153 } 154 155 template <class T> 156 constexpr auto operator>>(T lhs, std::size_t rhs) 157 -> std::enable_if_t<stlab::implementation::has_enabled_bitmask<T>, T> { 158 using underlying = std::make_unsigned<std::underlying_type_t<T>>; 159 160 return lhs = static_cast<underlying>(lhs) >> static_cast<underlying>(rhs); 161 } 162 163 template <class T> 164 constexpr auto operator^=(T& lhs, T rhs) 165 -> std::enable_if_t<stlab::implementation::has_enabled_bitmask<T>, T&> { 166 return lhs = lhs ^ rhs; 167 } 168 169 template <class T> 170 constexpr auto operator&=(T& lhs, T rhs) 171 -> std::enable_if_t<stlab::implementation::has_enabled_bitmask<T>, T&> { 172 return lhs = lhs & rhs; 173 } 174 175 template <class T> 176 constexpr auto operator|=(T& lhs, T rhs) 177 -> std::enable_if_t<stlab::implementation::has_enabled_bitmask<T>, T&> { 178 return lhs = lhs | rhs; 179 } 180 181 template <class T> 182 constexpr auto operator<<=(T& lhs, std::size_t rhs) 183 -> std::enable_if_t<stlab::implementation::has_enabled_bitmask<T>, T&> { 184 return lhs = lhs << rhs; 185 } 186 187 template <class T> 188 constexpr auto operator>>=(T& lhs, std::size_t rhs) 189 -> std::enable_if_t<stlab::implementation::has_enabled_bitmask<T>, T&> { 190 return lhs = lhs >> rhs; 191 } 192 193 template <class T, class U> 194 constexpr auto operator-(T lhs, U rhs) 195 -> std::enable_if_t<stlab::implementation::has_enabled_bitmask<T> && 196 stlab::implementation::is_convertible_to_underlying<U, T>::value, 197 T> { 198 using underlying = std::underlying_type_t<T>; 199 return static_cast<T>(static_cast<underlying>(lhs) - static_cast<underlying>(rhs)); 200 } 201 202 /**************************************************************************************************/ 203 204 template <class T> 205 constexpr auto operator+(T a) 206 -> std::enable_if_t<stlab::implementation::has_enabled_arithmetic<T>, T> { 207 using underlying = std::underlying_type_t<T>; 208 return static_cast<T>(+static_cast<underlying>(a)); 209 } 210 211 template <class T> 212 constexpr auto operator-(T a) 213 -> std::enable_if_t<stlab::implementation::has_enabled_arithmetic<T>, T> { 214 using underlying = std::underlying_type_t<T>; 215 return static_cast<T>(-static_cast<underlying>(a)); 216 } 217 218 template <class T> 219 constexpr auto operator+(T lhs, T rhs) 220 -> std::enable_if_t<stlab::implementation::has_enabled_arithmetic<T>, T> { 221 using underlying = std::underlying_type_t<T>; 222 return static_cast<T>(static_cast<underlying>(lhs) + static_cast<underlying>(rhs)); 223 } 224 225 template <class T> 226 constexpr auto operator-(T lhs, T rhs) 227 -> std::enable_if_t<stlab::implementation::has_enabled_arithmetic<T>, T> { 228 using underlying = std::underlying_type_t<T>; 229 return static_cast<T>(static_cast<underlying>(lhs) - static_cast<underlying>(rhs)); 230 } 231 232 template <class T, class U> 233 constexpr auto operator*(T lhs, U rhs) 234 -> std::enable_if_t<stlab::implementation::has_enabled_arithmetic<T> && 235 stlab::implementation::is_convertible_to_underlying<U, T>::value, 236 T> { 237 using underlying = std::underlying_type_t<T>; 238 return static_cast<T>(static_cast<underlying>(lhs) * rhs); 239 } 240 241 template <class U, class T> 242 constexpr auto operator*(U lhs, T rhs) 243 -> std::enable_if_t<stlab::implementation::has_enabled_arithmetic<T> && 244 stlab::implementation::is_convertible_to_underlying<U, T>::value, 245 T> { 246 using underlying = std::underlying_type_t<T>; 247 return static_cast<T>(lhs * static_cast<underlying>(rhs)); 248 } 249 250 template <class T, class U> 251 constexpr auto operator/(T lhs, U rhs) 252 -> std::enable_if_t<stlab::implementation::has_enabled_arithmetic<T> && 253 stlab::implementation::is_convertible_to_underlying<U, T>::value, 254 T> { 255 using underlying = std::underlying_type_t<T>; 256 return static_cast<T>(static_cast<underlying>(lhs) / rhs); 257 } 258 259 template <class T, class U> 260 constexpr auto operator%(T lhs, U rhs) 261 -> std::enable_if_t<stlab::implementation::has_enabled_arithmetic<T> && 262 stlab::implementation::is_convertible_to_underlying<U, T>::value, 263 T> { 264 using underlying = std::underlying_type_t<T>; 265 return static_cast<T>(static_cast<underlying>(lhs) % rhs); 266 } 267 268 template <class T> 269 constexpr auto operator+=(T& lhs, T rhs) 270 -> std::enable_if_t<stlab::implementation::has_enabled_arithmetic<T>, T&> { 271 return lhs = lhs + rhs; 272 } 273 274 template <class T> 275 constexpr auto operator-=(T& lhs, T rhs) 276 -> std::enable_if_t<stlab::implementation::has_enabled_arithmetic<T>, T&> { 277 return lhs = lhs - rhs; 278 } 279 280 template <class T, class U> 281 constexpr auto operator*=(T& lhs, U rhs) 282 -> std::enable_if_t<stlab::implementation::has_enabled_arithmetic<T> && 283 stlab::implementation::is_convertible_to_underlying<U, T>::value, 284 T&> { 285 return lhs = lhs * rhs; 286 } 287 288 template <class T, class U> 289 constexpr auto operator/=(T& lhs, U rhs) 290 -> std::enable_if_t<stlab::implementation::has_enabled_arithmetic<T> && 291 stlab::implementation::is_convertible_to_underlying<U, T>::value, 292 T&> { 293 return lhs = lhs / rhs; 294 } 295 296 template <class T, class U> 297 constexpr auto operator%=(T& lhs, U rhs) 298 -> std::enable_if_t<stlab::implementation::has_enabled_arithmetic<T> && 299 stlab::implementation::is_convertible_to_underlying<U, T>::value, 300 T&> { 301 return lhs = lhs % rhs; 302 } 303 304 template <class T> 305 constexpr auto operator++(T& lhs) 306 -> std::enable_if_t<stlab::implementation::has_enabled_arithmetic<T>, T&> { 307 return lhs += static_cast<T>(1); 308 } 309 310 template <class T> 311 constexpr auto operator++(T& lhs, int) 312 -> std::enable_if_t<stlab::implementation::has_enabled_arithmetic<T>, T> { 313 T result = lhs; 314 lhs += static_cast<T>(1); 315 return result; 316 } 317 318 template <class T> 319 constexpr auto operator--(T& lhs) 320 -> std::enable_if_t<stlab::implementation::has_enabled_arithmetic<T>, T&> { 321 return lhs -= static_cast<T>(1); 322 } 323 324 template <class T> 325 constexpr auto operator--(T& lhs, int) 326 -> std::enable_if_t<stlab::implementation::has_enabled_arithmetic<T>, T> { 327 T result = lhs; 328 lhs -= static_cast<T>(1); 329 return result; 330 } 331 332 /**************************************************************************************************/ 333 334 template <class T> 335 constexpr auto operator==(T lhs, std::nullptr_t) 336 -> stlab::implementation::enable_if_bitmask_or_arithmetic<T, bool> { 337 return !lhs; 338 } 339 340 template <class T> 341 constexpr auto operator==(std::nullptr_t, T rhs) 342 -> stlab::implementation::enable_if_bitmask_or_arithmetic<T, bool> { 343 return !rhs; 344 } 345 346 template <class T> 347 constexpr auto operator!=(T lhs, std::nullptr_t rhs) 348 -> stlab::implementation::enable_if_bitmask_or_arithmetic<T, bool> { 349 return !(lhs == rhs); 350 } 351 352 template <class T> 353 constexpr auto operator!=(std::nullptr_t lhs, T rhs) 354 -> stlab::implementation::enable_if_bitmask_or_arithmetic<T, bool> { 355 return !(lhs == rhs); 356 } 357 358 template <class T> 359 constexpr auto operator!(T lhs) -> stlab::implementation::enable_if_bitmask_or_arithmetic<T, bool> { 360 return !static_cast<bool>(lhs); 361 } 362 363 /**************************************************************************************************/ 364 365 #endif 366 367 /**************************************************************************************************/