diff --git a/libs/bt/include/psemek/bt/finally.hpp b/libs/bt/include/psemek/bt/finally.hpp new file mode 100644 index 00000000..0c6943e5 --- /dev/null +++ b/libs/bt/include/psemek/bt/finally.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include +#include + +namespace psemek::bt +{ + + template + node_ptr finally(node_ptr child, node_ptr finalizator) + { + trampoline t; + + return + t.rethrow( + bt::sequence( + bt::success(t.intercept(std::move(child))), + std::move(finalizator) + ) + ); + } + +} diff --git a/libs/bt/include/psemek/bt/trampoline.hpp b/libs/bt/include/psemek/bt/trampoline.hpp new file mode 100644 index 00000000..01aae101 --- /dev/null +++ b/libs/bt/include/psemek/bt/trampoline.hpp @@ -0,0 +1,125 @@ +#pragma once + +#include + +#include + +namespace psemek::bt +{ + + namespace detail + { + + template + struct trampoline_state + { + std::optional::status> cached_status; + }; + + } + + template + struct trampoline_intercept_node; + + template + struct trampoline_rethrow_node; + + template + struct trampoline_intercept_node> + : node> + { + using tree_type = tree; + using node_type = node; + using typename node_type::finished; + using typename node_type::status; + + trampoline_intercept_node(node_ptr child, std::shared_ptr>> state) + : child_(std::move(child)) + , state_(std::move(state)) + {} + + void start(Args ... args) override + { + child_->start(args...); + } + + status update(Time dt, Args ... args) override + { + auto result = child_->update(dt, args...); + if (auto f = std::get_if(&result)) + state_->cached_status = *f; + return result; + } + + bool event(Event const & event, Args ... args) override + { + return child_->event(event, args...); + } + + private: + node_ptr> child_; + std::shared_ptr>> state_; + }; + + template + struct trampoline_rethrow_node> + : node> + { + using tree_type = tree; + using node_type = node; + using typename node_type::finished; + using typename node_type::status; + + trampoline_rethrow_node(node_ptr child, std::shared_ptr>> state) + : child_(std::move(child)) + , state_(std::move(state)) + {} + + void start(Args ... args) override + { + child_->start(args...); + } + + status update(Time dt, Args ... args) override + { + auto result = child_->update(dt, args...); + if (std::get_if(&result) && state_->cached_status) + { + result = *state_->cached_status; + state_->cached_status = std::nullopt; + } + return result; + } + + bool event(Event const & event, Args ... args) override + { + return child_->event(event, args...); + } + + private: + node_ptr> child_; + std::shared_ptr>> state_; + }; + + template + struct trampoline + { + trampoline() + : state_(std::make_shared>()) + {} + + node_ptr intercept(node_ptr child) + { + return std::make_unique>(std::move(child), state_); + } + + node_ptr rethrow(node_ptr child) + { + return std::make_unique>(std::move(child), state_); + } + + private: + std::shared_ptr> state_; + }; + +}