stlab-enum-ops 1.1.0
Type-safe operators for enums
Loading...
Searching...
No Matches
enum_ops.hpp
Go to the documentation of this file.
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 <cassert>
14#include <type_traits>
15
16/**************************************************************************************************/
17
22
30
37
47
57
65
118
119/**************************************************************************************************/
120
122namespace stlab {
123
124/**************************************************************************************************/
125
128
131auto stlab_enable_bitmask_enum(...) -> std::false_type;
132
135auto stlab_enable_arithmetic_enum(...) -> std::false_type;
136
137// Don't use the `\ deprecated` Doxygen tag here because clang will warn that the
138// documentation marks the operations deprecated but the deprecated attribute is missing.
139
141auto adobe_enable_bitmask_enum(...) -> std::false_type;
142
144auto adobe_enable_arithmetic_enum(...) -> std::false_type;
145
147
148/**************************************************************************************************/
149
151namespace implementation {
152
153/**************************************************************************************************/
154
155template <class T>
156using has_enabled_bitmask_t = decltype(stlab_enable_bitmask_enum(std::declval<T>()));
157
158template <class T>
159using has_deprecated_bitmask_t = decltype(adobe_enable_bitmask_enum(std::declval<T>()));
160
161template <class T>
162constexpr bool has_enabled_bitmask =
163 has_enabled_bitmask_t<T>::value || has_deprecated_bitmask_t<T>::value;
164
165template <class T>
166using has_enabled_arithmetic_t = decltype(stlab_enable_arithmetic_enum(std::declval<T>()));
167
168template <class T>
169using has_deprecated_arithmetic_t = decltype(adobe_enable_arithmetic_enum(std::declval<T>()));
170
171template <class, bool>
172struct safe_underlying_type;
173
174template <class T>
175struct safe_underlying_type<T, true> {
176 using type = std::underlying_type_t<T>;
177};
178
179template <class T>
180struct safe_underlying_type<T, false> {
181 using type = void;
182};
183
184template <class T>
185using safe_underlying_type_t = typename safe_underlying_type<T, std::is_enum<T>::value>::type;
186
187template <class U, class T>
188using is_convertible_to_underlying =
189 std::is_convertible<U, stlab::implementation::safe_underlying_type_t<T>>;
190
191/**************************************************************************************************/
192
193} // namespace implementation
194
195/**************************************************************************************************/
196
199
201template <class T>
202constexpr bool has_enabled_bitmask = implementation::has_enabled_bitmask_t<T>::value ||
203 implementation::has_deprecated_bitmask_t<T>::value;
204
206template <class T>
207constexpr bool has_enabled_arithmetic = implementation::has_enabled_arithmetic_t<T>::value ||
208 implementation::has_deprecated_arithmetic_t<T>::value;
209
211template <class U, class T>
212constexpr bool is_compatible_scalar = implementation::is_convertible_to_underlying<U, T>::value;
213
215
216/**************************************************************************************************/
217
218} // namespace stlab
219
220/**************************************************************************************************/
221
224
225template <class T>
227constexpr auto operator&(T lhs, T rhs) -> std::enable_if_t<stlab::has_enabled_bitmask<T>, T> {
228 using underlying = std::underlying_type_t<T>;
229 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
230 return static_cast<T>(static_cast<underlying>(lhs) & static_cast<underlying>(rhs));
231}
232
233template <class T>
235constexpr auto operator~(T a) -> std::enable_if_t<stlab::has_enabled_bitmask<T>, T> {
236 using underlying = std::underlying_type_t<T>;
237 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
238 return static_cast<T>(~static_cast<underlying>(a));
239}
240
241template <class T>
243constexpr auto operator|(T lhs, T rhs) -> std::enable_if_t<stlab::has_enabled_bitmask<T>, T> {
244 using underlying = std::underlying_type_t<T>;
245 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
246 return static_cast<T>(static_cast<underlying>(lhs) | static_cast<underlying>(rhs));
247}
248
249template <class T>
251constexpr auto operator^(T lhs, T rhs) -> std::enable_if_t<stlab::has_enabled_bitmask<T>, T> {
252 using underlying = std::underlying_type_t<T>;
253 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
254 return static_cast<T>(static_cast<underlying>(lhs) ^ static_cast<underlying>(rhs));
255}
256
257template <class T>
259constexpr auto operator<<(T lhs, std::size_t rhs)
260 -> std::enable_if_t<stlab::has_enabled_bitmask<T>, T> {
261 using underlying = std::make_unsigned_t<std::underlying_type_t<T>>;
262 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
263 return static_cast<T>(static_cast<underlying>(lhs) << static_cast<underlying>(rhs));
264}
265
266template <class T>
268constexpr auto operator>>(T lhs, std::size_t rhs)
269 -> std::enable_if_t<stlab::has_enabled_bitmask<T>, T> {
270 using underlying = std::make_unsigned_t<std::underlying_type_t<T>>;
271 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
272 return static_cast<T>(static_cast<underlying>(lhs) >> static_cast<underlying>(rhs));
273}
274
275template <class T>
277constexpr auto operator^=(T& lhs, T rhs) -> std::enable_if_t<stlab::has_enabled_bitmask<T>, T&> {
278 return lhs = lhs ^ rhs;
279}
280
281template <class T>
283constexpr auto operator&=(T& lhs, T rhs) -> std::enable_if_t<stlab::has_enabled_bitmask<T>, T&> {
284 return lhs = lhs & rhs;
285}
286
287template <class T>
289constexpr auto operator|=(T& lhs, T rhs) -> std::enable_if_t<stlab::has_enabled_bitmask<T>, T&> {
290 return lhs = lhs | rhs;
291}
292
293template <class T>
295constexpr auto operator<<=(T& lhs, std::size_t rhs)
296 -> std::enable_if_t<stlab::has_enabled_bitmask<T>, T&> {
297 return lhs = lhs << rhs;
298}
299
300template <class T>
302constexpr auto operator>>=(T& lhs, std::size_t rhs)
303 -> std::enable_if_t<stlab::has_enabled_bitmask<T>, T&> {
304 return lhs = lhs >> rhs;
305}
306
307template <class T, class U>
310constexpr auto operator-(T lhs, U rhs)
311 -> std::enable_if_t<stlab::has_enabled_bitmask<T> && stlab::is_compatible_scalar<U, T>, T> {
312 assert(rhs == 0 || rhs == 1);
313 using underlying = std::underlying_type_t<T>;
314 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
315 return static_cast<T>(static_cast<underlying>(lhs) - static_cast<underlying>(rhs));
316}
317
318template <class T>
321constexpr auto operator-(std::nullptr_t lhs, T rhs)
322 -> std::enable_if_t<stlab::has_enabled_bitmask<T>, T> {
323 return -rhs;
324}
325
326template <class T, class U>
329constexpr auto operator+(T lhs, U rhs)
330 -> std::enable_if_t<stlab::has_enabled_bitmask<T> && stlab::is_compatible_scalar<U, T>, T> {
331 assert(rhs == 0 || rhs == 1);
332 using underlying = std::underlying_type_t<T>;
333 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
334 return static_cast<T>(static_cast<underlying>(lhs) + static_cast<underlying>(rhs));
335}
336
337template <class U, class T>
340constexpr auto operator+(U lhs, T rhs)
341 -> std::enable_if_t<stlab::has_enabled_bitmask<T> && stlab::is_compatible_scalar<U, T>, T> {
342 assert(lhs == 0 || lhs == 1);
343 using underlying = std::underlying_type_t<T>;
344 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
345 return static_cast<T>(static_cast<underlying>(lhs) + static_cast<underlying>(rhs));
346}
347
349
350/**************************************************************************************************/
351
354
355template <class T>
357constexpr auto operator+(T a) -> std::enable_if_t<stlab::has_enabled_arithmetic<T>, T> {
358 using underlying = std::underlying_type_t<T>;
359 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
360 return static_cast<T>(+static_cast<underlying>(a));
361}
362
363template <class T>
365constexpr auto operator+(T lhs, T rhs) -> std::enable_if_t<stlab::has_enabled_arithmetic<T>, T> {
366 using underlying = std::underlying_type_t<T>;
367 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
368 return static_cast<T>(static_cast<underlying>(lhs) + static_cast<underlying>(rhs));
369}
370
371template <class T>
373constexpr auto operator-(T lhs, T rhs) -> std::enable_if_t<stlab::has_enabled_arithmetic<T>, T> {
374 using underlying = std::underlying_type_t<T>;
375 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
376 return static_cast<T>(static_cast<underlying>(lhs) - static_cast<underlying>(rhs));
377}
378
379template <class T, class U>
381constexpr auto operator*(T lhs, U rhs)
382 -> std::enable_if_t<stlab::has_enabled_arithmetic<T> && stlab::is_compatible_scalar<U, T>, T> {
383 using underlying = std::underlying_type_t<T>;
384 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
385 return static_cast<T>(static_cast<underlying>(lhs) * rhs);
386}
387
388template <class U, class T>
390constexpr auto operator*(U lhs, T rhs)
391 -> std::enable_if_t<stlab::has_enabled_arithmetic<T> && stlab::is_compatible_scalar<U, T>, T> {
392 using underlying = std::underlying_type_t<T>;
393 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
394 return static_cast<T>(lhs * static_cast<underlying>(rhs));
395}
396
397template <class T, class U>
399constexpr auto operator/(T lhs, U rhs)
400 -> std::enable_if_t<stlab::has_enabled_arithmetic<T> && stlab::is_compatible_scalar<U, T>, T> {
401 using underlying = std::underlying_type_t<T>;
402 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
403 return static_cast<T>(static_cast<underlying>(lhs) / rhs);
404}
405
406template <class T, class U>
408constexpr auto operator%(T lhs, U rhs)
409 -> std::enable_if_t<stlab::has_enabled_arithmetic<T> && stlab::is_compatible_scalar<U, T>, T> {
410 using underlying = std::underlying_type_t<T>;
411 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
412 return static_cast<T>(static_cast<underlying>(lhs) % rhs);
413}
414
415template <class T>
417constexpr auto operator+=(T& lhs, T rhs) -> std::enable_if_t<stlab::has_enabled_arithmetic<T>, T&> {
418 return lhs = lhs + rhs;
419}
420
421template <class T>
423constexpr auto operator-=(T& lhs, T rhs) -> std::enable_if_t<stlab::has_enabled_arithmetic<T>, T&> {
424 return lhs = lhs - rhs;
425}
426
427template <class T, class U>
429constexpr auto operator*=(T& lhs, U rhs)
430 -> std::enable_if_t<stlab::has_enabled_arithmetic<T> && stlab::is_compatible_scalar<U, T>, T&> {
431 return lhs = lhs * rhs;
432}
433
434template <class T, class U>
436constexpr auto operator/=(T& lhs, U rhs)
437 -> std::enable_if_t<stlab::has_enabled_arithmetic<T> && stlab::is_compatible_scalar<U, T>, T&> {
438 return lhs = lhs / rhs;
439}
440
441template <class T, class U>
443constexpr auto operator%=(T& lhs, U rhs)
444 -> std::enable_if_t<stlab::has_enabled_arithmetic<T> && stlab::is_compatible_scalar<U, T>, T&> {
445 return lhs = lhs % rhs;
446}
447
448template <class T>
450constexpr auto operator++(T& lhs) -> std::enable_if_t<stlab::has_enabled_arithmetic<T>, T&> {
451 return lhs += static_cast<T>(1);
452}
453
454template <class T>
456constexpr auto operator++(T& lhs, int) -> std::enable_if_t<stlab::has_enabled_arithmetic<T>, T> {
457 T result = lhs;
458 lhs += static_cast<T>(1);
459 return result;
460}
461
462template <class T>
464constexpr auto operator--(T& lhs) -> std::enable_if_t<stlab::has_enabled_arithmetic<T>, T&> {
465 return lhs -= static_cast<T>(1);
466}
467
468template <class T>
470constexpr auto operator--(T& lhs, int) -> std::enable_if_t<stlab::has_enabled_arithmetic<T>, T> {
471 T result = lhs;
472 lhs -= static_cast<T>(1);
473 return result;
474}
475
477
478/**************************************************************************************************/
479
482
483template <class T>
486constexpr auto operator-(T a)
487 -> std::enable_if_t<stlab::has_enabled_arithmetic<T> || stlab::has_enabled_bitmask<T>, T> {
488 using underlying = std::underlying_type_t<T>;
489 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
490 return static_cast<T>(-static_cast<underlying>(a));
491}
492
493template <class T>
496constexpr auto operator==(T lhs, std::nullptr_t)
499 bool> {
500 return !lhs;
501}
502
503template <class T>
506constexpr auto operator==(std::nullptr_t, T rhs)
509 bool> {
510 return !rhs;
511}
512
513template <class T>
515constexpr auto operator!=(T lhs, std::nullptr_t rhs)
518 bool> {
519 return !(lhs == rhs);
520}
521
522template <class T>
524constexpr auto operator!=(std::nullptr_t lhs, T rhs)
527 bool> {
528 return !(lhs == rhs);
529}
530
531template <class T>
533constexpr auto operator!(T lhs)
534 -> std::enable_if_t<stlab::has_enabled_bitmask<T> || stlab::has_enabled_arithmetic<T>, bool> {
535 return !static_cast<bool>(lhs);
536}
537
539
540/**************************************************************************************************/
541
542#endif
543
544/**************************************************************************************************/
constexpr auto operator/(T lhs, U rhs) -> std::enable_if_t< stlab::has_enabled_arithmetic< T > &&stlab::is_compatible_scalar< U, T >, T >
Division by a scalar value.
Definition enum_ops.hpp:399
constexpr auto operator++(T &lhs) -> std::enable_if_t< stlab::has_enabled_arithmetic< T >, T & >
Prefix increment for arithmetic-enabled enums.
Definition enum_ops.hpp:450
constexpr auto operator-=(T &lhs, T rhs) -> std::enable_if_t< stlab::has_enabled_arithmetic< T >, T & >
Subtraction assignment for arithmetic-enabled enums.
Definition enum_ops.hpp:423
constexpr auto operator/=(T &lhs, U rhs) -> std::enable_if_t< stlab::has_enabled_arithmetic< T > &&stlab::is_compatible_scalar< U, T >, T & >
Division assignment by a scalar value.
Definition enum_ops.hpp:436
constexpr auto operator*(T lhs, U rhs) -> std::enable_if_t< stlab::has_enabled_arithmetic< T > &&stlab::is_compatible_scalar< U, T >, T >
Multiplication by a scalar value.
Definition enum_ops.hpp:381
constexpr auto operator*=(T &lhs, U rhs) -> std::enable_if_t< stlab::has_enabled_arithmetic< T > &&stlab::is_compatible_scalar< U, T >, T & >
Multiplication assignment by a scalar value.
Definition enum_ops.hpp:429
constexpr auto operator%(T lhs, U rhs) -> std::enable_if_t< stlab::has_enabled_arithmetic< T > &&stlab::is_compatible_scalar< U, T >, T >
Modulo by a scalar value.
Definition enum_ops.hpp:408
constexpr auto operator+=(T &lhs, T rhs) -> std::enable_if_t< stlab::has_enabled_arithmetic< T >, T & >
Addition assignment for arithmetic-enabled enums.
Definition enum_ops.hpp:417
constexpr auto operator%=(T &lhs, U rhs) -> std::enable_if_t< stlab::has_enabled_arithmetic< T > &&stlab::is_compatible_scalar< U, T >, T & >
Modulo assignment by a scalar value.
Definition enum_ops.hpp:443
constexpr auto operator--(T &lhs) -> std::enable_if_t< stlab::has_enabled_arithmetic< T >, T & >
Prefix decrement for arithmetic-enabled enums.
Definition enum_ops.hpp:464
constexpr auto operator|=(T &lhs, T rhs) -> std::enable_if_t< stlab::has_enabled_bitmask< T >, T & >
OR-assign for bitmask-enabled enums.
Definition enum_ops.hpp:289
constexpr auto operator&(T lhs, T rhs) -> std::enable_if_t< stlab::has_enabled_bitmask< T >, T >
Bitwise AND for bitmask-enabled enums; returns the same enum type.
Definition enum_ops.hpp:227
constexpr auto operator~(T a) -> std::enable_if_t< stlab::has_enabled_bitmask< T >, T >
Bitwise NOT for bitmask-enabled enums; returns the same enum type.
Definition enum_ops.hpp:235
constexpr auto operator>>=(T &lhs, std::size_t rhs) -> std::enable_if_t< stlab::has_enabled_bitmask< T >, T & >
Right shift-assign for bitmask-enabled enums.
Definition enum_ops.hpp:302
constexpr auto operator^=(T &lhs, T rhs) -> std::enable_if_t< stlab::has_enabled_bitmask< T >, T & >
XOR-assign for bitmask-enabled enums.
Definition enum_ops.hpp:277
constexpr auto operator|(T lhs, T rhs) -> std::enable_if_t< stlab::has_enabled_bitmask< T >, T >
Bitwise OR for bitmask-enabled enums.
Definition enum_ops.hpp:243
constexpr auto operator<<(T lhs, std::size_t rhs) -> std::enable_if_t< stlab::has_enabled_bitmask< T >, T >
Left shift for bitmask-enabled enums.
Definition enum_ops.hpp:259
constexpr auto operator+(T lhs, U rhs) -> std::enable_if_t< stlab::has_enabled_bitmask< T > &&stlab::is_compatible_scalar< U, T >, T >
Adds a 0 or 1 scalar value to a bitmask-enabled enum. Allows expressions like e & (e + 1) to clear tr...
Definition enum_ops.hpp:329
constexpr auto operator-(T lhs, U rhs) -> std::enable_if_t< stlab::has_enabled_bitmask< T > &&stlab::is_compatible_scalar< U, T >, T >
Subtracts a 0 or 1 scalar value from a bitmask-enabled enum. Allows expressions like e & (e - 1) to c...
Definition enum_ops.hpp:310
constexpr auto operator>>(T lhs, std::size_t rhs) -> std::enable_if_t< stlab::has_enabled_bitmask< T >, T >
Right shift for bitmask-enabled enums.
Definition enum_ops.hpp:268
constexpr auto operator&=(T &lhs, T rhs) -> std::enable_if_t< stlab::has_enabled_bitmask< T >, T & >
AND-assign for bitmask-enabled enums.
Definition enum_ops.hpp:283
constexpr auto operator^(T lhs, T rhs) -> std::enable_if_t< stlab::has_enabled_bitmask< T >, T >
Bitwise XOR for bitmask-enabled enums.
Definition enum_ops.hpp:251
constexpr auto operator<<=(T &lhs, std::size_t rhs) -> std::enable_if_t< stlab::has_enabled_bitmask< T >, T & >
Left shift-assign for bitmask-enabled enums.
Definition enum_ops.hpp:295
constexpr auto operator!(T lhs) -> std::enable_if_t< stlab::has_enabled_bitmask< T >||stlab::has_enabled_arithmetic< T >, bool >
Logical NOT for bitmask or arithmetic enums; true when the value converts to false.
Definition enum_ops.hpp:533
constexpr auto operator==(T lhs, std::nullptr_t) -> std::enable_if_t<(stlab::has_enabled_bitmask< T >||stlab::has_enabled_arithmetic< T >) &&!stlab::is_compatible_scalar< T, T >, bool >
Equality with nullptr for bitmask or arithmetic scoped enums; true when the value is zero.
Definition enum_ops.hpp:496
constexpr auto operator!=(T lhs, std::nullptr_t rhs) -> std::enable_if_t<(stlab::has_enabled_bitmask< T >||stlab::has_enabled_arithmetic< T >) &&!stlab::is_compatible_scalar< T, T >, bool >
Inequality with nullptr for bitmask or arithmetic scoped enums.
Definition enum_ops.hpp:515
auto adobe_enable_bitmask_enum(...) -> std::false_type
auto stlab_enable_arithmetic_enum(...) -> std::false_type
Overload this for your enum in the enum namespace to return std::true_type and enable arithmetic oper...
auto adobe_enable_arithmetic_enum(...) -> std::false_type
auto stlab_enable_bitmask_enum(...) -> std::false_type
Overload this for your enum in the enum namespace to return std::true_type and enable bitwise operato...
constexpr bool is_compatible_scalar
Whether the scalar type U is compatible with the enum type T.
Definition enum_ops.hpp:212
constexpr bool has_enabled_arithmetic
Whether the enum type has enabled arithmetic operations.
Definition enum_ops.hpp:207
constexpr bool has_enabled_bitmask
Whether the enum type has enabled bitmask operations.
Definition enum_ops.hpp:202
The stlab namespace.
Definition enum_ops.hpp:122