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,
&Http2Handler::processQueue);
std::optional<QGrpcDuration> deadline;
std::optional<std::chrono::milliseconds> deadline;
if (channelOpPtr->callOptions().deadline())
deadline = channelOpPtr->callOptions().deadline();
else if (parentChannel->channelOptions.deadline())
@ -589,7 +589,7 @@ QGrpcHttp2ChannelPrivate::QGrpcHttp2ChannelPrivate(const QUrl &uri,
const QGrpcChannelOptions &options)
: hostUri(uri), channelOptions(options)
{
const QByteArray formatSuffix = channelOptions.serializationFormat().suffix();
auto formatSuffix = channelOptions.serializationFormat().suffix();
const QByteArray defaultContentType = DefaultContentType.toByteArray();
const QByteArray contentTypeFromOptions = !formatSuffix.isEmpty()
? defaultContentType + '+' + formatSuffix

View File

@ -6,6 +6,10 @@
#include <QtProtobuf/qprotobufjsonserializer.h>
#include <QtProtobuf/qprotobufserializer.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qdebug.h>
#include <QtCore/qvariant.h>
using namespace QtGrpc;
QT_BEGIN_NAMESPACE
@ -13,6 +17,7 @@ QT_BEGIN_NAMESPACE
/*!
\class QGrpcSerializationFormat
\inmodule QtGrpc
\compares equality
\brief The QGrpcSerializationFormat class holds the protobuf message
serializer and the related content type suffix.
\since 6.8
@ -26,7 +31,7 @@ QT_BEGIN_NAMESPACE
\sa QAbstractGrpcChannel
*/
class QGrpcSerializationFormatPrivate
class QGrpcSerializationFormatPrivate : public QSharedData
{
public:
QGrpcSerializationFormatPrivate(QByteArrayView suffix_,
@ -34,14 +39,12 @@ public:
: suffix(suffix_.toByteArray()), serializer(std::move(serializer_))
{
}
QByteArray suffix;
std::shared_ptr<QAbstractProtobufSerializer> serializer;
};
static void dPtrDeleter(QGrpcSerializationFormatPrivate *ptr)
{
delete ptr;
}
QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QGrpcSerializationFormatPrivate)
/*!
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.
*/
QGrpcSerializationFormat::QGrpcSerializationFormat(SerializationFormat format)
: dPtr(format == SerializationFormat::Json
? new QGrpcSerializationFormatPrivate("json",
std::make_shared<QProtobufJsonSerializer>())
: new QGrpcSerializationFormatPrivate(format == SerializationFormat::Protobuf
? "proto"
: "",
std::make_shared<QProtobufSerializer>()),
dPtrDeleter)
: d_ptr(format == SerializationFormat::Json
? new QGrpcSerializationFormatPrivate("json",
std::make_shared<QProtobufJsonSerializer>())
: new QGrpcSerializationFormatPrivate(format == SerializationFormat::Protobuf
? "proto"
: "",
std::make_shared<QProtobufSerializer>()))
{
}
@ -73,29 +75,20 @@ QGrpcSerializationFormat::~QGrpcSerializationFormat() = default;
QGrpcSerializationFormat::QGrpcSerializationFormat(QByteArrayView suffix,
std::shared_ptr<QAbstractProtobufSerializer>
serializer)
: dPtr(new QGrpcSerializationFormatPrivate(suffix, std::move(serializer)),
dPtrDeleter)
: d_ptr(new QGrpcSerializationFormatPrivate(suffix, std::move(serializer)))
{
}
/*!
Constructs a copy of \a other.
*/
QGrpcSerializationFormat::QGrpcSerializationFormat(const QGrpcSerializationFormat &other)
: dPtr(new QGrpcSerializationFormatPrivate(*other.dPtr),
dPtrDeleter)
{
}
QGrpcSerializationFormat::QGrpcSerializationFormat(const QGrpcSerializationFormat &other) = default;
/*!
Assigns the \a other QGrpcSerializationFormat object to this one.
*/
QGrpcSerializationFormat &QGrpcSerializationFormat::operator=(const QGrpcSerializationFormat &other)
{
if (this != &other)
*dPtr = *other.dPtr;
return *this;
}
QGrpcSerializationFormat &
QGrpcSerializationFormat::operator=(const QGrpcSerializationFormat &other) = default;
/*!
\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.
*/
/*!
\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.
*/
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
{
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

View File

@ -8,14 +8,19 @@
#include <QtGrpc/qtgrpcnamespace.h>
#include <QtCore/qbytearrayview.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qtclasshelpermacros.h>
#include <memory>
QT_BEGIN_NAMESPACE
class QGrpcSerializationFormatPrivate;
class QAbstractProtobufSerializer;
class QDebug;
class QVariant;
class QGrpcSerializationFormatPrivate;
QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(QGrpcSerializationFormatPrivate, Q_GRPC_EXPORT)
class QGrpcSerializationFormat final
{
@ -29,19 +34,40 @@ public:
Q_GRPC_EXPORT QGrpcSerializationFormat &operator=(const QGrpcSerializationFormat &);
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>
serializer() const noexcept;
private:
std::unique_ptr<QGrpcSerializationFormatPrivate, void (*)(QGrpcSerializationFormatPrivate *)>
dPtr;
QExplicitlySharedDataPointer<QGrpcSerializationFormatPrivate> d_ptr;
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
#endif // QGRPCSERIALIZATIONFORMAT_H

View File

@ -1,30 +1,29 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QObject>
#include <QtTest/QTest>
#include <QtGrpc/qgrpcserializationformat.h>
#include <QtProtobuf/qprotobufjsonserializer.h>
#include <QtProtobuf/qprotobufserializer.h>
#include <memory>
#include <QtTest/qtest.h>
using namespace QtGrpc;
class QGrpcSerializationFormatTest : public QObject
{
Q_OBJECT
public:
QGrpcSerializationFormatTest() { }
private Q_SLOTS:
void constructEmbedded();
void constructCustom();
void copyMove();
void constructFromEnum() const;
void constructCustom() const;
void hasSpecialMemberFunctions() const;
void hasImplicitQVariant() const;
void hasMemberSwap() const;
void streamsToDebug() const;
};
void QGrpcSerializationFormatTest::constructEmbedded()
void QGrpcSerializationFormatTest::constructFromEnum() const
{
QGrpcSerializationFormat defaultFormat = SerializationFormat::Default;
QCOMPARE(defaultFormat.suffix(), "");
@ -39,14 +38,14 @@ void QGrpcSerializationFormatTest::constructEmbedded()
QVERIFY(dynamic_cast<QProtobufSerializer *>(protobufFormat.serializer().get()) != nullptr);
}
void QGrpcSerializationFormatTest::constructCustom()
void QGrpcSerializationFormatTest::constructCustom() const
{
QGrpcSerializationFormat customFormat("test", std::make_shared<QProtobufJsonSerializer>());
QCOMPARE(customFormat.suffix(), "test");
QVERIFY(dynamic_cast<QProtobufJsonSerializer *>(customFormat.serializer().get()) != nullptr);
}
void QGrpcSerializationFormatTest::copyMove()
void QGrpcSerializationFormatTest::hasSpecialMemberFunctions() const
{
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)
#include "tst_qgrpcserializationformat.moc"