diff --git a/libs/react/include/psemek/react/map.hpp b/libs/react/include/psemek/react/map.hpp index dc997acd..e6fc3945 100644 --- a/libs/react/include/psemek/react/map.hpp +++ b/libs/react/include/psemek/react/map.hpp @@ -5,42 +5,53 @@ namespace psemek::react { - template - auto map(F func, value ... args) + namespace detail { - using R = decltype(func(std::declval()...)); - auto node = std::make_shared>(); - auto weak_node = std::weak_ptr{node}; + template + auto map_impl(F func, value ... args) + { + using R = decltype(func(std::declval()...)); - auto internal_subscriber = [weak_node]{ - if (auto node = weak_node.lock()) - { - node->cached_value = std::nullopt; - node->internal_signal(); - } - }; + auto node = std::make_shared>(); + auto weak_node = std::weak_ptr{node}; - auto external_subscriber = [weak_node](auto const &){ - if (auto node = weak_node.lock()) - { - if (!node->cached_value) - node->external_signal(node->value()); - } - }; + auto internal_subscriber = [weak_node]{ + if (auto node = weak_node.lock()) + { + node->cached_value = std::nullopt; + node->internal_signal(); + } + }; - auto internal_tokens = std::tuple{args.subscribe(detail::internal_tag{}, internal_subscriber)...}; - auto external_tokens = std::tuple{args.subscribe(external_subscriber)...}; + auto external_subscriber = [weak_node](auto const &){ + if (auto node = weak_node.lock()) + { + if (!node->cached_value) + node->external_signal(node->value()); + } + }; - node->getter = [ - func = std::move(func), - internal_tokens = std::move(internal_tokens), - external_tokens = std::move(external_tokens), - args...]() -> R { - return func(*args...); - }; + auto internal_tokens = std::tuple{args.subscribe(detail::internal_tag{}, internal_subscriber)...}; + auto external_tokens = std::tuple{args.subscribe(external_subscriber)...}; - return value(detail::internal_tag{}, std::move(node)); + node->getter = [ + func = std::move(func), + internal_tokens = std::move(internal_tokens), + external_tokens = std::move(external_tokens), + args...]() -> R { + return func(*args...); + }; + + return value(detail::internal_tag{}, std::move(node)); + } + + } + + template + auto map(F func, Args && ... args) + { + return detail::map_impl(std::move(func), make_value(std::forward(args))...); } } diff --git a/libs/react/include/psemek/react/source.hpp b/libs/react/include/psemek/react/source.hpp index afc99963..087647ea 100644 --- a/libs/react/include/psemek/react/source.hpp +++ b/libs/react/include/psemek/react/source.hpp @@ -9,6 +9,13 @@ namespace psemek::react struct source : value { + source(source const &) = default; + source(source &&) = default; + + source() + : source(T{}) + {} + source(T x) : value(detail::internal_tag{}, std::make_shared>()) { @@ -22,12 +29,41 @@ namespace psemek::react this->node_->internal_signal(); this->node_->external_signal(this->node_->value()); } + + template + void modify(F && f) const + { + set(f(this->node_->value())); + } + + template + source modifier(H value, F && f) + { + std::weak_ptr weak_node = this->node_; + + source result(std::move(value)); + this->node_->subscriptions.push_back(result.subscribe([weak_node, f = std::forward(f)](H const & value) mutable { + if (auto node = weak_node.lock()) + { + T x = f(node->value(), value); + node->cached_value = std::nullopt; + node->getter = [x = std::move(x)]() mutable { return std::move(x); }; + node->internal_signal(); + node->external_signal(node->value()); + } + })); + + return result; + } }; template <> struct source : value { + source(source const &) = default; + source(source &&) = default; + source() : value(detail::internal_tag{}, std::make_shared>()) {} @@ -39,4 +75,10 @@ namespace psemek::react } }; + template + value make_value(source const & s) + { + return s; + } + } diff --git a/libs/react/include/psemek/react/value.hpp b/libs/react/include/psemek/react/value.hpp index 1dea6948..9f4017eb 100644 --- a/libs/react/include/psemek/react/value.hpp +++ b/libs/react/include/psemek/react/value.hpp @@ -4,6 +4,7 @@ #include #include +#include namespace psemek::react { @@ -21,11 +22,12 @@ namespace psemek::react util::signal external_signal; util::function getter; std::optional cached_value; + std::vector> subscriptions; T const & value() { if (!cached_value) - cached_value = getter(); + cached_value.emplace(getter()); return *cached_value; } }; @@ -52,15 +54,41 @@ namespace psemek::react using type = void; }; + template + constexpr bool is_container = requires { typename C::value_type; }; + } template struct value { + value() = default; + value(value const &) = default; + value(value && other) = default; + + template >> + value(Args && ... args) + : node_(std::make_shared>()) + { + node_->cached_value.emplace(std::forward(args)...); + } + + template + value(std::initializer_list list) + : node_(std::make_shared>()) + { + node_->cached_value.emplace(list); + } + value(detail::internal_tag, std::shared_ptr> node) : node_(node) {} + explicit operator bool() const + { + return static_cast(node_); + } + typename detail::node_value_type::type operator *() const { return node_->value(); @@ -87,4 +115,16 @@ namespace psemek::react std::shared_ptr> node_; }; + template + value make_value(T x) + { + return value(std::move(x)); + } + + template + value make_value(value const & v) + { + return v; + } + }