Bitwise operator templates for enums am: 1d20c7da0b
am: b0c0ff679b

Change-Id: I15919eb81fd31a9ec5a1959672ed77ec7e59c9c4
diff --git a/Android.bp b/Android.bp
index 5a6c614..a25205b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -87,6 +87,7 @@
     "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