Static Source of Qt Remote Objects

Keywords: Qt ipc

0. Preface

Qt Remote Objects is an RPC remote call module added from Qt5.9. For details, you can learn from the document:

https://doc.qt.io/qt-5/qtremoteobjects-gettingstarted.html

From the perspective of personal use practice, it also has some advantages over other popular RPC frameworks:

  • Qt like: if grpc is used, some   Qt's own types, such as QString and QByteArray, need to be converted into several basic types supported by grpc. There is a risk of inconsistent string encoding and decoding during serialization. Using QtRO naturally supports these types of QTS, and the defined interface supports signal slots.
  • Portability: QtRO module is a lightweight module and easy to use.

There are also some problems:

  • Too few functions: at present, only interface definitions on some meta objects, such as attributes and signal slots, are supported. There is no streaming interface, no common class members or functions.
  • The host cannot directly access the currently connected nodes. The server is shared by all connected nodes. If the host source sends a signal, all connected nodes will receive this signal. From this point of view, QtRO is more suitable for process interaction of a single client than concurrent access of multiple clients. If multiple clients want to operate independently, signals should not be used. The results can be returned through the return value of slot function.

1. Interface definition rep file

Before using the QtRO module, add remoteobjects to the pro file to enable the module

QT += remoteobjects

There are two ways to use, static souce and dynamic replica. This article is a note on the static source. The basic usage process is: write the interface definition file rep and generate   Replica (client) and source (server) interfaces are implemented, and the generated interface is used in the code for remote call.

Refer to the official document for interface definition:

https://doc.qt.io/qt-5/qtremoteobjects-repc.html

The definition of attribute and signal slot is similar to that of QObject subclass.

Here are some problems I have encountered:

  • ENUM must be defined in class, otherwise it cannot be accessed directly, because it will set a layer outside   QObject class.
  • POD members are attributes. If you want to use a custom structure and cannot write it directly in the rep, you need to include the structure header file, so you can use it as long as the custom types supported by the signal slot.
  • To use a custom type, you need to write a comparison operator to support comparison, such as in the automatically generated attribute set function   Input and output serialization of QDataStream.

Here are my simple.rep interface definition file and Define.h structure header file:

//rep document https://doc.qt.io/qt-5/qtremoteobjects-repc.html
//Import header file of structure definition
#include "Define.h"

//POD is similar to a structure, but its members are attributes
POD MyPod(QString name, int age, bool sex)

//MODEL is related to QAbstractItemModel. It's too chicken rib. It's omitted

//class
class MyObject{
    //attribute
    PROP(int value)
}
//Two interfaces are generated
//[class name Replica] to the client
//[class name Source] and [class name SimpleSource] to the server
class Interface{
    //attribute
    PROP(QString name="gongjianbo" READONLY)
    PROP(QString tag)
    //Use the class defined in rep as the attribute
    CLASS subObject(MyObject)
    //signal
    SIGNAL(dataChanged(const QString &data))
    //Slot function. If the return value is omitted, it will be void
    SLOT(void setData(const QString &data))
    SLOT(QString getData())
    //Enumeration. If it is not placed in the class, it will automatically set another layer of QObject class, which is inconvenient to access
    ENUM MyEnum{
        EnumA = 0,
        EnumB
    }
    SLOT(void testEnum(MyEnum t))
    //Use custom structures
    SLOT(void testStruct(MyStruct t))
}
#pragma once
#include <QObject>
#include <QDataStream>

//The user-defined structure is supported, but it can only be defined in include, not in rep
//Since the automatically generated code needs to be compared and serialized, some functions need to be overloaded
struct MyStruct
{
    QString name;
    int age;
    bool sex;
};
Q_DECLARE_METATYPE(MyStruct)

bool operator==(const MyStruct &left, const MyStruct &right);
bool operator!=(const MyStruct &left, const MyStruct &right);
//Overloads input and output operators to support datastream serialization custom structures
QDataStream& operator <<(QDataStream& out, const MyStruct& item);
QDataStream& operator >>(QDataStream& in, MyStruct& item);

After definition, the following is introduced at the interface Caller:

REPC_REPLICA += $$PWD/simple.rep

After construction, it will be generated in the output directory    rep_simple_replica.h, which implements the interface   QRemoteObjectReplica subclass.

Introduce at the interface server:

REPC_SOURCE += $$PWD/simple.rep

After construction, it will be generated in the output directory    rep_simple_source.h, which contains an interface class that implements some interfaces (slots need to be inherited and rewritten).

Of course, it can also be used   REPC_ Merge is introduced, and two sets of interfaces for calling and service will be generated at the same time.

2. Basic operation

After the interface class file is generated, it is introduced at the calling end and the server end respectively. The server may need to implement some virtual functions, which can be used directly by the caller.

The first is the basic operation of the caller (note that this Interface is called Interface because I define the rep Interface):

QRemoteObjectNode *remoteNode = new QRemoteObjectNode(this);
remoteNode->connectToNode(QUrl("local:qro_test"));
InterfaceReplica *replica = remoteNode->acquire<InterfaceReplica>();
//The caller can judge the connection and disconnection, and the server has no corresponding interface to query the caller list
connect(irep,&InterfaceReplica::stateChanged,
this,[this](QRemoteObjectReplica::State state, QRemoteObjectReplica::State oldState){
});
//Connect server signal
connect(replica,&InterfaceReplica::dataChanged,[]{});
//Call the interface of the server
if(replica->isReplicaValid())
    replica->setData("hello");

Unfortunately, the caller cannot send signals directly, but can only receive signals from the server and call slot functions. After the connecttonode is called, if the server is not turned on, it will automatically detect. You can also call the waitForSource interface of replica to take the initiative. After the server is restarted, the node will be automatically reconnected.

Basic operations of the server:

QRemoteObjectHost *host = new QRemoteObjectHost(this);
MySource source; //Inherit the generated class and implement some virtual functions
host->setHostUrl(QUrl("local:qro_test"));
host->enableRemoting(&source);
//After the service is started, it can send a signal or be called
emit source.dataChanged("hello");

See the official example for the complete code:

https://doc.qt.io/qt-5/remoteobjects-example-static-source.html

Then share my Demo:

https://github.com/gongjianbo/MyTestCode2021/tree/master/Qt/QtRemoteObjects

3. Reference

file: https://doc.qt.io/qt-5/qtremoteobjects-gettingstarted.html

Blog: https://zhuanlan.zhihu.com/p/347981991

Blog: https://zhuanlan.zhihu.com/p/36501814

Posted by HHawk on Sat, 04 Sep 2021 20:24:52 -0700