Serenity Operating System
at master 333 lines 14 kB view raw
1/* 2 * Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de> 3 * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> 4 * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org> 5 * 6 * SPDX-License-Identifier: BSD-2-Clause 7 */ 8 9#pragma once 10 11#include <AK/Format.h> 12#include <AK/Traits.h> 13#include <AK/Types.h> 14 15namespace AK { 16 17/** 18 * This implements a "distinct" numeric type that is intentionally incompatible 19 * to other incantations. The intention is that each "distinct" type that you 20 * want simply gets different values for `fn_length` and `line`. The macros 21 * `TYPEDEF_DISTINCT_NUMERIC_*()` at the bottom of `DistinctNumeric.h`. 22 * 23 * The tags in `DistinctNumericFeature` simply split up the space of operators into 6 simple categories: 24 * - No matter the values of these, `DistinctNumeric` always implements `==` and `!=`. 25 * - If `Arithmetic` is present, then `a+b`, `a-b`, `+a`, `-a`, `a*b`, `a/b`, `a%b`, and the respective `a_=b` versions are implemented. 26 * - If `CastToBool` is present, then `!a`, `a&&b`, and `a||b` are implemented (but not `operator bool()`, because of overzealous integer promotion rules). 27 * - If `Comparison` is present, then `a>b`, `a<b`, `a>=b`, and `a<=b` are implemented via operator<=> 28 * - If `Flags` is present, then `~a`, `a&b`, `a|b`, `a^b`, `a&=b`, `a|=b`, and `a^=b` are implemented. 29 * - If `Increment` is present, then `++a`, `a++`, `--a`, and `a--` are implemented. 30 * - If `Shift` is present, then `a<<b`, `a>>b`, `a<<=b`, `a>>=b` are implemented. 31 * The semantics are always those of the underlying basic type `T`. 32 * 33 * These can be combined arbitrarily. Want a numeric type that supports `++a` 34 * and `a >> b` but not `a > b`? Sure thing, just set 35 * `Increment, Comparison, Shift` and you're done! 36 * Furthermore, some of these overloads make more sense with specific types, like `a&&b` which should be able to operate 37 * 38 * I intentionally decided against overloading `&a` because these shall remain 39 * numeric types. 40 * 41 * The are many operators that do not work on `int`, so I left them out: 42 * `a[b]`, `*a`, `a->b`, `a.b`, `a->*b`, `a.*b`. 43 * 44 * There are many more operators that do not make sense for numerical types, 45 * or cannot be overloaded in the first place. Naturally, they are not implemented. 46 */ 47 48namespace DistinctNumericFeature { 49enum Arithmetic {}; 50enum CastToBool {}; 51enum CastToUnderlying {}; 52enum Comparison {}; 53enum Flags {}; 54enum Increment {}; 55enum Shift {}; 56}; 57 58template<typename T, typename X, typename... Opts> 59class DistinctNumeric { 60 using Self = DistinctNumeric<T, X, Opts...>; 61 62 struct Option { 63 template<typename K, typename... Os> 64 consteval Option(K option, Os... other_options) 65 : Option(other_options...) 66 { 67 set(option); 68 } 69 70 consteval Option() { } 71 72 constexpr void set(DistinctNumericFeature::Arithmetic const&) { arithmetic = true; } 73 constexpr void set(DistinctNumericFeature::CastToBool const&) { cast_to_bool = true; } 74 constexpr void set(DistinctNumericFeature::CastToUnderlying const&) { cast_to_underlying = true; } 75 constexpr void set(DistinctNumericFeature::Comparison const&) { comparisons = true; } 76 constexpr void set(DistinctNumericFeature::Flags const&) { flags = true; } 77 constexpr void set(DistinctNumericFeature::Increment const&) { increment = true; } 78 constexpr void set(DistinctNumericFeature::Shift const&) { shift = true; } 79 80 bool arithmetic { false }; 81 bool cast_to_bool { false }; 82 bool cast_to_underlying { false }; 83 bool comparisons { false }; 84 bool flags { false }; 85 bool increment { false }; 86 bool shift { false }; 87 }; 88 89 constexpr static Option options { Opts()... }; 90 91public: 92 using Type = T; 93 94 constexpr DistinctNumeric() = default; 95 96 constexpr DistinctNumeric(T value) 97 : m_value { value } 98 { 99 } 100 101 constexpr T const& value() const { return m_value; } 102 constexpr T& value() { return m_value; } 103 104 // Always implemented: identity. 105 constexpr bool operator==(Self const& other) const 106 { 107 return this->m_value == other.m_value; 108 } 109 110 // Only implemented when `CastToUnderlying` is true: 111 constexpr explicit operator T() const 112 { 113 static_assert(options.cast_to_underlying, "Cast to underlying type is only available for DistinctNumeric types with 'CastToUnderlying'."); 114 return value(); 115 } 116 117 // Only implemented when `Increment` is true: 118 constexpr Self& operator++() 119 { 120 static_assert(options.increment, "'++a' is only available for DistinctNumeric types with 'Increment'."); 121 this->m_value += 1; 122 return *this; 123 } 124 constexpr Self operator++(int) 125 { 126 static_assert(options.increment, "'a++' is only available for DistinctNumeric types with 'Increment'."); 127 Self ret = this->m_value; 128 this->m_value += 1; 129 return ret; 130 } 131 constexpr Self& operator--() 132 { 133 static_assert(options.increment, "'--a' is only available for DistinctNumeric types with 'Increment'."); 134 this->m_value -= 1; 135 return *this; 136 } 137 constexpr Self operator--(int) 138 { 139 static_assert(options.increment, "'a--' is only available for DistinctNumeric types with 'Increment'."); 140 Self ret = this->m_value; 141 this->m_value -= 1; 142 return ret; 143 } 144 145 // Only implemented when `Comparison` is true: 146 constexpr int operator<=>(Self const& other) const 147 { 148 static_assert(options.comparisons, "'a<=>b' is only available for DistinctNumeric types with 'Comparison'."); 149 return this->m_value > other.m_value ? 1 : this->m_value < other.m_value ? -1 150 : 0; 151 } 152 153 // Only implemented when `CastToBool` is true: 154 constexpr bool operator!() const 155 { 156 static_assert(options.cast_to_bool, "'!a' is only available for DistinctNumeric types with 'CastToBool'."); 157 return !this->m_value; 158 } 159 // Intentionally don't define `operator bool() const` here. C++ is a bit 160 // overzealous, and whenever there would be a type error, C++ instead tries 161 // to convert to a common int-ish type first. `bool` is int-ish, so 162 // `operator bool() const` would defy the entire point of this class. 163 164 // Only implemented when `Flags` is true: 165 constexpr Self operator~() const 166 { 167 static_assert(options.flags, "'~a' is only available for DistinctNumeric types with 'Flags'."); 168 return ~this->m_value; 169 } 170 constexpr Self operator&(Self const& other) const 171 { 172 static_assert(options.flags, "'a&b' is only available for DistinctNumeric types with 'Flags'."); 173 return this->m_value & other.m_value; 174 } 175 constexpr Self operator|(Self const& other) const 176 { 177 static_assert(options.flags, "'a|b' is only available for DistinctNumeric types with 'Flags'."); 178 return this->m_value | other.m_value; 179 } 180 constexpr Self operator^(Self const& other) const 181 { 182 static_assert(options.flags, "'a^b' is only available for DistinctNumeric types with 'Flags'."); 183 return this->m_value ^ other.m_value; 184 } 185 constexpr Self& operator&=(Self const& other) 186 { 187 static_assert(options.flags, "'a&=b' is only available for DistinctNumeric types with 'Flags'."); 188 this->m_value &= other.m_value; 189 return *this; 190 } 191 constexpr Self& operator|=(Self const& other) 192 { 193 static_assert(options.flags, "'a|=b' is only available for DistinctNumeric types with 'Flags'."); 194 this->m_value |= other.m_value; 195 return *this; 196 } 197 constexpr Self& operator^=(Self const& other) 198 { 199 static_assert(options.flags, "'a^=b' is only available for DistinctNumeric types with 'Flags'."); 200 this->m_value ^= other.m_value; 201 return *this; 202 } 203 204 // Only implemented when `Shift` is true: 205 // TODO: Should this take `int` instead? 206 constexpr Self operator<<(Self const& other) const 207 { 208 static_assert(options.shift, "'a<<b' is only available for DistinctNumeric types with 'Shift'."); 209 return this->m_value << other.m_value; 210 } 211 constexpr Self operator>>(Self const& other) const 212 { 213 static_assert(options.shift, "'a>>b' is only available for DistinctNumeric types with 'Shift'."); 214 return this->m_value >> other.m_value; 215 } 216 constexpr Self& operator<<=(Self const& other) 217 { 218 static_assert(options.shift, "'a<<=b' is only available for DistinctNumeric types with 'Shift'."); 219 this->m_value <<= other.m_value; 220 return *this; 221 } 222 constexpr Self& operator>>=(Self const& other) 223 { 224 static_assert(options.shift, "'a>>=b' is only available for DistinctNumeric types with 'Shift'."); 225 this->m_value >>= other.m_value; 226 return *this; 227 } 228 229 // Only implemented when `Arithmetic` is true: 230 constexpr Self operator+(Self const& other) const 231 { 232 static_assert(options.arithmetic, "'a+b' is only available for DistinctNumeric types with 'Arithmetic'."); 233 return this->m_value + other.m_value; 234 } 235 constexpr Self operator-(Self const& other) const 236 { 237 static_assert(options.arithmetic, "'a-b' is only available for DistinctNumeric types with 'Arithmetic'."); 238 return this->m_value - other.m_value; 239 } 240 constexpr Self operator+() const 241 { 242 static_assert(options.arithmetic, "'+a' is only available for DistinctNumeric types with 'Arithmetic'."); 243 return +this->m_value; 244 } 245 constexpr Self operator-() const 246 { 247 static_assert(options.arithmetic, "'-a' is only available for DistinctNumeric types with 'Arithmetic'."); 248 return -this->m_value; 249 } 250 constexpr Self operator*(Self const& other) const 251 { 252 static_assert(options.arithmetic, "'a*b' is only available for DistinctNumeric types with 'Arithmetic'."); 253 return this->m_value * other.m_value; 254 } 255 constexpr Self operator/(Self const& other) const 256 { 257 static_assert(options.arithmetic, "'a/b' is only available for DistinctNumeric types with 'Arithmetic'."); 258 return this->m_value / other.m_value; 259 } 260 constexpr Self operator%(Self const& other) const 261 { 262 static_assert(options.arithmetic, "'a%b' is only available for DistinctNumeric types with 'Arithmetic'."); 263 return this->m_value % other.m_value; 264 } 265 constexpr Self& operator+=(Self const& other) 266 { 267 static_assert(options.arithmetic, "'a+=b' is only available for DistinctNumeric types with 'Arithmetic'."); 268 this->m_value += other.m_value; 269 return *this; 270 } 271 constexpr Self& operator-=(Self const& other) 272 { 273 static_assert(options.arithmetic, "'a-=b' is only available for DistinctNumeric types with 'Arithmetic'."); 274 this->m_value -= other.m_value; 275 return *this; 276 } 277 constexpr Self& operator*=(Self const& other) 278 { 279 static_assert(options.arithmetic, "'a*=b' is only available for DistinctNumeric types with 'Arithmetic'."); 280 this->m_value *= other.m_value; 281 return *this; 282 } 283 constexpr Self& operator/=(Self const& other) 284 { 285 static_assert(options.arithmetic, "'a/=b' is only available for DistinctNumeric types with 'Arithmetic'."); 286 this->m_value /= other.m_value; 287 return *this; 288 } 289 constexpr Self& operator%=(Self const& other) 290 { 291 static_assert(options.arithmetic, "'a%=b' is only available for DistinctNumeric types with 'Arithmetic'."); 292 this->m_value %= other.m_value; 293 return *this; 294 } 295 296private: 297 T m_value {}; 298}; 299 300template<typename T, typename X, typename... Opts> 301struct Formatter<DistinctNumeric<T, X, Opts...>> : Formatter<T> { 302 ErrorOr<void> format(FormatBuilder& builder, DistinctNumeric<T, X, Opts...> value) 303 { 304 return Formatter<T>::format(builder, value.value()); 305 } 306}; 307} 308 309#define AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(T, NAME, ...) \ 310 struct NAME##_decl { \ 311 using Arithmetic [[maybe_unused]] = AK::DistinctNumericFeature::Arithmetic; \ 312 using CastToBool [[maybe_unused]] = AK::DistinctNumericFeature::CastToBool; \ 313 using CastToUnderlying [[maybe_unused]] = AK::DistinctNumericFeature::CastToUnderlying; \ 314 using Comparison [[maybe_unused]] = AK::DistinctNumericFeature::Comparison; \ 315 using Flags [[maybe_unused]] = AK::DistinctNumericFeature::Flags; \ 316 using Increment [[maybe_unused]] = AK::DistinctNumericFeature::Increment; \ 317 using Shift [[maybe_unused]] = AK::DistinctNumericFeature::Shift; \ 318 using NAME [[maybe_unused]] = DistinctNumeric<T, struct __##NAME##_tag, ##__VA_ARGS__>; \ 319 }; \ 320 using NAME = typename NAME##_decl::NAME; 321 322#define AK_TYPEDEF_DISTINCT_ORDERED_ID(T, NAME) AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(T, NAME, Comparison, CastToBool) 323// TODO: Further type aliases? 324 325template<typename T, typename X, typename... Opts> 326struct Traits<AK::DistinctNumeric<T, X, Opts...>> : public GenericTraits<AK::DistinctNumeric<T, X, Opts...>> { 327 static constexpr bool is_trivial() { return true; } 328 static constexpr auto hash(DistinctNumeric<T, X, Opts...> const& d) { return Traits<T>::hash(d.value()); } 329}; 330 331#if USING_AK_GLOBALLY 332using AK::DistinctNumeric; 333#endif