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