blob: 227cafd662d353c35cc0543fa5d7b7ecf4b562c7 [file] [log] [blame]
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef LIBBRILLO_BRILLO_ENUM_FLAGS_H_
#define LIBBRILLO_BRILLO_ENUM_FLAGS_H_
#include <type_traits>
// This is a helper for generating type-safe bitwise operators for flags that
// are defined by an enumeration. By default, when a bitwise operation is
// performed on two enumerators of an enumeration, the result is the base type
// (int), not a value of the enumeration:
//
// enum SomeEnumOfFlags {
// ONE = 1,
// TWO = 2,
// THREE = 4,
// // etc.
// };
//
// SomeEnumOfFlags flags = static_cast<SomeEnumOfFlags>(ONE | TWO);
//
// By enabling these operators for an enum type:
//
// DECLARE_FLAGS_ENUM(SomeEnumOfFlags);
//
// The syntax is simplified to:
//
// SomeEnumOfFlags flags = ONE | TWO;
//
// But the following still does not compile without using a cast (as is
// expected):
//
// SomeEnumOfFlags flags = ONE | 2;
// This is the macro used to declare that an enum type |ENUM| should have bit-
// wise operators defined for it.
#define DECLARE_FLAGS_ENUM(ENUM) \
template <typename> struct EnumFlagTraitType; \
template <> struct EnumFlagTraitType<ENUM> { using EnumFlagType = ENUM; }; \
EnumFlagTraitType<ENUM> GetEnumFlagTraitType(ENUM) __attribute__((used));
// Setup the templates used to declare that the operators should exist for a
// given type T.
namespace enum_details {
template <typename T>
using FlagEnumTraits = decltype(GetEnumFlagTraitType(std::declval<T>()));
template <typename T>
using Void = void;
template <typename T, typename = void>
struct IsFlagEnum : std::false_type {};
template <typename T>
struct IsFlagEnum<T, Void<typename FlagEnumTraits<T>::EnumFlagType>>
: std::true_type {};
} // namespace enum_details
// The operators themselves, conditional on having been declared that they are
// flag-style enums.
// T operator~(T&)
template <typename T>
constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type
operator~(const T& l) {
return static_cast<T>(
~static_cast<typename std::underlying_type<T>::type>(l));
}
// T operator|(T&, T&)
template <typename T>
constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type
operator|(const T& l, const T& r) {
return static_cast<T>(
static_cast<typename std::underlying_type<T>::type>(l) |
static_cast<typename std::underlying_type<T>::type>(r));
}
// T operator&(T&, T&)
template <typename T>
constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type
operator&(const T& l, const T& r) {
return static_cast<T>(
static_cast<typename std::underlying_type<T>::type>(l) &
static_cast<typename std::underlying_type<T>::type>(r));
}
// T operator^(T&, T&)
template <typename T>
constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type
operator^(const T& l, const T& r) {
return static_cast<T>(static_cast<typename std::underlying_type<T>::type>(l) ^
static_cast<typename std::underlying_type<T>::type>(r));
}
// T operator|=(T&, T&)
template <typename T>
constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type
operator|=(T& l, const T& r) {
return l = static_cast<T>(
static_cast<typename std::underlying_type<T>::type>(l) |
static_cast<typename std::underlying_type<T>::type>(r));
}
// T operator&=(T&, T&)
template <typename T>
constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type
operator&=(T& l, const T& r) {
return l = static_cast<T>(
static_cast<typename std::underlying_type<T>::type>(l) &
static_cast<typename std::underlying_type<T>::type>(r));
}
// T operator^=(T&, T&)
template <typename T>
constexpr typename std::enable_if<enum_details::IsFlagEnum<T>::value, T>::type
operator^=(T& l, const T& r) {
return l = static_cast<T>(
static_cast<typename std::underlying_type<T>::type>(l) ^
static_cast<typename std::underlying_type<T>::type>(r));
}
#endif // LIBBRILLO_BRILLO_ENUM_FLAGS_H_