Description of HelloWorld Sample

This document describes the implementation of “HellowWorldPlugin”, which is one of sample plugins.

Source code

The source code of this sample is as follows:

#include <cnoid/Plugin>
#include <cnoid/MenuManager>
#include <cnoid/MessageView>
#include <boost/bind.hpp>

using namespace cnoid;
using namespace boost;

class HelloWorldPlugin : public Plugin
{
public:

    HelloWorldPlugin() : Plugin("HelloWorld")
    {

    }

    virtual bool initialize()
    {
        Action* menuItem = menuManager().setPath("/View").addItem("Hello World");
        menuItem->sigTriggered().connect(bind(&HelloWorldPlugin::onHelloWorldTriggered, this));
        return true;
    }

private:

    void onHelloWorldTriggered()
    {
        MessageView::mainInstance()->putln("Hello World !");
    }
};

CNOID_IMPLEMENT_PLUGIN_ENTRY(HelloWorldPlugin)

This code is included in a source archive as “HellowWorldPlugin.cpp” in the directory “share/sampleplugins/HelloWorldPlugin”. Please use that file for checking and testing this sample. Note that there may be some slightly different part between the above code and one attached in a source archive depending on Choreonoid versions or the convenience for description.

When you build and install this sample, it adds a sub menu item “Hello World” to the “View” item of the main menu. Selecting this menu item puts “Hello World!” in the message view. This plugin just does it, but this is useful for learning the basics of the plugin development on Choreonoid.

Headers

First we describe the headers this sample is including.

#include <cnoid/Plugin>
#include <cnoid/MenuManager>
#include <cnoid/MessageView>

These three headers are provided by the Choreonoid framework. The provided headers are basically contained in sub directory “cnoid” of the include path of the framework, and the headers are usually included with the prefix “cnoid/”.

As header files to include do not have a extension, the headers are specified without extension like the above code. The form of header files without a extension is a standard way in C++ because this is employed in the standard C++ libraries, and the C++ libraries such as Eigen, Qt and OpenSceneGraph, which Choreonoid uses as its base, also employ this form. Choreonoid also employs this form to achieve a coding style unified with those libraries.

The three headers included here have the following functions.

  • cnoid/Plugin
This header defines the Plugin class. When you make a plugin, this header must be included and a plugin is defined as a class which inherits the Plugin class.
  • cnoid/MenuManager
This header defines the MenuManager class which manages menus. When you need to add some menu items, include this header to make the MenuManager available.
  • cnoid/MessageView
This header defines the MessageView class which corresponds the view object to put text messages. Include this header when you need to put some text messages to the message view.

The actual implementation of these headers are in “src/Base” of the source tree. See those files directly to know the actual class definitions. (Note that filenames of the actual implementation files have .h extension.) Although the reference manual of the class definitions can be generated by using a tool “Doxygen”, sorry, current source do not have sufficient comments for producing a good reference manuals. We will increase the comments for generating reference manuals.

#include <boost/bind.hpp>

This includes the header of the “Bind” library provided by Boost C++ libraries. Bind is a library for generating function objects in a flexible way, and it is often used in the Choreonoid framework to connect event handlers with the mechanism called “signal”. The details of it are described later.

Choreonoid also uses several other libraries provided by Boost, and plugin developers should know the overview of those libraries. Concretely, the libraries such as Smart Ptr, Signals, Function, Format, Dynamic Bitset and Multi-Array as well as Bind are closely related with the plugin development. To know the details of these libraries, please refer to documents in the official Boost site .

Note that the header files of Boost basically have .hpp extension in contrast to headers provided by the framework and other libraries. How difficult it is to unify this kind of coding format in C++!

Namespace using directive

Following directives allow namespace “cnoid” and “boost” to be omitted in subsequent code.

using namespace cnoid;
using namespace boost;

“cnoid” is the namespace of Choreonoid and classes and functions provided by Choreonoid are basically defined in this namespace. For example, the “Plugin” class used in this sample should be written as “cnoid::Plugin” so that it can distinguish the namespace, but you can simply write “Plugin” without the namespace prefix “cnoid::” if you write the above directives in advance.

