QGrpcSerializationFormat: transform into implicitly shared value class

As this class is copyable and really looks like a implicitly shared
value class transform it into such.

Also extend the testcase for this class.

As a drive-by use a view for the suffix() getter.

Task-number: QTBUG-123625
Pick-to: 6.8
Change-Id: I331c62e666a88e4ff6afbe5d484c639f5c96782e
Reviewed-by:  Alexey Edelev <alexey.edelev@qt.io>
This commit is contained in:
Dennis Oberst 2024-07-16 16:56:52 +02:00
parent 72a4cc0af6
commit c746115e11
4 changed files with 137 additions and 49 deletions

View File

@ -391,7 +391,7 @@ void QGrpcHttp2ChannelPrivate::Http2Handler::attachStream(QHttp2Stream *stream_)
QObject::connect(m_stream.get(), &QHttp2Stream::uploadFinished, this, QObject::connect(m_stream.get(), &QHttp2Stream::uploadFinished, this,
&Http2Handler::processQueue); &Http2Handler::processQueue);
std::optional<QGrpcDuration> deadline; std::optional<std::chrono::milliseconds> deadline;
if (channelOpPtr->callOptions().deadline()) if (channelOpPtr->callOptions().deadline())
deadline = channelOpPtr->callOptions().deadline(); deadline = channelOpPtr->callOptions().deadline();
else if (parentChannel->channelOptions.deadline()) else if (parentChannel->channelOptions.deadline())
@ -589,7 +589,7 @@ QGrpcHttp2ChannelPrivate::QGrpcHttp2ChannelPrivate(const QUrl &uri,
const QGrpcChannelOptions &options) const QGrpcChannelOptions &options)
: hostUri(uri), channelOptions(options) : hostUri(uri), channelOptions(options)
{ {
const QByteArray formatSuffix = channelOptions.serializationFormat().suffix(); auto formatSuffix = channelOptions.serializationFormat().suffix();
const QByteArray defaultContentType = DefaultContentType.toByteArray(); const QByteArray defaultContentType = DefaultContentType.toByteArray();
const QByteArray contentTypeFromOptions = !formatSuffix.isEmpty() const QByteArray contentTypeFromOptions = !formatSuffix.isEmpty()
? defaultContentType + '+' + formatSuffix ? defaultContentType + '+' + formatSuffix

View File

@ -6,6 +6,10 @@
#include <QtProtobuf/qprotobufjsonserializer.h> #include <QtProtobuf/qprotobufjsonserializer.h>
#include <QtProtobuf/qprotobufserializer.h> #include <QtProtobuf/qprotobufserializer.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qdebug.h>
#include <QtCore/qvariant.h>
using namespace QtGrpc; using namespace QtGrpc;
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -13,6 +17,7 @@ QT_BEGIN_NAMESPACE
/*! /*!
\class QGrpcSerializationFormat \class QGrpcSerializationFormat
\inmodule QtGrpc \inmodule QtGrpc
\compares equality
\brief The QGrpcSerializationFormat class holds the protobuf message \brief The QGrpcSerializationFormat class holds the protobuf message
serializer and the related content type suffix. serializer and the related content type suffix.
\since 6.8 \since 6.8
@ -26,7 +31,7 @@ QT_BEGIN_NAMESPACE
\sa QAbstractGrpcChannel \sa QAbstractGrpcChannel
*/ */
class QGrpcSerializationFormatPrivate class QGrpcSerializationFormatPrivate : public QSharedData
{ {
public: public:
QGrpcSerializationFormatPrivate(QByteArrayView suffix_, QGrpcSerializationFormatPrivate(QByteArrayView suffix_,
@ -34,14 +39,12 @@ public:
: suffix(suffix_.toByteArray()), serializer(std::move(serializer_)) : suffix(suffix_.toByteArray()), serializer(std::move(serializer_))
{ {
} }
QByteArray suffix; QByteArray suffix;
std::shared_ptr<QAbstractProtobufSerializer> serializer; std::shared_ptr<QAbstractProtobufSerializer> serializer;
}; };
static void dPtrDeleter(QGrpcSerializationFormatPrivate *ptr) QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QGrpcSerializationFormatPrivate)
{
delete ptr;
}
/*! /*!
Creates a new QGrpcSerializationFormat object with the given preset Creates a new QGrpcSerializationFormat object with the given preset
@ -50,14 +53,13 @@ static void dPtrDeleter(QGrpcSerializationFormatPrivate *ptr)
A \l QtGrpc::SerializationFormat::Default format is used by default. A \l QtGrpc::SerializationFormat::Default format is used by default.
*/ */
QGrpcSerializationFormat::QGrpcSerializationFormat(SerializationFormat format) QGrpcSerializationFormat::QGrpcSerializationFormat(SerializationFormat format)
: dPtr(format == SerializationFormat::Json : d_ptr(format == SerializationFormat::Json
? new QGrpcSerializationFormatPrivate("json", ? new QGrpcSerializationFormatPrivate("json",
std::make_shared<QProtobufJsonSerializer>()) std::make_shared<QProtobufJsonSerializer>())
: new QGrpcSerializationFormatPrivate(format == SerializationFormat::Protobuf : new QGrpcSerializationFormatPrivate(format == SerializationFormat::Protobuf
? "proto" ? "proto"
: "", : "",
std::make_shared<QProtobufSerializer>()), std::make_shared<QProtobufSerializer>()))
dPtrDeleter)
{ {
} }
@ -73,29 +75,20 @@ QGrpcSerializationFormat::~QGrpcSerializationFormat() = default;
QGrpcSerializationFormat::QGrpcSerializationFormat(QByteArrayView suffix, QGrpcSerializationFormat::QGrpcSerializationFormat(QByteArrayView suffix,
std::shared_ptr<QAbstractProtobufSerializer> std::shared_ptr<QAbstractProtobufSerializer>
serializer) serializer)
: dPtr(new QGrpcSerializationFormatPrivate(suffix, std::move(serializer)), : d_ptr(new QGrpcSerializationFormatPrivate(suffix, std::move(serializer)))
dPtrDeleter)
{ {
} }
/*! /*!
Constructs a copy of \a other. Constructs a copy of \a other.
*/ */
QGrpcSerializationFormat::QGrpcSerializationFormat(const QGrpcSerializationFormat &other) QGrpcSerializationFormat::QGrpcSerializationFormat(const QGrpcSerializationFormat &other) = default;
: dPtr(new QGrpcSerializationFormatPrivate(*other.dPtr),
dPtrDeleter)
{
}
/*! /*!
Assigns the \a other QGrpcSerializationFormat object to this one. Assigns the \a other QGrpcSerializationFormat object to this one.
*/ */
QGrpcSerializationFormat &QGrpcSerializationFormat::operator=(const QGrpcSerializationFormat &other) QGrpcSerializationFormat &
{ QGrpcSerializationFormat::operator=(const QGrpcSerializationFormat &other) = default;
if (this != &other)
*dPtr = *other.dPtr;
return *this;
}
/*! /*!
\fn QGrpcSerializationFormat::QGrpcSerializationFormat(QGrpcSerializationFormat &&other) noexcept \fn QGrpcSerializationFormat::QGrpcSerializationFormat(QGrpcSerializationFormat &&other) noexcept
@ -122,12 +115,22 @@ QGrpcSerializationFormat &QGrpcSerializationFormat::operator=(const QGrpcSeriali
Swaps this instance with \a other. This operation is very fast and never fails. Swaps this instance with \a other. This operation is very fast and never fails.
*/ */
/*!
\since 6.8
Constructs a new QVariant object from this QGrpcSerializationFormat.
*/
QGrpcSerializationFormat::operator QVariant() const
{
return QVariant::fromValue(*this);
}
/*! /*!
Returns the content type suffix for this serialization format. Returns the content type suffix for this serialization format.
*/ */
QByteArray QGrpcSerializationFormat::suffix() const noexcept QByteArrayView QGrpcSerializationFormat::suffix() const noexcept
{ {
return dPtr->suffix; Q_D(const QGrpcSerializationFormat);
return d->suffix;
} }
/*! /*!
@ -137,7 +140,25 @@ QByteArray QGrpcSerializationFormat::suffix() const noexcept
*/ */
std::shared_ptr<QAbstractProtobufSerializer> QGrpcSerializationFormat::serializer() const noexcept std::shared_ptr<QAbstractProtobufSerializer> QGrpcSerializationFormat::serializer() const noexcept
{ {
return dPtr->serializer; Q_D(const QGrpcSerializationFormat);
return d->serializer;
} }
#ifndef QT_NO_DEBUG_STREAM
/*!
\since 6.8
\fn QDebug QGrpcSerializationFormat::operator<<(QDebug debug, const QGrpcSerializationFormat &sfmt)
Writes \a sfmt to the specified stream \a debug.
*/
QDebug operator<<(QDebug debug, const QGrpcSerializationFormat &sfmt)
{
const QDebugStateSaver save(debug);
debug.nospace().noquote();
debug << "QGrpcSerializationFormat(suffix: " << sfmt.suffix() << ", serializer: { "
<< static_cast<void *>(sfmt.serializer().get())
<< ", useCount: " << sfmt.serializer().use_count() << " })";
return debug;
}
#endif
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -8,14 +8,19 @@
#include <QtGrpc/qtgrpcnamespace.h> #include <QtGrpc/qtgrpcnamespace.h>
#include <QtCore/qbytearrayview.h> #include <QtCore/qbytearrayview.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qtclasshelpermacros.h> #include <QtCore/qtclasshelpermacros.h>
#include <memory> #include <memory>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QGrpcSerializationFormatPrivate;
class QAbstractProtobufSerializer; class QAbstractProtobufSerializer;
class QDebug;
class QVariant;
class QGrpcSerializationFormatPrivate;
QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(QGrpcSerializationFormatPrivate, Q_GRPC_EXPORT)
class QGrpcSerializationFormat final class QGrpcSerializationFormat final
{ {
@ -29,19 +34,40 @@ public:
Q_GRPC_EXPORT QGrpcSerializationFormat &operator=(const QGrpcSerializationFormat &); Q_GRPC_EXPORT QGrpcSerializationFormat &operator=(const QGrpcSerializationFormat &);
QGrpcSerializationFormat(QGrpcSerializationFormat &&other) noexcept = default; QGrpcSerializationFormat(QGrpcSerializationFormat &&other) noexcept = default;
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QGrpcSerializationFormat) QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QGrpcSerializationFormat)
void swap(QGrpcSerializationFormat &other) noexcept { dPtr.swap(other.dPtr); } Q_GRPC_EXPORT Q_IMPLICIT operator QVariant() const;
[[nodiscard]] Q_GRPC_EXPORT QByteArray suffix() const noexcept; void swap(QGrpcSerializationFormat &other) noexcept { d_ptr.swap(other.d_ptr); }
[[nodiscard]] Q_GRPC_EXPORT QByteArrayView suffix() const noexcept;
[[nodiscard]] Q_GRPC_EXPORT std::shared_ptr<QAbstractProtobufSerializer> [[nodiscard]] Q_GRPC_EXPORT std::shared_ptr<QAbstractProtobufSerializer>
serializer() const noexcept; serializer() const noexcept;
private: private:
std::unique_ptr<QGrpcSerializationFormatPrivate, void (*)(QGrpcSerializationFormatPrivate *)> QExplicitlySharedDataPointer<QGrpcSerializationFormatPrivate> d_ptr;
dPtr;
friend bool comparesEqual(const QGrpcSerializationFormat &lhs,
const QGrpcSerializationFormat &rhs) noexcept
{
return lhs.suffix() == rhs.suffix() && lhs.serializer() == rhs.serializer();
}
Q_DECLARE_EQUALITY_COMPARABLE(QGrpcSerializationFormat)
friend size_t qHash(const QGrpcSerializationFormat &key, size_t seed = 0) noexcept
{
return qHashMulti(seed, key.suffix(), key.serializer().get());
}
#ifndef QT_NO_DEBUG_STREAM
friend Q_GRPC_EXPORT QDebug operator<<(QDebug debug, const QGrpcSerializationFormat &sfmt);
#endif
Q_DECLARE_PRIVATE(QGrpcSerializationFormat)
}; };
Q_DECLARE_SHARED(QGrpcSerializationFormat)
QT_END_NAMESPACE QT_END_NAMESPACE
#endif // QGRPCSERIALIZATIONFORMAT_H #endif // QGRPCSERIALIZATIONFORMAT_H

View File

@ -1,30 +1,29 @@
// Copyright (C) 2024 The Qt Company Ltd. // Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QObject>
#include <QtTest/QTest>
#include <QtGrpc/qgrpcserializationformat.h> #include <QtGrpc/qgrpcserializationformat.h>
#include <QtProtobuf/qprotobufjsonserializer.h> #include <QtProtobuf/qprotobufjsonserializer.h>
#include <QtProtobuf/qprotobufserializer.h> #include <QtProtobuf/qprotobufserializer.h>
#include <memory> #include <QtTest/qtest.h>
using namespace QtGrpc; using namespace QtGrpc;
class QGrpcSerializationFormatTest : public QObject class QGrpcSerializationFormatTest : public QObject
{ {
Q_OBJECT Q_OBJECT
public:
QGrpcSerializationFormatTest() { }
private Q_SLOTS: private Q_SLOTS:
void constructEmbedded(); void constructFromEnum() const;
void constructCustom(); void constructCustom() const;
void copyMove(); void hasSpecialMemberFunctions() const;
void hasImplicitQVariant() const;
void hasMemberSwap() const;
void streamsToDebug() const;
}; };
void QGrpcSerializationFormatTest::constructEmbedded() void QGrpcSerializationFormatTest::constructFromEnum() const
{ {
QGrpcSerializationFormat defaultFormat = SerializationFormat::Default; QGrpcSerializationFormat defaultFormat = SerializationFormat::Default;
QCOMPARE(defaultFormat.suffix(), ""); QCOMPARE(defaultFormat.suffix(), "");
@ -39,14 +38,14 @@ void QGrpcSerializationFormatTest::constructEmbedded()
QVERIFY(dynamic_cast<QProtobufSerializer *>(protobufFormat.serializer().get()) != nullptr); QVERIFY(dynamic_cast<QProtobufSerializer *>(protobufFormat.serializer().get()) != nullptr);
} }
void QGrpcSerializationFormatTest::constructCustom() void QGrpcSerializationFormatTest::constructCustom() const
{ {
QGrpcSerializationFormat customFormat("test", std::make_shared<QProtobufJsonSerializer>()); QGrpcSerializationFormat customFormat("test", std::make_shared<QProtobufJsonSerializer>());
QCOMPARE(customFormat.suffix(), "test"); QCOMPARE(customFormat.suffix(), "test");
QVERIFY(dynamic_cast<QProtobufJsonSerializer *>(customFormat.serializer().get()) != nullptr); QVERIFY(dynamic_cast<QProtobufJsonSerializer *>(customFormat.serializer().get()) != nullptr);
} }
void QGrpcSerializationFormatTest::copyMove() void QGrpcSerializationFormatTest::hasSpecialMemberFunctions() const
{ {
QGrpcSerializationFormat f1(SerializationFormat::Json); QGrpcSerializationFormat f1(SerializationFormat::Json);
@ -77,6 +76,48 @@ void QGrpcSerializationFormatTest::copyMove()
} }
} }
void QGrpcSerializationFormatTest::hasImplicitQVariant() const
{
QGrpcSerializationFormat sfmt1("test", std::make_shared<QProtobufJsonSerializer>());
QVariant v = sfmt1;
QCOMPARE_EQ(v.metaType(), QMetaType::fromType<QGrpcSerializationFormat>());
const auto sfmt2 = v.value<QGrpcSerializationFormat>();
QCOMPARE_EQ(sfmt1.suffix(), sfmt2.suffix());
QCOMPARE_EQ(sfmt1.serializer(), sfmt2.serializer());
}
void QGrpcSerializationFormatTest::hasMemberSwap() const
{
auto jsonSer = std::make_shared<QProtobufJsonSerializer>();
auto pbSer = std::make_shared<QProtobufSerializer>();
QGrpcSerializationFormat sfmt1("one", jsonSer);
QGrpcSerializationFormat sfmt2("two", pbSer);
sfmt1.swap(sfmt2);
auto check = [&](auto a, auto b) {
QCOMPARE_EQ(a.suffix(), QByteArray("two"));
QCOMPARE_EQ(a.serializer(), pbSer);
QCOMPARE_EQ(b.suffix(), QByteArray("one"));
QCOMPARE_EQ(b.serializer(), jsonSer);
};
check(sfmt1, sfmt2);
swap(sfmt1, sfmt2);
check(sfmt2, sfmt1);
}
void QGrpcSerializationFormatTest::streamsToDebug() const
{
QGrpcSerializationFormat sfmt("custom", std::make_shared<QProtobufJsonSerializer>());
QString storage;
QDebug dbg(&storage);
dbg.noquote().nospace();
dbg << sfmt;
QVERIFY(!storage.isEmpty());
std::unique_ptr<char[]> ustr(QTest::toString(sfmt));
QCOMPARE_EQ(storage, QString::fromUtf8(ustr.get()));
}
QTEST_MAIN(QGrpcSerializationFormatTest) QTEST_MAIN(QGrpcSerializationFormatTest)
#include "tst_qgrpcserializationformat.moc" #include "tst_qgrpcserializationformat.moc"