File indexing completed on 2026-01-07 10:16:09
0001
0002
0003
0004 #ifndef QCHECKEDINT_H
0005 #define QCHECKEDINT_H
0006
0007 #include <QtCore/qassert.h>
0008 #include <QtCore/qcompare.h>
0009 #include <QtCore/qhashfunctions.h>
0010 #include <QtCore/qnumeric.h>
0011
0012 #include <type_traits>
0013
0014 QT_BEGIN_NAMESPACE
0015
0016 namespace QtPrivate {
0017 namespace QCheckedIntegers {
0018
0019 template <typename Int>
0020 struct CheckIntTypeHelper
0021 {
0022 static_assert(std::is_integral_v<Int>, "Only integer types are supported");
0023 static_assert(std::is_signed_v<Int>, "Only signed types are supported");
0024 static_assert(std::is_same_v<Int, decltype(+Int{})>, "Only fully promoted types are supported");
0025 };
0026
0027
0028
0029 template <typename Int>
0030 struct SafeCheckImpl : private CheckIntTypeHelper<Int>
0031 {
0032 static inline constexpr Int MinInt = (std::numeric_limits<Int>::min)();
0033 static inline constexpr Int MaxInt = (std::numeric_limits<Int>::max)();
0034
0035 [[nodiscard]]
0036 static constexpr bool add(Int a, Int b, Int *result) noexcept
0037 {
0038 return !qAddOverflow(a, b, result);
0039 }
0040
0041 [[nodiscard]]
0042 static constexpr bool sub(Int a, Int b, Int *result) noexcept
0043 {
0044 return !qSubOverflow(a, b, result);
0045 }
0046
0047 [[nodiscard]]
0048 static constexpr bool mul(Int a, Int b, Int *result) noexcept
0049 {
0050 return !qMulOverflow(a, b, result);
0051 }
0052
0053 [[nodiscard]]
0054 static constexpr bool div(Int a, Int b, Int *result) noexcept
0055 {
0056 if (Q_UNLIKELY(b == 0))
0057 return false;
0058
0059 *result = a / b;
0060 return true;
0061 }
0062 };
0063
0064
0065 struct AssertReportPolicy
0066 {
0067 static constexpr void check(bool ok, const char *where, const char *description)
0068 {
0069 Q_ASSERT_X(ok, where, description);
0070 }
0071 };
0072
0073 template <typename Int,
0074 typename Impl = SafeCheckImpl<Int>,
0075 typename FailureReportPolicy = AssertReportPolicy>
0076 class QCheckedInt : private CheckIntTypeHelper<Int>
0077 {
0078 Int m_i;
0079
0080 #define Q_CHECKEDINT_POLICY_CHECK(cond, what) \
0081 FailureReportPolicy::check(cond, Q_FUNC_INFO, what)
0082
0083 public:
0084 template <typename AInt>
0085 using if_is_same_int = std::enable_if_t<std::is_same_v<Int, AInt>, bool>;
0086
0087 QCheckedInt() = default;
0088
0089 explicit constexpr QCheckedInt(Int i) noexcept
0090 : m_i(i)
0091 {}
0092
0093
0094 explicit constexpr operator Int() const noexcept
0095 {
0096 return m_i;
0097 }
0098
0099
0100 constexpr Int value() const noexcept { return m_i; }
0101 template <typename AInt, if_is_same_int<AInt> = true>
0102 constexpr void setValue(AInt i) noexcept { m_i = i; }
0103
0104 constexpr Int &as_underlying() & noexcept { return m_i; }
0105 constexpr const Int &as_underlying() const & noexcept { return m_i; }
0106 constexpr Int &&as_underlying() && noexcept { return std::move(m_i); }
0107 constexpr const Int &&as_underlying() const && noexcept { return std::move(m_i); }
0108
0109
0110 constexpr QCheckedInt operator+() const noexcept { return *this; }
0111 constexpr QCheckedInt operator-() const
0112 {
0113 QCheckedInt result{};
0114 const bool ok = Impl::sub(Int(0), m_i, &result.m_i);
0115 Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in unary negation");
0116 return result;
0117 }
0118
0119 constexpr QCheckedInt &operator++()
0120 {
0121 const bool ok = Impl::add(m_i, Int(1), &m_i);
0122 Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator++");
0123 return *this;
0124 }
0125
0126 constexpr QCheckedInt operator++(int)
0127 {
0128 QCheckedInt result = *this;
0129 ++*this;
0130 return result;
0131 }
0132
0133 constexpr QCheckedInt &operator--()
0134 {
0135 const bool ok = Impl::sub(m_i, Int(1), &m_i);
0136 Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator--");
0137 return *this;
0138 }
0139
0140 constexpr QCheckedInt operator--(int)
0141 {
0142 QCheckedInt result = *this;
0143 --*this;
0144 return result;
0145 }
0146
0147
0148 friend constexpr QCheckedInt operator+(QCheckedInt lhs, QCheckedInt rhs)
0149 {
0150 QCheckedInt result{};
0151 const bool ok = Impl::add(lhs.m_i, rhs.m_i, &result.m_i);
0152 Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator+");
0153 return result;
0154 }
0155
0156 template <typename AInt, if_is_same_int<AInt> = true>
0157 friend constexpr QCheckedInt operator+(QCheckedInt lhs, AInt rhs)
0158 {
0159 return lhs + QCheckedInt(rhs);
0160 }
0161
0162 template <typename AInt, if_is_same_int<AInt> = true>
0163 friend constexpr QCheckedInt operator+(AInt lhs, QCheckedInt rhs)
0164 {
0165 return QCheckedInt(lhs) + rhs;
0166 }
0167
0168 constexpr QCheckedInt &operator+=(QCheckedInt other)
0169 {
0170 return *this = *this + other;
0171 }
0172
0173 template <typename AInt, if_is_same_int<AInt> = true>
0174 constexpr QCheckedInt &operator+=(AInt other)
0175 {
0176 return *this = *this + QCheckedInt(other);
0177 }
0178
0179
0180 friend constexpr QCheckedInt operator-(QCheckedInt lhs, QCheckedInt rhs)
0181 {
0182 QCheckedInt result{};
0183 const bool ok = Impl::sub(lhs.m_i, rhs.m_i, &result.m_i);
0184 Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator-");
0185 return result;
0186 }
0187
0188 template <typename AInt, if_is_same_int<AInt> = true>
0189 friend constexpr QCheckedInt operator-(QCheckedInt lhs, AInt rhs)
0190 {
0191 return lhs - QCheckedInt(rhs);
0192 }
0193
0194 template <typename AInt, if_is_same_int<AInt> = true>
0195 friend constexpr QCheckedInt operator-(AInt lhs, QCheckedInt rhs)
0196 {
0197 return QCheckedInt(lhs) - rhs;
0198 }
0199
0200 constexpr QCheckedInt &operator-=(QCheckedInt other)
0201 {
0202 return *this = *this - other;
0203 }
0204
0205 template <typename AInt, if_is_same_int<AInt> = true>
0206 constexpr QCheckedInt &operator-=(AInt other)
0207 {
0208 return *this = *this - QCheckedInt(other);
0209 }
0210
0211
0212 friend constexpr QCheckedInt operator*(QCheckedInt lhs, QCheckedInt rhs)
0213 {
0214 QCheckedInt result{};
0215 const bool ok = Impl::mul(lhs.m_i, rhs.m_i, &result.m_i);
0216 Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator*");
0217 return result;
0218 }
0219
0220 template <typename AInt, if_is_same_int<AInt> = true>
0221 friend constexpr QCheckedInt operator*(QCheckedInt lhs, AInt rhs)
0222 {
0223 return lhs * QCheckedInt(rhs);
0224 }
0225
0226 template <typename AInt, if_is_same_int<AInt> = true>
0227 friend constexpr QCheckedInt operator*(AInt lhs, QCheckedInt rhs)
0228 {
0229 return QCheckedInt(lhs) * rhs;
0230 }
0231
0232 constexpr QCheckedInt &operator*=(QCheckedInt other)
0233 {
0234 return *this = *this * other;
0235 }
0236
0237 template <typename AInt, if_is_same_int<AInt> = true>
0238 constexpr QCheckedInt &operator*=(AInt other)
0239 {
0240 return *this = *this * QCheckedInt(other);
0241 }
0242
0243
0244 friend constexpr QCheckedInt operator/(QCheckedInt lhs, QCheckedInt rhs)
0245 {
0246 QCheckedInt result{};
0247 const bool ok = Impl::div(lhs.m_i, rhs.m_i, &result.m_i);
0248 Q_CHECKEDINT_POLICY_CHECK(ok, "Division by zero");
0249 return result;
0250 }
0251
0252 template <typename AInt, if_is_same_int<AInt> = true>
0253 friend constexpr QCheckedInt operator/(QCheckedInt lhs, AInt rhs)
0254 {
0255 return lhs / QCheckedInt(rhs);
0256 }
0257
0258 template <typename AInt, if_is_same_int<AInt> = true>
0259 friend constexpr QCheckedInt operator/(AInt lhs, QCheckedInt rhs)
0260 {
0261 return QCheckedInt(lhs) / rhs;
0262 }
0263
0264 constexpr QCheckedInt &operator/=(QCheckedInt other)
0265 {
0266 return *this = *this / other;
0267 }
0268
0269 template <typename AInt, if_is_same_int<AInt> = true>
0270 constexpr QCheckedInt &operator/=(AInt other)
0271 {
0272 return *this = *this / QCheckedInt(other);
0273 }
0274
0275 #undef Q_CHECKEDINT_POLICY_CHECK
0276
0277
0278 friend constexpr bool comparesEqual(QCheckedInt lhs, QCheckedInt rhs) noexcept
0279 {
0280 return lhs.m_i == rhs.m_i;
0281 }
0282
0283 template <typename AInt, if_is_same_int<AInt> = true>
0284 friend constexpr bool comparesEqual(QCheckedInt lhs, AInt rhs) noexcept
0285 {
0286 return lhs.m_i == rhs;
0287 }
0288
0289 friend constexpr Qt::strong_ordering compareThreeWay(QCheckedInt lhs, QCheckedInt rhs) noexcept
0290 {
0291 return Qt::compareThreeWay(lhs.m_i, rhs.m_i);
0292 }
0293
0294 template <typename AInt, if_is_same_int<AInt> = true>
0295 friend constexpr Qt::strong_ordering compareThreeWay(QCheckedInt lhs, AInt rhs) noexcept
0296 {
0297 return Qt::compareThreeWay(lhs.m_i, rhs);
0298 }
0299
0300 Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(QCheckedInt)
0301 Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(
0302 QCheckedInt,
0303 AInt,
0304 template <typename AInt, if_is_same_int<AInt> = true>)
0305
0306 private:
0307 friend size_t constexpr qHash(QCheckedInt key, size_t seed = 0) noexcept
0308 {
0309 using QT_PREPEND_NAMESPACE(qHash);
0310 return qHash(key.value(), seed);
0311 }
0312 };
0313
0314 }
0315 }
0316
0317 QT_END_NAMESPACE
0318
0319 #endif