Browse Source

Change API for calls with return value passed as parameter

- Replace reference with QPointer. This protects
  of mistake when reference removed by user when
  async operation in progress.
Alexey Edelev 6 years ago
parent
commit
74f9c01cec

+ 1 - 1
examples/addressbook/addressbookengine.cpp

@@ -65,7 +65,7 @@ AddressBookEngine::AddressBookEngine() : QObject()
     connect(m_client, &AddressBookClient::contactsUpdated, this, [this](const Contacts &contacts) {
         m_contacts->reset(contacts.list());
     });
-    m_client->subscribeCallStatusUpdates(qtprotobuf::examples::None(), m_callStatus);
+    m_client->subscribeCallStatusUpdates(qtprotobuf::examples::None(), QPointer<CallStatus>(&m_callStatus));
 }
 
 void AddressBookEngine::addContact(qtprotobuf::examples::Contact *contact)

+ 4 - 4
src/generator/templates.cpp

@@ -186,7 +186,7 @@ const char *Templates::MapSerializationRegisterTemplate = "qtprotobuf::ProtobufO
 const char *Templates::ClassDefinitionTemplate = "\nclass $classname$ : public $parent_class$\n"
                                                  "{\n";
 const char *Templates::QObjectMacro = "Q_OBJECT";
-const char *Templates::ClientMethodDeclarationSyncTemplate = "Q_INVOKABLE bool $method_name$(const $param_type$ &$param_name$, $return_type$ &$return_name$);\n";
+const char *Templates::ClientMethodDeclarationSyncTemplate = "Q_INVOKABLE bool $method_name$(const $param_type$ &$param_name$, const QPointer<$return_type$> &$return_name$);\n";
 const char *Templates::ClientMethodDeclarationAsyncTemplate = "Q_INVOKABLE qtprotobuf::AsyncReply *$method_name$(const $param_type$ &$param_name$);\n";
 const char *Templates::ClientMethodDeclarationAsync2Template = "Q_INVOKABLE void $method_name$(const $param_type$ &$param_name$, const QObject* context, const std::function<void(qtprotobuf::AsyncReply*)> &callback);\n";
 const char *Templates::ServerMethodDeclarationTemplate = "Q_INVOKABLE virtual $return_type$ $method_name$(const $param_type$ &$param_name$) = 0;\n";
@@ -195,7 +195,7 @@ const char *Templates::ServerMethodDeclarationTemplate = "Q_INVOKABLE virtual $r
 const char *Templates::ConstructorDefinitionSyncTemplate = "\n$classname$::$classname$() : $parent_class$(\"$service_name$\")\n"
                                                            "{\n"
                                                            "}\n";
-const char *Templates::ClientMethodDefinitionSyncTemplate = "\nbool $classname$::$method_name$(const $param_type$ &$param_name$, $return_type$ &$return_name$)\n"
+const char *Templates::ClientMethodDefinitionSyncTemplate = "\nbool $classname$::$method_name$(const $param_type$ &$param_name$, const QPointer<$return_type$> &$return_name$)\n"
                                                             "{\n"
                                                             "    return call(\"$method_name$\", $param_name$, $return_name$);\n"
                                                             "}\n";
@@ -219,12 +219,12 @@ const char *Templates::QmlRegisterTypeUncreatableTemplate = "qmlRegisterUncreata
 
 const char *Templates::ClientMethodSignalDeclarationTemplate = "Q_SIGNAL void $method_name$Updated(const $return_type$ &);\n";
 const char *Templates::ClientMethodServerStreamDeclarationTemplate = "void subscribe$method_name_upper$Updates(const $param_type$ &$param_name$);\n";
-const char *Templates::ClientMethodServerStream2DeclarationTemplate = "void subscribe$method_name_upper$Updates(const $param_type$ &$param_name$, $return_type$ &$return_name$);\n";
+const char *Templates::ClientMethodServerStream2DeclarationTemplate = "void subscribe$method_name_upper$Updates(const $param_type$ &$param_name$, const QPointer<$return_type$> &$return_name$);\n";
 const char *Templates::ClientMethodServerStreamDefinitionTemplate = "void $classname$::subscribe$method_name_upper$Updates(const $param_type$ &$param_name$)\n"
                                                                     "{\n"
                                                                     "    subscribe(\"$method_name$\", $param_name$, &$classname$::$method_name$Updated);\n"
                                                                     "}\n";
-const char *Templates::ClientMethodServerStream2DefinitionTemplate = "void $classname$::subscribe$method_name_upper$Updates(const $param_type$ &$param_name$, $return_type$ &$return_name$)\n"
+const char *Templates::ClientMethodServerStream2DefinitionTemplate = "void $classname$::subscribe$method_name_upper$Updates(const $param_type$ &$param_name$, const QPointer<$return_type$> &$return_name$)\n"
                                                                      "{\n"
                                                                      "    subscribe(\"$method_name$\", $param_name$, $return_name$);\n"
                                                                      "    subscribe(\"$method_name$\", $param_name$, &$classname$::$method_name$Updated);\n"

+ 19 - 6
src/grpc/abstractclient.h

@@ -55,11 +55,16 @@ protected:
     virtual ~AbstractClient();
 
     template<typename A, typename R>
