Search code examples
c++qtdbusqtdbus

Sharing objects over dbus


My project derives from this example: http://doc.qt.io/qt-5/qtdbus-remotecontrolledshower-example.html

There are two processes, shower and controller, and the controller can send simple messages to the shower (I just renamed the car in that project for shower) and changed the fancy car GUI for a simple Widget. I had no troubles doing this. My problem is when I try to send a message using a custom class

#ifndef MESSAGE_H
#define MESSAGE_H

#include <QtDBus>

class message
{
public:
    QString articleName;
    QString categoryName;

    message() : articleName(""), categoryName("") {}
    message( QString a, QString b ) : articleName(a), categoryName(b) {}
    message(const message &other) : articleName( other.articleName ), categoryName( other.categoryName ) {}
    message& operator=(const message &other)
    {
       articleName = other.articleName;
       categoryName = other.categoryName;
       return *this;
    }
    //register Message with the Qt type system
    static void registerMetaType()
    {
        qRegisterMetaType<message>("message");

        qDBusRegisterMetaType<message>();
    }

    friend QDBusArgument &operator<<(QDBusArgument &argument, const message &mess )
    {
        argument.beginStructure();
        argument << mess.articleName;
        argument << mess.categoryName;
        argument.endStructure();
        return argument;
    }

    friend const QDBusArgument &operator>>(const QDBusArgument &argument, message &mess )
    {
        argument.beginStructure();
        argument >> mess.articleName;
        argument >> mess.categoryName;
        argument.endStructure();

        return argument;
    }
};


Q_DECLARE_METATYPE( message )

#endif // MESSAGE_H

And I add a method in the shower class where I receive the message object

#ifndef shower_H
#define shower_H

#include "ui_Categories.h"
#include "message.h"

class shower : public QWidget
{
    Q_OBJECT

public:
    shower(QWidget *parent = 0);
    //shower();
    //QRectF boundingRect() const;

public Q_SLOTS:
    void accelerate();
    void decelerate();
    void turnLeft();
    void turnRight();
    void doThat();
    void addMessage( const message &msg );
    QString Get( int param );

Q_SIGNALS:
    void crashed();

protected:
    void timerEvent(QTimerEvent *event);
private:
    Ui::Categories ui;
    qreal wheelsAngle; // used when applying rotation
    qreal speed; // delta movement along the body axis
};

#endif // shower_H

This is the xml where I define the shower interface

<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
    "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="/com/trollech/examples/shower">
    <interface name="org.example.Examples.showerInterface">
        <method name="accelerate"/>
        <method name="decelerate"/>
        <method name="turnLeft"/>
        <method name="turnRight"/>
        <method name="addMessage">
            <arg name="param" type="a(ii)" direction="in"/>
            <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="message"/>
        </method>
        <signal name="crashed"/>
    </interface>
</node>

And the pro file

QT += dbus widgets

DBUS_ADAPTORS += shower.xml
HEADERS += shower.h \
    message.h
SOURCES += shower.cpp main.cpp

# install
target.path = $$[QT_INSTALL_EXAMPLES]/dbus/wikidbusqt/shower
INSTALLS += target

FORMS += \
    Categories.ui

OTHER_FILES += \
    shower.xml

I can't compile this, I get this error log

20:45:21: Running steps for project wikidbusqt...
20:45:21: Starting: "/usr/lib/x86_64-linux-gnu/qt5/bin/qmake" /home/hector/workspace/wikidbusqt/shower/shower.pro -r -spec linux-g++-64
20:45:21: The process "/usr/lib/x86_64-linux-gnu/qt5/bin/qmake" exited normally.
20:45:21: Starting: "/usr/bin/make" 
/usr/lib/x86_64-linux-gnu/qt5/bin/uic ../../wikidbusqt/shower/Categories.ui -o ui_Categories.h
g++ -c -m64 -pipe -O2 -Wall -W -D_REENTRANT -fPIE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_DBUS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I/usr/lib/x86_64-linux-gnu/qt5/mkspecs/linux-g++-64 -I../../wikidbusqt/shower -I/usr/include/qt5 -I/usr/include/qt5/QtWidgets -I/usr/include/qt5/QtDBus -I/usr/include/qt5/QtGui -I/usr/include/qt5/QtCore -I. -I. -I. -o shower.o ../../wikidbusqt/shower/shower.cpp
In file included from /usr/include/qt5/QtCore/qvariant.h:48:0,
                 from /usr/include/qt5/QtCore/QVariant:1,
                 from ./ui_Categories.h:12,
                 from ../../wikidbusqt/shower/shower.h:46,
                 from ../../wikidbusqt/shower/shower.cpp:42:
