Serenity Operating System
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