APIC – States development guide

Best Practices — State Architecture & Usage

State

A State is an implementation of a logical execution of a single state unit.

See How to Build an APIC System for Software Engineers for where defining state logic and switching conditions fits into the overall system-engineering workflow.

State Construction


    CSampleState: CACState({execution time interval in millisec})
    {}
The CSampleState constructor optionally pass a frame time in milliseconds, which sets the update interval of the state (default is run every frame).

State Controller

A State Controller groups multiple states into a logical execution unit. Only one state runs at a time in each controller.

Changing Controller Status

CStatesController::SetStateStatus("StateControllerName", CACState::EStateStatus::ePause);

Switching States

Inside state Execute():

switchToState = TypeFactory::instance().getObject<COtherState>();
return switchToState;

From anywhere else:

CStatesController::SwitchState(
"StateControllerName",
TypeFactory::instance().getObject<COtherState>()
);

State controller modes:

  • Play – state machine actively runs.
  • Pause – execution stops but the current state is preserved.
  • Reset – state controller resets current state and transitions to initial state.

Changing Controller Status

CStatesController::SetStateStatus("StateControllerName", CACState::EStateStatus::ePause);

Switching States

Inside state Execute():

switchToState = TypeFactory::instance().getObject<COtherState>();
return switchToState;

From anywhere else:

CStatesController::SwitchState(
"StateControllerName",
TypeFactory::instance().getObject<COtherState>()
);

Initializing State Controllers in the Model

Each model can declare and initialize its own state controllers:

// Add a controller with initial state
CStatesController::AddController<CSampleState>("StateControllerName");


// Set controller status
CStatesController::SetStateStatus(
"StateControllerName",
CACState::EStateStatus::ePlay
);


// Execute state
CStatesController::ExecuteState("StateControllerName");

State Lifecycle

OnStateChanged()

This method is called whenever the state controller changes the state status.

bool CSampleState::OnStateChanged(EStateStatus eStateStatus)
{
if (eStateStatus == EStateStatus::eReset) {
// Reset internal state
}
return true;
}

Execute()

The core state functionality runs here. The state may perform work, interact with LRUs, switch states, or maintain internal logic.

std::unique_ptr<CACState> CSampleState::Execute()
{
std::unique_ptr<CACState> switchToState;


// Access LRU
// lru_XXXX* pLRU = (lru_XXXX*)CLRUsProj::LRUXXXX->GetLRU();


// Change controller status if needed
// CStatesController::SetStateStatus("NormalFlow", CACState::EStateStatus::ePause);


// Switch to another state
// switchToState = TypeFactory::instance().getObject<COtherState>();


return switchToState;
}

State Switching Logic

State transitions occur by returning a new state object from Execute(). Returning nullptr keeps the current state active.

All state objects are generated via TypeFactory to enforce controlled allocation and deterministic memory behavior.

Best Practices

  • Minimize allocations; states should not dynamically allocate memory during Execute().
  • Do not perform blocking operations inside a state.
  • Ensure transitions are deterministic and predictable.
  • Use OnStateChanged() for cleanup/reset logic.
  • Keep Execute() small and fast — it is executed every model cycle.