Porting C++ code from NAOqi1 to NAOqi2¶
Introduction¶
This guide describes how to port c++ modules to NAOqi2. It is not necessary to follow all the steps at once, each step can be done independently, but if you start a section, you must do all the subsections.
Writing a NAOqi2 service¶
To create a module, you used to inherit from ALModule
and register all your
methods in your constructor:
// myservice.h
class MyService : public AL::ALModule
{
public:
MyService(boost::shared_ptr<AL::ALBroker> pBroker,
const std::string& name);
std::string myMethod(int i) const;
bool myOtherMethod(int i);
};
// myservice.cpp
MyService::MyService(boost::shared_ptr<AL::ALBroker> pBroker,
const std::string& name)
: ALModule(pBroker, pName)
{
setModuleDescription("This modules does stuff on things");
functionName("myMethod", getName(), "return a meaningless string");
addParam("i", "a meaningless integer");
setReturn("meaningless_string", "the string you asked for");
BIND_METHOD(MyService::myMethod);
functionName("myOtherMethod", getName(), "...");
addParam("i", "...");
setReturn("value", "...");
BIND_METHOD(MyService::myOtherMethod);
}
This now becomes:
// myservice.h
class MyService
{
public:
// you can omit the session if you don't need it
MyService(qi::SessionPtr session);
/** return a meaningless string
* \param i a meaningless string
* \return the string you asked for
*/
std::string myMethod(int i) const;
/** ...
*/
bool myOtherMethod(int i);
private:
qi::SessionPtr _session;
};
// myservice.cpp
MyService::MyService(qi::SessionPtr session)
: _session(session)
{
// Don't register your methods here
}
// this macro will register your methods
QI_REGISTER_MT_OBJECT(MyService, myMethod, myOtherMethod);
You will not see the documentation of your methods when calling qicli
yet.
For more information, or for registering overloaded functions, see guide-cxx-register-classes.
Creating a NAOqi2 module¶
C++ part¶
After you created your service, you used to create a module by writing a file like:
// myservicemain.cpp
// lots of #include
#ifndef MYSERVICE_IS_REMOTE
# ifndef ALCALL
# ifdef _WIN32
# define ALCALL __declspec(dllexport)
# else
# define ALCALL
# endif
# endif
#else
# define ALCALL
#endif
# ifdef __cplusplus
extern "C"
{
# endif
ALCALL int _createModule(boost::shared_ptr<AL::ALBroker> pBroker)
{
// init broker with the main broker instance
// from the parent executable
AL::ALBrokerManager::setInstance(pBroker->fBrokerManager.lock());
AL::ALBrokerManager::getInstance()->addBroker(pBroker);
// create module instances
AL::ALModule::createModule<MyService>(pBroker, "MyService");
return 0;
}
ALCALL int _closeModule()
{
return 0;
}
# ifdef __cplusplus
}
# endif
#ifdef MYSERVICE_IS_REMOTE
int main(int argc, char *argv[])
{
// pointer on createModule
TMainType sig;
sig = &_createModule;
// call main
ALTools::mainFunction("MyService", argc, argv, sig);
}
#endif
You can now write a simple main
function that connects to Naoqi and registers the service. No more need for
MYSERVICE_IS_REMOTE
, the service will always be remote.
#include <qi/applicationsession.hpp>
#include <boost/shared_ptr.hpp>
int main(int argc, char* argv[])
{
qi::ApplicationSession app(argc, argv);
app.start();
qi::SessionPtr session = app.session();
session->registerService("MyService", qi::AnyObject(boost::make_shared<MyService>()));
app.run();
return 0;
}
CMake part¶
Here is what you used to write in NAOqi1:
# Creating module launcher binary
option(MYSERVICE_IS_REMOTE
"is my service remote?"
OFF
)
if(MYSERVICE_IS_REMOTE)
add_definitions(" -DMYSERVICE_IS_REMOTE ")
qi_create_bin(myservice src/myservicemain.cpp)
qi_use_lib(myservice stuff stuff2)
qi_stage_bin(myservice)
else()
qi_create_lib(
myservice
SRC
src/myservice.h
src/myservice.cpp
src/myservicemain.cpp
MODULE
SUBFOLDER naoqi
)
qi_use_lib(myservice stuff stuff2)
endif()
Here also, you don’t need to care about being remote or not. This now becomes:
qi_create_bin(myservice
SRC myservice.h myservice.cpp main.cpp
DEPENDS BOOST QI
)
Now, see Creating a new application outside Choregraphe using the qi Framework to compile and install your service on your robot.
Calling a service¶
You can call a NAOqi1 or NAOqi2 service from both NAOqi1 and NAOqi2. In NAOqi1, you used to do:
boost::shared_ptr<ALProxy> myservice = getParentBroker()->getProxy("MyService");
std::string val = myservice.call<std::string>("myMethod", 18);
In NAOqi2, this now becomes:
qi::AnyObject myservice = _session.service("MyService");
std::string val = myservice.call<std::string>("myMethod", 18);
// asynchronous version
qi::Future<std::string> futval = myservice.async<std::string>("myMethod", 18);
It is up to you to store the session somehow. There is no equivalent to
getParentBroker()
or getRandomBroker()
.
There are no auto-generated specialized proxies yet in NAOqi2.
Note
If you don’t have a Session (because your module is still in NAOqi1), you
can get one from broker->session()
.
Getting a Session from a Broker¶
You can get a Session from a broker by using broker->session()
.
Subscribing to an event from ALMemory¶
You don’t need to change the way you subscribe to ALMemory events. The difference is that in NAOqi2, you can subscribe to an event even if you are not a registered service yourself.
class MyClass {
public:
MyClass(SessionPtr session) {
qi::AnyObject almemory = session->service("ALMemory");
// keep the object alive
_subscriber = almemory.call<qi::AnyObject>("subscriber", "Something/MyEvent");
_subscriber.connect("signal", boost::bind(&MyClass::myCallback, this, _1));
}
void myCallback(AL::ALValue value) { /*...*/ }
private:
qi::AnyObject _subscriber;
};