Development/Tutorials/D-Bus/Cpp2XmlPlugins

From KDE TechBase

As discussed over at CustomTypes, the stock qdbuscpp2xml doesn't understand non-builtin types, and will simply ignore any signals, slots, or properties that use them. In the past this meant resorting to editing the generating XML by hand, which defeats the whole purpose of qdbuscpp2xml. Below I present one strategy to overcome this deficiency, using plugins.

I began by modifying the qdbuscpp2xml source code, adding about 50 lines of code to it, and then rebuilding it. These source code changes are presented as a diff at the bottom of this page.

Then I wrote an example plugin to complement the chat example from CustomTypes, and the code for this is also listed below.

Finally I generated the XML file with the following command:

qdbuscpp2xml -x myplugin Chat.hpp


The XML generated by the above command is functionally identical to Chat.xml from the chat example.

qdbuscpp2xml-plugable.diff

diff -r -c 5.1.0-orig/qtbase/src/dbus/qdbusmetatype.h 5.1.0/qtbase/src/dbus/qdbusmetatype.h
*** 5.1.0-orig/qtbase/src/dbus/qdbusmetatype.h	Tue Jul  2 08:09:52 2013
--- 5.1.0/qtbase/src/dbus/qdbusmetatype.h	Tue Jul  9 21:44:06 2013
***************
*** 91,95 ****
--- 91,105 ----
  
  QT_END_NAMESPACE
  
+ #ifndef QT_NAMESPACE
+ class QDBusCpp2XmlPlugin
+ {
+ public:
+     virtual ~QDBusCpp2XmlPlugin() {}
+     virtual void registerMetaTypes() = 0;
+ };
+ Q_DECLARE_INTERFACE(QDBusCpp2XmlPlugin, "org.qt-project.Qt.DBus.Cpp2XmlPlugin")
+ #endif
+ 
  #endif // QT_NO_DBUS
  #endif
diff -r -c 5.1.0-orig/qtbase/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp 5.1.0/qtbase/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp
*** 5.1.0-orig/qtbase/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp	Tue Jul  2 08:10:01 2013
--- 5.1.0/qtbase/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp	Tue Jul  9 21:52:51 2013
***************
*** 47,52 ****
--- 47,55 ----
  #include <qbuffer.h>
  #include <qregexp.h>
  #include <qvector.h>
+ #include <qsharedpointer.h>
+ #include <qpluginloader.h>
+ #include <qcoreapplication.h>
  
  #include <stdio.h>
  #include <stdlib.h>
***************
*** 79,84 ****
--- 82,90 ----
  static QString outputFile;
  static int flags;
  
+ static QStringList pluginNames;
+ static QVector< QSharedPointer<QPluginLoader> > pluginLoaders;
+ 
  static const char help[] =
      "Usage: " PROGRAMNAME " [options...] [files...]\n"
      "Parses the C++ source or header file containing a QObject-derived class and\n"
***************
*** 90,95 ****
--- 96,102 ----
      "  -a             Output all scriptable contents (equivalent to -psm)\n"
      "  -A             Output all contents (equivalent to -PSM)\n"
      "  -o <filename>  Write the output to file <filename>\n"
+     "  -x <filename>  Load <filename> as a plugin\n"
      "  -h             Show this information\n"
      "  -V             Show the program version and quit.\n"
      "\n";
***************
*** 371,376 ****
--- 378,391 ----
              outputFile = arguments.takeAt(i + 1);
              break;
  
+         case 'x':
+             if (arguments.count() < i + 2 || arguments.at(i + 1).startsWith(QLatin1Char('-'))) {
+                 printf("-x expects a filename\n");
+                 exit(1);
+             }
+             pluginNames.append(arguments.takeAt(i + 1));
+             break;
+ 
          case 'h':
          case '?':
              showHelp();
***************
*** 393,402 ****
--- 408,442 ----
  
  int main(int argc, char **argv)
  {
+     int dummyArgc = 1;
+     QCoreApplication app(dummyArgc, argv);
+     
      QStringList args;
      for (int n = 1; n < argc; ++n)
          args.append(QString::fromLocal8Bit(argv[n]));
      parseCmdLine(args);
+     
+     for (int i = 0; i < pluginNames.count(); ++i) {
+         const QString name = pluginNames.at(i);
+         QPluginLoader *loader = new QPluginLoader(name);
+         pluginLoaders.resize(pluginLoaders.size() + 1);
+         pluginLoaders.back().reset(loader);
+         
+         if (loader->load()) {
+             QDBusCpp2XmlPlugin *plugin = qobject_cast<QDBusCpp2XmlPlugin*>(loader->instance());
+             if (plugin) {
+                 plugin->registerMetaTypes();
+             } else {
+                 fprintf(stderr, PROGRAMNAME ": plugin '%s' does not implement"
+                     " QDBusCpp2XmlPlugin\n", qPrintable(name));
+                 return 1;
+             }
+         } else {
+             fprintf(stderr, PROGRAMNAME ": could not load '%s': %s\n",
+                     qPrintable(name), qPrintable(loader->errorString()));
+             return 1;
+         }
+     }
  
      QList<ClassDef> classes;

myplugin.h

#ifndef MYPLUGIN
#define MYPLUGIN

#include <QObject>
#include <QDBusMetaType>

class MyPlugin : public QObject, public QDBusCpp2XmlPlugin
{
    Q_OBJECT
    Q_INTERFACES(QDBusCpp2XmlPlugin)
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.DBus.Cpp2XmlPlugin")
    
public:
    virtual void registerMetaTypes();
};

#endif //MYPLUGIN

myplugin.cpp

#include "myplugin.h"
#include "Message.hpp"
#include <QMetaType>

void MyPlugin::registerMetaTypes()
{
    Message::registerMetaType();
}

myplugin.pro

QT += core dbus
TARGET = myplugin
TEMPLATE = lib
CONFIG += plugin
SOURCES += myplugin.cpp Message.cpp
HEADERS += myplugin.h Message.hpp

Other files

From the chat example:

Future plans

I intend to use this approach in production, and then submit my diff upstream when I am satisfied it is sufficiently mature. If you want to submit it before then, you are free to do so. Please post about it here, if you do, so that we do not duplicate effort. Thanks. Oktal3700 (talk) 15:15, 10 July 2013 (BST)