C++ - How to write a qimessaging service

This document will teach you how to write your very own qimessaging service, so that your new shiny object can be used by other qimessaging services and clients, locally or remotely, and from many different programming languages.

Limitations

There are some restrictions on what you can do:

  • Do not use class member data.
  • Output parameters are not supported: you should not take pointers as arguments, or non-const references.
  • Do not use methods in transmitted struct, as they will not be available in other languages: put the methods in an associated class.

To understand those restrictions, keep in mind that qimessaging is a middleware that knows how to transport data (like primitive types, structs, and containers of the above), and how to make calls to remote methods, signals and properties, but cannot mix the two.

Write your service

Your service can be a simple class which is registered in the type system (see type registration):

#include <qi/anyobject.hpp>

struct Mosquito
{
  double yaw,theta,distance;
};
QI_TYPE_STRUCT(Mosquito, yaw, theta, distance)

class Cat
{
  public:
    void meow(int volume);
    bool setTarget(const Mosquito& m);

    qi::Property<float> hunger;
    qi::Property<float> boredom;
    qi::Property<float> cuteness;
    qi::Signal<Mosquito> onTargetDetected;
};
QI_REGISTER_OBJECT(Cat, meow, setTarget, hunger, boredom, cuteness,
    onTargetDetected);

Now your class and its types (Mosquito) are known by the type-system. If you don’t want to make a module, you can skip directly to the Manual way section.

You will then put this class in a module (like a library) that you will be able to load dynamically. You will also need to advertise a factory to be able to make a Cat out of this module, like this:

void registerCat(qi::ModuleBuilder* mb)
{
  mb->advertiseFactory<Cat>("Cat");
}
QI_REGISTER_MODULE("catsimulator", &registerCat);

Your module will be called catsimulator (must be the same name that you will put in your CMake file). You can now write a CMakeLists.txt and you’re done:

cmake_minimum_required(VERSION 2.8)
project(catsimulator)

find_package(qibuild)
find_package(qimodule)

qi_create_module(catsimulator
  SRC
    src/cat.cpp
  DEPENDS
    QI
)

Remember that the name you put in qi_create_module must be the same as the one in QI_REGISTER_MODULE. For more information about modules, see qi::AnyModule.

Running your service

Fully automated way

You can start your service by using qilaunch, like this:

qilaunch -n catsimulator --object catsimulator.Cat

qilaunch instantiates your service and registers it on the ServiceDirectory. It will forward its logs to the LogManager if present and will initialize breakpad to make a minidump when it crashes. You can stop it at any time with CTRL-C.

For more information on qilaunch, read Using qilaunch.

Semi-automated way

You can also instantiate your object manually and expose it on a session. In this example, ApplicationSession parses the command line arguments to find out what the user wants, i.e.:

  • which type of session (listening or connected to a ServiceDirectory)
  • on which address

And then it initializes a session accordingly.

#include <qi/applicationsession.hpp>

int main(int argc, char** argv)
{
  qi::ApplicationSession app(argc, argv);
  app.start();
  qi::SessionPtr session = app.session();

  session->loadService("catsimulator.Cat");

  app.run();
}

Manual way

If you don’t want to use a module at all but simply make a binary that publishes your service, you can just do, similarly to the example above:

#include <qi/applicationsession.hpp>

int main(int argc, char** argv)
{
  qi::ApplicationSession app(argc, argv);
  app.startSession();
  qi::SessionPtr session = app.session();

  auto cat = boost::make_shared<Cat>();
  session->registerService("CatService", cat);

  app.run();
}

Then, you don’t need to use qi_create_module in your CMake file, you can just use qi_create_bin.

The session will keep a reference on cat until it is closed or CatService is unregistered.