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", ®isterMe);
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¶
- Set your host config:
$ qibuild set-host-config linux64
- Build those host tools:
$ qibuild make-host-tools
- 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:
- Add mymodule as a dependency in your qiproject.xml.
- Add
mymodulelib
as a dependency of your binary:
qi_create_bin(main "main.cpp" DEPENDS QI MYMODULELIB)
- 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);