[graphite] Clean up bitwise operators for enum masks
* Rename TFlagsMask<> to Mask<>
* Make Mask<> implicitly convertible to bool
* Rename SKGPU_MAKE_BITFIELD_CLASS_OPS to SKGPU_MAKE_MASK_OPS
* Remove the macro for non-class enums
Change-Id: I86059335d731f34c9c9c20a4cd6d5491543aa3b1
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/456816
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/experimental/graphite/include/GraphiteTypes.h b/experimental/graphite/include/GraphiteTypes.h
index d8b1e35..2f7fbad 100644
--- a/experimental/graphite/include/GraphiteTypes.h
+++ b/experimental/graphite/include/GraphiteTypes.h
@@ -13,108 +13,64 @@
namespace skgpu {
/**
- * Defines overloaded bitwise operators to make it easier to use an enum as a
- * bitfield.
+ * Wraps an enum that is used for flags, and enables masking with type safety. Example:
+ *
+ * enum class MyFlags {
+ * kNone = 0,
+ * kA = 1,
+ * kB = 2,
+ * kC = 4,
+ * };
+ *
+ * SKGPU_MAKE_MASK_OPS(MyFlags)
+ *
+ * ...
+ *
+ * Mask<MyFlags> flags = MyFlags::kA | MyFlags::kB;
+ *
+ * if (flags & MyFlags::kB) {}
+ *
+ * ...
*/
-#define SKGPU_MAKE_BITFIELD_OPS(X) \
- inline X operator |(X a, X b) { \
- return (X) (+a | +b); \
- } \
- inline X& operator |=(X& a, X b) { \
- return (a = a | b); \
- } \
- inline X operator &(X a, X b) { \
- return (X) (+a & +b); \
- } \
- inline X& operator &=(X& a, X b) { \
- return (a = a & b); \
- } \
- template <typename T> \
- inline X operator &(T a, X b) { \
- return (X) (+a & +b); \
- } \
- template <typename T> \
- inline X operator &(X a, T b) { \
- return (X) (+a & +b); \
- } \
-
-#define SKGPU_DECL_BITFIELD_OPS_FRIENDS(X) \
- friend X operator |(X a, X b); \
- friend X& operator |=(X& a, X b); \
- \
- friend X operator &(X a, X b); \
- friend X& operator &=(X& a, X b); \
- \
- template <typename T> \
- friend X operator &(T a, X b); \
- \
- template <typename T> \
- friend X operator &(X a, T b); \
-
-/**
- * Wraps a C++11 enum that we use as a bitfield, and enables a limited amount of
- * masking with type safety. Instantiated with the ~ operator.
- */
-template<typename TFlags> class TFlagsMask {
+template<typename E>
+class Mask {
public:
- constexpr explicit TFlagsMask(TFlags value) : TFlagsMask(static_cast<int>(value)) {}
- constexpr explicit TFlagsMask(int value) : fValue(value) {}
- constexpr int value() const { return fValue; }
+ SK_ALWAYS_INLINE constexpr Mask(E e) : Mask((int)e) {}
+
+ SK_ALWAYS_INLINE constexpr operator bool() const { return fValue; }
+
+ SK_ALWAYS_INLINE bool operator==(Mask m) const { return fValue == m.fValue; }
+ SK_ALWAYS_INLINE bool operator!=(Mask m) const { return fValue != m.fValue; }
+
+ SK_ALWAYS_INLINE constexpr Mask operator|(Mask m) const { return Mask(fValue | m.fValue); }
+ SK_ALWAYS_INLINE constexpr Mask operator&(Mask m) const { return Mask(fValue & m.fValue); }
+ SK_ALWAYS_INLINE constexpr Mask operator^(Mask m) const { return Mask(fValue ^ m.fValue); }
+ SK_ALWAYS_INLINE constexpr Mask operator~() const { return Mask(~fValue); }
+
+ SK_ALWAYS_INLINE Mask& operator|=(Mask m) { return *this = *this | m; }
+ SK_ALWAYS_INLINE Mask& operator&=(Mask m) { return *this = *this & m; }
+ SK_ALWAYS_INLINE Mask& operator^=(Mask m) { return *this = *this ^ m; }
+
private:
- const int fValue;
+ SK_ALWAYS_INLINE constexpr explicit Mask(int value) : fValue(value) {}
+
+ int fValue;
};
/**
- * Defines bitwise operators that make it possible to use an enum class as a
- * basic bitfield.
+ * Defines functions that make it possible to use bitwise operators on an enum.
*/
-#define SKGPU_MAKE_BITFIELD_CLASS_OPS(X) \
- SK_MAYBE_UNUSED constexpr TFlagsMask<X> operator~(X a) { \
- return TFlagsMask<X>(~static_cast<int>(a)); \
- } \
- SK_MAYBE_UNUSED constexpr X operator|(X a, X b) { \
- return static_cast<X>(static_cast<int>(a) | static_cast<int>(b)); \
- } \
- SK_MAYBE_UNUSED inline X& operator|=(X& a, X b) { \
- return (a = a | b); \
- } \
- SK_MAYBE_UNUSED constexpr bool operator&(X a, X b) { \
- return SkToBool(static_cast<int>(a) & static_cast<int>(b)); \
- } \
- SK_MAYBE_UNUSED constexpr TFlagsMask<X> operator|(TFlagsMask<X> a, TFlagsMask<X> b) { \
- return TFlagsMask<X>(a.value() | b.value()); \
- } \
- SK_MAYBE_UNUSED constexpr TFlagsMask<X> operator|(TFlagsMask<X> a, X b) { \
- return TFlagsMask<X>(a.value() | static_cast<int>(b)); \
- } \
- SK_MAYBE_UNUSED constexpr TFlagsMask<X> operator|(X a, TFlagsMask<X> b) { \
- return TFlagsMask<X>(static_cast<int>(a) | b.value()); \
- } \
- SK_MAYBE_UNUSED constexpr X operator&(TFlagsMask<X> a, TFlagsMask<X> b) { \
- return static_cast<X>(a.value() & b.value()); \
- } \
- SK_MAYBE_UNUSED constexpr X operator&(TFlagsMask<X> a, X b) { \
- return static_cast<X>(a.value() & static_cast<int>(b)); \
- } \
- SK_MAYBE_UNUSED constexpr X operator&(X a, TFlagsMask<X> b) { \
- return static_cast<X>(static_cast<int>(a) & b.value()); \
- } \
- SK_MAYBE_UNUSED inline X& operator&=(X& a, TFlagsMask<X> b) { \
- return (a = a & b); \
- } \
+#define SKGPU_MAKE_MASK_OPS(E) \
+ SK_MAYBE_UNUSED constexpr skgpu::Mask<E> operator|(E a, E b) { return skgpu::Mask<E>(a) | b; } \
+ SK_MAYBE_UNUSED constexpr skgpu::Mask<E> operator&(E a, E b) { return skgpu::Mask<E>(a) & b; } \
+ SK_MAYBE_UNUSED constexpr skgpu::Mask<E> operator^(E a, E b) { return skgpu::Mask<E>(a) ^ b; } \
+ SK_MAYBE_UNUSED constexpr skgpu::Mask<E> operator~(E e) { return ~skgpu::Mask<E>(e); } \
-#define SKGPU_DECL_BITFIELD_CLASS_OPS_FRIENDS(X) \
- friend constexpr TFlagsMask<X> operator ~(X); \
- friend constexpr X operator |(X, X); \
- friend X& operator |=(X&, X); \
- friend constexpr bool operator &(X, X); \
- friend constexpr TFlagsMask<X> operator|(TFlagsMask<X>, TFlagsMask<X>); \
- friend constexpr TFlagsMask<X> operator|(TFlagsMask<X>, X); \
- friend constexpr TFlagsMask<X> operator|(X, TFlagsMask<X>); \
- friend constexpr X operator&(TFlagsMask<X>, TFlagsMask<X>); \
- friend constexpr X operator&(TFlagsMask<X>, X); \
- friend constexpr X operator&(X, TFlagsMask<X>); \
- friend X& operator &=(X&, TFlagsMask<X>)
+#define SKGPU_DECL_MASK_OPS_FRIENDS(E) \
+ friend constexpr skgpu::Mask<E> operator|(E, E); \
+ friend constexpr skgpu::Mask<E> operator&(E, E); \
+ friend constexpr skgpu::Mask<E> operator^(E, E); \
+ friend constexpr skgpu::Mask<E> operator~(E); \
/**
* Possible 3D APIs that may be used by Graphite.
diff --git a/gn/tests.gni b/gn/tests.gni
index 42946ae..9a4ef2e 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -389,6 +389,9 @@
"$_tests/PathOpsTightBoundsTest.cpp",
"$_tests/PathOpsTypesTest.cpp",
"$_tests/WangsFormulaTest.cpp",
+
+ # graphite
+ "$_tests/graphite/MaskTest.cpp",
]
skgpu_v1_tests_sources = [
diff --git a/tests/graphite/MaskTest.cpp b/tests/graphite/MaskTest.cpp
new file mode 100644
index 0000000..2a2e64a
--- /dev/null
+++ b/tests/graphite/MaskTest.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "experimental/graphite/include/GraphiteTypes.h"
+#include "tests/Test.h"
+
+enum class Flags {
+ kNone = 0,
+ kA = 1,
+ kB = 2,
+ kC = 4
+};
+
+SKGPU_MAKE_MASK_OPS(Flags);
+
+using namespace skgpu;
+
+DEF_TEST(skgpu_Mask, r) {
+ Mask<Flags> flags = Flags::kNone;
+ REPORTER_ASSERT(r, !flags);
+ flags |= Flags::kA;
+ REPORTER_ASSERT(r, flags);
+ REPORTER_ASSERT(r, !(flags & Flags::kB));
+ REPORTER_ASSERT(r, (flags & Flags::kA));
+ flags |= (Flags::kB | Flags::kC);
+ auto mask = Flags::kB | Flags::kC;
+ REPORTER_ASSERT(r, (flags & mask) == mask);
+ REPORTER_ASSERT(r, flags == (Flags::kA | Flags::kB | Flags::kC));
+ flags &= ~Flags::kC;
+ REPORTER_ASSERT(r, flags == (Flags::kA | Flags::kB));
+ REPORTER_ASSERT(r, (flags & mask) != mask);
+ REPORTER_ASSERT(r, (flags & mask) != Flags::kNone);
+ REPORTER_ASSERT(r, (flags & mask));
+ REPORTER_ASSERT(r, flags);
+ flags &= ~Flags::kB;
+ REPORTER_ASSERT(r, (flags & mask) == Flags::kNone);
+ REPORTER_ASSERT(r, !(flags & mask));
+ REPORTER_ASSERT(r, flags);
+ flags = (flags | flags) ^ (flags);
+ REPORTER_ASSERT(r, !flags);
+ flags ^= mask;
+ REPORTER_ASSERT(r, flags == mask);
+ REPORTER_ASSERT(r, !(Flags::kA & Flags::kB));
+ REPORTER_ASSERT(r, (Flags::kA ^ Flags::kB) == (Flags::kA | Flags::kB));
+}