Example: HelloWorld module

<< return to examples index

The whole example is available here: helloworld.zip

Module creation

This module is defined in the two following files:

Source file

The second one contains the implementation. Note the binding of the functions in the constructor. This binding is imperative: else, the methods won’t be available from outside the module.

helloworld.cpp

/**
 * Copyright (c) 2011 Aldebaran Robotics
 */

#include "helloworld.h"
#include <iostream>
#include <alcommon/albroker.h>
#include <qi/log.hpp>
#include <alproxies/altexttospeechproxy.h>

using namespace AL;

HelloWorld::HelloWorld(boost::shared_ptr<ALBroker> broker, const std::string& name):
  ALModule(broker, name)
{
  /** Describe the module here. This will appear on the webpage*/
  setModuleDescription("A hello world module.");

  /** Define callable methods with their descriptions:
  * This makes the method available to other cpp modules
  * and to python.
  * The name given will be the one visible from outside the module.
  * This method has no parameters or return value to describe
  */
  functionName("sayHello", getName(), "Say hello to the world");
  BIND_METHOD(HelloWorld::sayHello);

  functionName("sayText", getName(), "Say a given sentence.");
  /**
  * This enables to document the parameters of the method.
  * It is not compulsory to write this line.
  */
  addParam("toSay", "The sentence to be said.");
  BIND_METHOD(HelloWorld::sayText);

  functionName("sayTextAndReturnLength", getName(),
               "Say a given sentence, and return its length");
  addParam("toSay", "The sentence to be said.");
  /**
  * This enables to document the return of the method.
  * It is not compulsory to write this line.
  */
  setReturn("sentenceLength", "Length of the said sentence.");
  BIND_METHOD(HelloWorld::sayTextAndReturnLength);

  // If you had other methods, you could bind them here...
  /** Bound methods can only take const ref arguments of basic types,
  * or AL::ALValue or return basic types or an AL::ALValue.
  */
}

HelloWorld::~HelloWorld() {}

void HelloWorld::init()
{
  /** Init is called just after construction.
  *
  * Here we call sayHello, so that the module does something
  * without us having to explicitly call sayHello from somewhere else.
  */
  sayHello();
}


void HelloWorld::sayHello()
{
  const std::string phraseToSay("Hello world");
  std::cout << "Saying hello to the console ..." << std::endl;
  std::cout << phraseToSay << std::endl;
  std::cout << "Calling say method of ALTextToSpeech module ..." << std::endl;
  try
  {
    /** Create a proxy to TTS.*/
    ALTextToSpeechProxy tts(getParentBroker());
    /** Call the say method. */
    tts.say(phraseToSay);
    /** Note: on the desktop you won't hear anything, but you should see
    * some logs on the naoqi you are connected to.
    */
  }
  catch(const AL::ALError&) // no object name given to avoid warning
  {
    qiLogError("module.example") << "Could not get proxy to ALTextToSpeech" << std::endl;
  }

  qiLogInfo("module.example") << "Done !" << std::endl;
}

void HelloWorld::sayText(const std::string &toSay) {
  std::cout << "Saying the phrase in the console..." << std::endl;
  std::cout << toSay << std::endl;
  try
  {
    /** Create a proxy to TTS.*/
    ALTextToSpeechProxy tts(getParentBroker());
    /** Call the say method. */
    tts.say(toSay);
    /** Note: on the desktop you won't hear anything, but you should see
    * some logs on the naoqi you are connected to. */
  }
  catch(const AL::ALError&)
  {
    qiLogError("module.example") << "Could not get proxy to ALTextToSpeech" << std::endl;
  }
}

int HelloWorld::sayTextAndReturnLength(const std::string &toSay) {
  /** Say the sentence.*/
  sayText(toSay);
  /** Return its length.*/
  return toSay.length();
}

Main

The main.cpp contains lots of boilertemplate code. You should not worrying about this file. Just do not modify it unless you know what you are doing.

main.cpp

/**
 * Copyright (c) 2010 Aldebaran Robotics
 */

#ifndef _WIN32
# include <signal.h>
#endif

#include <alcommon/albroker.h>
#include <alcommon/albrokermanager.h>
#include <alcommon/altoolsmain.h>
#include "helloworld.h"

#ifdef HELLOWORLD_IS_REMOTE
# define ALCALL
#else
// when not remote, we're in a dll, so export the entry point
# ifdef _WIN32
#  define ALCALL __declspec(dllexport)
# else
#  define ALCALL
# endif
#endif