Note that you should not use the using directives more than is necessary because they make nonsense of namespace’s original purpose to avoid the name conflicts. In principle, the using directive should not be used and all the namespaces should be explicitly specified in header files. On the other hand, you can use them in implementation files (.cpp) to make their codes simpler as long as name conflicts do not happen.

Here “boost”, the namespace of Boost libraries, is also the target of the using directive. As libraries of Boost are often used in the Choreonoid’s plugin development, omitting the boost namespace can make the coding a little simpler. However boost namespace include a lot of functions and classes and possibility of the name conflicts or confusion would be increased by omitting the boost namespace. Thus whether the namespace omitting should be used or not should be selected depending on the situation. Or you can use the “using declaration” to only omit the namespace for particular classes and using it may be better for some situations.

Definition of the plugin class

Next, the class corresponding to the HellowWorld plugin is defined:

class HelloWorldPlugin : public Plugin
{
    ...
};

A plugin of Choreonoid is defined as a class which inherits “cnoid::Plugin” (here “cnoid::” is omitted). You can give an arbitrary name to an inherited plugin class, but making the name end with “Plugin” is our convention to make the name clearly be recognized as a plugin class. It is also important to give a name which would not cause the name conflicts with other plugins.

At least the following functions must be defined in a plugin class:

  • Constructor
  • “initialize” function

In the following, we describe how to write these functions.

Constructor

The constructor is written as follows:

HelloWorldPlugin() : Plugin("HelloWorld")
{

}

In the constructor, the constructor of the base Plugin class must be called with a name of the plugin. Usually the name which removes the last “Plugin” string from the class name is given to this parameter.

Although this sample has nothing in the constructor, the dependency relationship on other plugins should be informed by using the “require” function when the plugin requires other plugins. This will be described in the description of “Sample1Plugin”.

The “initialize” function

The initialization of a plugin is written in the “initialize” function as follows:

virtual bool initialize()
{
    ...
}

The initialize function is a virtual function defined in the base Plugin class, and overriding this allows a plugin to implement the actual initialization code. Such virtual functions also include “finalize”, “description”, etc.

While plugins are being loaded into the memory, each initialize function is called in order considering the dependency relationship between plugins. When necessary objects are created and the initialization succeeds, this function should returns true, or return false if the initialization failed. The return value is used by the system to know whether the plugin is successfully initialized or not.

Adding a menu item

Next let’s see the actual code in the initialize function.

Action* menuItem = menuManager().setPath("/View").addItem("Hello World");

Here a menu item is added. menuManager() is a member function defined in the Plugin class (more precisely it is defined in the ExtensionManager class, which is the base class of the Plugin class), and it returns a MenuManager object which is managing the main menu.

Calling setPath(“/View”) for this object sets the current management position to “View”, which is a sub menu of the root menu. In this way, MenuManager represents the menu hierarchy with the separation by slash characters similar to a filepath, and this is called “menu path”.

The setPath() functions of a MenuManager returns the self object after setting a path, so you can continuously call addItem(“Hello World”) to add “Hello World” item to “View” sub menu.

The addItem function returns the pointer of an “Action” object which corresponds to the added menu item. Here the returned object is once stored to variable “menuItem”, and an operation to the object is written in the next line.

Note that if you execute Choreonoid in the Japanese environment, sub menu “View” is translated into corresponding Japanese word “Hyouji”. This is done by an internationalization system. Even in this case, menu paths in the source code must be written using texts of the original English path components. You can know the original menu path texts by seeing some source files such as “Base/MainWindow.cpp”, or you can also see them by setting the English environment such as “C” to environmental variable “LANG”. We would like to describe the details of how to use the internationalization system in another document (sorry, it has not been written yet.)

Connecting a function to the menu item

The following code is setting the function which is called when a user activates the added menu item.

menuItem->sigTriggered().connect(bind(&HelloWorldPlugin::onHelloWorldTriggered, this));

