Overview
This example demonstrates the oneOf feature of Tesseract's PropertyTree system, which enables polymorphic configuration structures. A oneOf schema allows exactly one of multiple configuration branches to be active at a time.
OneOf is useful for:
- Union-like types (choose one of several variants)
- Mutually exclusive configurations
- Plugin selection (use either plugin A or plugin B)
- Shape definitions (circle OR rectangle)
- Protocol selection (USB OR Ethernet with type-specific fields)
Key Concepts
- Branches: Named child schemas, each defining a configuration variant
- Branch Selection: Automatic selection based on which required fields are present
- Flattening: After selection, the schema becomes the selected branch
- One Mandatory Match: Exactly one branch must match, no more, no less
- Validation: Constraints validate only in the selected branch
OneOf Workflow
The example demonstrates:
- Define a OneOf Schema with multiple branches
- Load Configuration specifying one branch
- Merge Config into Schema - branch selection occurs here
- Validate - checks constraints for selected branch only
- Extract Results from the flattened schema
Example Code
auto shape_schema = PropertyTreeBuilder()
.attribute(TYPE, ONEOF)
.container("circle")
.doubleNum("radius").required()
.doc("Circle radius").minimum(0.0).label("Radius").done()
.done()
.container("rectangle")
.doubleNum("width").required()
.doc("Rectangle width").minimum(0.0).label("Width").done()
.doubleNum("height").required()
.doc("Rectangle height").minimum(0.0).label("Height").done()
.done()
.build();
The ONEOF type defines a schema where exactly one child must be selected. Child nodes are branches.
{
std::cout << "\n Circle config:\n";
YAML::Node circle_config;
circle_config["radius"] = 5.5;
auto schema_copy = shape_schema;
try
{
schema_copy.mergeConfig(circle_config);
auto errors = schema_copy.validate();
if (errors.empty())
{
std::cout << " ✓ Selected circle branch\n";
std::cout << " Radius: " << schema_copy.at("radius").as<double>() << "\n";
}
else
{
std::cout << " ✗ Validation errors:\n";
for (const auto& e : errors)
std::cout << " - " << e << "\n";
}
}
catch (const std::exception& ex)
{
std::cout << " ✗ Merge failed: " << ex.what() << "\n";
}
}
During mergeConfig(), PropertyTree examines the config and selects the branch whose required fields match the provided keys. If multiple branches match or none match, an exception is thrown.
Running the Example
Execute the example:
./tesseract_common_oneof_property_tree_example
The example demonstrates successful branch selection, error cases, and how validation applies only to the selected branch.