Importing Types
- From libraries and from within the oroGen package itself
- From other oroGen packages (type definition, export and reuse)
- Opaque Types
- Creating an orogen package to export 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"
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:
acd
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",
"/name/of/another/type"
Once this is done, add the package to your build configuration