By this code, “onHelloWorldTriggered”, which is a member function of HellowWorldPlugin, will be called when the menu item is activated. The detailed meaning of this code is as follows.

First, the signal named “sigTriggered”, which is defined in the Action class, is obtained by menuItem->sigTriggered(). The signal is an object which notify a some corresponding event when it happens, and it is realized by using the Boost.Signals library. Each signal is defined for a particular event, and here “sigTriggered” is the signal to notify the event where a user activates the corresponding menu item.

A signal can set functions which are called when the event happens by using member function “connect”. It accepts a kind of function object the type of which is same as that defined for the signal or can be converted to that type. This description might not be clear for you, so here let’s see the actual definition of the Action class written in “src/Base/Action.h” to know the function type which is defined for “sigTriggered”. The function which returns sigTriggered is defined as follows.

SignalProxy< boost::signal<void(void)> > sigTriggered()

Code “void(void)” inside the return type definition shows that signal “sigTriggered” is defined to connect with the following function type.

void function(void)

For this definition, for example we assume that the function you want to connect is defined as a usual function like this:

void onHellowWorldTriggered(void)
{
    ...
}

In this case, this function can be directly given to the connect function as follows:

menuItem->sigTriggered().connect(onHellowWorldTriggered);

This is almost same as using a callback function in C language.

In this sample, we can write in this way. On the other hand, there will be a lot of cases where you want to connect not usual functions but class member functions in the actual plugin development. Considering this, our code in this sample written such that the signal is connected to a member function.

Note that (non-static) member functions actually have parameter “this” as a hidden first parameter, and they distinguish the actual instance by seeing it. Hence just giving a member function to the “connect” function in the same way as usual functions, it does not work because the signal cannot know the instance to call the member function. (Actually this results in a compile error).

The Boost.Bind library is useful to handle this. The “bind” template function provided by the Bind library can generate a function object the parameters of which are arranged from the original function. This description might not be clear, but anyway here we can use it for the purpose of making a member function be usual one. This corresponds to the following code.

bind(&HelloWorldPlugin::onHelloWorldTriggered, this)

First, give the original function to the initial parameter of bind. Here we give the member function “onHelloWorldTriggered” of the HellowWorldPlugin class by specifying “&HelloWorldPlugin::onHelloWorldTriggered”. Note that you must add initial “&” to explicitly give the pointer type of the function. Anyway, it is ok to write in the form of “&ClassName::FunctionName”.

Then we give “this” to the second parameter of bind. This specifies the actual instance which used to call this member function. In this way, “this” is given to the second parameter when you write the code to connect a member function in member functions of the same class. In fact there will be a lot of such cases. Of course you can connect a member function of another class, and a variable containing a instance of the class instead of “this” is given to the second parameter in that case. With the above coding, we can establish the situation where “onHelloWorldTriggered” is called when a user activate the menu item.

What we were doing here is actually very simple, how it is supported by the mechanisms such as Signal and bind in detail may seem a little complicated. In fact what is described above is very beginning of them, and you have to know more details of the mechanisms to make full use of the Choreonoid framework. Additional descriptions of the mechanisms will be presented in other sample explanations, but the descriptions themselves are not main topic of this guide, and it may be better to read documents about Boost.Signals and Boost.Bind. For example, the official page of Boost provides good documents about them. First you only have to understand the overview of them, and actual coding would not be so difficult because it is almost patterned one.

By the way, variable “menuItem” is introduced in this sample to separate the description into “adding a menu item” and “connecting a function”, but if it is not necessary, you can write these codes at one time as follows.

menuManager().setPath("/View").addItem("Hello World")->
    sigTriggered().connect(bind(&HelloWorldPlugin::onHelloWorldTriggered, this));

Supplemental: Qt’s signals and Choreonoid’s signals

