HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
safe_int.hpp
1// Copyright Take Vos 2019, 2021-2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "int_overflow.hpp"
8#include "../utility/utility.hpp"
9#include "../macros.hpp"
10#include <system_error>
11#include <type_traits>
12#include <limits>
13#include <concepts>
14
15
16
17namespace hi::inline v1 {
18
19enum class on_overflow_t {
21 Throw,
25 Assert,
27 Axiom,
28};
29
33constexpr on_overflow_t operator|(on_overflow_t lhs, on_overflow_t rhs) noexcept
34{
35 if (lhs == on_overflow_t::Throw || rhs == on_overflow_t::Throw) {
36 return on_overflow_t::Throw;
37 } else if (lhs == on_overflow_t::Saturate || rhs == on_overflow_t::Saturate) {
38 return on_overflow_t::Saturate;
39 } else if (lhs == on_overflow_t::Assert || rhs == on_overflow_t::Assert) {
40 return on_overflow_t::Assert;
41 } else {
42 return on_overflow_t::Axiom;
43 }
44}
45
51template<typename T, on_overflow_t OnOverflow>
52T safe_handle_overflow(T value, bool overflow, bool is_positive) noexcept(OnOverflow != on_overflow_t::Throw)
53{
54 if constexpr (OnOverflow == on_overflow_t::Throw) {
55 if (overflow) {
56 throw std::overflow_error("integer overflow");
57 }
58 } else if constexpr (OnOverflow == on_overflow_t::Assert) {
59 hi_assert(!overflow);
60 } else if constexpr (OnOverflow == on_overflow_t::Axiom) {
61 hi_axiom(!overflow);
62 } else if constexpr (OnOverflow == on_overflow_t::Saturate) {
63 if (overflow) {
65 }
66 }
67 return value;
68}
69
70template<typename T, on_overflow_t OnOverflow, typename U>
71T safe_convert(U const &rhs) noexcept(OnOverflow != on_overflow_t::Throw)
72{
73 T r;
74 // Optimized away when is_same_v<T,U>
75 hilet overflow = convert_overflow(rhs, &r);
76 return safe_handle_overflow<T, OnOverflow>(r, overflow, rhs >= 0);
77}
78
79template<on_overflow_t OnOverflow, typename T, typename U>
80make_promote_t<T, U> safe_add(T const &lhs, U const &rhs) noexcept(OnOverflow != on_overflow_t::Throw)
81{
83 hilet lhs_ = static_cast<make_promote_t<T, U>>(lhs);
84 hilet rhs_ = static_cast<make_promote_t<T, U>>(rhs);
85
86 hilet overflow = add_overflow(lhs_, rhs_, &r);
87 return safe_handle_overflow<T, OnOverflow>(r, overflow, rhs_ >= 0);
88}
89
90template<on_overflow_t OnOverflow, typename T, typename U>
91make_promote_t<T, U> safe_sub(T const &lhs, U const &rhs) noexcept(OnOverflow != on_overflow_t::Throw)
92{
94 hilet lhs_ = static_cast<make_promote_t<T, U>>(lhs);
95 hilet rhs_ = static_cast<make_promote_t<T, U>>(rhs);
96
97 hilet overflow = sub_overflow(lhs_, rhs_, &r);
98 return safe_handle_overflow<T, OnOverflow>(r, overflow, rhs_ >= 0);
99}
100
101template<on_overflow_t OnOverflow, typename T, typename U>
102make_promote_t<T, U> safe_mul(T const &lhs, U const &rhs) noexcept(OnOverflow != on_overflow_t::Throw)
103{
105 hilet lhs_ = static_cast<make_promote_t<T, U>>(lhs);
106 hilet rhs_ = static_cast<make_promote_t<T, U>>(rhs);
107
108 hilet overflow = mul_overflow(lhs_, rhs_, &r);
109 return safe_handle_overflow<T, OnOverflow>(r, overflow, rhs_ >= 0);
110}
111
112template<typename T, on_overflow_t OnOverflow = on_overflow_t::Assert>
113struct safe_int {
114 using value_type = T;
115 constexpr static on_overflow_t on_overflow = OnOverflow;
116
117 T value;
118
119 safe_int() : value(0) {}
120 ~safe_int() = default;
121 safe_int(safe_int const &) = default;
122 safe_int(safe_int &&) = default;
123 safe_int &operator=(safe_int const &) = default;
124 safe_int &operator=(safe_int &&) = default;
125
126 explicit safe_int(std::integral auto const &other) noexcept(OnOverflow != on_overflow_t::Throw) :
127 value(safe_convert<T, OnOverflow>(other))
128 {
129 }
130
131 explicit safe_int(std::floating_point auto const &other) noexcept(OnOverflow != on_overflow_t::Throw) :
132 value(safe_convert<T, OnOverflow>(other))
133 {
134 }
135
136 template<typename O, on_overflow_t OtherOnOverflow>
137 explicit safe_int(safe_int<O, OtherOnOverflow> const &other) noexcept(OnOverflow != on_overflow_t::Throw) :
138 value(safe_convert<T, OnOverflow>(other.value))
139 {
140 }
141
142 safe_int &operator=(std::integral auto const &other) noexcept(OnOverflow != on_overflow_t::Throw)
143 {
144 value = safe_convert<T, OnOverflow>(other);
145 return *this;
146 }
147
148 template<typename O, on_overflow_t OtherOnOverflow>
149 safe_int &operator=(safe_int<O, OtherOnOverflow> const &other) noexcept(OnOverflow != on_overflow_t::Throw)
150 {
151 value = safe_convert<T, OnOverflow>(other.value);
152 return *this;
153 }
154
155 template<std::integral O>
156 explicit operator O() const noexcept(OnOverflow != on_overflow_t::Throw)
157 {
158 return safe_convert<O, OnOverflow>(value);
159 }
160
161 template<std::floating_point O>
162 explicit operator O() const noexcept
163 {
164 return static_cast<O>(value);
165 }
166};
167
168#define TEMPLATE(op) \
169 template<typename T, on_overflow_t TO, typename U, on_overflow_t UO> \
170 bool operator op(safe_int<T, TO> const &lhs, safe_int<U, UO> const &rhs) noexcept \
171 { \
172 return lhs.value op rhs.value; \
173 } \
174\
175 template<typename T, on_overflow_t TO, typename U> \
176 bool operator op(safe_int<T, TO> const &lhs, U const &rhs) noexcept \
177 { \
178 return lhs.value op rhs; \
179 } \
180 template<typename T, typename U, on_overflow_t UO> \
181 bool operator op(T const &lhs, safe_int<U, UO> const &rhs) noexcept \
182 { \
183 return lhs op rhs.value; \
184 }
185
186TEMPLATE(==)
187TEMPLATE(!=)
188TEMPLATE(<)
189TEMPLATE(>)
190TEMPLATE(<=)
191TEMPLATE(>=)
192#undef TEMPLATE
193
194#define TEMPLATE(op, func) \
195 template<typename T, on_overflow_t TO, typename U, on_overflow_t UO> \
196 safe_int<make_promote_t<T, U>, TO | UO> operator op(safe_int<T, TO> const &lhs, safe_int<U, UO> const &rhs) noexcept( \
197 (TO | UO) != on_overflow_t::Throw) \
198 { \
199 return safe_int<make_promote_t<T, U>, TO | UO>{func<TO | UO>(lhs.value, rhs.value)}; \
200 } \
201\
202 template<typename T, on_overflow_t TO, typename U> \
203 safe_int<make_promote_t<T, U>, TO> operator op(safe_int<T, TO> const &lhs, U const &rhs) noexcept( \
204 TO != on_overflow_t::Throw) \
205 { \
206 return safe_int<make_promote_t<T, U>, TO>{func<TO>(lhs.value, rhs)}; \
207 } \
208\
209 template<typename T, typename U, on_overflow_t UO> \
210 safe_int<make_promote_t<T, U>, UO> operator op(T const &lhs, safe_int<U, UO> const &rhs) noexcept( \
211 UO != on_overflow_t::Throw) \
212 { \
213 return safe_int<make_promote_t<T, U>, UO>{func<UO>(lhs, rhs.value)}; \
214 }
215
216TEMPLATE(+, safe_add)
217TEMPLATE(-, safe_sub)
218TEMPLATE(*, safe_mul)
219#undef TEMPLATE
220
229
238
247
256
257} // namespace hi::inline v1
258
259template<>
260class std::numeric_limits<hi::safe_int<signed long long, hi::on_overflow_t::Saturate>> :
261 public std::numeric_limits<signed long long> {
262};
263template<>
264class std::numeric_limits<hi::safe_int<signed long, hi::on_overflow_t::Saturate>> : public std::numeric_limits<signed long> {
265};
266template<>
267class std::numeric_limits<hi::safe_int<signed int, hi::on_overflow_t::Saturate>> : public std::numeric_limits<signed int> {
268};
269template<>
270class std::numeric_limits<hi::safe_int<signed short, hi::on_overflow_t::Saturate>> : public std::numeric_limits<signed short> {
271};
272template<>
273class std::numeric_limits<hi::safe_int<signed char, hi::on_overflow_t::Saturate>> : public std::numeric_limits<signed char> {
274};
275template<>
276class std::numeric_limits<hi::safe_int<unsigned long long, hi::on_overflow_t::Saturate>> :
277 public std::numeric_limits<unsigned long long> {
278};
279template<>
280class std::numeric_limits<hi::safe_int<unsigned long, hi::on_overflow_t::Saturate>> : public std::numeric_limits<unsigned long> {
281};
282template<>
283class std::numeric_limits<hi::safe_int<unsigned int, hi::on_overflow_t::Saturate>> : public std::numeric_limits<unsigned int> {
284};
285template<>
286class std::numeric_limits<hi::safe_int<unsigned short, hi::on_overflow_t::Saturate>> :
287 public std::numeric_limits<unsigned short> {
288};
289template<>
290class std::numeric_limits<hi::safe_int<unsigned char, hi::on_overflow_t::Saturate>> : public std::numeric_limits<unsigned char> {
291};
292
293template<>
294class std::numeric_limits<hi::safe_int<signed long long, hi::on_overflow_t::Assert>> :
295 public std::numeric_limits<signed long long> {
296};
297template<>
298class std::numeric_limits<hi::safe_int<signed long, hi::on_overflow_t::Assert>> : public std::numeric_limits<signed long> {
299};
300template<>
301class std::numeric_limits<hi::safe_int<signed int, hi::on_overflow_t::Assert>> : public std::numeric_limits<signed int> {
302};
303template<>
304class std::numeric_limits<hi::safe_int<signed short, hi::on_overflow_t::Assert>> : public std::numeric_limits<signed short> {
305};
306template<>
307class std::numeric_limits<hi::safe_int<signed char, hi::on_overflow_t::Assert>> : public std::numeric_limits<signed char> {
308};
309template<>
310class std::numeric_limits<hi::safe_int<unsigned long long, hi::on_overflow_t::Assert>> :
311 public std::numeric_limits<unsigned long long> {
312};
313template<>
314class std::numeric_limits<hi::safe_int<unsigned long, hi::on_overflow_t::Assert>> : public std::numeric_limits<unsigned long> {
315};
316template<>
317class std::numeric_limits<hi::safe_int<unsigned int, hi::on_overflow_t::Assert>> : public std::numeric_limits<unsigned int> {
318};
319template<>
320class std::numeric_limits<hi::safe_int<unsigned short, hi::on_overflow_t::Assert>> : public std::numeric_limits<unsigned short> {
321};
322template<>
323class std::numeric_limits<hi::safe_int<unsigned char, hi::on_overflow_t::Assert>> : public std::numeric_limits<unsigned char> {
324};
325
326template<>
327class std::numeric_limits<hi::safe_int<signed long long, hi::on_overflow_t::Throw>> :
328 public std::numeric_limits<signed long long> {
329};
330template<>
331class std::numeric_limits<hi::safe_int<signed long, hi::on_overflow_t::Throw>> : public std::numeric_limits<signed long> {
332};
333template<>
334class std::numeric_limits<hi::safe_int<signed int, hi::on_overflow_t::Throw>> : public std::numeric_limits<signed int> {
335};
336template<>
337class std::numeric_limits<hi::safe_int<signed short, hi::on_overflow_t::Throw>> : public std::numeric_limits<signed short> {
338};
339template<>
340class std::numeric_limits<hi::safe_int<signed char, hi::on_overflow_t::Throw>> : public std::numeric_limits<signed char> {
341};
342template<>
343class std::numeric_limits<hi::safe_int<unsigned long long, hi::on_overflow_t::Throw>> :
344 public std::numeric_limits<unsigned long long> {
345};
346template<>
347class std::numeric_limits<hi::safe_int<unsigned long, hi::on_overflow_t::Throw>> : public std::numeric_limits<unsigned long> {
348};
349template<>
350class std::numeric_limits<hi::safe_int<unsigned int, hi::on_overflow_t::Throw>> : public std::numeric_limits<unsigned int> {
351};
352template<>
353class std::numeric_limits<hi::safe_int<unsigned short, hi::on_overflow_t::Throw>> : public std::numeric_limits<unsigned short> {
354};
355template<>
356class std::numeric_limits<hi::safe_int<unsigned char, hi::on_overflow_t::Throw>> : public std::numeric_limits<unsigned char> {
357};
358
359template<>
360class std::numeric_limits<hi::safe_int<signed long long, hi::on_overflow_t::Axiom>> :
361 public std::numeric_limits<signed long long> {
362};
363template<>
364class std::numeric_limits<hi::safe_int<signed long, hi::on_overflow_t::Axiom>> : public std::numeric_limits<signed long> {
365};
366template<>
367class std::numeric_limits<hi::safe_int<signed int, hi::on_overflow_t::Axiom>> : public std::numeric_limits<signed int> {
368};
369template<>
370class std::numeric_limits<hi::safe_int<signed short, hi::on_overflow_t::Axiom>> : public std::numeric_limits<signed short> {
371};
372template<>
373class std::numeric_limits<hi::safe_int<signed char, hi::on_overflow_t::Axiom>> : public std::numeric_limits<signed char> {
374};
375template<>
376class std::numeric_limits<hi::safe_int<unsigned long long, hi::on_overflow_t::Axiom>> :
377 public std::numeric_limits<unsigned long long> {
378};
379template<>
380class std::numeric_limits<hi::safe_int<unsigned long, hi::on_overflow_t::Axiom>> : public std::numeric_limits<unsigned long> {
381};
382template<>
383class std::numeric_limits<hi::safe_int<unsigned int, hi::on_overflow_t::Axiom>> : public std::numeric_limits<unsigned int> {
384};
385template<>
386class std::numeric_limits<hi::safe_int<unsigned short, hi::on_overflow_t::Axiom>> : public std::numeric_limits<unsigned short> {
387};
388template<>
389class std::numeric_limits<hi::safe_int<unsigned char, hi::on_overflow_t::Axiom>> : public std::numeric_limits<unsigned char> {
390};
DOXYGEN BUG.
Definition algorithm.hpp:16
T safe_handle_overflow(T value, bool overflow, bool is_positive) noexcept(OnOverflow !=on_overflow_t::Throw)
Definition safe_int.hpp:52
bool mul_overflow(T lhs, T rhs, T *r) noexcept
Multiply with overflow detection.
Definition int_overflow.hpp:99
on_overflow_t
Definition safe_int.hpp:19
@ Axiom
On overflow assert and teminate in debug, assume in release.
@ Throw
On overflow throw an exception.
@ Assert
On overflow assert and terminate.
@ Saturate
On overflow saturate the result in the appropiate direction.
geometry/margins.hpp
Definition lookahead_iterator.hpp:5
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
Definition safe_int.hpp:113
T max(T... args)
T min(T... args)