Core: Add a QTypeRevision class
QTypeRevision is needed to encode the value of the new two-argument Q_REVISION(major, minor) macros. Those, in turn are necessary because the minor version resets to 0, and we need to take the major version into account when stating revisions for Qt classes. Task-number: QTBUG-71278 Change-Id: I63eff6eab7d6e4f8f32b359a216767c98947a106 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Paul Wicking <paul.wicking@qt.io> Reviewed-by: Simon Hausmann <simon.hausmann@qt.io> Reviewed-by: Lars Knoll <lars.knoll@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
This commit is contained in:
parent
daf1f3f9ce
commit
ed080c64ae
@ -556,5 +556,199 @@ uint qHash(const QVersionNumber &key, uint seed)
|
||||
return seed;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
/*!
|
||||
\class QTypeRevision
|
||||
\inmodule QtCore
|
||||
\since 6.0
|
||||
\brief The QTypeRevision class contains a lightweight representation of
|
||||
a version number with two 8-bit segments, major and minor, either
|
||||
of which can be unknown.
|
||||
|
||||
Use this class to describe revisions of a type. Compatible revisions can be
|
||||
expressed as increments of the minor version. Breaking changes can be
|
||||
expressed as increments of the major version. The return values of
|
||||
\l QMetaMethod::revision() and \l QMetaProperty::revision() can be passed to
|
||||
\l QTypeRevision::fromEncodedVersion(). The resulting major and minor versions
|
||||
specify in which Qt versions the properties and methods were added.
|
||||
|
||||
\sa QMetaMethod::revision(), QMetaProperty::revision()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template<typename Integer> static bool QTypeRevision::isValidSegment(Integer segment)
|
||||
|
||||
Returns true if the given number can be used as either major or minor
|
||||
version in a QTypeRevision. Valid segments need to be \c {>= 0} and \c {< 255}.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QTypeRevision::QTypeRevision()
|
||||
|
||||
Produces an invalid revision.
|
||||
|
||||
\sa isValid()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename Major, typename Minor> static QTypeRevision QTypeRevision::fromVersion(Major majorVersion, Minor minorVersion)
|
||||
|
||||
Produces a QTypeRevision from the given \a majorVersion and \a minorVersion,
|
||||
both of which need to be a valid segments.
|
||||
|
||||
\sa isValidSegment()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename Major> static QTypeRevision QTypeRevision::fromMajorVersion(Major majorVersion)
|
||||
|
||||
Produces a QTypeRevision from the given \a majorVersion with an invalid minor
|
||||
version. \a majorVersion needs to be a valid segment.
|
||||
|
||||
\sa isValidSegment()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename Minor> static QTypeRevision QTypeRevision::fromMinorVersion(Minor minorVersion)
|
||||
|
||||
Produces a QTypeRevision from the given \a minorVersion with an invalid major
|
||||
version. \a minorVersion needs to be a valid segment.
|
||||
|
||||
\sa isValidSegment()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename Integer> static QTypeRevision QTypeRevision::fromEncodedVersion(Integer value)
|
||||
|
||||
Produces a QTypeRevision from the given \a value. \a value encodes both the
|
||||
minor and major versions in the least significant and second least
|
||||
significant byte, respectively.
|
||||
|
||||
\a value must not have any bits outside the least significant two bytes set.
|
||||
\c Integer needs to be at least 16 bits wide, and must not have a sign bit
|
||||
in the least significant 16 bits.
|
||||
|
||||
\sa toEncodedVersion()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn static QTypeRevision QTypeRevision::zero()
|
||||
|
||||
Produces a QTypeRevision with major and minor version \c{0}.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn bool QTypeRevision::hasMajorVersion() const
|
||||
|
||||
Returns true if the major version is known, otherwise false.
|
||||
|
||||
\sa majorVersion(), hasMinorVersion()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn quint8 QTypeRevision::majorVersion() const
|
||||
|
||||
Returns the major version encoded in the revision.
|
||||
|
||||
\sa hasMajorVersion(), minorVersion()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn bool QTypeRevision::hasMinorVersion() const
|
||||
|
||||
Returns true if the minor version is known, otherwise false.
|
||||
|
||||
\sa minorVersion(), hasMajorVersion()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn quint8 QTypeRevision::minorVersion() const
|
||||
|
||||
Returns the minor version encoded in the revision.
|
||||
|
||||
\sa hasMinorVersion(), majorVersion()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn bool QTypeRevision::isValid() const
|
||||
|
||||
Returns true if the major version or the minor version is known,
|
||||
otherwise false.
|
||||
|
||||
\sa hasMajorVersion(), hasMinorVersion()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template<typename Integer> Integer QTypeRevision::toEncodedVersion() const
|
||||
|
||||
Transforms the revision into an integer value, encoding the minor
|
||||
version into the least significant byte, and the major version into
|
||||
the second least significant byte.
|
||||
|
||||
\c Integer needs to be at least 16 bits wide, and must not have a sign bit
|
||||
in the least significant 16 bits.
|
||||
|
||||
\sa fromEncodedVersion()
|
||||
*/
|
||||
|
||||
#ifndef QT_NO_DATASTREAM
|
||||
/*!
|
||||
\fn QDataStream& operator<<(QDataStream &out, const QTypeRevision &revision)
|
||||
\relates QTypeRevision
|
||||
\since 6.0
|
||||
|
||||
Writes the revision \a revision to stream \a out.
|
||||
*/
|
||||
QDataStream& operator<<(QDataStream &out, const QTypeRevision &revision)
|
||||
{
|
||||
return out << revision.toEncodedVersion<quint16>();
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QDataStream& operator>>(QDataStream &in, QTypeRevision &revision)
|
||||
\relates QTypeRevision
|
||||
\since 6.0
|
||||
|
||||
Reads a revision from stream \a in and stores it in \a revision.
|
||||
*/
|
||||
QDataStream& operator>>(QDataStream &in, QTypeRevision &revision)
|
||||
{
|
||||
quint16 value;
|
||||
in >> value;
|
||||
revision = QTypeRevision::fromEncodedVersion(value);
|
||||
return in;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef QT_NO_DEBUG_STREAM
|
||||
QDebug operator<<(QDebug debug, const QTypeRevision &revision)
|
||||
{
|
||||
QDebugStateSaver saver(debug);
|
||||
if (revision.hasMajorVersion()) {
|
||||
if (revision.hasMinorVersion())
|
||||
debug.nospace() << revision.majorVersion() << '.' << revision.minorVersion();
|
||||
else
|
||||
debug.nospace().noquote() << revision.majorVersion() << ".x";
|
||||
} else {
|
||||
if (revision.hasMinorVersion())
|
||||
debug << revision.minorVersion();
|
||||
else
|
||||
debug.noquote() << "invalid";
|
||||
}
|
||||
return debug;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
\fn uint qHash(const QTypeRevision &key, uint seed)
|
||||
\relates QHash
|
||||
\since 6.0
|
||||
|
||||
Returns the hash value for the \a key, using \a seed to seed the
|
||||
calculation.
|
||||
*/
|
||||
uint qHash(const QTypeRevision &key, uint seed)
|
||||
{
|
||||
return qHash(key.toEncodedVersion<quint16>(), seed);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -309,8 +309,123 @@ Q_REQUIRED_RESULT inline bool operator==(const QVersionNumber &lhs, const QVersi
|
||||
Q_REQUIRED_RESULT inline bool operator!=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
|
||||
{ return QVersionNumber::compare(lhs, rhs) != 0; }
|
||||
|
||||
class QTypeRevision;
|
||||
Q_CORE_EXPORT uint qHash(const QTypeRevision &key, uint seed = 0);
|
||||
|
||||
#ifndef QT_NO_DATASTREAM
|
||||
Q_CORE_EXPORT QDataStream& operator<<(QDataStream &out, const QTypeRevision &revision);
|
||||
Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QTypeRevision &revision);
|
||||
#endif
|
||||
|
||||
class QTypeRevision
|
||||
{
|
||||
public:
|
||||
template<typename Integer>
|
||||
using if_valid_segment_type = typename std::enable_if<
|
||||
std::is_integral<Integer>::value, bool>::type;
|
||||
|
||||
template<typename Integer>
|
||||
using if_valid_value_type = typename std::enable_if<
|
||||
std::is_integral<Integer>::value
|
||||
&& (sizeof(Integer) > sizeof(quint16)
|
||||
|| (sizeof(Integer) == sizeof(quint16)
|
||||
&& !std::is_signed<Integer>::value)), bool>::type;
|
||||
|
||||
template<typename Integer, if_valid_segment_type<Integer> = true>
|
||||
static constexpr bool isValidSegment(Integer segment)
|
||||
{
|
||||
return segment >= Integer(0) && segment < Integer(SegmentUnknown);
|
||||
}
|
||||
|
||||
static constexpr bool isValidSegment(qint8 segment) { return segment >= 0; }
|
||||
|
||||
template<typename Major, typename Minor,
|
||||
if_valid_segment_type<Major> = true,
|
||||
if_valid_segment_type<Minor> = true>
|
||||
static constexpr QTypeRevision fromVersion(Major majorVersion, Minor minorVersion)
|
||||
{
|
||||
Q_ASSERT(isValidSegment(majorVersion));
|
||||
Q_ASSERT(isValidSegment(minorVersion));
|
||||
return QTypeRevision(quint8(majorVersion), quint8(minorVersion));
|
||||
}
|
||||
|
||||
template<typename Major, if_valid_segment_type<Major> = true>
|
||||
static constexpr QTypeRevision fromMajorVersion(Major majorVersion)
|
||||
{
|
||||
Q_ASSERT(isValidSegment(majorVersion));
|
||||
return QTypeRevision(quint8(majorVersion), SegmentUnknown);
|
||||
}
|
||||
|
||||
template<typename Minor, if_valid_segment_type<Minor> = true>
|
||||
static constexpr QTypeRevision fromMinorVersion(Minor minorVersion)
|
||||
{
|
||||
Q_ASSERT(isValidSegment(minorVersion));
|
||||
return QTypeRevision(SegmentUnknown, quint8(minorVersion));
|
||||
}
|
||||
|
||||
template<typename Integer, if_valid_value_type<Integer> = true>
|
||||
static constexpr QTypeRevision fromEncodedVersion(Integer value)
|
||||
{
|
||||
Q_ASSERT((value & ~Integer(0xffff)) == Integer(0));
|
||||
return QTypeRevision((value & Integer(0xff00)) >> 8, value & Integer(0xff));
|
||||
}
|
||||
|
||||
static constexpr QTypeRevision zero() { return QTypeRevision(0, 0); }
|
||||
|
||||
constexpr QTypeRevision() = default;
|
||||
|
||||
constexpr bool hasMajorVersion() const { return m_majorVersion != SegmentUnknown; }
|
||||
constexpr quint8 majorVersion() const { return m_majorVersion; }
|
||||
|
||||
constexpr bool hasMinorVersion() const { return m_minorVersion != SegmentUnknown; }
|
||||
constexpr quint8 minorVersion() const { return m_minorVersion; }
|
||||
|
||||
constexpr bool isValid() const { return hasMajorVersion() || hasMinorVersion(); }
|
||||
|
||||
template<typename Integer, if_valid_value_type<Integer> = true>
|
||||
constexpr Integer toEncodedVersion() const
|
||||
{
|
||||
return Integer(m_majorVersion << 8) | Integer(m_minorVersion);
|
||||
}
|
||||
|
||||
private:
|
||||
enum { SegmentUnknown = quint8(~0U) };
|
||||
|
||||
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
||||
constexpr QTypeRevision(quint8 major, quint8 minor)
|
||||
: m_minorVersion(minor), m_majorVersion(major) {}
|
||||
|
||||
quint8 m_minorVersion = SegmentUnknown;
|
||||
quint8 m_majorVersion = SegmentUnknown;
|
||||
#else
|
||||
constexpr QTypeRevision(quint8 major, quint8 minor)
|
||||
: m_majorVersion(major), m_minorVersion(minor) {}
|
||||
|
||||
quint8 m_majorVersion = SegmentUnknown;
|
||||
quint8 m_minorVersion = SegmentUnknown;
|
||||
#endif
|
||||
};
|
||||
|
||||
inline constexpr bool operator==(QTypeRevision lhs, QTypeRevision rhs)
|
||||
{
|
||||
return lhs.toEncodedVersion<quint16>() == rhs.toEncodedVersion<quint16>();
|
||||
}
|
||||
|
||||
inline constexpr bool operator!=(QTypeRevision lhs, QTypeRevision rhs)
|
||||
{
|
||||
return lhs.toEncodedVersion<quint16>() != rhs.toEncodedVersion<quint16>();
|
||||
}
|
||||
|
||||
Q_STATIC_ASSERT(sizeof(QTypeRevision) == 2);
|
||||
Q_DECLARE_TYPEINFO(QTypeRevision, Q_MOVABLE_TYPE);
|
||||
|
||||
#ifndef QT_NO_DEBUG_STREAM
|
||||
Q_CORE_EXPORT QDebug operator<<(QDebug, const QTypeRevision &revision);
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
Q_DECLARE_METATYPE(QVersionNumber)
|
||||
Q_DECLARE_METATYPE(QTypeRevision)
|
||||
|
||||
#endif //QVERSIONNUMBER_H
|
||||
|
@ -81,6 +81,9 @@ private slots:
|
||||
void serialize();
|
||||
void moveSemantics();
|
||||
void qtVersion();
|
||||
void qPropertyRevision_data();
|
||||
void qPropertyRevision();
|
||||
void qPropertyRevisionTypes();
|
||||
};
|
||||
|
||||
void tst_QVersionNumber::singleInstanceData()
|
||||
@ -645,6 +648,118 @@ void tst_QVersionNumber::qtVersion()
|
||||
QCOMPARE(v.toString(), QString(qVersion()));
|
||||
}
|
||||
|
||||
template<typename Integer>
|
||||
void compileTestRevisionMajorMinor()
|
||||
{
|
||||
const Integer major = 8;
|
||||
const Integer minor = 4;
|
||||
|
||||
const QTypeRevision r2 = QTypeRevision::fromVersion(major, minor);
|
||||
QCOMPARE(r2.majorVersion(), 8);
|
||||
QCOMPARE(r2.minorVersion(), 4);
|
||||
|
||||
const QTypeRevision r3 = QTypeRevision::fromMajorVersion(major);
|
||||
QCOMPARE(r3.majorVersion(), 8);
|
||||
QVERIFY(!r3.hasMinorVersion());
|
||||
|
||||
const QTypeRevision r4 = QTypeRevision::fromMinorVersion(minor);
|
||||
QVERIFY(!r4.hasMajorVersion());
|
||||
QCOMPARE(r4.minorVersion(), 4);
|
||||
}
|
||||
|
||||
|
||||
template<typename Integer>
|
||||
void compileTestRevision()
|
||||
{
|
||||
if (std::is_signed<Integer>::value)
|
||||
compileTestRevision<typename QIntegerForSize<sizeof(Integer) / 2>::Signed>();
|
||||
else
|
||||
compileTestRevision<typename QIntegerForSize<sizeof(Integer) / 2>::Unsigned>();
|
||||
|
||||
const Integer value = 0x0510;
|
||||
const QTypeRevision r = QTypeRevision::fromEncodedVersion(value);
|
||||
|
||||
QCOMPARE(r.majorVersion(), 5);
|
||||
QCOMPARE(r.minorVersion(), 16);
|
||||
QCOMPARE(r.toEncodedVersion<Integer>(), value);
|
||||
|
||||
compileTestRevisionMajorMinor<Integer>();
|
||||
}
|
||||
|
||||
template<>
|
||||
void compileTestRevision<qint16>()
|
||||
{
|
||||
compileTestRevisionMajorMinor<quint8>();
|
||||
}
|
||||
|
||||
template<>
|
||||
void compileTestRevision<quint8>()
|
||||
{
|
||||
compileTestRevisionMajorMinor<quint8>();
|
||||
}
|
||||
|
||||
template<>
|
||||
void compileTestRevision<qint8>()
|
||||
{
|
||||
compileTestRevisionMajorMinor<qint8>();
|
||||
}
|
||||
|
||||
void tst_QVersionNumber::qPropertyRevision_data()
|
||||
{
|
||||
QTest::addColumn<QTypeRevision>("revision");
|
||||
QTest::addColumn<bool>("valid");
|
||||
QTest::addColumn<int>("major");
|
||||
QTest::addColumn<int>("minor");
|
||||
|
||||
QTest::addRow("Qt revision") << QTypeRevision::fromVersion(QT_VERSION_MAJOR, QT_VERSION_MINOR)
|
||||
<< true << QT_VERSION_MAJOR << QT_VERSION_MINOR;
|
||||
QTest::addRow("invalid") << QTypeRevision() << false << 0xff << 0xff;
|
||||
QTest::addRow("major") << QTypeRevision::fromMajorVersion(6) << true << 6 << 0xff;
|
||||
QTest::addRow("minor") << QTypeRevision::fromMinorVersion(15) << true << 0xff << 15;
|
||||
QTest::addRow("zero") << QTypeRevision::fromVersion(0, 0) << true << 0 << 0;
|
||||
|
||||
// We're intentionally not testing negative numbers.
|
||||
// There are asserts against negative numbers in QTypeRevision.
|
||||
// You must not pass them as major or minor versions, or values.
|
||||
}
|
||||
|
||||
void tst_QVersionNumber::qPropertyRevision()
|
||||
{
|
||||
const QTypeRevision other = QTypeRevision::fromVersion(127, 128);
|
||||
|
||||
QFETCH(QTypeRevision, revision);
|
||||
|
||||
QFETCH(bool, valid);
|
||||
QFETCH(int, major);
|
||||
QFETCH(int, minor);
|
||||
|
||||
QCOMPARE(revision.isValid(), valid);
|
||||
QCOMPARE(revision.majorVersion(), major);
|
||||
QCOMPARE(revision.minorVersion(), minor);
|
||||
|
||||
QCOMPARE(revision.hasMajorVersion(), QTypeRevision::isValidSegment(major));
|
||||
QCOMPARE(revision.hasMinorVersion(), QTypeRevision::isValidSegment(minor));
|
||||
|
||||
const QTypeRevision copy = QTypeRevision::fromEncodedVersion(revision.toEncodedVersion<int>());
|
||||
QCOMPARE(copy, revision);
|
||||
|
||||
QVERIFY(revision != other);
|
||||
QVERIFY(copy != other);
|
||||
}
|
||||
|
||||
void tst_QVersionNumber::qPropertyRevisionTypes()
|
||||
{
|
||||
compileTestRevision<quint64>();
|
||||
compileTestRevision<qint64>();
|
||||
|
||||
QVERIFY(!QTypeRevision::isValidSegment(0xff));
|
||||
QVERIFY(!QTypeRevision::isValidSegment(-1));
|
||||
|
||||
const QTypeRevision maxRevision = QTypeRevision::fromVersion(254, 254);
|
||||
QVERIFY(maxRevision.hasMajorVersion());
|
||||
QVERIFY(maxRevision.hasMinorVersion());
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_QVersionNumber)
|
||||
|
||||
#include "tst_qversionnumber.moc"
|
||||
|
Loading…
x
Reference in New Issue
Block a user