The Action (cnoid::Action) class mentioned above is a class which extends the “QAction” class by inheriting it, and the class is newly defined in the “Base” module (in src/Base) of Choreonoid. The purpose of the extension is to increase the usability of QAction in the Choreonoid framework, and the extension mainly consists of additional functions such as “sigTriggered()” for obtaining the signal objects. In fact a number of classes which similarly extend well-used QT classes are defined, and their names is given by just removing “Q” prefix from the original name. (The accurate names include “cnoid::” namespace prefix.)

Although it is needless to say for those who know Qt, Qt has the original signal system called “Signal/Slot”, and for example QAction originally has signal “triggered” based on this system. In fact using this can achieve the same thing described above. The development of Boost.Signals seems to be inspired by Qt’s Signal/Slot system and the extension in our Action class just captures original Qt’s signals and re-process them as the signals based on Boost.Signals, which does not seem so smart.

Nevertheless, why do we design the framework so that signals based on Boost.Signals can be used by going out of our way to extend the original classes instead of using the original signal system? The answer is to increase the unification with the part which does not depend on Qt, and anyway we think the extension can increase the simplicity and flexibility of coding.

First of all, Choreonoid contains non-GUI modules which do not depend on Qt. Those modules include the “Util” module in “src/Util” and the “Body” module in “src/Body”. The design of those modules assumes that their use can be independent of the Choreonoid GUI. For example, they may be used in the control system which runs on the PC embedded in a robot body. Therefore those modules should not depend on a big GUI library as much as possible. In addition, the GUI libraries which have been used and changed in the development history of Choreonoid include wxWidgets, Gtk+(Gtkmm), and Qt, and even the future of Qt might not be promising. Hence we think the part which depends on a specific GUI library should be as small as possible. This also holds for GUI-related modules. In fact the classes which do not inherit Qt’s classed define all their signals as those based on Boost.Signals.

On the other hand, there may be a way to use Qt’s signal system for Qt related classes. However, the coding of it is so different from that of Boost.Signals and we also have to do additional pre-processing using the MOC command with special coding in header files defining classes.This will lose the unified feel of coding and actual coding will be a little complicated. We couldn’t accept this and employed the straightforward way of extension to achieve the simplicity and unity of the coding.

Coding the behavior when the menu item is activated

The implementation of function “onHelloWorldTriggered()”, which is called when the menu item is activated, is as follows:

void onHelloWorldTriggered()
{
    MessageView::mainInstance()->putln("Hello World!");
}

Here the MessageView is used to output a message. First an MessageView instance is obtained by using a static member function “mainInstance()” of the MessageView class. This is a kind of singleton pattern.

Note

Usually the function for getting a singleton instance is “instance()”, but the function used here is named “mainInstance()”. The reason why we named so was that there might be several message views used for different purposes, and “main” can mean the most main instance of them. However, this name is a little confusing and currently there is only one instance used in the system, so we are going to provide usual “instance()” function from the next version (from 1.2). The same thing is also applied for some other classes such as ItemTreeView and SceneView.

MessageView provides several functions for putting texts to the view, and here one of those functions, “putln”, is used for putting a message with a line feed.

MessageView also provides function “cout()”, which returns an ostream-type object. By using this, you can do the iostream-style text output, which is the same as outputting to “std::cout”.

In addition to MessageView used in this sample, other useful views, toolbars and pre-created instances of other classes are available in Choreonoid. Those can be basically used by including the headers corresponding the classes and obtaining their instance with functions such as “mainInstance()” or “instance()”. If you want to know what kinds of functions are provided by each class, please see the reference manual generated by Doxygen or the header files themselves currently.

Defining the plugin entry

Finally, you must write the following code for each plugin class:

CNOID_IMPLEMENT_PLUGIN_ENTRY(HelloWorldPlugin)

This is a macro defined in the header “cnoid/Plugin”, and giving the name of a plugin to this macro defines the function with which the system obtains the plugin instance from the DLL of the plugin. Please don’t forget to write this macro because the plugin DLL cannot be recognized as a plugin without this macro.

Note that each plugin must be implemented in a DLL which implements only one plugin and a single DLL cannot implement multiple plugins. In other words, the above macro cannot be written more than twice.

