diff options
author | Aaron Wood <aaronwood@google.com> | 2017-12-07 09:26:13 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2017-12-07 09:26:13 +0000 |
commit | 1015853ceea363bb0c5979748fa53985657323af (patch) | |
tree | 0ad3be6b5c5acc6874ddc5903b201cf0471c5038 | |
parent | 3fc687df5eb71ef99f3b0e98c5252fcfa54833f2 (diff) | |
parent | 76f2ea30c6a76fefff34cb18ee872ffa427ec145 (diff) | |
download | platform_external_libbrillo-1015853ceea363bb0c5979748fa53985657323af.tar.gz platform_external_libbrillo-1015853ceea363bb0c5979748fa53985657323af.tar.bz2 platform_external_libbrillo-1015853ceea363bb0c5979748fa53985657323af.zip |
Bitwise operator templates for enums am: 1d20c7da0b am: b0c0ff679b
am: 76f2ea30c6
Change-Id: I51a6156e3aeccbf43f7163a9703842020655d091
-rw-r--r-- | Android.bp | 1 | ||||
-rw-r--r-- | brillo/enum_flags.h | 127 | ||||
-rw-r--r-- | brillo/enum_flags_unittest.cc | 241 |
3 files changed, 369 insertions, 0 deletions
@@ -87,6 +87,7 @@ libbrillo_test_sources = [ "brillo/asynchronous_signal_handler_unittest.cc", "brillo/backoff_entry_unittest.cc", "brillo/data_encoding_unittest.cc", + "brillo/enum_flags_unittest.cc", "brillo/errors/error_codes_unittest.cc", "brillo/errors/error_unittest.cc", "brillo/file_utils_unittest.cc", diff --git a/brillo/enum_flags.h b/brillo/enum_flags.h new file mode 100644 index 0000000..9630dd0 --- /dev/null +++ b/brillo/enum_flags.h @@ -0,0 +1,127 @@ +// 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_ diff --git a/brillo/enum_flags_unittest.cc b/brillo/enum_flags_unittest.cc new file mode 100644 index 0000000..e57b4ad --- /dev/null +++ b/brillo/enum_flags_unittest.cc @@ -0,0 +1,241 @@ +// 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. + +#include "brillo/enum_flags.h" + +#include <gtest/gtest.h> + +namespace brillo { + +class EnumFlagsTest : public testing::Test {}; + +enum SomeFlagsEnum /* : int */ { + FLAG_NONE = 0, + FLAG_ONE = 1, + FLAG_TWO = 2, + FLAG_THREE = 4, +}; + +enum class SomeFlagsEnumClass /* : int */ { + NONE = 0, + ONE = 1, + TWO = 2, + THREE = 4, +}; + +enum SomeBigFlagsEnum : int64_t { + BIG_FLAG_NONE = 0, + BIG_FLAG_ONE = 1, + BIG_FLAG_TWO = 2, + BIG_FLAG_THREE = 4, + BIG_FLAG_FOUR = 8, +}; + +DECLARE_FLAGS_ENUM(SomeFlagsEnum); +DECLARE_FLAGS_ENUM(SomeFlagsEnumClass); +DECLARE_FLAGS_ENUM(SomeBigFlagsEnum); + +// These first tests show how these operators are meant to be used. + +TEST_F(EnumFlagsTest, SampleUsage) { + SomeFlagsEnum value = FLAG_NONE; + + // Set a flag. + value |= FLAG_ONE; + EXPECT_EQ(FLAG_ONE, value); + + // Set another + value |= FLAG_THREE; + EXPECT_EQ(FLAG_ONE | FLAG_THREE, value); + + // Clear a flag + value &= ~FLAG_ONE; + EXPECT_EQ(FLAG_THREE, value); + + // Toggle a flag + value ^= FLAG_TWO; + EXPECT_EQ(FLAG_THREE | FLAG_TWO, value); +} + +TEST_F(EnumFlagsTest, SampleUsageOfMasks) { + SomeFlagsEnum flags = FLAG_ONE | FLAG_THREE; + + EXPECT_TRUE(flags & FLAG_ONE); + EXPECT_TRUE(flags & FLAG_THREE); + EXPECT_FALSE(flags & FLAG_TWO); + EXPECT_TRUE(flags & ~FLAG_TWO); +} + +TEST_F(EnumFlagsTest, SampleUsageWithEnumClass) { + SomeFlagsEnumClass value = SomeFlagsEnumClass::NONE; + + // Set a flag. + value |= SomeFlagsEnumClass::ONE; + EXPECT_EQ(SomeFlagsEnumClass::ONE, value); + + // Set another + value |= SomeFlagsEnumClass::THREE; + EXPECT_EQ(SomeFlagsEnumClass::ONE | SomeFlagsEnumClass::THREE, value); + + // Clear a flag + value &= ~SomeFlagsEnumClass::ONE; + EXPECT_EQ(SomeFlagsEnumClass::THREE, value); + + // Toggle a flag + value ^= SomeFlagsEnumClass::TWO; + EXPECT_EQ(SomeFlagsEnumClass::THREE | SomeFlagsEnumClass::TWO, value); +} + +TEST_F(EnumFlagsTest, SampleUsageWithBigEnumType) { + SomeBigFlagsEnum value = BIG_FLAG_NONE; + + // Set a flag. + value |= BIG_FLAG_ONE; + EXPECT_EQ(BIG_FLAG_ONE, value); + + // Set another + value |= BIG_FLAG_THREE; + EXPECT_EQ(FLAG_ONE | BIG_FLAG_THREE, value); + + // Clear a flag + value &= ~BIG_FLAG_ONE; + EXPECT_EQ(BIG_FLAG_THREE, value); + + // Toggle a flag + value ^= BIG_FLAG_TWO; + EXPECT_EQ(BIG_FLAG_THREE | BIG_FLAG_TWO, value); +} + +// These following tests verify the binary behavior of the operators. They do +// not demonstrate standard usage. + +TEST_F(EnumFlagsTest, BinaryBehaviorOfAssignmentOperators) { + SomeFlagsEnum value = FLAG_NONE; + + // Set a flag. + value |= FLAG_ONE; + EXPECT_EQ(1, value); + + // Set another + value |= FLAG_THREE; + EXPECT_EQ(5, value); + + // Clear a flag + value &= ~FLAG_ONE; + EXPECT_EQ(4, value); + + // Toggle a flag + value ^= FLAG_TWO; + EXPECT_EQ(6, value); +} + +TEST_F(EnumFlagsTest, BinaryBehaviorOfAssignmentOperatorsWithEnumClass) { + SomeFlagsEnumClass value = SomeFlagsEnumClass::NONE; + + // Set a flag. + value |= SomeFlagsEnumClass::ONE; + EXPECT_EQ(1, static_cast<int>(value)); // + + // Set another + value |= SomeFlagsEnumClass::THREE; + EXPECT_EQ(5, static_cast<int>(value)); + + // Clear a flag + value &= ~SomeFlagsEnumClass::ONE; + EXPECT_EQ(4, static_cast<int>(value)); + + // Toggle a flag + value ^= SomeFlagsEnumClass::TWO; + EXPECT_EQ(6, static_cast<int>(value)); +} + +TEST_F(EnumFlagsTest, BinaryBehaviorOfSimpleOperations) { + // These values are set directly with a cast for clarity. + const int all_bits_int = -1; + const SomeFlagsEnum all_bits = static_cast<SomeFlagsEnum>(all_bits_int); + const SomeFlagsEnum just_2_bits = static_cast<SomeFlagsEnum>(3); + + // Inverting a flag should result in all bits set in the base type but that + // one. + EXPECT_EQ(-2, ~FLAG_ONE); + EXPECT_EQ(-3, ~FLAG_TWO); + + // OR'ing two flags should result in both being set. + EXPECT_EQ(3, FLAG_ONE | FLAG_TWO); + + // AND'ing two flags should result in 0. + EXPECT_EQ(FLAG_NONE, FLAG_ONE & FLAG_TWO); + + // AND'ing a mask with a flag should result in that flag. + EXPECT_EQ(FLAG_ONE, all_bits & FLAG_ONE); + + // XOR'ing two flags should result in both being set. + EXPECT_EQ(3, FLAG_ONE ^ FLAG_TWO); + + // XOR'ing a mask with a flag should toggle that flag in the mask. + EXPECT_EQ(FLAG_ONE, FLAG_NONE ^ FLAG_ONE); + EXPECT_EQ(FLAG_TWO, just_2_bits ^ FLAG_ONE); +} + +TEST_F(EnumFlagsTest, BinaryBehaviorOfSimpleOperationsOnEnumClass) { + // These values are set directly with a cast for clarity. + const int all_bits_int = -1; + const SomeFlagsEnumClass all_bits = + static_cast<SomeFlagsEnumClass>(all_bits_int); + const SomeFlagsEnumClass just_2_bits = static_cast<SomeFlagsEnumClass>(3); + + // Inverting a flag should result in all bits set in the base type but that + // one. + EXPECT_EQ(-2, static_cast<int>(~SomeFlagsEnumClass::ONE)); + EXPECT_EQ(-3, static_cast<int>(~SomeFlagsEnumClass::TWO)); + + // OR'ing two flags should result in both being set. + EXPECT_EQ( + 3, static_cast<int>(SomeFlagsEnumClass::ONE | SomeFlagsEnumClass::TWO)); + + // AND'ing two flags should result in 0. + EXPECT_EQ(SomeFlagsEnumClass::NONE, + SomeFlagsEnumClass::ONE & SomeFlagsEnumClass::TWO); + + // AND'ing a mask with a flag should result in that flag. + EXPECT_EQ(SomeFlagsEnumClass::ONE, all_bits & SomeFlagsEnumClass::ONE); + + // XOR'ing two flags should result in both being set. + EXPECT_EQ( + 3, static_cast<int>(SomeFlagsEnumClass::ONE ^ SomeFlagsEnumClass::TWO)); + + // XOR'ing a mask with a flag should toggle that flag in the mask. + EXPECT_EQ(SomeFlagsEnumClass::ONE, + SomeFlagsEnumClass::NONE ^ SomeFlagsEnumClass::ONE); + EXPECT_EQ(SomeFlagsEnumClass::TWO, just_2_bits ^ SomeFlagsEnumClass::ONE); +} + +TEST_F(EnumFlagsTest, BinaryBehaviorOfSimpleOperationsWithBaseType) { + // These values are set directly with a cast for clarity. + const int64_t all_bits_int = -1; + const SomeBigFlagsEnum all_bits = static_cast<SomeBigFlagsEnum>(all_bits_int); + const SomeBigFlagsEnum just_2_bits = static_cast<SomeBigFlagsEnum>(3); + + // Inverting a flag should result in all bits set in the base type but that + // one. + EXPECT_EQ(all_bits ^ BIG_FLAG_ONE, ~BIG_FLAG_ONE); + + // OR'ing two flags should result in both being set. + EXPECT_EQ(3, BIG_FLAG_ONE | BIG_FLAG_TWO); + + // AND'ing two flags should result in 0. + EXPECT_EQ(BIG_FLAG_NONE, BIG_FLAG_ONE & BIG_FLAG_TWO); + + // AND'ing a mask with a flag should result in that flag. + EXPECT_EQ(BIG_FLAG_ONE, all_bits & BIG_FLAG_ONE); + + // XOR'ing two flags should result in both being set. + EXPECT_EQ(3, BIG_FLAG_ONE ^ BIG_FLAG_TWO); + + // XOR'ing a mask with a flag should toggle that flag in the mask. + EXPECT_EQ(BIG_FLAG_ONE, BIG_FLAG_NONE ^ BIG_FLAG_ONE); + EXPECT_EQ(BIG_FLAG_TWO, just_2_bits ^ BIG_FLAG_ONE); +} + +} // namespace brillo |