HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
safe_int.hpp
1// Copyright 2019 Pokitec
2// All rights reserved.
3
4#pragma once
5
6#include "TTauri/Foundation/int_overflow.hpp"
7#include "TTauri/Foundation/type_traits.hpp"
8#include "TTauri/Foundation/exceptions.hpp"
9#include <system_error>
10#include <type_traits>
11#include <limits>
12
13namespace tt {
14
15enum class on_overflow_t {
17 Throw,
19 Saturate,
21 Assert,
23 Axiom,
24};
25
29constexpr on_overflow_t operator|(on_overflow_t lhs, on_overflow_t rhs) noexcept {
30 if (lhs == on_overflow_t::Throw || rhs == on_overflow_t::Throw) {
31 return on_overflow_t::Throw;
32 } else if (lhs == on_overflow_t::Saturate || rhs == on_overflow_t::Saturate) {
33 return on_overflow_t::Saturate;
34 } else if (lhs == on_overflow_t::Assert || rhs == on_overflow_t::Assert) {
35 return on_overflow_t::Assert;
36 } else {
37 return on_overflow_t::Axiom;
38 }
39}
40
46template<typename T, on_overflow_t OnOverflow>
47tt_force_inline T safe_handle_overflow(T value, bool overflow, bool is_positive) noexcept(OnOverflow != on_overflow_t::Throw)
48{
49 if constexpr (OnOverflow == on_overflow_t::Throw) {
50 if (overflow) {
51 TTAURI_THROW(math_error("safe_int"));
52 }
53 } else if constexpr (OnOverflow == on_overflow_t::Assert) {
54 tt_assert(!overflow);
55 } else if constexpr (OnOverflow == on_overflow_t::Axiom) {
56 tt_assume(!overflow);
57 } else if constexpr (OnOverflow == on_overflow_t::Saturate) {
58 if (tt_unlikely(overflow)) {
59 value = is_positive ? std::numeric_limits<T>::max() : std::numeric_limits<T>::min();
60 }
61 }
62 return value;
63}
64
65template<typename T, on_overflow_t OnOverflow, typename U>
66tt_force_inline T safe_convert(U const &rhs) noexcept(OnOverflow != on_overflow_t::Throw)
67{
68 T r;
69 // Optimized away when is_same_v<T,U>
70 ttlet overflow = convert_overflow(rhs, &r);
71 return safe_handle_overflow<T,OnOverflow>(r, overflow, rhs >= 0);
72}
73
74template<on_overflow_t OnOverflow, typename T, typename U>
75tt_force_inline make_promote_t<T,U> safe_add(T const &lhs, U const &rhs) noexcept(OnOverflow != on_overflow_t::Throw)
76{
77 make_promote_t<T,U> r;
78 ttlet lhs_ = static_cast<make_promote_t<T,U>>(lhs);
79 ttlet rhs_ = static_cast<make_promote_t<T,U>>(rhs);
80
81 ttlet overflow = add_overflow(lhs_, rhs_, &r);
82 return safe_handle_overflow<T,OnOverflow>(r, overflow, rhs_ >= 0);
83}
84
85template<on_overflow_t OnOverflow, typename T, typename U>
86tt_force_inline make_promote_t<T,U> safe_sub(T const &lhs, U const &rhs) noexcept(OnOverflow != on_overflow_t::Throw)
87{
88 make_promote_t<T,U> r;
89 ttlet lhs_ = static_cast<make_promote_t<T,U>>(lhs);
90 ttlet rhs_ = static_cast<make_promote_t<T,U>>(rhs);
91
92 ttlet overflow = sub_overflow(lhs_, rhs_, &r);
93 return safe_handle_overflow<T,OnOverflow>(r, overflow, rhs_ >= 0);
94}
95
96template<on_overflow_t OnOverflow, typename T, typename U>
97tt_force_inline make_promote_t<T,U> safe_mul(T const &lhs, U const &rhs) noexcept(OnOverflow != on_overflow_t::Throw)
98{
99 make_promote_t<T,U> r;
100 ttlet lhs_ = static_cast<make_promote_t<T,U>>(lhs);
101 ttlet rhs_ = static_cast<make_promote_t<T,U>>(rhs);
102
103 ttlet overflow = mul_overflow(lhs_, rhs_, &r);
104 return safe_handle_overflow<T,OnOverflow>(r, overflow, rhs_ >= 0);
105}
106
107template<typename T, on_overflow_t OnOverflow=on_overflow_t::Assert>
108struct safe_int {
109 using value_type = T;
110 constexpr static on_overflow_t on_overflow = OnOverflow;
111
112 T value;
113
114 safe_int() : value(0) {}
115 ~safe_int() = default;
116 safe_int(safe_int const &) = default;
117 safe_int(safe_int &&) = default;
118 safe_int &operator=(safe_int const &) = default;
119 safe_int &operator=(safe_int &&) = default;
120
121 template<typename O, std::enable_if_t<std::is_integral_v<O>,int> = 0>
122 explicit safe_int(O const &other) noexcept(OnOverflow != on_overflow_t::Throw) :
123 value(safe_convert<T,OnOverflow>(other)) {}
124
125 template<typename O, std::enable_if_t<std::is_floating_point_v<O>,int> = 0>
126 explicit safe_int(O const &other) noexcept(OnOverflow != on_overflow_t::Throw) :
127 value(safe_convert<T,OnOverflow>(other)) {}
128
129 template<typename O, on_overflow_t OtherOnOverflow>
130 explicit safe_int(safe_int<O,OtherOnOverflow> const &other) noexcept(OnOverflow != on_overflow_t::Throw) :
131 value(safe_convert<T,OnOverflow>(other.value)) {}
132
133 template<typename O, std::enable_if_t<std::is_integral_v<O>,int> = 0>
134 safe_int &operator=(O const &other) noexcept(OnOverflow != on_overflow_t::Throw) {
135 value = safe_convert<T,OnOverflow>(other);
136 return *this;
137 }
138
139 template<typename O, on_overflow_t OtherOnOverflow>
140 safe_int &operator=(safe_int<O,OtherOnOverflow> const &other) noexcept(OnOverflow != on_overflow_t::Throw) {
141 value = safe_convert<T,OnOverflow>(other.value);
142 return *this;
143 }
144
145 template<typename O, std::enable_if_t<std::is_integral_v<O>,int> = 0>
146 explicit operator O () const noexcept(OnOverflow != on_overflow_t::Throw) {
147 return safe_convert<O,OnOverflow>(value);
148 }
149
150 template<typename O, std::enable_if_t<std::is_floating_point_v<O>,int> = 0>
151 explicit operator O () const noexcept {
152 return static_cast<O>(value);
153 }
154};
155
156#define TEMPLATE(op)\
157 template<typename T, on_overflow_t TO, typename U, on_overflow_t UO>\
158 tt_force_inline bool operator op (safe_int<T,TO> const &lhs, safe_int<U,UO> const &rhs) noexcept {\
159 return lhs.value op rhs.value;\
160 }\
161 \
162 template<typename T, on_overflow_t TO, typename U>\
163 tt_force_inline bool operator op (safe_int<T,TO> const &lhs, U const &rhs) noexcept {\
164 return lhs.value op rhs;\
165 }\
166 template<typename T, typename U, on_overflow_t UO>\
167 tt_force_inline bool operator op (T const &lhs, safe_int<U,UO> const &rhs) noexcept {\
168 return lhs op rhs.value;\
169 }
170
171TEMPLATE(==)
172TEMPLATE(!=)
173TEMPLATE(<)
174TEMPLATE(>)
175TEMPLATE(<=)
176TEMPLATE(>=)
177#undef TEMPLATE
178
179#define TEMPLATE(op, func)\
180 template<typename T, on_overflow_t TO, typename U, on_overflow_t UO>\
181 tt_force_inline safe_int<make_promote_t<T,U>,TO|UO> operator op(safe_int<T,TO> const &lhs, safe_int<U,UO> const &rhs) noexcept((TO|UO) != on_overflow_t::Throw) {\
182 return safe_int<make_promote_t<T,U>,TO|UO>{ func<TO|UO>(lhs.value, rhs.value) };\
183 }\
184 \
185 template<typename T, on_overflow_t TO, typename U>\
186 tt_force_inline safe_int<make_promote_t<T,U>,TO> operator op(safe_int<T,TO> const &lhs, U const &rhs) noexcept(TO != on_overflow_t::Throw) {\
187 return safe_int<make_promote_t<T,U>,TO>{ func<TO>(lhs.value, rhs) };\
188 }\
189 \
190 template<typename T, typename U, on_overflow_t UO>\
191 tt_force_inline safe_int<make_promote_t<T,U>,UO> operator op(T const &lhs, safe_int<U,UO> const &rhs) noexcept(UO != on_overflow_t::Throw) {\
192 return safe_int<make_promote_t<T,U>,UO>{ func<UO>(lhs, rhs.value) };\
193 }
194
195TEMPLATE(+, safe_add)
196TEMPLATE(-, safe_sub)
197TEMPLATE(*, safe_mul)
198#undef TEMPLATE
199
208
217
226
235
236}
237
238namespace std {
239
240template<> class numeric_limits<tt::safe_int<signed long long,tt::on_overflow_t::Saturate>>: public numeric_limits<signed long long> {};
241template<> class numeric_limits<tt::safe_int<signed long,tt::on_overflow_t::Saturate>>: public numeric_limits<signed long> {};
242template<> class numeric_limits<tt::safe_int<signed int,tt::on_overflow_t::Saturate>>: public numeric_limits<signed int> {};
243template<> class numeric_limits<tt::safe_int<signed short,tt::on_overflow_t::Saturate>>: public numeric_limits<signed short> {};
244template<> class numeric_limits<tt::safe_int<signed char,tt::on_overflow_t::Saturate>>: public numeric_limits<signed char> {};
245template<> class numeric_limits<tt::safe_int<unsigned long long,tt::on_overflow_t::Saturate>>: public numeric_limits<unsigned long long> {};
246template<> class numeric_limits<tt::safe_int<unsigned long,tt::on_overflow_t::Saturate>>: public numeric_limits<unsigned long> {};
247template<> class numeric_limits<tt::safe_int<unsigned int,tt::on_overflow_t::Saturate>>: public numeric_limits<unsigned int> {};
248template<> class numeric_limits<tt::safe_int<unsigned short,tt::on_overflow_t::Saturate>>: public numeric_limits<unsigned short> {};
249template<> class numeric_limits<tt::safe_int<unsigned char,tt::on_overflow_t::Saturate>>: public numeric_limits<unsigned char> {};
250
251template<> class numeric_limits<tt::safe_int<signed long long,tt::on_overflow_t::Assert>>: public numeric_limits<signed long long> {};
252template<> class numeric_limits<tt::safe_int<signed long,tt::on_overflow_t::Assert>>: public numeric_limits<signed long> {};
253template<> class numeric_limits<tt::safe_int<signed int,tt::on_overflow_t::Assert>>: public numeric_limits<signed int> {};
254template<> class numeric_limits<tt::safe_int<signed short,tt::on_overflow_t::Assert>>: public numeric_limits<signed short> {};
255template<> class numeric_limits<tt::safe_int<signed char,tt::on_overflow_t::Assert>>: public numeric_limits<signed char> {};
256template<> class numeric_limits<tt::safe_int<unsigned long long,tt::on_overflow_t::Assert>>: public numeric_limits<unsigned long long> {};
257template<> class numeric_limits<tt::safe_int<unsigned long,tt::on_overflow_t::Assert>>: public numeric_limits<unsigned long> {};
258template<> class numeric_limits<tt::safe_int<unsigned int,tt::on_overflow_t::Assert>>: public numeric_limits<unsigned int> {};
259template<> class numeric_limits<tt::safe_int<unsigned short,tt::on_overflow_t::Assert>>: public numeric_limits<unsigned short> {};
260template<> class numeric_limits<tt::safe_int<unsigned char,tt::on_overflow_t::Assert>>: public numeric_limits<unsigned char> {};
261
262template<> class numeric_limits<tt::safe_int<signed long long,tt::on_overflow_t::Throw>>: public numeric_limits<signed long long> {};
263template<> class numeric_limits<tt::safe_int<signed long,tt::on_overflow_t::Throw>>: public numeric_limits<signed long> {};
264template<> class numeric_limits<tt::safe_int<signed int,tt::on_overflow_t::Throw>>: public numeric_limits<signed int> {};
265template<> class numeric_limits<tt::safe_int<signed short,tt::on_overflow_t::Throw>>: public numeric_limits<signed short> {};
266template<> class numeric_limits<tt::safe_int<signed char,tt::on_overflow_t::Throw>>: public numeric_limits<signed char> {};
267template<> class numeric_limits<tt::safe_int<unsigned long long,tt::on_overflow_t::Throw>>: public numeric_limits<unsigned long long> {};
268template<> class numeric_limits<tt::safe_int<unsigned long,tt::on_overflow_t::Throw>>: public numeric_limits<unsigned long> {};
269template<> class numeric_limits<tt::safe_int<unsigned int,tt::on_overflow_t::Throw>>: public numeric_limits<unsigned int> {};
270template<> class numeric_limits<tt::safe_int<unsigned short,tt::on_overflow_t::Throw>>: public numeric_limits<unsigned short> {};
271template<> class numeric_limits<tt::safe_int<unsigned char,tt::on_overflow_t::Throw>>: public numeric_limits<unsigned char> {};
272
273template<> class numeric_limits<tt::safe_int<signed long long,tt::on_overflow_t::Axiom>>: public numeric_limits<signed long long> {};
274template<> class numeric_limits<tt::safe_int<signed long,tt::on_overflow_t::Axiom>>: public numeric_limits<signed long> {};
275template<> class numeric_limits<tt::safe_int<signed int,tt::on_overflow_t::Axiom>>: public numeric_limits<signed int> {};
276template<> class numeric_limits<tt::safe_int<signed short,tt::on_overflow_t::Axiom>>: public numeric_limits<signed short> {};
277template<> class numeric_limits<tt::safe_int<signed char,tt::on_overflow_t::Axiom>>: public numeric_limits<signed char> {};
278template<> class numeric_limits<tt::safe_int<unsigned long long,tt::on_overflow_t::Axiom>>: public numeric_limits<unsigned long long> {};
279template<> class numeric_limits<tt::safe_int<unsigned long,tt::on_overflow_t::Axiom>>: public numeric_limits<unsigned long> {};
280template<> class numeric_limits<tt::safe_int<unsigned int,tt::on_overflow_t::Axiom>>: public numeric_limits<unsigned int> {};
281template<> class numeric_limits<tt::safe_int<unsigned short,tt::on_overflow_t::Axiom>>: public numeric_limits<unsigned short> {};
282template<> class numeric_limits<tt::safe_int<unsigned char,tt::on_overflow_t::Axiom>>: public numeric_limits<unsigned char> {};
283
284}
STL namespace.
Definition safe_int.hpp:108
T max(T... args)
T min(T... args)