That’s all of the description of the source code. Next we describe how to compile this source.

How to compile

Basically following conditions must be satisfied to compile and use a plugin:

  • The header files and binaries of the libraries such as Boost, Eigen, Qt, OpenSeneGraph, etc., on which Choreonoid depends, must be available to the build tools.
  • The header files and library files provided by Choreonoid must be also available to the build tools.
  • The build environment and options must be the same as those used for building the dependency libraries and the main Choreonoid modules. (Basically using the same compiler on the same OS and PC architecture will meet this condition.)
  • The binary for the plugin must be built as a shared (dynamic-link) library.
  • The name of the binary must be “libCnoidXXXPlugin.so” (XXX corresponds to the plugin name) for Linux, or “CnoidXXXPlugin.dll” for Windows.
  • The binary must be installed in the plugin directory of Choreonoid. The plugin directory is “lib/choreonoid-x.x/” (x.x corresponds to the version number) of the Choreonoid installed directory.

As long as the above conditions are satisfied, basically any ways to compile a plugin can be employed. In this document, we describe the following two ways which are standard ones to compile a plugin.

  • Using Choreonoid SDK installed in the system
  • Using the build environment for the main Choreonoid source

Using Choreonoid SDK installed in the system

You can compile a plugin with Choreonoid SDK which has been installed by doing “make install”. In this case Choreonoid is used as an external library.

To do this, please turn on the “INSTALL_SDK” option in the CMake setting in advance, and do “make install”. It installs not only the execution files but also header files and library files.

The installed SDK can be used as a usual library and you can compile a plugin in arbitrary ways. As one way to compile the plugin, here we present the following Makefile. This Makefile is contained in the HelloWorldPlugin source directory. (Since version 1.2, the makefile is contained in “extplugin/sample/HellowWorldPlugin” with name “ManualMakfile”.)

CXXFLAGS += `pkg-config --cflags choreonoid`
PLUGIN = libCnoidHelloWorldPlugin.so

$(PLUGIN): HelloWorldPlugin.o
       g++ -shared `pkg-config --libs choreonoid` -o $(PLUGIN) HelloWorldPlugin.o

install: $(PLUGIN)
       install -s $(PLUGIN) `pkg-config --variable=plugindir choreonoid`
clean:
       rm -f *.o *.so

Doing “make” with this Makefile will produce the binary of the plugin. (Please type “make -f ManualMakfile” to use ManualMakefile.) Then “make install” copies the binary into the plugin directory of the installed Choreonoid. As a result, the plugin is loaded when Choreonoid is executed.

This makefile uses a tool called “pkg-config” to detect parameters such as include path, link path, and libraries to link. “pkg-config” is a standard tool used in Unix-like systems for this purpose. When a library name and some options are given to this command like the above makefile, it produces the strings such as include path, link path and libraries to link for the supported library. By passing the output strings to the compiler arguments, you can compile a program using the library without considering the details of it. Please see the manual of pkg-config to know the details.

Note that if you modified “CMAKE_INSTALL_PREFIX” of CMake from the default “/usr/local” setting, pkg-config cannot find the configuration file of Choreonoid. In this case, you must set the full path of the sub directory “lib/pkgconfig” of the installed Choreonoid directory to the environmental variable “PKG_CONFIG_PATH”.

Note

There is a mistake in the pkg-config configuration in the source archive of the version 1.1.0 and earlier versions. We will fix it from the next version (1.2). Before that, please replace the part “-lCnoidGuiBase” at line 14 of “misc/pkgconfig/choreonoid.pc.in” with “-lCnoidBase” to fix this problem.

For Windows, you might be able to compile a plugin with this kind of Makefile and pkg-config, but it is more usual to make a project on VisualC++ IDE. In that case, you have to manually specify include path, library path, and libraries in the project configuration dialog to compile the plugin. Or it may be better to use the next way.

Using the build environment for the main Choreonoid source

If you compiled Choreonoid from the source, you can use the environment for it to compile plugins. That’s actually very simple idea where your sources of plugins are added to the Choreonoid source and they are compiled together.

