Skip to content

state machine #57

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 1 addition & 40 deletions example/coro/example_natural_nums.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,7 @@
#include <coroutine>
#include <iostream>

template <typename T>
struct Generator {
struct promise_type {
using coro_handle = std::coroutine_handle<promise_type>;

T current_value;
auto yield_value(T value) {
current_value = value;
return std::suspend_always();
}

std::suspend_always initial_suspend() { return {}; }

std::suspend_always final_suspend() noexcept { return {}; }

auto get_return_object() { return coro_handle ::from_promise(*this); }

auto unhandled_exception() { std::terminate(); }
};

Generator(promise_type::coro_handle handle) : handle_(handle) {}

~Generator() noexcept {
if (handle_) handle_.destroy();
}

bool resume() {
if (!handle_.done()) handle_();
return !handle_.done();
}

T current_value() const { return handle_.promise().current_value; }

bool move_next() const {
return (handle_ ? (handle_.resume(), !handle_.done()) : false);
}

private:
promise_type::coro_handle handle_;
};
#include "generator.h"

Generator<int> natural_nums() {
int num = 0;
Expand Down
183 changes: 183 additions & 0 deletions example/coro/example_stm.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
//
// Created by konstantin on 27.07.24.
//

#include <any>
#include <cassert>
#include <concepts>
#include <coroutine>
#include <iostream>
#include <unordered_map>

#include "generator.h"

struct Resumable final {
struct promise_type {
using coro_handle = std::coroutine_handle<promise_type>;

size_t current_value{};

std::suspend_always yield_value(const size_t pos) {
current_value = pos;
return {};
}

auto get_return_object() { return coro_handle ::from_promise(*this); }

std::suspend_always initial_suspend() { return {}; }

std::suspend_always final_suspend() noexcept { return {}; }

auto unhandled_exception() { std::terminate(); }
};

Resumable(promise_type::coro_handle handle) : handle_(handle) {}

~Resumable() {
if (handle_) handle_.destroy();
}

promise_type::coro_handle handle() {
promise_type::coro_handle h = handle_;
handle_ = nullptr;
return h;
}

bool resume() {
if (handle_) handle_.resume();
return !handle_.done();
}

private:
promise_type::coro_handle handle_;
};

using coro_t = std::coroutine_handle<>;

enum class Sym : char { A, B, Term };
enum class State { A, B };

template <class State, class Sym>
class StateMachine;

using stm_t = StateMachine<State, Sym>;

template <class F>
concept CanInvokeWithStm = requires(F f, stm_t& stm) {
{ f(stm) } -> std::same_as<Resumable>;
};

Generator<Sym> input_seq(std::string seq) {
for (char c : seq) {
switch (c) {
case 'a':
co_yield Sym::A;
break;
case 'b':
co_yield Sym::B;
break;
default:
co_yield Sym::Term;
break;
}
}
for (;;) {
co_yield Sym::Term;
}
}

template <typename TableTransition, typename SM>
struct stm_awaiter : public TableTransition {
SM& stm;
stm_awaiter(TableTransition transition, SM& stm)
: TableTransition(transition), stm(stm) {}

bool await_ready() const noexcept { return false; }
coro_t await_suspend(std::coroutine_handle<>) noexcept {
stm.gennext();
auto sym = stm.genval();
auto new_state = TableTransition::operator()(sym);
return stm[new_state];
}
[[nodiscard]] bool await_resume() const noexcept {
return stm.genval() == Sym::Term;
}
};

template <class State, class Sym>
class StateMachine final {
public:
StateMachine(Generator<Sym> gen) : gen(std::move(gen)) {}

coro_t operator[](State s) { return states[s]; }

template <typename F>
auto get_awaiter(F transition) {
return stm_awaiter(transition, *this);
}

template <CanInvokeWithStm F>
void add_state(State state, F stf) {
states[state] = stf(*this).handle();
}

void run(State initial) {
current_state = initial;
states[current_state].resume();
}

Sym genval() const { return gen.current_value(); }

void gennext() { gen.move_next(); }

State current() const { return current_state; }

private:
State current_state{};
std::unordered_map<State, coro_t> states{};
Generator<Sym> gen;
};

Resumable StateA(stm_t& stm) {
auto transmission = [](auto sym) {
if (sym == Sym::B) {
return State::B;
}
return State::A;
};
for (;;) {
std::cout << "State A" << std::endl;
bool finish = co_await stm.get_awaiter(transmission);
if (finish) {
break;
}
}
}

Resumable StateB(stm_t& stm) {
auto transmission = [](auto sym) {
if (sym == Sym::A) {
return State::A;
}
return State::B;
};
for (;;) {
std::cout << "State B" << std::endl;
bool finish = co_await stm.get_awaiter(transmission);
if (finish) {
break;
}
}
}

int main() {
auto gen = input_seq("aaabbaba");
stm_t stm(gen);
stm.add_state(State::A, StateA);
stm.add_state(State::B, StateB);

stm.run(State::A);

auto curr = stm.current();
assert(curr == State::A);
}
48 changes: 48 additions & 0 deletions example/coro/generator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// Created by konstantin on 27.07.24.
//

#include <coroutine>
#include <iostream>

template <typename T>
struct Generator {
struct promise_type {
using coro_handle = std::coroutine_handle<promise_type>;

T current_value;
auto yield_value(T value) {
current_value = value;
return std::suspend_always();
}

std::suspend_always initial_suspend() { return {}; }

std::suspend_always final_suspend() noexcept { return {}; }

auto get_return_object() { return coro_handle ::from_promise(*this); }

auto unhandled_exception() { std::terminate(); }
};

Generator(promise_type::coro_handle handle) : handle_(handle) {}

~Generator() noexcept {
if (handle_) handle_.destroy();
}

bool resume() {
if (!handle_.done()) handle_();
return !handle_.done();
}

T current_value() const { return handle_.promise().current_value; }

bool move_next() const {
return (handle_ ? (handle_.resume(), !handle_.done()) : false);
}

private:
promise_type::coro_handle handle_;
};

4 changes: 4 additions & 0 deletions example/coro/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,7 @@ example_subscription = executable('example_subscription',
example_mutex = executable('example_mutex',
'example_mutex.cpp'
)

example_stm = executable('example_stm',
'example_stm.cpp'
)
Loading