qtqt4protocol-buffersqtembeddedqdbus

qdbus and marshalling of custom types


I'd like to send a custom c++ class through the Qt DBUS API. I've created the class from the .proto file using the protoc compiler and added them to my project in QtCreator. Now I want to verify that I can send the custom class as a QVariant through the dbus API. I have a receiver and sender program and can send a simple test string so Dbus works. I am having trouble sending the protocol buffer class after adding it as a metatypes.

My test .proto file contains only ints:

message MyData {
  required int32 name = 1;
  required int32 id = 2;
  optional int32 email = 3;
}

To the protocol buffer class header file I added:

#include <QMetaType>
#include <QDBusMetaType>
...
friend QDBusArgument &operator<<(QDBusArgument &argument, const MyData &dataToWrite);
friend const QDBusArgument &operator>>(const QDBusArgument &argument, MyData &dataToWrite);
...
Q_DECLARE_METATYPE(MyData )

And to the protocol buffer class implementation file I added:

#include <QDebug>
...
#include <QMetaType>
#include <QDBusMetaType>

// Marshall the MyData data into a D-Bus argument
QDBusArgument &operator<<(QDBusArgument &argument, const MyData &dataToWrite)
{

    qDebug() << "OPERATOR<<";

    argument.beginStructure();

    // Break out the various properties of dataToWrite protocol buffer
    int name = dataToWrite.name();
    int id = dataToWrite.id();
    int email = dataToWrite.email();
    qDebug() << name;
    qDebug() << id;
    qDebug() << email;

    argument << name;
    argument << id;
    argument << email;
    argument.endStructure();
    return argument;

}

// Retrieve the MyData data from the D-Bus argument
const QDBusArgument &operator>>(const QDBusArgument &argument, MyData &dataToWrite)
{

    qDebug() << "OPERATOR>>";

    argument.beginStructure();

    // Break out the various properties of dataToWrite protocol buffer
    int name = dataToWrite.name();
    int id = dataToWrite.id();
    int email = dataToWrite.email();
    qDebug() << name;
    qDebug() << id;
    qDebug() << email;

    argument >> name;
    argument >> id;
    argument >> email;
    argument.endStructure();
    return argument;

}

Main simply looks like this:

QCoreApplication a(argc, argv);

    dbussender* client = new dbussender("com.one.two.three.nvram", "/dbusReadWriteNvRam", QDBusConnection::sessionBus(), 0);

    // Create a protocol buffer class and provide its properties with values
    MyData dataToWrite;
    dataToWrite.set_name(2);
    dataToWrite.set_id(3);
    dataToWrite.set_email(4);

    QString command3 = "Contacting Protobuf Receiver and calling WRITENVRAM...";
    QString response3 = client->writeNVRam(dataToWrite);

    std::cout << "Command:   " << command3.toStdString() << std::endl;
    std::cout << "Response:   " << response3.toStdString() << std::endl;

My dbussender class calls the remote function like this:

inline QDBusPendingReply<QString> writeNVRam(MyData dataToWrite)
    {

        qDebug() << "Sending " << dataToWrite.name();
        qDebug() << "Sending " << dataToWrite.id();
        qDebug() << "Sending " << dataToWrite.email();

        QList<QVariant> argumentList;
        argumentList << QVariant::fromValue<MyData>(dataToWrite);
        return asyncCallWithArgumentList(QLatin1String("writeNVRam"), argumentList);
    }

Ultimately in my receiver program, this function is called but always returns 0:

// Write NVRAM
QString dbusReadWriteNvRam::writeNVRam(MyData dataToWrite) {

    qDebug() << "WRITE NVRAM COMMAND CALLED";

    qDebug() << "Unpacking: " << dataToWrite.name();
    qDebug() << "Unpacking: " << dataToWrite.id();
    qDebug() << "Unpacking: " << dataToWrite.email();

    return "HELLO CLASS";

}

Here is the output of the Sender program:

Sending  2 
Sending  3 
Sending  4 
OPERATOR<< 
0 
0 
0 
OPERATOR<< 
2 
3 
4 
Command:   Contacting Protobuf Receiver and calling WRITENVRAM...
Response:   HELLO CLASS

And here is the output of the Receiver program:

OPERATOR<< 
0 
0 
0 
OPERATOR>> 
0 
0 
0 
WRITE NVRAM COMMAND CALLED 
Unpacking:  0 
Unpacking:  0 
Unpacking:  0

Why does it seem that the marshalling functions are being called twice? And howcome the second call seems to include the valid values of 2,3,4 for the 3 properties of my protocol buffer but the first call is all 0's? The Receiver only seems to see the All 0's and never receives a protocol buffer object with valid values.

Is there something wrong with my marshalling code? What else could be going on?


Solution

  • To make this work, implement the operators like so:

    // PROTOBUF-MODIFICATION-DBUS
    // Marshall the companyData data into a D-Bus argument
    QDBusArgument &operator<<(QDBusArgument &argument, const companyData &dataToWrite)
    {
    
        argument.beginStructure();
    
        // Break out the various properties of dataToWrite protocol buffer
        int name = dataToWrite.name();
        int id = dataToWrite.id();
        int email = dataToWrite.email();
        argument << name;
        argument << id;
        argument << email;
        argument.endStructure();
        return argument;
    
    }
    
    // PROTOBUF-MODIFICATION-DBUS
    // Retrieve the companyData data from the D-Bus argument
    const QDBusArgument &operator>>(const QDBusArgument &argument, companyData &dataToWrite)
    {
    
        int name, id, email;
    
        argument.beginStructure();
        argument >> name;
        argument >> id;
        argument >> email;
        argument.endStructure();
        dataToWrite.set_name(name);
        dataToWrite.set_id(id);
        dataToWrite.set_email(email);
        return argument;
    
    }