Compiling a C++ D-Bus program

From Apertium
Jump to navigation Jump to search

Read the guide to installing the C++ D-Bus bindings if you don't have the library installed already.

Since both D-Bus and C++ use statically typed systems, C++ needs to have the D-Bus interface descriptions of the objects it will use available at compile time. The dbusxx-introspect program introspects D-Bus objects and produces D-Bus XML interface descriptions. To introspect the object / in the service org.apertium.mode, one issues the command:

dbusxx-introspect / org.apertium.mode

This outputs the XML description stdout. Append > org.apertium.mode.xml to the above command to write the data to the file org.apertium.mode.xml. The contents out the created XML file should look something like:

<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="/">
  <interface name="org.freedesktop.DBus.Introspectable">
    <method name="Introspect">
      <arg direction="out" type="s" />
    </method>
  </interface>
  <interface name="org.apertium.Translate">
    <method name="translate">
      <arg direction="in"  type="s" name="pair" />
      <arg direction="in"  type="a{ss}" name="options" />
      <arg direction="in"  type="s" name="text" />
      <arg direction="out" type="s" />
    </method>
  </interface>
  <node name="af_en"/>
  <node name="en_af"/>
  <node name="es_fr"/>
  <node name="fr_es"/>
</node>


Now one can use the tool dbusxx-xml2cpp to generate a C++ proxy class for the D-Bus object. We will create a file called Translation-glue.h from our generated XML file by issuing:

dbusxx-xml2cpp org.apertium.mode.xml --proxy=Translation-glue.h

(Note that dbusxx-xml2cpp can also generate adaptor objects with the flag --adaptor. This is meant for creating D-Bus services with C++, which is outside of the scope of this guide.)

The generated file should look something like this:

/*
 *      This file was automatically generated by dbusxx-xml2cpp; DO NOT EDIT!
 */

#ifndef __dbusxx__Translate_h__PROXY_MARSHAL_H
#define __dbusxx__Translate_h__PROXY_MARSHAL_H

#include <dbus-c++/dbus.h>

namespace org {
namespace apertium {

class Translate
 : public ::DBus::InterfaceProxy
{
public:

    Translate()
    : ::DBus::InterfaceProxy("org.apertium.Translate")
    {
    }

public:

    /* methods exported by this interface,
     * this functions will invoke the corresponding methods on the remote objects
     */
    ::DBus::String translate( const ::DBus::String& pair, 
                              const std::map< ::DBus::String, ::DBus::String >& options, 
                              const ::DBus::String& text )
    {
        ::DBus::CallMessage call;
        ::DBus::MessageIter wi = call.writer();

        wi << pair;
        wi << options;
        wi << text;
        call.member("translate");
        ::DBus::Message ret = invoke_method(call);
        ::DBus::MessageIter ri = ret.reader();

        ::DBus::String argout;
        ri >> argout;
        return argout;
    }


public:

    /* signal handlers for this interface
     */

private:

    /* unmarshalers (to unpack the DBus message before calling the actual signal handler)
     */
};

} }
#endif//__dbusxx__Translate_h__PROXY_MARSHAL_H

The next step is to create our minimal C++ program. Create a file called dbus_test.cpp with the following contents:

#include <map>
#include <iostream>
#include <sstream>
#include <string>

#include <dbus-c++/dbus.h>
#include "Translate-glue.h"

static const char* TRANSLATE_SERVICE_NAME = "org.apertium.mode";
static const char* TRANSLATE_OBJECT_PATH = "/";

class Translate
: public org::apertium::Translate,
  public DBus::IntrospectableProxy,
  public DBus::ObjectProxy
{
public:
    Translate(DBus::Connection& connection, const char* path, const char* name)
    : DBus::ObjectProxy(connection, path, name) {
    }
};

DBus::BusDispatcher dispatcher;

int main(int argc, char** argv) {
    DBus::default_dispatcher = &dispatcher;
    DBus::Connection bus = DBus::Connection::SessionBus();

    std::stringstream input;
    std::string mode;
    std::map< ::DBus::String, ::DBus::String> translate_options;

    if (argc != 2) {
        std::cerr << "usage: dbus_test <mode>" << std::endl;
        return 1;
    }

    mode = std::string(argv[1]);

    while (!std::cin.eof() and std::cin) {
        std::string str;
        std::cin >> str;
        input << str << " ";
    }

    Translate translator(bus, TRANSLATE_OBJECT_PATH, TRANSLATE_SERVICE_NAME);
    std::cout << translator.translate(mode, translate_options, input.str()) 
              << std::endl;
}

To compile this program, you only need to put libdbus-c++'s include directory in your include path and link to libdbus-c++-1. You compile command should look something like this (I installed the library to /usr/local, so update the include directory to match your installation directory):

 
g++ -I/usr/local/include/dbus-c++-1/ dbus_test.cpp -ldbus-c++-1

That's it!