blob: 666a1219f9ec15341f22d875eba1718dc98b3ab6 [file] [log] [blame]
// Copyright 2020 The Fuchsia 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 FBL_ENUM_BITS_H_
#define FBL_ENUM_BITS_H_
#include <zircon/compiler.h>
#include <type_traits>
// Utility to make using enum classes as bit fields more ergonomic.
//
// This utility supplies overloads of the bitwise operators that bind only to
// enum types tagged with the macro FBL_ENABLE_ENUM_BITS(type). The macro must
// be called in the same namespace as the enum type being tagged.
//
// The operators only accept values of the same type. Mixing types requres
// casting.
//
// Examples:
//
// enum class Bits : uint64_t {
// FieldA = 0b0001,
// FieldB = 0b0010,
// Mask = FieldA | FieldB,
// };
// FBL_ENABLE_ENUM_BITS(Bits)
//
//
// namespace foo {
// enum class Bits : uint32_t {
// // ...
// };
// FBL_ENABLE_ENUM_BITS(Bits)
// } // namespace foo
//
// struct Bar {
// enum class Bits : uint8_t {
// // ...
// };
// // ...
// };
// FBL_ENABLE_ENUM_BITS(Bar::Bits)
//
// Tags the given enum for use as a bit field, enabling the overloaded bitwise
// operators. Must be called in the same scope that |type| is defined in.
#define FBL_ENABLE_ENUM_BITS(type) \
static_assert(std::is_enum_v<type>, "Type must be an enum: type=" #type); \
[[maybe_unused]] static constexpr bool FBL__EnumBitsTag(const type&) { return true; }
namespace fbl::internal {
// Looks for a function defined in the same namespace as T with the signature
// bool FBL__EnumBitsTag(const T&) via ADL.
template <typename T>
using EnumBitsTag = decltype(FBL__EnumBitsTag(std::declval<const T>()));
// Evaluates to true if type T is tagged
template <typename, typename = void>
struct IsEnumBits : std::false_type {};
template <typename T>
struct IsEnumBits<T, std::void_t<EnumBitsTag<T>>> : std::true_type {};
// Enable if T is properly a properly tagged enum. Double checks that T is an
// enum to as defense in depth.
template <typename T>
using EnableIfEnumBits = std::enable_if_t<std::is_enum_v<T> && IsEnumBits<T>::value>;
} // namespace fbl::internal
// Operator overloads at global scope enabled only for types tagged as enum
// fields.
template <typename T, typename = ::fbl::internal::EnableIfEnumBits<T>>
constexpr T operator|(T a, T b) {
using U = typename std::underlying_type<T>::type;
return static_cast<T>(static_cast<U>(a) | static_cast<U>(b));
}
template <typename T, typename = ::fbl::internal::EnableIfEnumBits<T>>
constexpr T operator&(T a, T b) {
using U = typename std::underlying_type<T>::type;
return static_cast<T>(static_cast<U>(a) & static_cast<U>(b));
}
template <typename T, typename = ::fbl::internal::EnableIfEnumBits<T>>
constexpr T operator^(T a, T b) {
using U = typename std::underlying_type<T>::type;
return static_cast<T>(static_cast<U>(a) ^ static_cast<U>(b));
}
template <typename T, typename = ::fbl::internal::EnableIfEnumBits<T>>
constexpr T& operator|=(T& a, T b) {
a = a | b;
return a;
}
template <typename T, typename = ::fbl::internal::EnableIfEnumBits<T>>
constexpr T& operator&=(T& a, T b) {
a = a & b;
return a;
}
template <typename T, typename = ::fbl::internal::EnableIfEnumBits<T>>
constexpr T& operator^=(T& a, T b) {
a = a ^ b;
return a;
}
template <typename T, typename = ::fbl::internal::EnableIfEnumBits<T>>
constexpr T operator~(T value) {
using U = typename std::underlying_type<T>::type;
return static_cast<T>(~static_cast<U>(value));
}
template <typename T, typename = ::fbl::internal::EnableIfEnumBits<T>>
constexpr bool operator!(T value) {
using U = typename std::underlying_type<T>::type;
return !static_cast<U>(value);
}
#endif // FBL_ENUM_BITS_H_