SoftBank Robotics documentation What's new in NAOqi 2.5?

Warning

qilang is still under active development and its API may not be backward-compatible in the future.

Using qilang to generate strongly-typed proxies

qilang allows the usage of strongly-typed proxies in client code. This means that instead of writing:

std::vector<int> ret = myservice.call<std::vector<int> >("workIt", "blah", 12);

and maybe trigger a run-time error because the argument types are wrong, using qilang allows you to write:

std::vector<int> ret = myservice->workIt("blah", 12);

and get a compile-time error in case you got the argument types incorrect.

If you need details about the qilang syntax, see qilang IDL syntax.

Creating an object/service with qilang

A typical work tree will look like this:

worktree
└── mymodule
    ├── mymodule
    │   ├── api.hpp
    │   └── myservice.idl.qi
    ├── myservice.cpp
    ├── qiproject.xml
    └── CMakeLists.txt

The api.hpp file is still needed and must contain:

#ifndef MYMODULE_API_HPP
#define MYMODULE_API_HPP

#include <qi/macro.hpp>

#define MYMODULE_API QI_LIB_API(mymodule)

#endif

The argument of QI_LIB_API must be the same as the name of your CMake target.

Then write the API of your service in myservice.idl.qi:

package mymodule

interface MyService
  fn emitPing(value: int)
  fn workIt(name: str, value: int) -> Vec<int>

  sig ping(value: int)

  prop pingEnabled(value: bool)
end

This defines the interface of your service, and then you can implement it in myservice.cpp:

// same name as your package in qilang
namespace mymodule {
  class MyServiceImpl {
  public:
    void emitPing(int value) {
      if (pingEnabled.get())
        QI_EMIT ping(value);
    }

    qi::Future<std::vector<int> > workIt(std::string name, int value) {
      return longCall().andThenR<std::vector<int> >(handleResults, _1);
    }

    qi::Signal<int> ping;
    qi::Property<bool> pingEnabled;
  };
}

// declare your implementation of MyService
REGISTER_MYSERVICE(mymodule::MyServiceImpl);

void registerMe(qi::ModuleBuilder* mb) {
  mb->advertiseFactory<mymodule::MyService>("MyService");
}
QI_REGISTER_MODULE("mymodule", &registerMe);

Now that the code is done, to be able to build it, you must add libqilang as a host dependency to your project. A host dependency is different from a normal dependency because when cross-compiling, you must have the host dependency compiled for your host and not your target. Here is how you do that:

<project name="mymodule">
  <depends buildtime="true" runtime="runtime" names="libqi"/>
  <depends host="true" names="libqilang"/>
</project>

And finally enable the code generation through your CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)

project(mymodule)
find_package(qibuild)
find_package(qimodule)

include_directories(".")
qi_sanitize_compile_flags()

find_package(qilang-tools)

# generate all the new files
qi_gen_idl(mymodule_idl CPP "mymodule" "${CMAKE_CURRENT_BINARY_DIR}" mymodule/myservice.idl.qi)
include_directories(${CMAKE_CURRENT_BINARY_DIR})

# install the headers so that people can use your proxies
qi_install_header(
  ${mymodule_idl_INTERFACE}
  mymodule/api.hpp
  SUBFOLDER mymodule)
# create a lib with the proxies only
qi_create_lib(mymodulelib
  ${mymodule_idl_INTERFACE}
  ${mymodule_idl_LOCAL}
  ${mymodule_idl_REMOTE}
  DEPENDS QI)
qi_stage_lib(mymodulelib)

# create a module with your implementation of the MyService interface
qi_create_module(
  mymodule
  SRC myservice.cpp
  DEPENDS QI QIPACKAGE mymodulelib
)

Now you are ready to build.

Building a qilang project

  1. Set your host config:
$ qibuild set-host-config linux64
  1. Build those host tools:
$ qibuild make-host-tools
  1. Build your project
$ qibuild configure
$ qibuild make

Using strongly typed proxies in another project

In another project, to benefit from the proxies you generated in mymodule, follow these steps:

  1. Add mymodule as a dependency in your qiproject.xml.
  2. Add mymodulelib as a dependency of your binary:
qi_create_bin(main "main.cpp" DEPENDS QI MYMODULELIB)
  1. Use the proxy in your code:
#include <qi/applicationsession.hpp>
#include <mymodule/myservice.hpp>

qiLogCategory("myclient");

int main(int argc, char* argv[])
{
  qi::ApplicationSession app(argc, argv);
  app.start();

  qi::SessionPtr session = app.session();
  mymodule::MyServicePtr myserv = session->service("MyService");

  for (const auto& value : myserv->workIt("blah", 42))
    qiLogInfo() << "value: " << value;

  return 0;
}

You can also make asynchronous calls like this:

auto future = myserv->async().workIt("blah", 42);
future.connect(mycallback);