aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Wood <aaronwood@google.com>2017-12-07 08:39:50 +0000
committerandroid-build-merger <android-build-merger@google.com>2017-12-07 08:39:50 +0000
commitb0c0ff679bc08f5986ae9c8ec1c477b5b17ac6a3 (patch)
tree0ad3be6b5c5acc6874ddc5903b201cf0471c5038
parent2053650b8ba7a88b3c79fb5ed62bd2b6ce050ffc (diff)
parent1d20c7da0b8833ffda7dfff2066205ea1af7e771 (diff)
downloadplatform_external_libbrillo-b0c0ff679bc08f5986ae9c8ec1c477b5b17ac6a3.tar.gz
platform_external_libbrillo-b0c0ff679bc08f5986ae9c8ec1c477b5b17ac6a3.tar.bz2
platform_external_libbrillo-b0c0ff679bc08f5986ae9c8ec1c477b5b17ac6a3.zip
Bitwise operator templates for enums
am: 1d20c7da0b Change-Id: I55dcf921c9ab89f1e07bd4f85871d0be1d84a53e
-rw-r--r--Android.bp1
-rw-r--r--brillo/enum_flags.h127
-rw-r--r--brillo/enum_flags_unittest.cc241
3 files changed, 369 insertions, 0 deletions
diff --git a/Android.bp b/Android.bp
index 5a6c614..a25205b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -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