Skip to content

Commit 5ccd9d2

Browse files
committed
modm::Saturated: better support for T& and comparison operators
1 parent 78308b0 commit 5ccd9d2

File tree

3 files changed

+292
-132
lines changed

3 files changed

+292
-132
lines changed

src/modm/math/saturation/saturated.hpp

Lines changed: 173 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class Saturated
4242
using TP = std::remove_reference_t<T>;
4343
using TS = std::conditional_t<std::is_signed_v<T>, T, std::make_signed_t<modm::WideType<TP>>>;
4444

45-
T value = 0;
45+
T value{0};
4646
private:
4747
static constexpr TP min = std::numeric_limits<TP>::min();
4848
static constexpr TP max = std::numeric_limits<TP>::max();
@@ -55,36 +55,63 @@ class Saturated
5555

5656
template<typename U>
5757
requires std::integral<std::remove_reference_t<U>>
58-
constexpr Saturated(const U& v)
59-
{ value = std::clamp< modm::fits_any_t<TP, U> >(v, min, max); }
58+
constexpr Saturated(const U& value)
59+
{ this->value = std::clamp< modm::fits_any_t<TP, U> >(value, min, max); }
6060

6161
template<typename U>
6262
requires std::floating_point<std::remove_reference_t<U>>
63-
constexpr Saturated(const U& v)
64-
{ value = std::clamp<float>(v, min, max); }
63+
constexpr Saturated(const U& value)
64+
{ this->value = std::clamp<float>(value, min, max); }
6565

6666
template<typename U>
6767
requires std::integral<std::remove_reference_t<U>>
6868
constexpr Saturated(const Saturated<U>& other)
6969
{ value = std::clamp< modm::fits_any_t<TP, U> >(other.value, min, max); }
7070

71-
TP
72-
getValue() const
73-
{ return value; }
74-
75-
// Implicitely serve underlying type so you can f.e. pass Saturated to std::abs()
76-
operator T&() { return value; }
71+
// Implicit conversion to underlying type
72+
// @see: https://en.cppreference.com/w/cpp/language/implicit_conversion
7773
operator T() const { return value; }
7874

7975
// comparison operators
8076
constexpr auto
8177
operator<=>(const Saturated<T>&) const = default;
8278

79+
template <typename U>
80+
constexpr bool
81+
operator==(const U& other) const
82+
{ return value == other; }
83+
84+
template <typename U>
85+
constexpr bool
86+
operator>(const U& other) const
87+
{ return value > other; }
88+
89+
template <typename U>
90+
constexpr bool
91+
operator<(const U& other) const
92+
{ return value < other; }
93+
94+
template <typename U>
95+
constexpr bool
96+
operator>=(const U& other) const
97+
{ return value >= other; }
98+
99+
template <typename U>
100+
constexpr bool
101+
operator<=(const U& other) const
102+
{ return value <= other; }
103+
83104
// operator=
84105
void
85106
operator=(const Saturated& other)
86107
{ value = other.value; }
87108

109+
template<typename U>
110+
requires std::integral<std::remove_reference_t<U>>
111+
void
112+
operator=(const U& other)
113+
{ value = std::clamp< modm::fits_any_t<TP, U> >(other, min, max); }
114+
88115
template<typename U>
89116
requires std::integral<std::remove_reference_t<U>>
90117
void
@@ -124,6 +151,17 @@ class Saturated
124151
}
125152

126153
// operator+=, operator-=, operator*=
154+
template<typename U>
155+
requires std::unsigned_integral<std::remove_reference_t<U>>
156+
Saturated&
157+
operator+=(const U& other)
158+
{
159+
if (__builtin_add_overflow(value, other, &value))
160+
value = max;
161+
162+
return *this;
163+
}
164+
127165
template<typename U>
128166
requires std::unsigned_integral<std::remove_reference_t<U>>
129167
Saturated&
@@ -135,6 +173,22 @@ class Saturated
135173
return *this;
136174
}
137175

176+
template<typename U>
177+
requires std::signed_integral<std::remove_reference_t<U>>
178+
Saturated&
179+
operator+=(const U& other)
180+
{
181+
if (other < 0) {
182+
if (__builtin_sub_overflow(value, -other, &value))
183+
value = min;
184+
} else {
185+
if (__builtin_add_overflow(value, other, &value))
186+
value = max;
187+
}
188+
189+
return *this;
190+
}
191+
138192
template<typename U>
139193
requires std::signed_integral<std::remove_reference_t<U>>
140194
Saturated&
@@ -151,6 +205,17 @@ class Saturated
151205
return *this;
152206
}
153207

208+
template<typename U>
209+
requires std::unsigned_integral<std::remove_reference_t<U>>
210+
Saturated&
211+
operator-=(const U& other)
212+
{
213+
if (__builtin_sub_overflow(value, other, &value))
214+
value = min;
215+
216+
return *this;
217+
}
218+
154219
template<typename U>
155220
requires std::unsigned_integral<std::remove_reference_t<U>>
156221
Saturated&
@@ -162,6 +227,22 @@ class Saturated
162227
return *this;
163228
}
164229

