// <!-- // 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. //
// --> Overview / Examples / API / FAQ
-DNTEST
- see FAQ)C++20 (Clang-15+, GCC-12+)
#include/#import
)virtual
used (-fno-rtti
)exceptions
required (-fno-exceptions
)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"
--
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
How to integrate with CMake.FetchContent?
include(FetchContent)
FetchContent_Declare(
qlibs.sml
GIT_REPOSITORY https://github.com/qlibs/sml
GIT_TAG v3.0.0
)
FetchContent_MakeAvailable(qlibs.sml)
target_link_libraries(${PROJECT_NAME} PUBLIC qlibs.sml);
Acknowledgments
Similar projects?