If the main Choreonoid source has successfully been compiled, the dependency libraries as well as Choreonoid’s headers and libraries are to be correctly configured and compiling a plugin can be done without newly considering those things. This also leads to using build system CMake, which is employed by Choreonoid. If you know the syntax of CMake files, you can write build settings of your plugins more easily than writing Makefiles. For Windows, CMake can generate project files of Visual C++. This enables you to build your plugins on the Visual C++ IDE without doing complicated settings using Visual C++’s dialogs.

Consequently we recommend this method for users who use Choreonoid which was compiled from the source.

Let’s begin to describe concrete procedure. First, the Choreonoid source provides the “extplugin” directory, where the sources of plugins should be placed. Please make the sub directory for a plugin and put the source files of the plugin in it. CMakeLists.txt, which describes build settings for CMake, should be also contained in the directory. Note that the last part of the directory must be ended with “Plugin” because directories with such names are recognized as additional plugins sources.

For the HelloWorldPlugin sample, sub directory “HelloWorldPlugin” is contained in directory “extplugin/sample” as a sample of this compile method. Sample plugins are placed in “sample” sub directory to distinguish them from user’s plugins. In addition, for the versions earlier than 1.2, source file “HelloWorldPlugin.cpp” is placed in “share/sampleplugins/HelloWorldPlugin”, and the CMakeLists.txt is written to use it. In this way, the source file placement of samples is a little different from usual cases, where you can just put all source files in the corresponding sub directory in “extplugin”.

CMakeLists.txt for HelloWorldPlugin is as follows.

option(BUILD_HELLO_WORLD_SAMPLE "Building a Hello World sample plugin" OFF)
if(NOT BUILD_HELLO_WORLD_SAMPLE)
  return()
endif()

set(target CnoidHelloWorldPlugin)
set(srcdir ${PROJECT_SOURCE_DIR}/share/sampleplugins/HelloWorldPlugin)
add_library(${target} SHARED ${srcdir}/HelloWorldPlugin.cpp)
target_link_libraries(${target} CnoidBase)
apply_common_setting_for_plugin(${target})

First, the following code is written to enable a user to select whether this plugin is compiled or not.

option(BUILD_HELLO_WORLD_SAMPLE "Building a Hello World sample plugin" OFF)
if(NOT BUILD_HELLO_WORLD_SAMPLE)
  return()
endif()

This value is off by default, and this can be switched with a CMake command such as “ccmake”. It is recommended to write this kind of code for your additional plugins.

Next let’s see the subsequent part, which is applied when BUILD_HELLO_WORLD_SAMPLE is set to ON.

set(target CnoidHelloWorldPlugin)

This code is to replace a long plugin name with variable “target”.

set(srcdir ${PROJECT_SOURCE_DIR}/share/sampleplugins/HelloWorldPlugin)
add_library(${target} SHARED ${srcdir}/HelloWorldPlugin.cpp)

This code is to build a shared library corresponding the plugin. Although this code is a little complicated for using the source file contained in another directory, usually you can pass source files to the “add_library” command.

target_link_libraries(${target} CnoidBase)

This code describes the dependency libraries to link. For the libraries and plugins compiled in the Choreonoid source, you can just write their names like this. “CnoidBase” is a library which corresponds to the base GUI framework of Choreonoid. If your plugin only uses the base functions of framework, you only have to specify this library. Note that the libraries which the specified library depends on are automatically linked. Thus usually it is sufficient to specify the libraries of Choreonoid unless the plugin uses other external libraries which are not originally used in Choreonoid.

apply_common_setting_for_plugin(${target})

This function is defined in the CMakeLists.txt which is placed in the top directory of the Choreonoid source. It applies the build settings which should be commonly applied to plugins. Writing this code do the processes such as installing binary files when “make install” is done.

Please read the manual of CMake to know the details of how to write CMakeLists.txt. In addition, you will be able to know more about how to write CMakeLists for plugins by reading those for other libraries and plugins in the Choreonoid source.