QGrpc{Call,Channel}Options: provide noexcept guarantees to metadata()

Before, we did a lazy initialization in the deprecated noexcept getters
to provide a way of not storing the metadata twice. There is just no way
to preserve correctness whilst providing this option.

Whilst const-ref return values provide the most efficient way to access
them, it also most tightly constrains the implementation. Let that be a
lesson ...

We also remove the free-standing 'operator==' as we're comparing with
the matching containers now, there is no need for them (They should also
not be provided like this from QtGrpc, even though private).

Amends: 778371b8ea.

Pick-to: 6.10
Change-Id: I05c4a1f7d2eab00f41ebcad54d9a096b2dcdf540
Reviewed-by: Alexey Edelev <alexey.edelev@qt.io>
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
This commit is contained in:
Dennis Oberst 2025-07-11 14:20:30 +02:00
parent f636865a23
commit 7695e0fc90
5 changed files with 26 additions and 69 deletions

View File

@ -12,7 +12,6 @@
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
using namespace QtGrpcPrivate;
/*!
\class QGrpcCallOptions
@ -177,7 +176,7 @@ QHash<QByteArray, QByteArray> QGrpcCallOptions::metadata() &&
*/
QGrpcCallOptions &QGrpcCallOptions::setMetadata(const QHash<QByteArray, QByteArray> &metadata)
{
if (d_ptr->metadata(QtGrpc::MultiValue) == metadata)
if (d_ptr->metadata() == metadata)
return *this;
d_ptr.detach();
Q_D(QGrpcCallOptions);
@ -186,7 +185,7 @@ QGrpcCallOptions &QGrpcCallOptions::setMetadata(const QHash<QByteArray, QByteArr
}
QGrpcCallOptions &QGrpcCallOptions::setMetadata(QHash<QByteArray, QByteArray> &&metadata)
{
if (d_ptr->metadata(QtGrpc::MultiValue) == metadata)
if (d_ptr->metadata() == metadata)
return *this;
d_ptr.detach();
Q_D(QGrpcCallOptions);

View File

@ -14,7 +14,6 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
using namespace QtGrpc;
using namespace QtGrpcPrivate;
/*!
\class QGrpcChannelOptions
@ -183,7 +182,7 @@ QHash<QByteArray, QByteArray> QGrpcChannelOptions::metadata() &&
*/
QGrpcChannelOptions &QGrpcChannelOptions::setMetadata(const QHash<QByteArray, QByteArray> &metadata)
{
if (d_ptr->metadata(QtGrpc::MultiValue) == metadata)
if (d_ptr->metadata() == metadata)
return *this;
d_ptr.detach();
Q_D(QGrpcChannelOptions);
@ -192,7 +191,7 @@ QGrpcChannelOptions &QGrpcChannelOptions::setMetadata(const QHash<QByteArray, QB
}
QGrpcChannelOptions &QGrpcChannelOptions::setMetadata(QHash<QByteArray, QByteArray> &&metadata)
{
if (d_ptr->metadata(QtGrpc::MultiValue) == metadata)
if (d_ptr->metadata() == metadata)
return *this;
d_ptr.detach();
Q_D(QGrpcChannelOptions);

View File

@ -7,10 +7,8 @@ QT_BEGIN_NAMESPACE
#if QT_DEPRECATED_SINCE(6, 13)
namespace QtGrpcPrivate {
QHash<QByteArray, QByteArray>
toHash(const QMultiHash<QByteArray, QByteArray> &multiHash)
QtGrpcPrivate::toHash(const QMultiHash<QByteArray, QByteArray> &multiHash)
{
// Transform a QMultiHash into a QHash by keeping only the first value for each key.
// The first value will be the newest one when iterating the multi-hash.
@ -21,47 +19,19 @@ toHash(const QMultiHash<QByteArray, QByteArray> &multiHash)
return out;
}
bool operator==(const QMultiHash<QByteArray, QByteArray> &multiHash,
const QHash<QByteArray, QByteArray> &hash)
{
if (hash.size() != multiHash.size())
return false;
for (const auto &[k, v] : hash.asKeyValueRange()) {
const auto [f, l] = multiHash.equal_range(k);
if (f == l || std::next(f) != l || *f != v)
return false;
}
return true;
}
inline bool operator!=(const QMultiHash<QByteArray, QByteArray> &multiHash,
const QHash<QByteArray, QByteArray> &hash)
{
return !(multiHash == hash);
}
}
using namespace QtGrpcPrivate;
/*!
//! [metadata]
Returns the metadata. If this field is unset, returns empty
metadata.
//! [metadata]
*/
const QHash<QByteArray, QByteArray> &QGrpcCommonOptions::metadata() const &
const QHash<QByteArray, QByteArray> &QGrpcCommonOptions::metadata() const & noexcept
{
m_deprecatedQHashRefUsed = true;
if (m_metadataMulti != m_metadata)
m_metadata = toHash(m_metadataMulti);
return m_metadata;
return m_deprecatedMetadata;
}
QHash<QByteArray, QByteArray> QGrpcCommonOptions::metadata() &&
{
if (m_metadataMulti != m_metadata)
m_metadata = toHash(m_metadataMulti);
return std::move(m_metadata);
return std::move(m_deprecatedMetadata);
}
/*!
@ -82,15 +52,13 @@ QHash<QByteArray, QByteArray> QGrpcCommonOptions::metadata() &&
*/
void QGrpcCommonOptions::setMetadata(const QHash<QByteArray, QByteArray> &md)
{
if (m_deprecatedQHashRefUsed)
m_metadata = md;
m_metadataMulti = QMultiHash<QByteArray, QByteArray>(md);
m_deprecatedMetadata = md;
m_metadata = QMultiHash<QByteArray, QByteArray>(md);
}
void QGrpcCommonOptions::setMetadata(QHash<QByteArray, QByteArray> &&md)
{
if (m_deprecatedQHashRefUsed)
m_metadata = md;
m_metadataMulti = QMultiHash<QByteArray, QByteArray>(std::move(md));
m_deprecatedMetadata = std::move(md);
m_metadata = QMultiHash<QByteArray, QByteArray>(m_deprecatedMetadata);
}
#endif // QT_DEPRECATED_SINCE(6, 13)
@ -136,13 +104,13 @@ void QGrpcCommonOptions::setDeadlineTimeout(std::chrono::milliseconds t)
//! [metadata-multi]
*/
const QMultiHash<QByteArray, QByteArray> &
QGrpcCommonOptions::metadata(QtGrpc::MultiValue_t /*tag*/) const &
QGrpcCommonOptions::metadata(QtGrpc::MultiValue_t /*tag*/) const & noexcept
{
return m_metadataMulti;
return m_metadata;
}
QMultiHash<QByteArray, QByteArray> QGrpcCommonOptions::metadata(QtGrpc::MultiValue_t /*tag*/) &&
{
return std::move(m_metadataMulti);
return std::move(m_metadata);
}
/*!
@ -153,19 +121,17 @@ QMultiHash<QByteArray, QByteArray> QGrpcCommonOptions::metadata(QtGrpc::MultiVal
*/
void QGrpcCommonOptions::setMetadata(const QMultiHash<QByteArray, QByteArray> &md)
{
m_metadataMulti = md;
m_metadata = md;
#if QT_DEPRECATED_SINCE(6, 13)
if (m_deprecatedQHashRefUsed)
m_metadata = toHash(m_metadataMulti);
m_deprecatedMetadata = QtGrpcPrivate::toHash(m_metadata);
#endif
}
void QGrpcCommonOptions::setMetadata(QMultiHash<QByteArray, QByteArray> &&md)
{
m_metadataMulti = std::move(md);
m_metadata = std::move(md);
#if QT_DEPRECATED_SINCE(6, 13)
if (m_deprecatedQHashRefUsed)
m_metadata = toHash(m_metadataMulti);
m_deprecatedMetadata = QtGrpcPrivate::toHash(m_metadata);
#endif
}
@ -183,10 +149,9 @@ void QGrpcCommonOptions::setMetadata(QMultiHash<QByteArray, QByteArray> &&md)
void QGrpcCommonOptions::addMetadata(QByteArray &&key, QByteArray &&value)
{
#if QT_DEPRECATED_SINCE(6, 13)
if (m_deprecatedQHashRefUsed)
m_metadata.insertOrAssign(key, value);
m_deprecatedMetadata.insertOrAssign(key, value);
#endif
m_metadataMulti.emplace(std::move(key), std::move(value));
m_metadata.emplace(std::move(key), std::move(value));
}
bool QGrpcCommonOptions::containsMetadata(QByteArrayView key, QByteArrayView value) const

View File

@ -37,12 +37,13 @@ public:
void setDeadlineTimeout(std::chrono::milliseconds t);
#if QT_DEPRECATED_SINCE(6, 13)
const QHash<QByteArray, QByteArray> &metadata() const &;
const QHash<QByteArray, QByteArray> &metadata() const & noexcept;
QHash<QByteArray, QByteArray> metadata() &&;
void setMetadata(const QHash<QByteArray, QByteArray> &md);
void setMetadata(QHash<QByteArray, QByteArray> &&md);
#endif
const QMultiHash<QByteArray, QByteArray> &metadata(QtGrpc::MultiValue_t /*tag*/) const &;
const QMultiHash<QByteArray, QByteArray> &
metadata(QtGrpc::MultiValue_t /*tag*/) const & noexcept;
QMultiHash<QByteArray, QByteArray> metadata(QtGrpc::MultiValue_t /*tag*/) &&;
void setMetadata(const QMultiHash<QByteArray, QByteArray> &md);
void setMetadata(QMultiHash<QByteArray, QByteArray> &&md);
@ -51,19 +52,14 @@ public:
private:
std::optional<std::chrono::milliseconds> m_timeout;
QMultiHash<QByteArray, QByteArray> m_metadataMulti;
QMultiHash<QByteArray, QByteArray> m_metadata;
#if QT_DEPRECATED_SINCE(6, 13)
mutable QHash<QByteArray, QByteArray> m_metadata;
mutable bool m_deprecatedQHashRefUsed = false;
QHash<QByteArray, QByteArray> m_deprecatedMetadata;
#endif
};
namespace QtGrpcPrivate {
bool operator==(const QMultiHash<QByteArray, QByteArray> &multiHash,
const QHash<QByteArray, QByteArray> &hash);
bool operator!=(const QMultiHash<QByteArray, QByteArray> &multiHash,
const QHash<QByteArray, QByteArray> &hash);
#if QT_DEPRECATED_SINCE(6, 13)
QHash<QByteArray, QByteArray> toHash(const QMultiHash<QByteArray, QByteArray> &multiHash);
#endif

View File

@ -138,9 +138,7 @@ QT_WARNING_DISABLE_DEPRECATED
QCOMPARE_NE(mdRef, md);
md.insert("keyD", "valD");
QCOMPARE_EQ(mdRef, md);
// Check that the handed out reference gets updates for QHash setter
o1.setMetadata(md);
QCOMPARE_EQ(multiMdRef, toMulti(md));
QCOMPARE_EQ(mdRef, md);
// Check shared state mutation due to lazy evaluation in metadata()