blob: 70e1263ac3059dd54f814105ba11ad805a8adddd [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>
#include <base/stl_util.h>
// 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>(~base::to_underlying(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>(base::to_underlying(l) | base::to_underlying(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>(base::to_underlying(l) & base::to_underlying(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>(base::to_underlying(l) ^ base::to_underlying(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) { // NOLINT(runtime/references)
return l = static_cast<T>(base::to_underlying(l) | base::to_underlying(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) { // NOLINT(runtime/references)
return l = static_cast<T>(base::to_underlying(l) & base::to_underlying(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) { // NOLINT(runtime/references)
return l = static_cast<T>(base::to_underlying(l) ^ base::to_underlying(r));
}
#endif // LIBBRILLO_BRILLO_ENUM_FLAGS_H_