/usr/include/qt5/QtCore/qmetatype.h:1708:12: error: specialization of 'QMetaTypeId<message>' after instantiation
     struct QMetaTypeId< TYPE >                                          \
            ^
../../wikidbusqt/shower/message.h:53:1: note: in expansion of macro 'Q_DECLARE_METATYPE'
 Q_DECLARE_METATYPE( message )
 ^
/usr/include/qt5/QtCore/qmetatype.h:1708:12: error: redefinition of 'struct QMetaTypeId<message>'
     struct QMetaTypeId< TYPE >                                          \
            ^
../../wikidbusqt/shower/message.h:53:1: note: in expansion of macro 'Q_DECLARE_METATYPE'
 Q_DECLARE_METATYPE( message )
 ^
/usr/include/qt5/QtCore/qmetatype.h:1491:8: error: previous definition of 'struct QMetaTypeId<message>'
 struct QMetaTypeId : public QMetaTypeIdQObject<T>
        ^
In file included from /usr/include/qt5/QtCore/qatomic.h:42:0,
                 from /usr/include/qt5/QtCore/qvariant.h:45,
                 from /usr/include/qt5/QtCore/QVariant:1,
                 from ./ui_Categories.h:12,
                 from ../../wikidbusqt/shower/shower.h:46,
                 from ../../wikidbusqt/shower/shower.cpp:42:
/usr/include/qt5/QtCore/qmetatype.h: In instantiation of 'int qMetaTypeId() [with T = message]':
/usr/include/qt5/QtDBus/qdbusmetatype.h:85:29:   required from 'int qDBusRegisterMetaType(T*) [with T = message]'
../../wikidbusqt/shower/message.h:28:40:   required from here
/usr/include/qt5/QtCore/qglobal.h:679:85: error: invalid application of 'sizeof' to incomplete type 'QStaticAssertFailure<false>'
     enum {Q_STATIC_ASSERT_PRIVATE_JOIN(q_static_assert_result, __COUNTER__) = sizeof(QStaticAssertFailure<!!(Condition)>)}
                                                                                     ^
/usr/include/qt5/QtCore/qglobal.h:684:47: note: in expansion of macro 'Q_STATIC_ASSERT'
 #define Q_STATIC_ASSERT_X(Condition, Message) Q_STATIC_ASSERT(Condition)
                                               ^
/usr/include/qt5/QtCore/qmetatype.h:1638:5: note: in expansion of macro 'Q_STATIC_ASSERT_X'
     Q_STATIC_ASSERT_X(QMetaTypeId2<T>::Defined, "Type is not registered, please use the Q_DECLARE_METATYPE macro to make it known to Qt's meta-object system");
     ^
make: *** [shower.o] Error 1
20:45:24: The process "/usr/bin/make" exited with code 2.
Error while building/deploying project wikidbusqt (kit: Desktop)
When executing step 'Make'
20:45:24: Elapsed time: 00:03.

I am at loss about what's happening, I was following the steps outlined in this tutorial: https://techbase.kde.org/Development/Tutorials/D-Bus/CustomTypes#DBusChat.pro and I don't know what am I overlooking.


Solution

  • You can only invoke qRegisterMetaType and similar methods after Q_DECLARE_METATYPE is in scope. Thus, you must move the body of registerMetaType() out of the class, into a .cpp file:

    message.h (relevant fragments)

    ...
    class message
    {
      ...
      /// Register message with the Qt type system.
      static void registerMetaType();
      ...
    };
    Q_DECLARE_METATYPE( message )
    ...
    

    message.cpp (entire file)

    #include "message.h"
    
    void message::registerMetaType()
    {
      qRegisterMetaType<message>("message");
      qDBusRegisterMetaType<message>();
    }
    

    You cannot put the out-of-class definition of registerMetaType into the header file, as it will break the single definition rule and the linker will complain of multiply-defined symbols.