Skip to content

Policy-Based FSM Configuration

fsmc provides a modern, expressive, and zero-boilerplate Policy-Based Configuration system.

In traditional C++ template libraries, configuring complex engines often results in "template parameter explosion", forcing developers to provide long lists of positional template arguments:

// [Positional Syntax] (up to 8 positional parameters)
using MyFSM = fsm::fsm<
    MyTable, 
    fsm::no_ports, 
    fsm::no_ports, 
    MyRegisters, 
    fsm::no_services, 
    typename MyTable::initial_state, 
    fsm::no_observer, 
    16
>;

With fsm::config and semantic modifiers, you specify only what you need, in any order, with zero runtime or space overhead:

// [Modern Policy-Based Syntax]
using MyFSM = fsm::make_fsm<MyTable, fsm::with_registers<MyRegisters>>;

1. Semantic Policy Modifiers

fsmc provides a set of lightweight modifier tags in the fsm:: namespace:

Policy Modifier Purpose Default if Omitted
fsm::with_registers<T> Binds internal discrete datapath state variables fsm::no_registers
fsm::with_ports<In, Out> Binds continuous input and output hardware port structs fsm::no_ports, fsm::no_ports
fsm::with_services<Srv> Binds external OS/hardware interface services (drivers, timers) fsm::no_services
fsm::with_observer<Obs> Configures compile-time telemetry and transition hooks fsm::no_observer
fsm::with_trace_buffer<N> Configures zero-allocation circular flight recorder with capacity \(N\) None (or default observer)
fsm::with_timer_capacity<N> Configures capacity \(N\) for deterministic synchronous timer manager 0 (timers disabled)
fsm::with_initial_state<State> Overrides the table's default initial state Table::initial_state
fsm::with_deferred_capacity<N> Configures static capacity of deferred event queues 16
fsm::with_queue_capacity<N> Configures ring buffer capacity in SPSC/async engines 64

2. Order Invariance

Policies are order-independent. The metaprogramming reflection engine extracts and maps each type domain to its respective engine interface regardless of how modifiers are sequenced:

// Both configurations produce identical compile-time types:
using ConfigA = fsm::config<
    FlightTable,
    fsm::with_registers<FlightRegisters>,
    fsm::with_ports<SensorIn, ActuatorOut>,
    fsm::with_services<HardwareDrivers>,
    fsm::with_deferred_capacity<32>
>;

using ConfigB = fsm::config<
    FlightTable,
    fsm::with_deferred_capacity<32>,
    fsm::with_services<HardwareDrivers>,
    fsm::with_registers<FlightRegisters>,
    fsm::with_ports<SensorIn, ActuatorOut>
>;

static_assert(std::is_same_v<ConfigA::registers_type, ConfigB::registers_type>);
static_assert(std::is_same_v<ConfigA::services_type, ConfigB::services_type>);

3. Factory Aliases

fsmc provides three dedicated factory aliases corresponding to the three execution models:

A. Synchronous Deterministic Engine (make_fsm)

For hard real-time single-threaded control loops:

#include <fsm/backend/cpp/runtime/fsm.hpp>

// Single-parameter instantiation (stateless FSM)
fsm::make_fsm<SimpleTable> machine;

// Configured FSM with internal registers
FlightRegisters regs{100.0, 0.0};
fsm::make_fsm<FlightTable, fsm::with_registers<FlightRegisters>> machine(regs);

// Configured FSM with Blackbox Flight Recorder and Deterministic Timers
using SafeFlightFsm = fsm::make_fsm<
    FlightTable,
    fsm::with_registers<FlightRegisters>,
    fsm::with_trace_buffer<64>,
    fsm::with_timer_capacity<8>
>;
SafeFlightFsm flight_sm(regs);

B. Lock-Free SPSC Ring Buffer Engine (make_spsc_fsm)

For zero-overhead interrupt service routines (ISR) and sensor tasks:

#include <fsm/backend/cpp/runtime/spsc_fsm.hpp>

fsm::make_spsc_fsm<
    SensorTable,
    fsm::with_registers<SensorRegisters>,
    fsm::with_queue_capacity<256>
> spsc_machine(regs);

C. Thread-Safe Worker Engine (make_thread_safe_fsm)

For multi-threaded event dispatching with background executor loops:

#include <fsm/backend/cpp/runtime/thread_safe_fsm.hpp>

fsm::make_thread_safe_fsm<
    NetworkTable,
    fsm::with_registers<NetworkRegisters>,
    fsm::with_services<SocketManager>
> async_machine(regs, sockets);


4. Positional Syntax Compatibility

The positional template syntax fsm<Table, In, Out, Reg, Srv...> remains fully supported alongside the modern policy-based configuration:

// Modern Policy Syntax (Recommended)
using ModernFSM = fsm::make_fsm<
    MyTable,
    fsm::with_registers<MyRegisters>,
    fsm::with_ports<MyInPorts, MyOutPorts>
>;

// Positional Syntax
using PositionalFSM = fsm::fsm<
    MyTable,
    MyInPorts,
    MyOutPorts,
    MyRegisters
>;