tin  1.5.9
authoring_model

tsm separates the state-machine artifact from the mutable application data that the machine operates on. The artifact defines the vocabulary and behavior: states, events, transitions, guards, actions, entry methods, and exit methods. The runtime context owns mutable data, service handles, counters, latched fault fields, hardware adapter references, and other side-effect endpoints.

Small machines may still use one type for both roles. Larger machines should name a separate context_type so the authored machine remains easy to review and the application state can evolve without changing the transition vocabulary.

struct ArmContext {
bool drive_ready{};
unsigned home_attempts{};
bool can_home() const { return drive_ready; }
void begin_home() { ++home_attempts; }
};
struct ArmMachine {
using context_type = ArmContext;
struct Disabled {};
struct Homing {};
struct Ready {};
struct Enable {};
struct HomeComplete {};
using transitions =
tsm::T<Homing, HomeComplete, Ready>>;
};
using ArmHsm = tsm::hsm<ArmMachine>;
consteval auto transitions(TransitionEntries...)
Definition: tsm.h:2448
transition_table< TransitionEntries... > Ts
Definition: tsm.h:2445

ArmHsm::context() returns ArmContext&. Guards, actions, entry methods, exit methods, and handled-event methods are invoked against that context type. The HSM object remains the owner of active state, hierarchy bookkeeping, history, and dispatch semantics.

Composition

Application-level objects can compose HSMs and expose domain operations without making the authored definitions aware of the wrapper. A surgical robot example can own a supervisor HSM, multiple arm HSMs, and an instrument HSM, while each machine keeps its own definition and context.

struct RobotRuntime {
tsm::hsm<SystemSupervisor> supervisor{};
std::array<tsm::hsm<PatientSideArm>, 6> arms{};
tsm::hsm<InstrumentEnergy> instrument{};
void prepare_arm(std::size_t index) {
auto& arm = arms[index];
arm.context().drape_present = true;
arm.context().sterile_adapter_ok = true;
arm.handle(PatientSideArm::Home{});
}
};

This keeps machine definitions portable across Linux, FreeRTOS, Zephyr, bare metal, tests, and generated simulators. Platform-specific services remain in the context or in the application runtime that owns the HSMs.

Callback Shape

Callbacks should be written against the context when they operate on application data. A transition declaration can still live in the machine definition because the behavior belongs to the machine vocabulary.

using transitions =
tsm::Ts<tsm::T<SwitchOnDisabled,
Shutdown,
ReadyToSwitchOn,
&PowerDriveContext::record_shutdown,
&PowerDriveContext::voltage_ready>>;

State types may define entry, exit, or handled-event methods when that is clearer than placing all behavior on the context. Those methods should still receive the context, not the HSM wrapper, so state behavior does not depend on runtime queue storage, dispatch model, or platform executor choices.

Review Guidance

Read the definition first to understand the legal behavior. Read the context next to understand data ownership and side effects. Read the application runtime last to understand composition, transport adapters, queues, executors, and platform binding.

This ordering keeps the semantic model visible: transition declarations describe what the machine may do, while the context and runtime describe where data and effects live.