Allow qMin, qMax and qBound for types that can be losslessly converted

Add overloads for qMin and friends where the arguments are of different
type, but one can be easily promoted to the other. Return the promoted
type. Promotions are only allowed if both types are either signed,
unsigned or floating point numbers.

This should simplify writing code in many case (as for example
qMin(myint64, 1)) and also help reduce source incompatibilities between
Qt 5 and Qt 6, where the return types for sizes of our containers changes
from int to qsizetype.

Change-Id: Ia6bcf16bef0469ea568063e7c32f532da610d1cd
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
bb10
Lars Knoll 2020-02-26 12:50:31 +01:00
parent 0dbd2dd863
commit 95e84c0ea9
6 changed files with 74 additions and 0 deletions

View File

@ -617,6 +617,24 @@ Q_DECL_CONSTEXPR inline qint64 qRound64(double d)
Q_DECL_CONSTEXPR inline qint64 qRound64(float d)
{ return d >= 0.0f ? qint64(d + 0.5f) : qint64(d - float(qint64(d-1)) + 0.5f) + qint64(d-1); }
namespace QTypeTraits {
namespace detail {
template<typename T, typename U,
typename = std::enable_if_t<std::is_arithmetic_v<T> && std::is_arithmetic_v<U> &&
std::is_floating_point_v<T> == std::is_floating_point_v<U> &&
std::is_signed_v<T> == std::is_signed_v<U>> >
struct Promoted
{
using type = decltype(T() + U());
};
}
template <typename T, typename U>
using Promoted = typename detail::Promoted<T, U>::type;
}
template <typename T>
constexpr inline const T &qMin(const T &a, const T &b) { return (a < b) ? a : b; }
template <typename T>
@ -624,6 +642,31 @@ constexpr inline const T &qMax(const T &a, const T &b) { return (a < b) ? b : a;
template <typename T>
constexpr inline const T &qBound(const T &min, const T &val, const T &max)
{ return qMax(min, qMin(max, val)); }
template <typename T, typename U>
constexpr inline QTypeTraits::Promoted<T, U> qMin(const T &a, const U &b)
{
using P = QTypeTraits::Promoted<T, U>;
P _a = a;
P _b = b;
return (_a < _b) ? _a : _b;
}
template <typename T, typename U>
constexpr inline QTypeTraits::Promoted<T, U> qMax(const T &a, const U &b)
{
using P = QTypeTraits::Promoted<T, U>;
P _a = a;
P _b = b;
return (_a < _b) ? _b : _a;
}
template <typename T, typename U>
constexpr inline QTypeTraits::Promoted<T, U> qBound(const T &min, const U &val, const T &max)
{ return qMax(min, qMin(max, val)); }
template <typename T, typename U>
constexpr inline QTypeTraits::Promoted<T, U> qBound(const T &min, const T &val, const U &max)
{ return qMax(min, qMin(max, val)); }
template <typename T, typename U>
constexpr inline QTypeTraits::Promoted<T, U> qBound(const U &min, const T &val, const T &max)
{ return qMax(min, qMin(max, val)); }
#ifndef Q_FORWARD_DECLARE_OBJC_CLASS
# ifdef __OBJC__

View File

@ -51,6 +51,7 @@ private slots:
void integerForSize();
void buildAbiEndianness();
void testqOverload();
void testqMinMax();
};
extern "C" { // functions in qglobal.c
@ -575,6 +576,32 @@ void tst_QGlobal::testqOverload()
#endif
}
// enforce that types are identical when comparing
template<typename T>
void compare(T a, T b)
{ QCOMPARE(a, b); }
void tst_QGlobal::testqMinMax()
{
// signed types
compare(qMin(float(1), double(-1)), double(-1));
compare(qMin(double(1), float(-1)), double(-1));
compare(qMin(short(1), int(-1)), int(-1));
compare(qMin(short(1), long(-1)), long(-1));
compare(qMin(qint64(1), short(-1)), qint64(-1));
compare(qMax(float(1), double(-1)), double(1));
compare(qMax(short(1), long(-1)), long(1));
compare(qMax(qint64(1), short(-1)), qint64(1));
// unsigned types
compare(qMin(ushort(1), ulong(2)), ulong(1));
compare(qMin(quint64(1), ushort(2)), quint64(1));
compare(qMax(ushort(1), ulong(2)), ulong(2));
compare(qMax(quint64(1), ushort(2)), quint64(2));
}
QTEST_APPLESS_MAIN(tst_QGlobal)
#include "tst_qglobal.moc"

View File

@ -26,6 +26,7 @@
**
****************************************************************************/
#include <QtCore/qglobal.h>
// SCENARIO 1
// this is the "no harm done" version. Only operator% is active,

View File

@ -26,6 +26,7 @@
**
****************************************************************************/
#include <QtCore/qglobal.h>
// SCENARIO 2
// this is the "full" version. Operator+ is replaced by a QStringBuilder

View File

@ -26,6 +26,7 @@
**
****************************************************************************/
#include <QtCore/qglobal.h>
// SCENARIO 3
// this is the "no harm done" version. Only operator% is active,

View File

@ -26,6 +26,7 @@
**
****************************************************************************/
#include <QtCore/qglobal.h>
// SCENARIO 4
// this is the "full" version. Operator+ is replaced by a QStringBuilder