Skip to content

Instantly share code, notes, and snippets.

@fried-water
Created July 9, 2019 15:24
Show Gist options
  • Save fried-water/bd7e5aa6f0dc639bcde2b505c2576aa9 to your computer and use it in GitHub Desktop.
Save fried-water/bd7e5aa6f0dc639bcde2b505c2576aa9 to your computer and use it in GitHub Desktop.
#include <cassert>
#include <variant>
#include <optional>
#include <string>
template <typename Child, typename Initial, typename Accepting, typename... Rest>
struct StateMachine {
using StateVariant = std::variant<Initial, Accepting, Rest...>;
template <typename Events>
static std::optional<Accepting> process(Events events, Initial init) {
StateVariant current_state{std::move(init)};
for(auto& e : events) {
current_state = std::visit([&](auto state) {
return Child::transition(std::move(state), std::move(e));
}, std::move(current_state));
}
return std::holds_alternative<Accepting>(current_state)
? std::optional(std::get<Accepting>(std::move(current_state)))
: std::nullopt;
}
};
struct Line { std::string str; };
struct Accept { std::string str; };
struct Fail {};
struct NewLineTerminated : StateMachine<NewLineTerminated, Line, Accept, Fail> {
static StateVariant transition(Line l, char c) {
if(c == '\n') {
return Accept{std::move(l.str)};
} else {
return Line{l.str + c};
}
}
static StateVariant transition(Accept, char) { return Fail{}; }
static StateVariant transition(Fail, char) { return Fail{}; }
};
int main() {
assert(NewLineTerminated::process(std::string("abcd\n"), Line{})->str == "abcd");
assert(NewLineTerminated::process(std::string("abcd\ndef"), Line{}) == std::nullopt);
assert(NewLineTerminated::process(std::string("ab"), Line{"cd"}) == std::nullopt);
assert(NewLineTerminated::process(std::string("ab\n"), Line{"cd"})->str == "cdab");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment