ソースを参照

AK: Add optional explicit cast to underlying type to DistinctNumeric

Sam Atkins 2 年 前
コミット
cf046dbfdb
2 ファイル変更31 行追加1 行削除
  1. 11 0
      AK/DistinctNumeric.h
  2. 20 1
      Tests/AK/TestDistinctNumeric.cpp

+ 11 - 0
AK/DistinctNumeric.h

@@ -51,6 +51,7 @@ namespace AK {
 namespace DistinctNumericFeature {
 namespace DistinctNumericFeature {
 enum Arithmetic {};
 enum Arithmetic {};
 enum CastToBool {};
 enum CastToBool {};
+enum CastToUnderlying {};
 enum Comparison {};
 enum Comparison {};
 enum Flags {};
 enum Flags {};
 enum Increment {};
 enum Increment {};
@@ -73,6 +74,7 @@ class DistinctNumeric {
 
 
         constexpr void set(DistinctNumericFeature::Arithmetic const&) { arithmetic = true; }
         constexpr void set(DistinctNumericFeature::Arithmetic const&) { arithmetic = true; }
         constexpr void set(DistinctNumericFeature::CastToBool const&) { cast_to_bool = true; }
         constexpr void set(DistinctNumericFeature::CastToBool const&) { cast_to_bool = true; }
+        constexpr void set(DistinctNumericFeature::CastToUnderlying const&) { cast_to_underlying = true; }
         constexpr void set(DistinctNumericFeature::Comparison const&) { comparisons = true; }
         constexpr void set(DistinctNumericFeature::Comparison const&) { comparisons = true; }
         constexpr void set(DistinctNumericFeature::Flags const&) { flags = true; }
         constexpr void set(DistinctNumericFeature::Flags const&) { flags = true; }
         constexpr void set(DistinctNumericFeature::Increment const&) { increment = true; }
         constexpr void set(DistinctNumericFeature::Increment const&) { increment = true; }
@@ -80,6 +82,7 @@ class DistinctNumeric {
 
 
         bool arithmetic { false };
         bool arithmetic { false };
         bool cast_to_bool { false };
         bool cast_to_bool { false };
+        bool cast_to_underlying { false };
         bool comparisons { false };
         bool comparisons { false };
         bool flags { false };
         bool flags { false };
         bool increment { false };
         bool increment { false };
@@ -105,6 +108,13 @@ public:
         return this->m_value == other.m_value;
         return this->m_value == other.m_value;
     }
     }
 
 
+    // Only implemented when `CastToUnderlying` is true:
+    constexpr explicit operator T() const
+    {
+        static_assert(options.cast_to_underlying, "Cast to underlying type is only available for DistinctNumeric types with 'CastToUnderlying'.");
+        return value();
+    }
+
     // Only implemented when `Increment` is true:
     // Only implemented when `Increment` is true:
     constexpr Self& operator++()
     constexpr Self& operator++()
     {
     {
@@ -316,6 +326,7 @@ struct Formatter<DistinctNumeric<T, X, Opts...>> : Formatter<T> {
     struct NAME##_decl {                                                                        \
     struct NAME##_decl {                                                                        \
         using Arithmetic [[maybe_unused]] = AK::DistinctNumericFeature::Arithmetic;             \
         using Arithmetic [[maybe_unused]] = AK::DistinctNumericFeature::Arithmetic;             \
         using CastToBool [[maybe_unused]] = AK::DistinctNumericFeature::CastToBool;             \
         using CastToBool [[maybe_unused]] = AK::DistinctNumericFeature::CastToBool;             \
+        using CastToUnderlying [[maybe_unused]] = AK::DistinctNumericFeature::CastToUnderlying; \
         using Comparison [[maybe_unused]] = AK::DistinctNumericFeature::Comparison;             \
         using Comparison [[maybe_unused]] = AK::DistinctNumericFeature::Comparison;             \
         using Flags [[maybe_unused]] = AK::DistinctNumericFeature::Flags;                       \
         using Flags [[maybe_unused]] = AK::DistinctNumericFeature::Flags;                       \
         using Increment [[maybe_unused]] = AK::DistinctNumericFeature::Increment;               \
         using Increment [[maybe_unused]] = AK::DistinctNumericFeature::Increment;               \

+ 20 - 1
Tests/AK/TestDistinctNumeric.cpp

@@ -41,7 +41,8 @@ AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, BoolNumeric, CastToBool);
 AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, FlagsNumeric, Flags);
 AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, FlagsNumeric, Flags);
 AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, ShiftNumeric, Shift);
 AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, ShiftNumeric, Shift);
 AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, ArithNumeric, Arithmetic);
 AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, ArithNumeric, Arithmetic);
-AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, GeneralNumeric, Arithmetic, CastToBool, Comparison, Flags, Increment, Shift);
+AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, UnderlyingNumeric, CastToUnderlying);
+AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, GeneralNumeric, Arithmetic, CastToBool, CastToUnderlying, Comparison, Flags, Increment, Shift);
 
 
 TEST_CASE(address_identity)
 TEST_CASE(address_identity)
 {
 {
@@ -105,6 +106,14 @@ TEST_CASE(operator_bool)
     EXPECT_EQ(!c, false);
     EXPECT_EQ(!c, false);
 }
 }
 
 
+TEST_CASE(operator_underlying)
+{
+    UnderlyingNumeric a = 0;
+    UnderlyingNumeric b = 42;
+    EXPECT_EQ(static_cast<int>(a), 0);
+    EXPECT_EQ(static_cast<int>(b), 42);
+}
+
 TEST_CASE(operator_flags)
 TEST_CASE(operator_flags)
 {
 {
     FlagsNumeric a = 0;
     FlagsNumeric a = 0;
@@ -216,6 +225,9 @@ TEST_CASE(composability)
     EXPECT_EQ(-b, GeneralNumeric(-1));
     EXPECT_EQ(-b, GeneralNumeric(-1));
     EXPECT_EQ(a + b, b);
     EXPECT_EQ(a + b, b);
     EXPECT_EQ(b * GeneralNumeric(42), GeneralNumeric(42));
     EXPECT_EQ(b * GeneralNumeric(42), GeneralNumeric(42));
+    // Underlying
+    EXPECT_EQ(static_cast<int>(a), 0);
+    EXPECT_EQ(static_cast<int>(b), 1);
 }
 }
 
 
 /*
 /*
@@ -268,6 +280,13 @@ TEST_CASE(negative_arith)
     // error: static assertion failed: 'a+b' is only available for DistinctNumeric types with 'Arithmetic'.
     // error: static assertion failed: 'a+b' is only available for DistinctNumeric types with 'Arithmetic'.
 }
 }
 
 
+TEST_CASE(negative_underlying)
+{
+    BareNumeric a = 12;
+    [[maybe_unused]] int res = static_cast<int>(a);
+    // error: static assertion failed: Cast to underlying type is only available for DistinctNumeric types with 'CastToUnderlying'.
+}
+
 TEST_CASE(negative_incompatible)
 TEST_CASE(negative_incompatible)
 {
 {
     GeneralNumeric a = 12;
     GeneralNumeric a = 12;