230+
template<typename U>
231+
requires std::signed_integral<std::remove_reference_t<U>>
232+
Saturated&
233+
operator-=(const U& other)
234+
{
235+
if (other < 0) {
236+
if (__builtin_add_overflow(value, -other, &value))
237+
value = max;
238+
} else {
239+
if (__builtin_sub_overflow(value, other, &value))
240+
value = min;
241+
}
242+
243+
return *this;
244+
}
245+
165246
template<typename U>
166247
requires std::signed_integral<std::remove_reference_t<U>>
167248
Saturated&
@@ -181,62 +262,73 @@ class Saturated
181262
template<typename U>
182263
requires std::unsigned_integral<std::remove_reference_t<U>>
183264
Saturated&
184-
operator*=(const Saturated<U>& other)
265+
operator*=(const U& other)
185266
{
186-
if (__builtin_mul_overflow(value, other.value, &value))
267+
if (__builtin_mul_overflow(value, other, &value))
187268
value = max;
188269

189270
return *this;
190271
}
191272

192273
template<typename U>
193-
requires std::signed_integral<std::remove_reference_t<U>>
274+
requires std::unsigned_integral<std::remove_reference_t<U>>
194275
Saturated&
195276
operator*=(const Saturated<U>& other)
196277
{
197-
if (other.value < 0) {
198-
if (__builtin_mul_overflow(value, -other.value, &value))
199-
value = max;
200-
value = -value;
201-
} else {
202-
if (__builtin_mul_overflow(value, other.value, &value))
203-
value = max;
204-
}
278+
if (__builtin_mul_overflow(value, other.value, &value))
279+
value = max;
205280

206281
return *this;
207282
}
208283

209-
// OPTIMIZE By whatever reason, for operator*= the compiler doesn't implicitly construct Saturated types.
210-
// Overload plain types for now:
211284
template<typename U>
212-
requires std::unsigned_integral<std::remove_reference_t<U>>
285+
requires std::signed_integral<std::remove_reference_t<U>>
213286
Saturated&
214-
operator*=(const U& v)
287+
operator*=(const U& other)
215288
{
216-
if (__builtin_mul_overflow(value, v, &value))
217-
value = max;
289+
if (other < 0) {
290+
if (__builtin_mul_overflow(value, -other, &value))
291+
value = max;
292+
value = -value;
293+
} else {
294+
if (__builtin_mul_overflow(value, other, &value))
295+
value = max;
296+
}
218297

219298
return *this;
220299
}
221300

222301
template<typename U>
223302
requires std::signed_integral<std::remove_reference_t<U>>
224303
Saturated&
225-
operator*=(const U& v)
304+
operator*=(const Saturated<U>& other)
226305
{
227-
if (v < 0) {
228-
if (__builtin_mul_overflow(value, -v, &value))
306+
if (other.value < 0) {
307+
if (__builtin_mul_overflow(value, -other.value, &value))
229308
value = max;
230309
value = -value;
231310
} else {
232-
if (__builtin_mul_overflow(value, v, &value))
311+
if (__builtin_mul_overflow(value, other.value, &value))
233312
value = max;
234313
}
235314

236315
return *this;
237316
}
238317

239318
// operator+, operator-, operator*
319+
template<typename U>
320+
requires std::unsigned_integral<std::remove_reference_t<U>>
321+
TP
322+
operator+(const U& other)
323+
{
324+
Saturated<TP> tmp;
325+
326+
if (__builtin_add_overflow(value, other, &tmp.value))
327+
tmp.value = max;
328+
329+
return tmp.value;
330+
}
331+
240332
template<typename U>
241333
requires std::unsigned_integral<std::remove_reference_t<U>>
242334
TP
@@ -250,6 +342,24 @@ class Saturated
250342
return tmp.value;
251343
}
252344

345+
template<typename U>
346+
requires std::signed_integral<std::remove_reference_t<U>>
347+
TP
348+
operator+(const U& other)
349+
{
350+
Saturated<TP> tmp;
351+
352+
if (other < 0) {
353+
if (__builtin_sub_overflow(value, -other, &tmp.value))
354+
tmp.value = min;
355+
} else {
356+
if (__builtin_add_overflow(value, other, &tmp.value))
357+
tmp.value = max;
358+
}
359+
360+
return tmp.value;
361+
}
362+
253363
template<typename U>
254364
requires std::signed_integral<std::remove_reference_t<U>>
255365
TP
@@ -268,6 +378,19 @@ class Saturated
268378
return tmp.value;
269379
}
270380

381+
template<typename U>
382+
requires std::unsigned_integral<std::remove_reference_t<U>>
383+
TP
384+
operator-(const U& other)
385+
{
386+
Saturated<TP> tmp;
387+
388+
if (__builtin_sub_overflow(value, other, &tmp.value))
389+
tmp.value = min;
390+
391+
return tmp.value;
392+
}
393+
271394
template<typename U>
272395
requires std::unsigned_integral<std::remove_reference_t<U>>
273396
TP
@@ -281,6 +404,24 @@ class Saturated
281404
return tmp.value;
282405
}
283406

407+
template<typename U>
408+
requires std::signed_integral<std::remove_reference_t<U>>
409+
TP
410+
operator-(const U& other)
411+
{
412+
Saturated<TP> tmp;
413+
414+
if (other < 0) {
415+
if (__builtin_add_overflow(value, -other, &tmp.value))
416+
tmp.value = max;
417+
} else {
418+
if (__builtin_sub_overflow(value, other, &tmp.value))
419+
tmp.value = min;
420+
}
421+
422+
return tmp.value;
423+
}
424+
284425
template<typename U>
285426
requires std::signed_integral<std::remove_reference_t<U>>
286427
TP

0 commit comments

Comments
 (0)