diff --git a/lager/reader.hpp b/lager/reader.hpp index 4791b5fe..27cde59b 100644 --- a/lager/reader.hpp +++ b/lager/reader.hpp @@ -54,10 +54,10 @@ struct reader_mixin return with(deriv_()).zoom(std::forward(l)); } - template + template auto setter(FnT&& fn) const { - return with_setter(deriv_(), std::forward(fn)); + return with_setter(deriv_(), std::forward(fn), TagT{}); } const DerivT& make() const& { return static_cast(*this); } diff --git a/lager/setter.hpp b/lager/setter.hpp index f70f10ad..eec8ec25 100644 --- a/lager/setter.hpp +++ b/lager/setter.hpp @@ -16,18 +16,20 @@ #include #include +#include namespace lager { namespace detail { -template +template class setter_node : public cursor_node { using base_t = cursor_node; std::shared_ptr parent_; FnT setter_fn_; + bool recomputed_ = false; public: using value_type = typename ParentT::value_type; @@ -38,26 +40,43 @@ class setter_node : public cursor_node , setter_fn_{std::move(fn)} {} - void recompute() final { this->push_down(parent_->current()); } - void refresh() final { parent_->refresh(); } + void recompute() final + { + if (recomputed_) + recomputed_ = false; + else + this->push_down(parent_->current()); + } + + void refresh() final {} void send_up(const value_type& value) override { - this->push_down(value); setter_fn_(value); + this->push_down(value); + if constexpr (std::is_same_v) { + recomputed_ = true; + this->send_down(); + this->notify(); + } } void send_up(value_type&& value) override { - this->push_down(std::move(value)); setter_fn_(value); + this->push_down(std::move(value)); + if constexpr (std::is_same_v) { + recomputed_ = true; + this->send_down(); + this->notify(); + } } }; -template +template auto make_setter_node(std::shared_ptr p, FnT&& fn) { - using node_t = setter_node>; + using node_t = setter_node, TagT>; auto&& pv = *p; auto n = std::make_shared(std::move(p), std::forward(fn)); pv.link(n); @@ -66,13 +85,19 @@ auto make_setter_node(std::shared_ptr p, FnT&& fn) } // namespace detail -template +template auto with_setter(reader_base r, FnT&& fn) { - auto node = make_setter_node(detail::access::node(std::move(r)), - std::forward(fn)); + auto node = detail::make_setter_node( + detail::access::node(std::move(r)), std::forward(fn)); using node_t = typename decltype(node)::element_type; return cursor_base{std::move(node)}; } +template +auto with_setter(ReaderT&& r, FnT&& fn, TagT = {}) +{ + return with_setter(std::forward(r), std::forward(fn)); +} + } // namespace lager diff --git a/lager/with.hpp b/lager/with.hpp index 60f22bab..f0cf1241 100644 --- a/lager/with.hpp +++ b/lager/with.hpp @@ -16,6 +16,8 @@ #include #include +#include + #include namespace lager { @@ -160,10 +162,11 @@ class with_expr_base return cursor_t{node}; } - template + template auto setter(FnT&& fn) && { - return std::move(*this).make().setter(std::forward(fn)); + return std::move(*this).make().template setter( + std::forward(fn)); } auto make_node_() && diff --git a/test/setter.cpp b/test/setter.cpp index 2fc00280..617be201 100644 --- a/test/setter.cpp +++ b/test/setter.cpp @@ -41,3 +41,30 @@ TEST_CASE("combine setter with store") CHECK(cursor.get() == 5); CHECK(store.get() == 5); } + +TEST_CASE("combine automatic setter with store") +{ + auto store = lager::make_store( + 0, [](int s, int a) { return a; }, lager::with_manual_event_loop{}); + auto cursor = + store.xform(zug::identity).setter([&](int x) { + store.dispatch(x); + }); + + CHECK(cursor.get() == 0); + + store.dispatch(42); + CHECK(cursor.get() == 0); + + lager::commit(store); + CHECK(store.get() == 42); + CHECK(cursor.get() == 42); + + cursor.set(5); + CHECK(cursor.get() == 5); + CHECK(store.get() == 42); + + lager::commit(store); + CHECK(cursor.get() == 5); + CHECK(store.get() == 5); +}