extern "C"
{
  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<HelloWorld>(pBroker, "HelloWorld");
    return 0;
  }

  ALCALL int _closeModule(  )
  {
    return 0;
  }
} // extern "C"


#ifdef HELLOWORLD_IS_REMOTE
  int main(int argc, char *argv[])
  {
    // pointer to createModule
    TMainType sig;
    sig = &_createModule;
    // call main
    return ALTools::mainFunction("HelloWorld", argc, argv, sig);
  }
#endif

Note: if you want to understand what this main.cpp does or want to change it, you can read about it in the Creating a custom main section.

CMakeLists.txt

The CMakeLists.txt file can generate a .sln file or a Makefile project for you. Do not change it unless you know what you are doing.

CMakeLists.txt

# Copyright (C) 2011 Aldebaran Robotics
cmake_minimum_required(VERSION 2.6.4 FATAL_ERROR)

# Define the name of the project
project(helloworld)

# This include enable you to use qibuild framework
include("qibuild.cmake")


# Create an option to make is possible compiling the module
# as a remote executable, or as a local shared library
option(HELLOWORLD_IS_REMOTE
  "module is compiled as a remote module (ON or OFF)"
  ON)

# Create a list of source files
set(_srcs
  helloworld.cpp
  helloworld.h
  main.cpp
)

if(HELLOWORLD_IS_REMOTE)
  # Add a compile flag because code changes a little bit
  # when we are compiling an executable
  # This will let you use #ifdef HELLOWORLD_IS_REMOTE
  # in the C++ code
  add_definitions( " -DHELLOWORLD_IS_REMOTE ")

  # Create an executable
  qi_create_bin(helloworld ${_srcs})
else()
  # Create a plugin, that is a shared library, and make
  # sure it is built in lib/naoqi, so that the naoqi executable
  # can find it later
  qi_create_lib(helloworld SHARED ${_srcs} SUBFOLDER naoqi)
endif()

# Tell CMake that sayhelloworld depends on ALCOMMON.
# This will set the libraries to link sayhelloworld with,
# the include paths, and so on
qi_use_lib(helloworld ALCOMMON)



# Also create a simple executable capable of creating
# a proxy to the helloworld module
qi_create_bin(testhelloworld testhelloworld.cpp)
qi_use_lib(testhelloworld ALCOMMON)

Module access

Here is an example on how to access or “HelloWorld” module from the outside.

testhelloworld.cpp

/**
* Copyright (c) 2011 Aldebaran Robotics. All Rights Reserved
*
* \file testhelloworld.cpp
* \brief Test how to call a module from the outside.
*
* An example on how to call the example "HelloWorld" module from the outside.
*
*/

#include <iostream>
#include <alerror/alerror.h>
#include <alcommon/alproxy.h>

int main(int argc, char* argv[]) {
  if(argc != 2)
  {
    std::cerr << "Wrong number of arguments!" << std::endl;
    std::cerr << "Usage: testhelloworld NAO_IP" << std::endl;
    exit(2);
  }

  const std::string robotIP = argv[1];
  int port = 9559;

  try {
    /** Create a generic proxy to "HelloWorld" module.
    * Arguments for the constructor are
    * - name of the module
    * - string containing the IP adress of the robot
    * - port (default is 9559)
    */
    boost::shared_ptr<AL::ALProxy> testProxy
    = boost::shared_ptr<AL::ALProxy>(new AL::ALProxy("HelloWorld", robotIP, port));

    /** Call the sayHello method from the module using its bound name.
    * Since it returns nothing, use the callVoid method.
    */
    testProxy->callVoid("sayHello");

    /** Call the sayText method from the module using its bound name and giving
    * the right argument. Since it returns nothing, use the callVoid method.
    */
    testProxy->callVoid("sayText", std::string("This is a test."));

    /** Call the sayTextAndReturnLength method from the module using its bound
    * name and giving the right argument.
    * Since the method returns something, use the call method templated with
    * the right return type.
    */
    int sentenceLength = testProxy->call<int>("sayTextAndReturnLength",
                         std::string("This is another test"));
    std::cout << "Sentence length is " << sentenceLength << std::endl;
  }
  catch (const AL::ALError& e) {
    std::cerr << e.what() << std::endl;
  }
}