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
60 *result = a / b;
61 return true;
62 }
63};
64
65// Reports failed checks through Q_ASSERT.
67{
68 static constexpr void check(bool ok, const char *where, const char *description)
69 {
70 Q_ASSERT_X(ok, where, description);
71 }
72};
73
74template <typename Int,
75 typename Impl = SafeCheckImpl<Int>,
76 typename FailureReportPolicy = AssertReportPolicy>
77class QCheckedInt : private CheckIntTypeHelper<Int>
78{
79 Int m_i;
80
81#define Q_CHECKEDINT_POLICY_CHECK(cond, what)
82 FailureReportPolicy::check(cond, Q_FUNC_INFO, what)
83
84public:
85 template <typename AInt>
87
88 QCheckedInt() = default;
89
90 explicit constexpr QCheckedInt(Int i) noexcept
91 : m_i(i)
92 {}
93
94 // Conversions
95 explicit constexpr operator Int() const noexcept
96 {
97 return m_i;
98 }
99
100 // Accessors
101 constexpr Int value() const noexcept { return m_i; }
102 template <typename AInt, if_is_same_int<AInt> = true>
103 constexpr void setValue(AInt i) noexcept { m_i = i; }
104
105 constexpr Int &as_underlying() & noexcept { return m_i; }
106 constexpr const Int &as_underlying() const & noexcept { return m_i; }
107 constexpr Int &&as_underlying() && noexcept { return std::move(m_i); }
108 constexpr const Int &&as_underlying() const && noexcept { return std::move(m_i); }
109
110 // Unary ops
111 constexpr QCheckedInt operator+() const noexcept { return *this; }
112 constexpr QCheckedInt operator-() const
113 {
114 QCheckedInt result{};
115 const bool ok = Impl::sub(Int(0), m_i, &result.m_i);
116 Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in unary negation");
117 return result;
118 }
119
120 constexpr QCheckedInt &operator++()
121 {
122 const bool ok = Impl::add(m_i, Int(1), &m_i);
123 Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator++");
124 return *this;
125 }
126
127 constexpr QCheckedInt operator++(int)
128 {
129 QCheckedInt result = *this;
130 ++*this;
131 return result;
132 }
133
134 constexpr QCheckedInt &operator--()
135 {
136 const bool ok = Impl::sub(m_i, Int(1), &m_i);
137 Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator--");
138 return *this;
139 }
140
141 constexpr QCheckedInt operator--(int)
142 {
143 QCheckedInt result = *this;
144 --*this;
145 return result;
146 }
147
148 // Addition
149 friend constexpr QCheckedInt operator+(QCheckedInt lhs, QCheckedInt rhs)
150 {
151 QCheckedInt result{};
152 const bool ok = Impl::add(lhs.m_i, rhs.m_i, &result.m_i);
153 Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator+");
154 return result;
155 }
156
157 template <typename AInt, if_is_same_int<AInt> = true>
158 friend constexpr QCheckedInt operator+(QCheckedInt lhs, AInt rhs)
159 {
160 return lhs + QCheckedInt(rhs);
161 }
162
163 template <typename AInt, if_is_same_int<AInt> = true>
164 friend constexpr QCheckedInt operator+(AInt lhs, QCheckedInt rhs)
165 {
166 return QCheckedInt(lhs) + rhs;
167 }
168
169 constexpr QCheckedInt &operator+=(QCheckedInt other)
170 {
171 return *this = *this + other;
172 }
173
174 template <typename AInt, if_is_same_int<AInt> = true>
175 constexpr QCheckedInt &operator+=(AInt other)
176 {
177 return *this = *this + QCheckedInt(other);
178 }
179
180 // Subtraction
181 friend constexpr QCheckedInt operator-(QCheckedInt lhs, QCheckedInt rhs)
182 {
183 QCheckedInt result{};
184 const bool ok = Impl::sub(lhs.m_i, rhs.m_i, &result.m_i);
185 Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator-");
186 return result;
187 }
188
189 template <typename AInt, if_is_same_int<AInt> = true>
190 friend constexpr QCheckedInt operator-(QCheckedInt lhs, AInt rhs)
191 {
192 return lhs - QCheckedInt(rhs);
193 }
194
195 template <typename AInt, if_is_same_int<AInt> = true>
196 friend constexpr QCheckedInt operator-(AInt lhs, QCheckedInt rhs)
197 {
198 return QCheckedInt(lhs) - rhs;
199 }
200
201 constexpr QCheckedInt &operator-=(QCheckedInt other)
202 {
203 return *this = *this - other;
204 }
205
206 template <typename AInt, if_is_same_int<AInt> = true>
207 constexpr QCheckedInt &operator-=(AInt other)
208 {
209 return *this = *this - QCheckedInt(other);
210 }
211
212 // Multiplication
213 friend constexpr QCheckedInt operator*(QCheckedInt lhs, QCheckedInt rhs)
214 {
215 QCheckedInt result{};
216 const bool ok = Impl::mul(lhs.m_i, rhs.m_i, &result.m_i);
217 Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator*");
218 return result;
219 }
220
221 template <typename AInt, if_is_same_int<AInt> = true>
222 friend constexpr QCheckedInt operator*(QCheckedInt lhs, AInt rhs)
223 {
224 return lhs * QCheckedInt(rhs);
225 }
226
227 template <typename AInt, if_is_same_int<AInt> = true>
228 friend constexpr QCheckedInt operator*(AInt lhs, QCheckedInt rhs)
229 {
230 return QCheckedInt(lhs) * rhs;
231 }
232
233 constexpr QCheckedInt &operator*=(QCheckedInt other)
234 {
235 return *this = *this * other;
236 }
237
238 template <typename AInt, if_is_same_int<AInt> = true>
239 constexpr QCheckedInt &operator*=(AInt other)
240 {
241 return *this = *this * QCheckedInt(other);
242 }
243
244 // Division
245 friend constexpr QCheckedInt operator/(QCheckedInt lhs, QCheckedInt rhs)
246 {
247 QCheckedInt result{};
248 const bool ok = Impl::div(lhs.m_i, rhs.m_i, &result.m_i);
249 Q_CHECKEDINT_POLICY_CHECK(ok, "Division by zero");
250 return result;
251 }
252
253 template <typename AInt, if_is_same_int<AInt> = true>
254 friend constexpr QCheckedInt operator/(QCheckedInt lhs, AInt rhs)
255 {
256 return lhs / QCheckedInt(rhs);
257 }
258
259 template <typename AInt, if_is_same_int<AInt> = true>
260 friend constexpr QCheckedInt operator/(AInt lhs, QCheckedInt rhs)
261 {
262 return QCheckedInt(lhs) / rhs;
263 }
264
265 constexpr QCheckedInt &operator/=(QCheckedInt other)
266 {
267 return *this = *this / other;
268 }
269
270 template <typename AInt, if_is_same_int<AInt> = true>
271 constexpr QCheckedInt &operator/=(AInt other)
272 {
273 return *this = *this / QCheckedInt(other);
274 }
275
276#undef Q_CHECKEDINT_POLICY_CHECK
277
278 // Comparisons
279 friend constexpr bool comparesEqual(QCheckedInt lhs, QCheckedInt rhs) noexcept
280 {
281 return lhs.m_i == rhs.m_i;
282 }
283
284 template <typename AInt, if_is_same_int<AInt> = true>
285 friend constexpr bool comparesEqual(QCheckedInt lhs, AInt rhs) noexcept
286 {
287 return lhs.m_i == rhs;
288 }
289
290 friend constexpr Qt::strong_ordering compareThreeWay(QCheckedInt lhs, QCheckedInt rhs) noexcept
291 {
292 return Qt::compareThreeWay(lhs.m_i, rhs.m_i);
293 }
294
295 template <typename AInt, if_is_same_int<AInt> = true>
296 friend constexpr Qt::strong_ordering compareThreeWay(QCheckedInt lhs, AInt rhs) noexcept
297 {
298 return Qt::compareThreeWay(lhs.m_i, rhs);
299 }
300
304 AInt,
305 template <typename AInt, if_is_same_int<AInt> = true>)
306
307private:
308 friend size_t constexpr qHash(QCheckedInt key, size_t seed = 0) noexcept
309 {
310 using QT_PREPEND_NAMESPACE(qHash); // ### needed?
311 return qHash(key.value(), seed);
312 }
313};
314
315} // namespace QCheckedIntegers
316} // namespace QtPrivate
317
318QT_END_NAMESPACE
319
320#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