2022-10-20 11:49:27 +00:00
|
|
|
// Copyright (C) 2022 The Qt Company Ltd.
|
|
|
|
// Copyright (C) 2019 Alexey Edelev <semlanik@gmail.com>
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
|
|
|
|
2023-02-02 09:03:34 +00:00
|
|
|
#include <QtGrpc/private/qabstractgrpcchannel_p.h>
|
2024-09-06 14:24:31 +00:00
|
|
|
#include <QtGrpc/private/qtgrpclogging_p.h>
|
2024-07-03 09:02:17 +00:00
|
|
|
#include <QtGrpc/qgrpcclientbase.h>
|
2024-07-30 11:47:44 +00:00
|
|
|
#include <QtGrpc/qgrpcoperation.h>
|
2022-10-20 11:49:27 +00:00
|
|
|
|
2024-07-30 11:47:44 +00:00
|
|
|
#include <QtProtobuf/qprotobufmessage.h>
|
2024-07-03 09:02:17 +00:00
|
|
|
#include <QtProtobuf/qprotobufserializer.h>
|
2023-01-02 15:06:11 +00:00
|
|
|
|
2024-05-29 14:31:05 +00:00
|
|
|
#include <QtCore/private/qminimalflatset_p.h>
|
2024-07-03 09:02:17 +00:00
|
|
|
#include <QtCore/private/qobject_p.h>
|
2024-07-30 11:47:44 +00:00
|
|
|
#include <QtCore/qbytearray.h>
|
|
|
|
#include <QtCore/qlatin1stringview.h>
|
|
|
|
|
2024-08-21 16:29:39 +00:00
|
|
|
#include <optional>
|
2024-09-16 19:46:11 +00:00
|
|
|
#include <type_traits>
|
2022-10-20 11:49:27 +00:00
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
|
|
|
/*!
|
2024-04-19 12:18:32 +00:00
|
|
|
\class QGrpcClientBase
|
2023-03-10 09:49:15 +00:00
|
|
|
\inmodule QtGrpc
|
2024-04-19 12:18:32 +00:00
|
|
|
\brief The QGrpcClientBase class is bridge between gRPC clients
|
2022-12-08 12:53:08 +00:00
|
|
|
and channels.
|
2022-10-20 11:49:27 +00:00
|
|
|
|
2024-04-19 12:18:32 +00:00
|
|
|
QGrpcClientBase provides a set of functions for client classes
|
2022-12-08 12:53:08 +00:00
|
|
|
generated out of protobuf services.
|
2024-04-19 12:18:32 +00:00
|
|
|
QGrpcClientBase enforces thread safety for startStream() and call() methods
|
2022-12-08 12:53:08 +00:00
|
|
|
of generated clients.
|
2024-04-19 12:18:32 +00:00
|
|
|
The methods QGrpcClientBase::call() and QGrpcClientBase::startStream()
|
2023-02-21 14:34:07 +00:00
|
|
|
should only be called by the generated client classes.
|
2022-12-08 12:53:08 +00:00
|
|
|
*/
|
2022-10-20 11:49:27 +00:00
|
|
|
|
|
|
|
/*!
|
2024-07-04 14:50:07 +00:00
|
|
|
\fn template <typename StreamType, QGrpcClientBase::if_qtgrpc_stream<StreamType> = true> std::shared_ptr<StreamType> QGrpcClientBase::startStream(QLatin1StringView method, const QProtobufMessage &arg, const QGrpcCallOptions &options)
|
2022-10-20 11:49:27 +00:00
|
|
|
|
2023-09-06 10:53:08 +00:00
|
|
|
Starts the stream \a method of the \e StreamType type with the message
|
2022-12-08 12:53:08 +00:00
|
|
|
argument \a arg to the attached channel.
|
2023-08-13 16:00:42 +00:00
|
|
|
|
|
|
|
Uses \a options argument to set additional parameter in the stream
|
|
|
|
communication.
|
|
|
|
|
2023-09-06 10:53:08 +00:00
|
|
|
The implementation is only available for \e StreamType:
|
2024-08-08 14:04:36 +00:00
|
|
|
QGrpcServerStream, QGrpcClientStream, and QGrpcBidiStream.
|
2022-12-08 12:53:08 +00:00
|
|
|
*/
|
2022-10-20 11:49:27 +00:00
|
|
|
|
2024-01-14 19:03:29 +00:00
|
|
|
/*!
|
2024-04-19 12:18:32 +00:00
|
|
|
\fn void QGrpcClientBase::channelChanged()
|
2024-01-14 19:03:29 +00:00
|
|
|
\since 6.7
|
|
|
|
|
|
|
|
Indicates that a new channel is attached to the client.
|
|
|
|
*/
|
|
|
|
|
2024-01-14 19:22:07 +00:00
|
|
|
/*!
|
2024-04-19 12:18:32 +00:00
|
|
|
\fn void QGrpcClientBase::errorOccurred(const QGrpcStatus &status);
|
2024-01-14 19:22:07 +00:00
|
|
|
|
|
|
|
Indicates that an error occurred during serialization.
|
|
|
|
|
|
|
|
This signal is emitted when an error with \a status occurs in the channel
|
|
|
|
or during serialization.
|
|
|
|
*/
|
|
|
|
|
2024-09-16 19:46:11 +00:00
|
|
|
namespace {
|
|
|
|
template <typename Operation>
|
|
|
|
inline constexpr bool IsStream = false;
|
|
|
|
template <>
|
|
|
|
inline constexpr bool IsStream<QGrpcServerStream> = true;
|
|
|
|
template <>
|
|
|
|
inline constexpr bool IsStream<QGrpcClientStream> = true;
|
|
|
|
template <>
|
|
|
|
inline constexpr bool IsStream<QGrpcBidiStream> = true;
|
|
|
|
}
|
|
|
|
|
2024-04-19 12:18:32 +00:00
|
|
|
class QGrpcClientBasePrivate : public QObjectPrivate
|
2022-10-20 11:49:27 +00:00
|
|
|
{
|
2024-04-19 12:18:32 +00:00
|
|
|
Q_DECLARE_PUBLIC(QGrpcClientBase)
|
2022-10-20 11:49:27 +00:00
|
|
|
public:
|
2024-08-22 15:51:48 +00:00
|
|
|
explicit QGrpcClientBasePrivate(QLatin1StringView service) : service(service) { }
|
2022-10-20 11:49:27 +00:00
|
|
|
|
2024-05-29 14:31:05 +00:00
|
|
|
void addStream(QGrpcOperation *stream);
|
2024-08-21 16:29:39 +00:00
|
|
|
std::optional<QByteArray> trySerialize(const QProtobufMessage &arg) const;
|
2024-08-22 15:51:48 +00:00
|
|
|
bool isReady() const;
|
2024-07-04 15:12:20 +00:00
|
|
|
|
2024-08-22 15:51:48 +00:00
|
|
|
std::shared_ptr<QAbstractProtobufSerializer> serializer() const
|
|
|
|
{
|
2024-08-21 16:29:39 +00:00
|
|
|
return channel ? channel->serializer() : nullptr;
|
2024-04-02 12:05:34 +00:00
|
|
|
}
|
2023-08-06 13:42:23 +00:00
|
|
|
|
2024-09-16 19:46:11 +00:00
|
|
|
template <typename T>
|
|
|
|
[[nodiscard]] std::unique_ptr<T> initOperation(QLatin1StringView method,
|
|
|
|
const QProtobufMessage &arg,
|
|
|
|
const QGrpcCallOptions &options)
|
|
|
|
{
|
|
|
|
if (!isReady())
|
|
|
|
return {};
|
|
|
|
|
|
|
|
const auto argData = trySerialize(arg);
|
|
|
|
if (!argData)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
using ChannelFn = std::unique_ptr<T> (QAbstractGrpcChannel::*)(QLatin1StringView,
|
|
|
|
QLatin1StringView,
|
|
|
|
QByteArrayView,
|
|
|
|
const QGrpcCallOptions &);
|
|
|
|
constexpr ChannelFn initializer = [&]() -> ChannelFn {
|
|
|
|
if constexpr (std::is_same_v<T, QGrpcServerStream>) {
|
|
|
|
return &QAbstractGrpcChannel::serverStream;
|
|
|
|
} else if constexpr (std::is_same_v<T, QGrpcClientStream>) {
|
|
|
|
return &QAbstractGrpcChannel::clientStream;
|
|
|
|
} else if constexpr (std::is_same_v<T, QGrpcBidiStream>) {
|
|
|
|
return &QAbstractGrpcChannel::bidiStream;
|
|
|
|
} else if constexpr (std::is_same_v<T, QGrpcCallReply>) {
|
|
|
|
return &QAbstractGrpcChannel::call;
|
|
|
|
} else {
|
|
|
|
Q_UNREACHABLE_RETURN(nullptr);
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
std::unique_ptr<T> operation = ((*channel).*(initializer))(method, service, *argData,
|
|
|
|
options);
|
|
|
|
|
|
|
|
if constexpr (IsStream<T>)
|
|
|
|
addStream(operation.get());
|
|
|
|
return operation;
|
|
|
|
}
|
|
|
|
|
2022-12-09 13:03:47 +00:00
|
|
|
std::shared_ptr<QAbstractGrpcChannel> channel;
|
2023-09-22 14:56:02 +00:00
|
|
|
const QLatin1StringView service;
|
2024-05-29 14:31:05 +00:00
|
|
|
QMinimalFlatSet<QGrpcOperation *> activeStreams;
|
2022-10-20 11:49:27 +00:00
|
|
|
};
|
|
|
|
|
2024-05-29 14:31:05 +00:00
|
|
|
void QGrpcClientBasePrivate::addStream(QGrpcOperation *grpcStream)
|
2023-08-13 16:00:42 +00:00
|
|
|
{
|
2024-05-29 14:31:05 +00:00
|
|
|
Q_ASSERT(grpcStream);
|
|
|
|
|
2024-04-19 12:18:32 +00:00
|
|
|
Q_Q(QGrpcClientBase);
|
2024-05-29 14:31:05 +00:00
|
|
|
// Remove the operation pointer upon QObject destruction if it hasn't
|
2024-08-21 16:16:34 +00:00
|
|
|
// already been gracefully removed by receiving finished()
|
2024-05-29 14:31:05 +00:00
|
|
|
QObject::connect(grpcStream, &QObject::destroyed, q, [this, grpcStream](QObject *obj) {
|
|
|
|
Q_ASSERT(obj == grpcStream);
|
|
|
|
activeStreams.remove(grpcStream);
|
|
|
|
});
|
|
|
|
|
2024-08-08 09:26:41 +00:00
|
|
|
QObject::connect(
|
|
|
|
grpcStream, &QGrpcOperation::finished, q,
|
|
|
|
[this, grpcStream] {
|
|
|
|
Q_ASSERT(activeStreams.contains(grpcStream));
|
|
|
|
activeStreams.remove(grpcStream);
|
|
|
|
},
|
|
|
|
Qt::SingleShotConnection);
|
2024-05-29 14:31:05 +00:00
|
|
|
const auto it = activeStreams.insert(grpcStream);
|
|
|
|
Q_ASSERT(it.second);
|
2023-08-13 16:00:42 +00:00
|
|
|
}
|
|
|
|
|
2024-08-21 16:29:39 +00:00
|
|
|
std::optional<QByteArray> QGrpcClientBasePrivate::trySerialize(const QProtobufMessage &arg) const
|
|
|
|
{
|
|
|
|
if (auto s = serializer())
|
|
|
|
return s->serialize(&arg);
|
|
|
|
|
|
|
|
qGrpcWarning("Serializing failed. Serializer is not ready");
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2024-08-22 15:51:48 +00:00
|
|
|
bool QGrpcClientBasePrivate::isReady() const
|
|
|
|
{
|
|
|
|
Q_Q(const QGrpcClientBase);
|
|
|
|
if (q->thread() != QThread::currentThread()) {
|
|
|
|
qGrpcWarning("QtGrpc doesn't support invocation from a different thread");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!channel) {
|
|
|
|
qGrpcWarning("No channel(s) attached");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-04-19 12:18:32 +00:00
|
|
|
QGrpcClientBase::QGrpcClientBase(QLatin1StringView service, QObject *parent)
|
|
|
|
: QObject(*new QGrpcClientBasePrivate(service), parent)
|
2022-10-20 11:49:27 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2024-04-19 12:18:32 +00:00
|
|
|
QGrpcClientBase::~QGrpcClientBase() = default;
|
2022-10-20 11:49:27 +00:00
|
|
|
|
|
|
|
/*!
|
|
|
|
Attaches \a channel to client as transport layer for gRPC.
|
2022-12-08 12:53:08 +00:00
|
|
|
|
|
|
|
Parameters and return values will be serialized to the channel
|
|
|
|
in a format it supports.
|
2022-10-20 11:49:27 +00:00
|
|
|
|
2022-12-13 11:28:02 +00:00
|
|
|
\note \b Warning: Qt GRPC doesn't guarantee thread safety on the channel level.
|
2022-12-08 12:53:08 +00:00
|
|
|
You have to invoke the channel-related functions on the same thread as
|
2024-04-19 12:18:32 +00:00
|
|
|
QGrpcClientBase.
|
2022-10-20 11:49:27 +00:00
|
|
|
*/
|
2024-08-21 16:16:34 +00:00
|
|
|
bool QGrpcClientBase::attachChannel(std::shared_ptr<QAbstractGrpcChannel> channel)
|
2022-10-20 11:49:27 +00:00
|
|
|
{
|
2024-07-04 15:12:20 +00:00
|
|
|
Q_D(QGrpcClientBase);
|
|
|
|
// channel is not a QObject so we compare against the threadId set on construction.
|
2024-08-25 11:50:58 +00:00
|
|
|
if (channel->d_func()->threadId != QThread::currentThreadId()) {
|
2024-08-22 15:51:48 +00:00
|
|
|
qGrpcWarning("QtGrpc doesn't allow attaching the channel from a different thread");
|
2024-08-21 16:16:34 +00:00
|
|
|
return false;
|
2022-10-20 11:49:27 +00:00
|
|
|
}
|
2024-08-21 16:16:34 +00:00
|
|
|
|
2024-08-21 16:29:39 +00:00
|
|
|
for (const auto &stream : d->activeStreams) {
|
2023-08-13 16:00:42 +00:00
|
|
|
assert(stream != nullptr);
|
2023-08-06 20:07:05 +00:00
|
|
|
stream->cancel();
|
2023-08-13 16:00:42 +00:00
|
|
|
}
|
2022-12-08 12:53:08 +00:00
|
|
|
|
2024-04-26 11:31:24 +00:00
|
|
|
d->channel = std::move(channel);
|
2023-07-25 12:29:23 +00:00
|
|
|
emit channelChanged();
|
2024-08-21 16:16:34 +00:00
|
|
|
return true;
|
2023-07-25 12:29:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
2024-01-14 19:03:29 +00:00
|
|
|
\since 6.7
|
|
|
|
Returns the channel attached to this client.
|
|
|
|
*/
|
2024-09-04 21:36:57 +00:00
|
|
|
std::shared_ptr<QAbstractGrpcChannel> QGrpcClientBase::channel() const
|
2023-07-25 12:29:23 +00:00
|
|
|
{
|
2024-04-19 12:18:32 +00:00
|
|
|
Q_D(const QGrpcClientBase);
|
2023-07-25 12:29:23 +00:00
|
|
|
return d->channel;
|
2022-10-20 11:49:27 +00:00
|
|
|
}
|
|
|
|
|
Migrate to std::unique_ptr return value for all RPCs
The use of shared pointers has potential risk of storing the
QGrpcOperation children forever and leaking in user code. The problem
is clearly in the lambda connections that we encourage to use in the
docs and examples:
auto stream = testStream(...);
QObject::connect(stream.get(), &QGrpcOperation::finished,
ctx, [ctx, stream]{...});
The above code will hold the 'stream' forever, unless user will make
the explicit disconnect in the lambda.
By using std::unique_ptr we partially solve this, or at least convince
user to solve this. When user creates lambda he knows the 'stream'
lifetime and most probably should consider that after the move, lambda
is owning the QGrpcOperation, so the need of disconnect is more clear
in this case:
auto stream = testStream(...);
auto *streamPtr = stream.get();
QObject::connect(streamPtr, &QGrpcOperation::finished,
ctx, [ctx, stream = std::move(stream)]{...});
The code becomes a bit more complicated, but it points explicitly to
the potential risk. Also it disallows to make this trick to multiple
lambdas at the same time.
Of course using the lambda context to control the QGrpcOperation
lifetime in this case is not necessary. But even if users will decide
to manage the QGrpcOperation lifetime differently, the use of
std::unique_ptr will clearly point to the ownership.
[ChangeLog][Grpc] All generated RPC methods now return std::unique_ptr
instead of std::shared_ptr. This change explicitly defines that caller
takes the ownership of the returned pointers.
Pick-to: 6.8
Change-Id: I271b91454f0c1b12b77127a7e025fa493367e279
Reviewed-by: Dennis Oberst <dennis.oberst@qt.io>
2024-08-28 15:15:33 +00:00
|
|
|
std::unique_ptr<QGrpcCallReply> QGrpcClientBase::call(QLatin1StringView method,
|
2024-07-04 15:01:12 +00:00
|
|
|
const QProtobufMessage &arg,
|
|
|
|
const QGrpcCallOptions &options)
|
2022-10-20 11:49:27 +00:00
|
|
|
{
|
2024-04-19 12:18:32 +00:00
|
|
|
Q_D(QGrpcClientBase);
|
2024-09-16 19:46:11 +00:00
|
|
|
return d->initOperation<QGrpcCallReply>(method, arg, options);
|
2022-10-20 11:49:27 +00:00
|
|
|
}
|
|
|
|
|
Migrate to std::unique_ptr return value for all RPCs
The use of shared pointers has potential risk of storing the
QGrpcOperation children forever and leaking in user code. The problem
is clearly in the lambda connections that we encourage to use in the
docs and examples:
auto stream = testStream(...);
QObject::connect(stream.get(), &QGrpcOperation::finished,
ctx, [ctx, stream]{...});
The above code will hold the 'stream' forever, unless user will make
the explicit disconnect in the lambda.
By using std::unique_ptr we partially solve this, or at least convince
user to solve this. When user creates lambda he knows the 'stream'
lifetime and most probably should consider that after the move, lambda
is owning the QGrpcOperation, so the need of disconnect is more clear
in this case:
auto stream = testStream(...);
auto *streamPtr = stream.get();
QObject::connect(streamPtr, &QGrpcOperation::finished,
ctx, [ctx, stream = std::move(stream)]{...});
The code becomes a bit more complicated, but it points explicitly to
the potential risk. Also it disallows to make this trick to multiple
lambdas at the same time.
Of course using the lambda context to control the QGrpcOperation
lifetime in this case is not necessary. But even if users will decide
to manage the QGrpcOperation lifetime differently, the use of
std::unique_ptr will clearly point to the ownership.
[ChangeLog][Grpc] All generated RPC methods now return std::unique_ptr
instead of std::shared_ptr. This change explicitly defines that caller
takes the ownership of the returned pointers.
Pick-to: 6.8
Change-Id: I271b91454f0c1b12b77127a7e025fa493367e279
Reviewed-by: Dennis Oberst <dennis.oberst@qt.io>
2024-08-28 15:15:33 +00:00
|
|
|
std::unique_ptr<QGrpcServerStream> QGrpcClientBase::serverStream(QLatin1StringView method,
|
2024-08-25 11:57:51 +00:00
|
|
|
const QProtobufMessage &arg,
|
|
|
|
const QGrpcCallOptions &options)
|
2022-10-20 11:49:27 +00:00
|
|
|
{
|
2024-04-19 12:18:32 +00:00
|
|
|
Q_D(QGrpcClientBase);
|
2024-09-16 19:46:11 +00:00
|
|
|
return d->initOperation<QGrpcServerStream>(method, arg, options);
|
2023-08-13 16:00:42 +00:00
|
|
|
}
|
|
|
|
|
Migrate to std::unique_ptr return value for all RPCs
The use of shared pointers has potential risk of storing the
QGrpcOperation children forever and leaking in user code. The problem
is clearly in the lambda connections that we encourage to use in the
docs and examples:
auto stream = testStream(...);
QObject::connect(stream.get(), &QGrpcOperation::finished,
ctx, [ctx, stream]{...});
The above code will hold the 'stream' forever, unless user will make
the explicit disconnect in the lambda.
By using std::unique_ptr we partially solve this, or at least convince
user to solve this. When user creates lambda he knows the 'stream'
lifetime and most probably should consider that after the move, lambda
is owning the QGrpcOperation, so the need of disconnect is more clear
in this case:
auto stream = testStream(...);
auto *streamPtr = stream.get();
QObject::connect(streamPtr, &QGrpcOperation::finished,
ctx, [ctx, stream = std::move(stream)]{...});
The code becomes a bit more complicated, but it points explicitly to
the potential risk. Also it disallows to make this trick to multiple
lambdas at the same time.
Of course using the lambda context to control the QGrpcOperation
lifetime in this case is not necessary. But even if users will decide
to manage the QGrpcOperation lifetime differently, the use of
std::unique_ptr will clearly point to the ownership.
[ChangeLog][Grpc] All generated RPC methods now return std::unique_ptr
instead of std::shared_ptr. This change explicitly defines that caller
takes the ownership of the returned pointers.
Pick-to: 6.8
Change-Id: I271b91454f0c1b12b77127a7e025fa493367e279
Reviewed-by: Dennis Oberst <dennis.oberst@qt.io>
2024-08-28 15:15:33 +00:00
|
|
|
std::unique_ptr<QGrpcClientStream> QGrpcClientBase::clientStream(QLatin1StringView method,
|
2024-08-25 11:57:51 +00:00
|
|
|
const QProtobufMessage &arg,
|
|
|
|
const QGrpcCallOptions &options)
|
2023-08-13 16:00:42 +00:00
|
|
|
{
|
2024-04-19 12:18:32 +00:00
|
|
|
Q_D(QGrpcClientBase);
|
2024-09-16 19:46:11 +00:00
|
|
|
return d->initOperation<QGrpcClientStream>(method, arg, options);
|
2023-08-13 16:00:42 +00:00
|
|
|
}
|
|
|
|
|
Migrate to std::unique_ptr return value for all RPCs
The use of shared pointers has potential risk of storing the
QGrpcOperation children forever and leaking in user code. The problem
is clearly in the lambda connections that we encourage to use in the
docs and examples:
auto stream = testStream(...);
QObject::connect(stream.get(), &QGrpcOperation::finished,
ctx, [ctx, stream]{...});
The above code will hold the 'stream' forever, unless user will make
the explicit disconnect in the lambda.
By using std::unique_ptr we partially solve this, or at least convince
user to solve this. When user creates lambda he knows the 'stream'
lifetime and most probably should consider that after the move, lambda
is owning the QGrpcOperation, so the need of disconnect is more clear
in this case:
auto stream = testStream(...);
auto *streamPtr = stream.get();
QObject::connect(streamPtr, &QGrpcOperation::finished,
ctx, [ctx, stream = std::move(stream)]{...});
The code becomes a bit more complicated, but it points explicitly to
the potential risk. Also it disallows to make this trick to multiple
lambdas at the same time.
Of course using the lambda context to control the QGrpcOperation
lifetime in this case is not necessary. But even if users will decide
to manage the QGrpcOperation lifetime differently, the use of
std::unique_ptr will clearly point to the ownership.
[ChangeLog][Grpc] All generated RPC methods now return std::unique_ptr
instead of std::shared_ptr. This change explicitly defines that caller
takes the ownership of the returned pointers.
Pick-to: 6.8
Change-Id: I271b91454f0c1b12b77127a7e025fa493367e279
Reviewed-by: Dennis Oberst <dennis.oberst@qt.io>
2024-08-28 15:15:33 +00:00
|
|
|
std::unique_ptr<QGrpcBidiStream> QGrpcClientBase::bidiStream(QLatin1StringView method,
|
2024-08-25 11:57:51 +00:00
|
|
|
const QProtobufMessage &arg,
|
|
|
|
const QGrpcCallOptions &options)
|
2023-08-13 16:00:42 +00:00
|
|
|
{
|
2024-04-19 12:18:32 +00:00
|
|
|
Q_D(QGrpcClientBase);
|
2024-09-16 19:46:11 +00:00
|
|
|
return d->initOperation<QGrpcBidiStream>(method, arg, options);
|
2022-10-20 11:49:27 +00:00
|
|
|
}
|
|
|
|
|
2024-07-10 16:42:57 +00:00
|
|
|
bool QGrpcClientBase::event(QEvent *event)
|
|
|
|
{
|
|
|
|
return QObject::event(event);
|
|
|
|
}
|
|
|
|
|
2022-10-20 11:49:27 +00:00
|
|
|
QT_END_NAMESPACE
|
2022-12-09 12:03:04 +00:00
|
|
|
|
2024-04-19 12:18:32 +00:00
|
|
|
#include "moc_qgrpcclientbase.cpp"
|