qlibs / sml

C++20 State Machine library
166 stars 7 forks source link
compile-time cpp20 state-machine

// <!-- // The MIT License (MIT) // // Copyright (c) 2024 Kris Jusiak kris@jusiak.net // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //

if 0

// --> Overview / Examples / API / FAQ

SML: UML-2.5 State Machine Language

MIT Licence Version Build Try it online

https://en.wikipedia.org/wiki/Finite-state_machine

https://www.omg.org/spec/UML/2.5.1

Features

Requirements


Overview

State Machine (https://godbolt.org/z/s9a6EW5j9)

// events
struct connect {};
struct established {};
struct ping { bool valid{}; };
struct disconnect {};
struct timeout {};

int main() {
  // guards/actions
  auto establish = [] { std::puts("establish"); };
  auto close     = [] { std::puts("close"); };
  auto reset     = [] { std::puts("reset"); };

  // states
  struct Disconnected {};
  struct Connecting {};
  struct Connected {};

  // transitions
  sml::sm connection = sml::overload{
    [](Disconnected, connect)      -> Connecting   { establish(); },
    [](Connecting,   established)  -> Connected    { },
    [](Connected,    ping event)                   { if (event.valid) { reset(); } },
    [](Connected,    timeout)      -> Connecting   { establish(); },
    [](Connected,    disconnect)   -> Disconnected { close(); },
  };

  static_assert(sizeof(connection) == 1u);

  assert(connection.visit(is<Disconnected>));

  assert(connection.process_event(connect{}));
  assert(connection.visit(is<Connecting>));

  assert(connection.process_event(established{}));
  assert(connection.visit(is<Connected>));

  assert(connection.process_event(ping{.valid = true}));
  assert(connection.visit(is<Connected>));

  assert(connection.process_event(disconnect{}));
  assert(connection.visit(is<Disconnected>));
}
main: // $CXX -O3 -fno-exceptions -fno-rtti
  push    rax
  lea     rdi, [rip + .L.str.8]
  call    puts@PLT
  lea     rdi, [rip + .L.str.9]
  call    puts@PLT
  lea     rdi, [rip + .L.str.10]
  call    puts@PLT
  xor     eax, eax
  pop     rcx
  ret

.L.str.8:  .asciz  "establish"
.L.str.9:  .asciz  "reset"
.L.str.10: .asciz  "close"

Examples

--

API

template<class T>
  requires (requires (T t) { t(); })
struct sm {
  constexpr sm(T&&);
  template<class TEvent, auto dispatch = if_else>
    requires dispatchable<TEvent>
  constexpr auto process_event(const TEvent& event) -> bool ;
  constexpr auto visit(auto&& fn) const;
};
template<class... Ts> struct overload;
inline constexpr auto if_else; // if_else dispatch policy
inline constexpr auto jmp_table; // jmp_table dispatch policy
struct X {}; // terminate state

FAQ