Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qcheckedint_impl.h
Go to the documentation of this file.
1// Copyright (C) 2025 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#ifndef QCHECKEDINT_H
6#define QCHECKEDINT_H
7
8#include <QtCore/qassert.h>
9#include <QtCore/qcompare.h>
10#include <QtCore/qhashfunctions.h>
11#include <QtCore/qnumeric.h>
12
13#include <type_traits>
14
15QT_BEGIN_NAMESPACE
16
17namespace QtPrivate {
19
20template <typename Int>
22{
23 static_assert(std::is_integral_v<Int>, "Only integer types are supported");
24 static_assert(std::is_signed_v<Int>, "Only signed types are supported");
25 static_assert(std::is_same_v<Int, decltype(+Int{})>, "Only fully promoted types are supported");
26};
27
28// Implements wraparound semantics, and checks for overflow.
29// Never causes UB on any operation.
30template <typename Int>
31struct SafeCheckImpl : private CheckIntTypeHelper<Int>
32{
33 static inline constexpr Int MinInt = (std::numeric_limits<Int>::min)();
34 static inline constexpr Int MaxInt = (std::numeric_limits<Int>::max)();
35
36 [[nodiscard]]
37 static constexpr bool add(Int a, Int b, Int *result) noexcept
38 {
39 return !qAddOverflow(a, b, result);
40 }
41
42 [[nodiscard]]
43 static constexpr bool sub(Int a, Int b, Int *result) noexcept
44 {
45 return !qSubOverflow(a, b, result);
46 }
47
48 [[nodiscard]]
49 static constexpr bool mul(Int a, Int b, Int *result) noexcept
50 {
51 return !qMulOverflow(a, b, result);
52 }
53
54 [[nodiscard]]
55 static constexpr bool div(Int a, Int b, Int *result) noexcept
56 {
57 if (Q_UNLIKELY(b == 0))
58 return false;
59 if (b == -1 && a == MinInt)
60 return false;
61
62 *result = a / b;
63 return true;
64 }
65};
66
67// Reports failed checks through Q_ASSERT.
69{
70 static constexpr void check(bool ok, const char *where, const char *description)
71 {
72 Q_ASSERT_X(ok, where, description);
73 }
74};
75
76template <typename Int,
77 typename Impl = SafeCheckImpl<Int>,
78 typename FailureReportPolicy = AssertReportPolicy>
79class QCheckedInt : private CheckIntTypeHelper<Int>
80{
81 Int m_i;
82
83#define Q_CHECKEDINT_POLICY_CHECK(cond, what)
84 FailureReportPolicy::check(cond, Q_FUNC_INFO, what)
85
86public:
87 template <typename AInt>
89
90 QCheckedInt() = default;
91
92 explicit constexpr QCheckedInt(Int i) noexcept
93 : m_i(i)
94 {}
95
96 // Conversions
97 explicit constexpr operator Int() const noexcept
98 {
99 return m_i;
100 }
101
102 // Accessors
103 constexpr Int value() const noexcept { return m_i; }
104 template <typename AInt, if_is_same_int<AInt> = true>
105 constexpr void setValue(AInt i) noexcept { m_i = i; }
106
107 constexpr Int &as_underlying() & noexcept { return m_i; }
108 constexpr const Int &as_underlying() const & noexcept { return m_i; }
109 constexpr Int &&as_underlying() && noexcept { return std::move(m_i); }
110 constexpr const Int &&as_underlying() const && noexcept { return std::move(m_i); }
111
112 // Unary ops
113 constexpr QCheckedInt operator+() const noexcept { return *this; }
114 constexpr QCheckedInt operator-() const
115 {
116 QCheckedInt result{};
117 const bool ok = Impl::sub(Int(0), m_i, &result.m_i);
118 Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in unary negation");
119 return result;
120 }
121
122 constexpr QCheckedInt &operator++()
123 {
124 const bool ok = Impl::add(m_i, Int(1), &m_i);
125 Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator++");
126 return *this;
127 }
128
129 constexpr QCheckedInt operator++(int)
130 {
131 QCheckedInt result = *this;
132 ++*this;
133 return result;
134 }
135
136 constexpr QCheckedInt &operator--()
137 {
138 const bool ok = Impl::sub(m_i, Int(1), &m_i);
139 Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator--");
140 return *this;
141 }
142
143 constexpr QCheckedInt operator--(int)
144 {
145 QCheckedInt result = *this;
146 --*this;
147 return result;
148 }
149
150 // Addition
151 friend constexpr QCheckedInt operator+(QCheckedInt lhs, QCheckedInt rhs)
152 {
153 QCheckedInt result{};
154 const bool ok = Impl::add(lhs.m_i, rhs.m_i, &result.m_i);
155 Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator+");
156 return result;
157 }
158
159 template <typename AInt, if_is_same_int<AInt> = true>
160 friend constexpr QCheckedInt operator+(QCheckedInt lhs, AInt rhs)
161 {
162 return lhs + QCheckedInt(rhs);
163 }
164
165 template <typename AInt, if_is_same_int<AInt> = true>
166 friend constexpr QCheckedInt operator+(AInt lhs, QCheckedInt rhs)
167 {
168 return QCheckedInt(lhs) + rhs;
169 }
170
171 constexpr QCheckedInt &operator+=(QCheckedInt other)
172 {
173 return *this = *this + other;
174 }
175
176 template <typename AInt, if_is_same_int<AInt> = true>
177 constexpr QCheckedInt &operator+=(AInt other)
178 {
179 return *this = *this + QCheckedInt(other);
180 }
181
182 // Subtraction
183 friend constexpr QCheckedInt operator-(QCheckedInt lhs, QCheckedInt rhs)
184 {
185 QCheckedInt result{};
186 const bool ok = Impl::sub(lhs.m_i, rhs.m_i, &result.m_i);
187 Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator-");
188 return result;
189 }
190
191 template <typename AInt, if_is_same_int<AInt> = true>
192 friend constexpr QCheckedInt operator-(QCheckedInt lhs, AInt rhs)
193 {
194 return lhs - QCheckedInt(rhs);
195 }
196
197 template <typename AInt, if_is_same_int<AInt> = true>
198 friend constexpr QCheckedInt operator-(AInt lhs, QCheckedInt rhs)
199 {
200 return QCheckedInt(lhs) - rhs;
201 }
202
203 constexpr QCheckedInt &operator-=(QCheckedInt other)
204 {
205 return *this = *this - other;
206 }
207
208 template <typename AInt, if_is_same_int<AInt> = true>
209 constexpr QCheckedInt &operator-=(AInt other)
210 {
211 return *this = *this - QCheckedInt(other);
212 }
213
214 // Multiplication
215 friend constexpr QCheckedInt operator*(QCheckedInt lhs, QCheckedInt rhs)
216 {
217 QCheckedInt result{};
218 const bool ok = Impl::mul(lhs.m_i, rhs.m_i, &result.m_i);
219 Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator*");
220 return result;
221 }
222
223 template <typename AInt, if_is_same_int<AInt> = true>
224 friend constexpr QCheckedInt operator*(QCheckedInt lhs, AInt rhs)
225 {
226 return lhs * QCheckedInt(rhs);
227 }
228
229 template <typename AInt, if_is_same_int<AInt> = true>
230 friend constexpr QCheckedInt operator*(AInt lhs, QCheckedInt rhs)
231 {
232 return QCheckedInt(lhs) * rhs;
233 }
234
235 constexpr QCheckedInt &operator*=(QCheckedInt other)
236 {
237 return *this = *this * other;
238 }
239
240 template <typename AInt, if_is_same_int<AInt> = true>
241 constexpr QCheckedInt &operator*=(AInt other)
242 {
243 return *this = *this * QCheckedInt(other);
244 }
245
246 // Division
247 friend constexpr QCheckedInt operator/(QCheckedInt lhs, QCheckedInt rhs)
248 {
249 QCheckedInt result{};
250 const bool ok = Impl::div(lhs.m_i, rhs.m_i, &result.m_i);
251 Q_CHECKEDINT_POLICY_CHECK(ok, "Division by zero");
252 return result;
253 }
254
255 template <typename AInt, if_is_same_int<AInt> = true>
256 friend constexpr QCheckedInt operator/(QCheckedInt lhs, AInt rhs)
257 {
258 return lhs / QCheckedInt(rhs);
259 }
260
261 template <typename AInt, if_is_same_int<AInt> = true>
262 friend constexpr QCheckedInt operator/(AInt lhs, QCheckedInt rhs)
263 {
264 return QCheckedInt(lhs) / rhs;
265 }
266
267 constexpr QCheckedInt &operator/=(QCheckedInt other)
268 {
269 return *this = *this / other;
270 }
271
272 template <typename AInt, if_is_same_int<AInt> = true>
273 constexpr QCheckedInt &operator/=(AInt other)
274 {
275 return *this = *this / QCheckedInt(other);
276 }
277
278#undef Q_CHECKEDINT_POLICY_CHECK
279
280 // Comparisons
281 friend constexpr bool comparesEqual(QCheckedInt lhs, QCheckedInt rhs) noexcept
282 {
283 return lhs.m_i == rhs.m_i;
284 }
285
286 template <typename AInt, if_is_same_int<AInt> = true>
287 friend constexpr bool comparesEqual(QCheckedInt lhs, AInt rhs) noexcept
288 {
289 return lhs.m_i == rhs;
290 }
291
292 friend constexpr Qt::strong_ordering compareThreeWay(QCheckedInt lhs, QCheckedInt rhs) noexcept
293 {
294 return Qt::compareThreeWay(lhs.m_i, rhs.m_i);
295 }
296
297 template <typename AInt, if_is_same_int<AInt> = true>
298 friend constexpr Qt::strong_ordering compareThreeWay(QCheckedInt lhs, AInt rhs) noexcept
299 {
300 return Qt::compareThreeWay(lhs.m_i, rhs);
301 }
302
306 AInt,
307 template <typename AInt, if_is_same_int<AInt> = true>)
308
309private:
310 friend size_t constexpr qHash(QCheckedInt key, size_t seed = 0) noexcept
311 {
312 using QT_PREPEND_NAMESPACE(qHash); // ### needed?
313 return qHash(key.value(), seed);
314 }
315};
316
317} // namespace QCheckedIntegers
318} // namespace QtPrivate
319
320QT_END_NAMESPACE
321
322#endif // QCHECKEDINT_H
friend constexpr QCheckedInt operator+(AInt lhs, QCheckedInt rhs)
constexpr QCheckedInt & operator-=(AInt other)
friend constexpr QCheckedInt operator/(AInt lhs, QCheckedInt rhs)
friend constexpr QCheckedInt operator/(QCheckedInt lhs, QCheckedInt rhs)
constexpr QCheckedInt operator+() const noexcept
constexpr QCheckedInt & operator*=(QCheckedInt other)
constexpr const Int & as_underlying() const &noexcept
friend constexpr QCheckedInt operator+(QCheckedInt lhs, QCheckedInt rhs)
friend constexpr QCheckedInt operator-(QCheckedInt lhs, QCheckedInt rhs)
friend constexpr Qt::strong_ordering compareThreeWay(QCheckedInt lhs, AInt rhs) noexcept
constexpr QCheckedInt & operator*=(AInt other)
constexpr const Int && as_underlying() const &&noexcept
constexpr void setValue(AInt i) noexcept
constexpr QCheckedInt & operator/=(AInt other)
friend constexpr QCheckedInt operator-(AInt lhs, QCheckedInt rhs)
constexpr QCheckedInt & operator+=(QCheckedInt other)
friend constexpr bool comparesEqual(QCheckedInt lhs, QCheckedInt rhs) noexcept
constexpr Int && as_underlying() &&noexcept
constexpr QCheckedInt & operator/=(QCheckedInt other)
constexpr Int & as_underlying() &noexcept
constexpr Int value() const noexcept
friend constexpr Qt::strong_ordering compareThreeWay(QCheckedInt lhs, QCheckedInt rhs) noexcept
friend constexpr QCheckedInt operator-(QCheckedInt lhs, AInt rhs)
constexpr operator Int() const noexcept
friend constexpr QCheckedInt operator*(AInt lhs, QCheckedInt rhs)
friend constexpr QCheckedInt operator*(QCheckedInt lhs, QCheckedInt rhs)
friend constexpr QCheckedInt operator*(QCheckedInt lhs, AInt rhs)
friend constexpr bool comparesEqual(QCheckedInt lhs, AInt rhs) noexcept
constexpr QCheckedInt operator-() const
friend constexpr QCheckedInt operator+(QCheckedInt lhs, AInt rhs)
constexpr QCheckedInt & operator+=(AInt other)
constexpr QCheckedInt & operator-=(QCheckedInt other)
friend constexpr QCheckedInt operator/(QCheckedInt lhs, AInt rhs)
#define Q_CHECKEDINT_POLICY_CHECK(cond, what)
static constexpr void check(bool ok, const char *where, const char *description)
static constexpr bool mul(Int a, Int b, Int *result) noexcept
static constexpr bool add(Int a, Int b, Int *result) noexcept
static constexpr bool sub(Int a, Int b, Int *result) noexcept
static constexpr bool div(Int a, Int b, Int *result) noexcept