Importing Types

From libraries and from within the oroGen package itself

In an oroGen project, one adds one or more import_types_from statements to include headers from within the oroGen package, headers from other packages or to import all types that have already been defined within another oroGen package. The template generated by rock-create-orogen has created a header file for this purpose:

import_types_from "myprojectTypes.hpp"

Such headers must be self-contained, that is include all the headers they, themselves, require. Moreover, only the types that are directly defined in the imported header (and the types they themselves use) will be exported in the typekit. Finally, one can directly use types defined in a library, provided that this library gives a pkg-config file for dependency discovery.

Let's consider a drivers/hokuyo package that would define a hokuyo::Statistics structure. Assuming that this package (1) installs a hokuyo.pc file - all Rock packages do by default - and (2) installs the relevant header as Statistics.hpp, one can import the type with

using_library "hokuyo"
import_types_from "hokuyo/Statistics.hpp"

Note the pkg-config name of a Rock library package is the package's basename (i.e. hokuyo for drivers/hokuyo).

Important The using_library "library_name" stanza implicitly create a dependency between the oroGen package you're working on and the library package. This dependency must be made explicit by adding the corresponding <depend name="..." /> line to the oroGen package's manifest.xml.

It is mandatory that this type of dependency defines a pkg-config file. All Rock packages do, but 3rd party libraries may not. If they do not, you will have to follow this step-by-step to work around these.

From other oroGen packages (type definition, export and reuse)

A given type can be imported at the same time by more than one oroGen package. The Rock component implementation supports this. However, this is pretty wasteful in terms of compilation times and code size. One would rather decide to import types that already have been used by other oroGen packages. In some cases, one can also decide to create packages for the sole purpose of having a common set of ready-to-use types, a subject we cover later in this section.

Just a bit of vocabulary here: a type is defined by an oroGen package if it has been imported by it. It is however exported by an oroGen package if the package registers this type within the Rock component type system. Only exported types can be used on a component's interface. A limitation of oroGen is that a type can only be exported by the package that defined it first.

To reduce compilation time, oroGen packages that have task definitions will only export the types these tasks use - the ones that are used on the task's interface. This allows to reduce the amount of code to be compiled (and avoid pollution in the type system) to what is necessary for the package.

Types that are exported by a given oroGen package can be reused within another oroGen package with import_types_from, e.g.

import_types_from 'base'
# /base/Time is now available. No new code will be generated

In addition to the import_types_from stanza, one must add the corresponding package to manifest.xml, e.g.:

<depend name="base/orogen/types" />

Note the name of an oroGen package as used in import_types_from is the package's basename (i.e. hokuyo for drivers/orogen/hokuyo). An oroGen package and a library can share the same basename (e.g. drivers/hokuyo and drivers/orogen/hokuyo). This is even a recommended behavior when an orogen package is mainly tied to a certain library.

In case one needs to export extra types from a given package, it is feasible by adding an typekit.export_types stanza to the orogen file:

typekit.export_types "/some/other/type"

The following description of opaque types and type import packages can be passed on a first reading. You can skip it to go straight to how component interfaces are defined.

Opaque Types

Opaque types are a way to enable oroGen to handle types that it cannot handle completely automatically. The general idea is that you provide oroGen with a "marshalling structure" that (1) it can understand and (2) can hold all the data that the "real type" holds. Then, you have to implement two conversion functions: one that converts from the marshalling type, and one to the marshalling type.

So, it involves doing one copy. What is the gain ?

Opaque types provide you with the advantage that other types that use opaque types (i.e. structures with fields that are from opaque types, std::vector, arrays) will be automatically handled by oroGen. I.e. you write the conversion function for the types that oroGen can't handle and let it do the rest of the work.

Moreover, oroGen will be able to generate typekits for all the transports it can handle.

Finally, the conversion to and from the marshalling type is only done in inter-process transports. When communicating across threads, the data structure is copied as-is.

To use opaque types, you first have to create a wrapper type (a.k.a. "intermediate type") for the opaque. In the case of Eigen::Vector3d, a suitable wrapper would be

namespace wrappers
    struct Vector3d
        double x, y, z;

The wrapper is usually defined within the oroGen package itself, in a wrappers/ subdirectory placed at the root of the package. It then needs to be imported with import_types_from. Finally, one can use opaque_type to declare the opaque.

import_types_from "wrappers/Vector3d.hpp"
opaque_type "/Eigen/Vector3d", "/wrappers/Vector3d"

where wrappers::Vector3d is the marshalling structure defined in wrappers/Vector3d.hpp. Moreover, if getting the definition of the opaque type requires new include directories that are not yet added to the typekit through the using_library mechanism, you will have to detect them in the Ruby code and add them with the include: option

import_types_from "wrappers/Vector3d.hpp"
opaque_type "/Eigen/Vector3d", "/wrappers/Vector3d", include: eigen_prefix

Once you have re-generated the project, a typekit/ directory is created with two files, Opaques.cpp and Opaques.hpp in it. These files hold the toIntermediate and fromIntermediate conversion functions that should be used by oroGen to convert the opaque to the wrapper and the wrapper to the opaque. Note that any function will do: you may change the plain functions to e.g. templates if you need to defined opaques for many types (as base/orogen/types does for the Eigen types).

Updates to Opaques.hpp/Opaques.cpp If you add new opaques to an orogen project that already has some, you will need to copy the corresponding toIntermediate/fromIntermediate conversion functions manually from templates/typekit/Opaques.cpp. Note that this is a general behavior: oroGen will always refuse to modify a file that already exists, but update a "fresh" template within templates/.

As explained, once you have defined an opaque type, oroGen will take care of other types that use this opaque. For instance

struct Position
    base::Time time;
    Eigen::Vector3d position;

can be used in your task interfaces without any modifications. This works for structures, std::vector and static-size arrays. Before you may do this, however, you need to import_types_from the orogen package that declared the opaque in the first place.

Creating an orogen package to export types

In some cases, one might want to export a set of types in order to have a common base, but not depend on extra libraries that provide much more functionality. This can be done by creating an oroGen package for the purpose of the type export. This is a package without any task definition, and only import_types_from and export_types statement(s)

Let's assume we want to re-create the drivers/orogen/raw_io package:

cd drivers/orogen
rock-create-orogen raw_io
cd raw_io
# Edit raw_io.orogen

If we are going to use this package only for type definitions, you will have to delete all the task_context definitions. The orogen file will end up looking like this:

name "raw_io"
version "0.1"

# Import types from other orgen projects as well as
# C++ headers within the orogen package itself
import_types_from "..."
import_types_from "..."
import_types_from "..."
import_types_from "..."

# Import types from libraries
using_library "other_lib"
import_types_from "other_lib/Header.hpp"

# Choose which types are going to be usable on
# component interfaces
typekit.export_types "/name/of/type",

Once this is done, add the package to your build configuration