-    bool call(const QString &method, const A &arg, R &ret) {
+    bool call(const QString &method, const A &arg, const QPointer<R> &ret) {
+        if (ret.isNull()) {
+            qProtoCritical() << "Unable to call method: " << method << ". Pointer to return data is null";
+            return false;
+        }
+
         QByteArray retData;
         if (call(method, arg.serialize(), retData)) {
             try {
-                ret.deserialize(retData);
+                ret->deserialize(retData);
             } catch (std::invalid_argument &) {
                 qProtoCritical() << "Response deserialization failed invalid field found";
                 return false;
@@ -91,13 +96,21 @@ protected:
     }
 
     template<typename A, typename R>
-    void subscribe(const QString &method, const A &arg, R &ret) {
-        subscribe_p(method, arg.serialize(), [&ret](const QByteArray &data) {
-            ret.deserialize(data);
+    void subscribe(const QString &method, const A &arg, const QPointer<R> &ret) {
+        if (ret.isNull()) {
+            qProtoCritical() << "Unable to subscribe method: " << method << ". Pointer to return data is null";
+            return;
+        }
+
+        subscribe_p(method, arg.serialize(), [ret](const QByteArray &data) {
+            if (!ret.isNull()) {
+                ret->deserialize(data);
+            } else {
+                qProtoCritical() << "Pointer to return data is null while subscription update received";
+            }
         });
     }
 
-
 private:
     bool call(const QString &method, const QByteArray& arg, QByteArray& ret);
     AsyncReply *call(const QString &method, const QByteArray& arg);

+ 13 - 10
tests/test_grpc/clienttest.cpp

@@ -52,11 +52,12 @@ TEST_F(ClientTest, CheckMethodsGeneration)
 {
     //Dummy compile time check of functions generation and interface compatibility
     TestServiceClient testClient;
-    SimpleStringMessage result;
     SimpleStringMessage request;
-    testClient.testMethod(result, request);
-    testClient.testMethod(result);
-    testClient.testMethod(result, &testClient, [](AsyncReply*){});
+    QPointer<SimpleStringMessage> result(new SimpleStringMessage);
+    testClient.testMethod(request, result);
+    testClient.testMethod(request);
+    testClient.testMethod(request, &testClient, [](AsyncReply*){});
+    delete result;
 }
 
 TEST_F(ClientTest, StringEchoTest)
@@ -65,11 +66,12 @@ TEST_F(ClientTest, StringEchoTest)
     QCoreApplication app(argc, nullptr);
     TestServiceClient testClient;
     testClient.attachChannel(std::make_shared<Http2Channel>("localhost", 50051, InsecureCredentials()));
-    SimpleStringMessage result;
     SimpleStringMessage request;
+    QPointer<SimpleStringMessage> result(new SimpleStringMessage);
     request.setTestFieldString("Hello beach!");
     ASSERT_TRUE(testClient.testMethod(request, result));
-    ASSERT_STREQ(result.testFieldString().toStdString().c_str(), "Hello beach!");
+    ASSERT_STREQ(result->testFieldString().toStdString().c_str(), "Hello beach!");
+    delete result;
 }
 
 TEST_F(ClientTest, StringEchoAsyncTest)
@@ -78,8 +80,8 @@ TEST_F(ClientTest, StringEchoAsyncTest)
     QCoreApplication app(argc, nullptr);
     TestServiceClient testClient;
     testClient.attachChannel(std::make_shared<Http2Channel>("localhost", 50051, InsecureCredentials()));
-    SimpleStringMessage result;
     SimpleStringMessage request;
+    SimpleStringMessage result;
     request.setTestFieldString("Hello beach!");
     QEventLoop waiter;
 
@@ -210,8 +212,9 @@ TEST_F(ClientTest, StringEchoStreamTestRetUpdates)
     QCoreApplication app(argc, nullptr);
     TestServiceClient testClient;
     testClient.attachChannel(std::make_shared<Http2Channel>("localhost", 50051, InsecureCredentials()));
-    SimpleStringMessage result;
     SimpleStringMessage request;
+    QPointer<SimpleStringMessage> result(new SimpleStringMessage);
+
     request.setTestFieldString("Stream");
 
     QEventLoop waiter;
@@ -219,7 +222,7 @@ TEST_F(ClientTest, StringEchoStreamTestRetUpdates)
     testClient.subscribeTestMethodServerStreamUpdates(request, result);
 
     int i = 0;
-    QObject::connect(&result, &SimpleStringMessage::testFieldStringChanged, &app, [&i]() {
+    QObject::connect(result.data(), &SimpleStringMessage::testFieldStringChanged, &app, [&i]() {
         i++;
     });
 
@@ -227,7 +230,7 @@ TEST_F(ClientTest, StringEchoStreamTestRetUpdates)
     waiter.exec();
 
     ASSERT_EQ(i, 4);
-    ASSERT_STREQ(result.testFieldString().toStdString().c_str(), "Stream4");
+    ASSERT_STREQ(result->testFieldString().toStdString().c_str(), "Stream4");
     ASSERT_EQ(testClient.lastError(), AbstractChannel::StatusCodes::Ok);
 }