python/unit/generate_unit.py) that reads a .unit.yaml file and emits a strongly-typed C++ base class for your unit. You implement only the handler functions — all pub-sub wiring, synchronizer setup, publisher/subscriber creation, and topic-name template resolution are generated automatically.
How generation works
The generator performs these steps in order:Validate the YAML
The unit YAML is loaded with PyYAML and validated against
unit/schema.yaml using jsonschema. Invalid YAML is rejected before any files are written.Derive C++ names
Topic names (e.g.
/camera_left) are converted to valid C++ identifiers by replacing every non-alphanumeric character with an underscore and stripping leading underscores. /camera_left becomes camera_left.The serializer:CppType type field is split on the first :. The serializer prefix is used to select the right #include and the C++ type undergoes .→:: substitution for protobuf packages.Render Jinja2 templates
Each file is produced by a Jinja2 template in
python/unit/templates/. The generator uses jinja2.StrictUndefined, so any template variable that is missing causes an error rather than silently expanding to an empty string.Running the generator
| Argument | Description |
|---|---|
unit_definition_file | Path to the .unit.yaml file. The base name (without .unit.yaml) is used as the unit name and C++ class name. |
output_dir | Directory for generated (do-not-edit) files. CMake uses ${CMAKE_CURRENT_BINARY_DIR}/generated. |
source_dir | Directory for user-owned files. The generator writes to include/, src/, and template/ under this path. |
Example
Generated files
The generator writes two categories of files.Generated (do not edit)
These files live under<output_dir>/unit/<UnitName>/ and are overwritten every time the generator runs. Never edit them directly.
| File | Description |
|---|---|
unit_base.h | Defines unit::UnitName::Args, unit::UnitName::Base, and all HandlerPubSub subclasses. |
unit_base.cpp | Implements Base::SetupSerializationHelpers(), Base::all_templated_topics, Args::argument_metadatas, and each handler’s PubSub::SetupPubSub(). |
create_unit.cpp | Exports the CreateUnit C entry point used by the Basis unit loader. |
handlers/<HandlerName>.h | One file per handler. Defines Input, Output, Synchronizer, and the PubSub struct. |
<source_dir>/template/<UnitName>.example.h and <source_dir>/template/<UnitName>.example.cpp are written on every run as annotated reference copies. They are safe to inspect but are regenerated on every run.
User-owned (edit these)
These files are written only once — if they already exist, the generator skips them. They are yours to edit.| File | Description |
|---|---|
<source_dir>/include/<UnitName>.h | Your unit class header. Declares the class and lists the handler overrides. |
<source_dir>/src/<UnitName>.cpp | Your unit class implementation. Implement each handler here. |
Generated handler signature
For a handler namedStereoMatch with inputs /camera_left and /camera_right and output /camera_stereo, the generator produces:
src/MyUnit.cpp receives a const Input& and returns an Output:
input.time is the monotonic clock time at which the synchronizer fired — useful for logging and for deterministic replay.
For handlers with accumulate, the input field type is a vector:
The generated unit class
include/<UnitName>.h starts with:
unit::MyUnit::Base inherits from basis::SingleThreadedUnit. Its Initialize() is final — you never override it. All setup is done by Base itself through the generated CreatePublishersSubscribers().
CMake integration
Add the following to your unit’sCMakeLists.txt:
generate_unit is a CMake function defined in cmake/Unit.cmake. It:
- Registers a custom command that runs
generate_unit.pywhenever the.unit.yamlor any template changes. - Creates a shared library target
unit_MyUnit(output nameMyUnit.unit.so) from the generated sources and yoursrc/MyUnit.cpp. - Creates a standalone executable target
unit_MyUnit_bin(output nameMyUnit) that links the unit library againstbasis::unit::main. - Defines an alias
unit::MyUnitpointing to the shared library. - Installs the
.sotounit/, the YAML tounit/, and the binary tobin/.
cmake/Unit.cmake (excerpt)
Rebuilding after YAML changes
Because CMake tracks the.unit.yaml file as a dependency, rebuilding is as simple as:
File layout summary
Next steps
Unit YAML reference
All fields you can declare in a .unit.yaml file
Serialization
How the serializer prefix resolves to a plugin
Units
The generated Base class and HandlerPubSub in depth
Launch files
How to run your compiled unit in a launch file