Skip to content

DBus and Qt programming - part 2

March 26th, 2007 - Software(2 mins)

Note: This is a really old post which was originally published in 2007. Stuff may have changed since both with Qt and D-BUS

The D-Bus translation

First, note the “arg0” annotation, required for the struct type:

<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="MyStruct"></annotation>

This actually helps the qdbusxml2cpp to typ-ify the generated code.

On the client (controller) part, we use the following command:

qdbusxml2cpp  -c CarInterface
              -p car_interface_p.h:car_interface.cpp
              -i mystruct.h
              car.xml

On the server (car) part, use the command:

qdbusxml2cpp  -i mystruct.h
              -c CarAdaptor
              -a car_adaptor_p.h:car_adaptor.cpp
              car.xml

for the same car.xml file.

The XML resulted from the introspection description for structer is translated by the Qt into:

public Q_SLOTS:
MyStruct structer(qlonglong &out1);

aka first “out” argument is passed as the return argument for the function. The other arguments are passes as function arguments. It’s good to know this, because you can implement your own method without looking at the *_p.h files for guidance.

… but not yet.

The Server Side

First issue is that qdbusxml2cpp is not smart enough to implement custom marshaling. It offers instead a guideline for us:

MyStruct CarAdaptor::structer(qlonglong &out1)
{
    // handle method call com.trolltech.Examples.CarInterface.structer
    //return static_cast<yourobjecttype *>(parent())->structer(out1);
}

once we change the commented line to something like:

MyStruct s = static_cast<car *>(parent())->structer(out1);

qDebug() < < "Parameters:" << out1;
qDebug() << "           " << s.element1 << s.element2 << s.element3;

return s;

we’re good to go. The line

MyStruct s = static_cast<car *>(parent())->structer(out1);

implies that the parent() object is a pointer to Car, which, in turn contains a structer() implementation. You can change the call into whatever code capable of updating the out1 parameter and returning a MyStruct struct.

For the purpose of this exercise, we implement a structer() method in our car.cpp like so:

MyStruct Car::structer(qlonglong &out1) {
    printf("Calling structer (car.cpp)n");
    s.element1 = "one";
    s.element2 = 2;
    s.element3 = "three";
    out1 = 12345;
    return s;
}

and include the car.h in.

The downfall of this server approach is that it requires manual adjustment of a generated file. So, once you have the .cpp and the _p.h files available, save them to your repository. Have them regenerated only if needed. Note that Trolltech’s Qt tarball contains already generated files, instead of scripting :)

The Client Side

Here the fun begins! First, open the .ui file and add a new button to call the structer(). In controller.h complete the on_XXXX_clicked() with the new button ID (let’s call it on_structer clicked())

The implementation follows the D-Bus guidelines from the other buttons:

void Controller::on_structer_clicked()
{
    qlonglong r2;
    QDBusReply<mystruct> r1 = car->structer(r2);
    printf(">>>>>> %dn", r2);
}

The result is that every time you press the button, it’s going to call the server (car) and get the MyStruct structure from the server.

… but not yet! :)

The Glue

D-Bus is a relatively simple mechanism and supports a predefined list of types. Composite types are sent as structures and have to have custom marshaling implemented. This is done via <</>> C++ operators.

Once you have the structure defined, the Qt way is a three-step process.

Declare the Metatype

Place the line

Q_CREATE_METATYPE(MyStruct)

somewhere outside body/class/namespace blocks. I’ve put it in mystruct.h after the struct declaration.

Declare/Implement the Marshaller

This actually means you need to declare/implement the <</>> operators. For convenience, I’ve declared them in mystruct.h as well and implemented them in mystruct.cpp like this:

mystruct.h

QDBusArgument &operator < < (QDBusArgument &arg,
    const MyStruct &mystruct);

const QDBusArgument &operator >> (const QDBusArgument &arg,
    MyStruct &mystruct);

mystruct.cpp

// Marshall the MyStructure data into a D-BUS argument
QDBusArgument &operator < < (QDBusArgument &arg,
    const MyStruct &mystruct)
{
    arg.beginStructure();
    arg << mystruct.element1 << mystruct.element2 << mystruct.element3;
    arg.endStructure();
    return arg;
}

// Retrieve the MyStructure data from the D-BUS argument
const QDBusArgument &operator >> (const QDBusArgument &arg,
    MyStruct &mystruct)
{
    arg.beginStructure();
    arg >> mystruct.element1 >> mystruct.element2 >> mystruct.element3;
    arg.endStructure();
    return arg;
}

Register the Type with D-Bus

Once you have done the above steps, everything is almost ready. Now you only need to add the type to the list of custom marshallers with qDBusRegisterMetaType.

In mystruct.h, add:

inline void registerCommTypes() {
        qDBusRegisterMetaType<mystruct>();
}

and have it called in both the client and server, somewhere before the actual D-Bus methods are called (e.g. in main() or in some constructor).

Look at the attached example for details.

Good luck at hacking your own D-Bus stuff!

Tagged with
All Tags
Share on
Reddit
Linked in
Whatsapp

A little experiment: