C++ Error Handling
RAII Everywhere
All HORUS C++ types use RAII — destructors clean up automatically:
{
horus::Scheduler sched; // creates Rust scheduler
horus::TensorPool pool(1, 1024*1024, 64); // creates SHM pool
horus::Image img(pool, 640, 480); // allocates from pool
// ... use them ...
} // all destroyed in reverse order, SHM cleaned up
Move-only types prevent accidental copies:
auto pub = sched.advertise<horus::msg::CmdVel>("cmd");
auto pub2 = std::move(pub); // OK — transfer ownership
// auto pub3 = pub2; // ERROR — copy deleted
Null Safety
All C API functions handle null gracefully:
horus_scheduler_destroy(nullptr); // no-op
horus_image_width(nullptr); // returns 0
horus_params_has(nullptr, nullptr); // returns false
horus_action_client_send_goal(nullptr, nullptr); // returns nullptr
C++ wrappers check validity:
horus::TensorPool pool(1, 1024, 4);
if (!pool) {
// Pool creation failed (e.g., SHM permission denied)
}
auto img = horus::Image(pool, 640, 480);
if (!img) {
// Image allocation failed (pool full)
}
Exception Safety
The FFI boundary uses catch_unwind to prevent Rust panics from crossing into C++:
C++ tick lambda
└─ extern "C" trampoline function
└─ CppNode::tick() in Rust
└─ std::panic::catch_unwind
└─ your callback
└─ if panic → caught, node marked failed, continues
If a C++ exception is thrown inside a tick callback, it must be caught before returning. Unwinding through extern "C" is undefined behavior.
sched.add("safe_node")
.tick([&] {
try {
risky_operation();
} catch (const std::exception& e) {
horus::log::error("node", e.what());
}
})
.build();
Failed Nodes
If a Rust panic occurs in a node's tick, the node is disabled:
- First panic: caught, logged, node marked
failed - Subsequent ticks: silently skipped (no-op)
- Other nodes continue running
This prevents one misbehaving node from taking down the entire system.
Error Logging
Use horus::log for structured error reporting:
horus::log::info("sensor", "Calibration complete");
horus::log::warn("controller", "PID output near saturation");
horus::log::error("safety", "Watchdog timeout on motor driver");
horus::blackbox::record("crash", "Segfault in vision pipeline");
All messages appear in horus log CLI output.