Prepare for QByteArrayView number parsing modernization

Remove the unholy bool out parameter and make QtPrivate::to{Double,
Float,{Signed,Unsigned}Integer}() return a struct instead.

The struct contains what we'll most likely need for a full
QParsedNumber in the future: the value, an error code (always zero
atm), and a pointer to the first character that wasn't parsed (always
nullptr atm), so we don't need to change the ABI when QParsedNumber
eventually lands.

As an immediate positive contribution, even without the backend ported
away from bool out parameters, the functions can now be marked as PURE
and, in case of the FP versions, also noexcept (the int versions have
a narrow contract d/t the base argument, which, unlike the return
value, can be fixed later, by overloading).

Pick-to: 6.3
Change-Id: I67945af80a9b53d6f170502a6df3384895e82d3e
Reviewed-by: Sona Kurazyan <sona.kurazyan@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Marc Mutz 2022-02-22 16:06:08 +01:00
parent 1931689c7d
commit 1775029c9b
3 changed files with 75 additions and 35 deletions

View File

@ -3514,7 +3514,7 @@ bool QByteArray::isNull() const noexcept
return d->isNull(); return d->isNull();
} }
qlonglong QtPrivate::toSignedInteger(QByteArrayView data, bool *ok, int base) auto QtPrivate::toSignedInteger(QByteArrayView data, int base) -> ParsedNumber<qlonglong>
{ {
#if defined(QT_CHECK_RANGE) #if defined(QT_CHECK_RANGE)
if (base != 0 && (base < 2 || base > 36)) { if (base != 0 && (base < 2 || base > 36)) {
@ -3522,16 +3522,17 @@ qlonglong QtPrivate::toSignedInteger(QByteArrayView data, bool *ok, int base)
base = 10; base = 10;
} }
#endif #endif
if (data.isEmpty()) { if (data.isEmpty())
if (ok) return {};
*ok = false;
return 0;
}
return QLocaleData::bytearrayToLongLong(data, base, ok); bool ok = false;
const auto i = QLocaleData::bytearrayToLongLong(data, base, &ok);
if (ok)
return ParsedNumber(i);
return {};
} }
qulonglong QtPrivate::toUnsignedInteger(QByteArrayView data, bool *ok, int base) auto QtPrivate::toUnsignedInteger(QByteArrayView data, int base) -> ParsedNumber<qulonglong>
{ {
#if defined(QT_CHECK_RANGE) #if defined(QT_CHECK_RANGE)
if (base != 0 && (base < 2 || base > 36)) { if (base != 0 && (base < 2 || base > 36)) {
@ -3539,13 +3540,14 @@ qulonglong QtPrivate::toUnsignedInteger(QByteArrayView data, bool *ok, int base)
base = 10; base = 10;
} }
#endif #endif
if (data.isEmpty()) { if (data.isEmpty())
if (ok) return {};
*ok = false;
return 0;
}
return QLocaleData::bytearrayToUnsLongLong(data, base, ok); bool ok = false;
const auto u = QLocaleData::bytearrayToUnsLongLong(data, base, &ok);
if (ok)
return ParsedNumber(u);
return {};
} }
/*! /*!
@ -3800,14 +3802,15 @@ double QByteArray::toDouble(bool *ok) const
return QByteArrayView(*this).toDouble(ok); return QByteArrayView(*this).toDouble(ok);
} }
double QtPrivate::toDouble(QByteArrayView a, bool *ok) auto QtPrivate::toDouble(QByteArrayView a) noexcept -> ParsedNumber<double>
{ {
bool nonNullOk = false; bool nonNullOk = false;
int processed = 0; int processed = 0;
double d = qt_asciiToDouble(a.data(), a.size(), nonNullOk, processed, WhitespacesAllowed); double d = qt_asciiToDouble(a.data(), a.size(), nonNullOk, processed, WhitespacesAllowed);
if (ok) if (nonNullOk)
*ok = nonNullOk; return ParsedNumber{d};
return d; else
return {};
} }
/*! /*!
@ -3840,9 +3843,15 @@ float QByteArray::toFloat(bool *ok) const
return QLocaleData::convertDoubleToFloat(toDouble(ok), ok); return QLocaleData::convertDoubleToFloat(toDouble(ok), ok);
} }
float QtPrivate::toFloat(QByteArrayView a, bool *ok) auto QtPrivate::toFloat(QByteArrayView a) noexcept -> ParsedNumber<float>
{ {
return QLocaleData::convertDoubleToFloat(a.toDouble(ok), ok); if (const auto r = toDouble(a)) {
bool ok = true;
const auto f = QLocaleData::convertDoubleToFloat(*r, &ok);
if (ok)
return ParsedNumber(f);
}
return {};
} }
/*! /*!

View File

@ -76,10 +76,31 @@ qsizetype count(QByteArrayView haystack, QByteArrayView needle) noexcept;
[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool isValidUtf8(QByteArrayView s) noexcept; [[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool isValidUtf8(QByteArrayView s) noexcept;
[[nodiscard]] Q_CORE_EXPORT double toDouble(QByteArrayView a, bool *ok); template <typename T>
[[nodiscard]] Q_CORE_EXPORT float toFloat(QByteArrayView a, bool *ok); class ParsedNumber
[[nodiscard]] Q_CORE_EXPORT qlonglong toSignedInteger(QByteArrayView data, bool *ok, int base); {
[[nodiscard]] Q_CORE_EXPORT qulonglong toUnsignedInteger(QByteArrayView data, bool *ok, int base); T m_value;
quint32 m_error : 1;
quint32 m_reserved : 31;
void *m_reserved2 = nullptr;
public:
constexpr ParsedNumber() noexcept : m_value(), m_error(true), m_reserved(0) {}
constexpr explicit ParsedNumber(T v) : m_value(v), m_error(false), m_reserved(0) {}
// minimal optional-like API:
explicit operator bool() const noexcept { return !m_error; }
T &operator*() { Q_ASSERT(*this); return m_value; }
const T &operator*() const { Q_ASSERT(*this); return m_value; }
T *operator->() noexcept { return *this ? &m_value : nullptr; }
const T *operator->() const noexcept { return *this ? &m_value : nullptr; }
template <typename U> // not = T, as that'd allow calls that are incompatible with std::optional
T value_or(U &&u) const { return *this ? m_value : T(std::forward<U>(u)); }
};
[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION ParsedNumber<double> toDouble(QByteArrayView a) noexcept;
[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION ParsedNumber<float> toFloat(QByteArrayView a) noexcept;
[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION ParsedNumber<qlonglong> toSignedInteger(QByteArrayView data, int base);
[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION ParsedNumber<qulonglong> toUnsignedInteger(QByteArrayView data, int base);
// QByteArrayView has incomplete type here, and we can't include qbytearrayview.h, // QByteArrayView has incomplete type here, and we can't include qbytearrayview.h,
// since it includes qbytearrayalgorithms.h. Use the ByteArrayView template type as // since it includes qbytearrayalgorithms.h. Use the ByteArrayView template type as
@ -88,18 +109,18 @@ template <typename T, typename ByteArrayView,
typename = std::enable_if_t<std::is_same_v<ByteArrayView, QByteArrayView>>> typename = std::enable_if_t<std::is_same_v<ByteArrayView, QByteArrayView>>>
static inline T toIntegral(ByteArrayView data, bool *ok, int base) static inline T toIntegral(ByteArrayView data, bool *ok, int base)
{ {
auto val = [&] { const auto val = [&] {
if constexpr (std::is_unsigned_v<T>) if constexpr (std::is_unsigned_v<T>)
return toUnsignedInteger(data, ok, base); return toUnsignedInteger(data, base);
else else
return toSignedInteger(data, ok, base); return toSignedInteger(data, base);
}(); }();
if (T(val) != val) { const bool failed = !val || T(*val) != *val;
if (ok) if (ok)
*ok = false; *ok = !failed;
val = 0; if (failed)
} return 0;
return T(val); return T(*val);
} }
} // namespace QtPrivate } // namespace QtPrivate

View File

@ -261,9 +261,19 @@ public:
[[nodiscard]] qulonglong toULongLong(bool *ok = nullptr, int base = 10) const [[nodiscard]] qulonglong toULongLong(bool *ok = nullptr, int base = 10) const
{ return QtPrivate::toIntegral<qulonglong>(*this, ok, base); } { return QtPrivate::toIntegral<qulonglong>(*this, ok, base); }
[[nodiscard]] float toFloat(bool *ok = nullptr) const [[nodiscard]] float toFloat(bool *ok = nullptr) const
{ return QtPrivate::toFloat(*this, ok); } {
const auto r = QtPrivate::toFloat(*this);
if (ok)
*ok = bool(r);
return r.value_or(0.0f);
}
[[nodiscard]] double toDouble(bool *ok = nullptr) const [[nodiscard]] double toDouble(bool *ok = nullptr) const
{ return QtPrivate::toDouble(*this, ok); } {
const auto r = QtPrivate::toDouble(*this);
if (ok)
*ok = bool(r);
return r.value_or(0.0);
}
[[nodiscard]] bool startsWith(QByteArrayView other) const noexcept [[nodiscard]] bool startsWith(QByteArrayView other) const noexcept
{ return QtPrivate::startsWith(*this, other); } { return QtPrivate::startsWith(*this, other); }