React library wip

This commit is contained in:
Nikita Lisitsa 2023-03-14 12:48:25 +03:00
parent cac70befe5
commit b52039d47c
3 changed files with 123 additions and 30 deletions

View file

@ -5,42 +5,53 @@
namespace psemek::react namespace psemek::react
{ {
template <typename F, typename ... Args> namespace detail
auto map(F func, value<Args> ... args)
{ {
using R = decltype(func(std::declval<Args const &>()...));
auto node = std::make_shared<detail::node<R>>(); template <typename F, typename ... Args>
auto weak_node = std::weak_ptr{node}; auto map_impl(F func, value<Args> ... args)
{
using R = decltype(func(std::declval<Args const &>()...));
auto internal_subscriber = [weak_node]{ auto node = std::make_shared<detail::node<R>>();
if (auto node = weak_node.lock()) auto weak_node = std::weak_ptr{node};
{
node->cached_value = std::nullopt;
node->internal_signal();
}
};
auto external_subscriber = [weak_node](auto const &){ auto internal_subscriber = [weak_node]{
if (auto node = weak_node.lock()) if (auto node = weak_node.lock())
{ {
if (!node->cached_value) node->cached_value = std::nullopt;
node->external_signal(node->value()); node->internal_signal();
} }
}; };
auto internal_tokens = std::tuple{args.subscribe(detail::internal_tag{}, internal_subscriber)...}; auto external_subscriber = [weak_node](auto const &){
auto external_tokens = std::tuple{args.subscribe(external_subscriber)...}; if (auto node = weak_node.lock())
{
if (!node->cached_value)
node->external_signal(node->value());
}
};
node->getter = [ auto internal_tokens = std::tuple{args.subscribe(detail::internal_tag{}, internal_subscriber)...};
func = std::move(func), auto external_tokens = std::tuple{args.subscribe(external_subscriber)...};
internal_tokens = std::move(internal_tokens),
external_tokens = std::move(external_tokens),
args...]() -> R {
return func(*args...);
};
return value<R>(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<R>(detail::internal_tag{}, std::move(node));
}
}
template <typename F, typename ... Args>
auto map(F func, Args && ... args)
{
return detail::map_impl(std::move(func), make_value(std::forward<Args>(args))...);
} }
} }

View file

@ -9,6 +9,13 @@ namespace psemek::react
struct source struct source
: value<T> : value<T>
{ {
source(source const &) = default;
source(source &&) = default;
source()
: source(T{})
{}
source(T x) source(T x)
: value<T>(detail::internal_tag{}, std::make_shared<detail::node<T>>()) : value<T>(detail::internal_tag{}, std::make_shared<detail::node<T>>())
{ {
@ -22,12 +29,41 @@ namespace psemek::react
this->node_->internal_signal(); this->node_->internal_signal();
this->node_->external_signal(this->node_->value()); this->node_->external_signal(this->node_->value());
} }
template <typename F>
void modify(F && f) const
{
set(f(this->node_->value()));
}
template <typename H, typename F>
source<H> modifier(H value, F && f)
{
std::weak_ptr weak_node = this->node_;
source<H> result(std::move(value));
this->node_->subscriptions.push_back(result.subscribe([weak_node, f = std::forward<F>(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 <> template <>
struct source<void> struct source<void>
: value<void> : value<void>
{ {
source(source const &) = default;
source(source &&) = default;
source() source()
: value<void>(detail::internal_tag{}, std::make_shared<detail::node<void>>()) : value<void>(detail::internal_tag{}, std::make_shared<detail::node<void>>())
{} {}
@ -39,4 +75,10 @@ namespace psemek::react
} }
}; };
template <typename T>
value<T> make_value(source<T> const & s)
{
return s;
}
} }

View file

@ -4,6 +4,7 @@
#include <optional> #include <optional>
#include <memory> #include <memory>
#include <initializer_list>
namespace psemek::react namespace psemek::react
{ {
@ -21,11 +22,12 @@ namespace psemek::react
util::signal<T> external_signal; util::signal<T> external_signal;
util::function<T()> getter; util::function<T()> getter;
std::optional<T> cached_value; std::optional<T> cached_value;
std::vector<std::shared_ptr<void>> subscriptions;
T const & value() T const & value()
{ {
if (!cached_value) if (!cached_value)
cached_value = getter(); cached_value.emplace(getter());
return *cached_value; return *cached_value;
} }
}; };
@ -52,15 +54,41 @@ namespace psemek::react
using type = void; using type = void;
}; };
template <typename C>
constexpr bool is_container = requires { typename C::value_type; };
} }
template <typename T> template <typename T>
struct value struct value
{ {
value() = default;
value(value const &) = default;
value(value && other) = default;
template <typename ... Args, typename = std::enable_if_t<std::is_constructible_v<T, Args...>>>
value(Args && ... args)
: node_(std::make_shared<detail::node<T>>())
{
node_->cached_value.emplace(std::forward<Args>(args)...);
}
template <typename C = T, typename = typename C::value_type>
value(std::initializer_list<typename C::value_type> list)
: node_(std::make_shared<detail::node<T>>())
{
node_->cached_value.emplace(list);
}
value(detail::internal_tag, std::shared_ptr<detail::node<T>> node) value(detail::internal_tag, std::shared_ptr<detail::node<T>> node)
: node_(node) : node_(node)
{} {}
explicit operator bool() const
{
return static_cast<bool>(node_);
}
typename detail::node_value_type<T>::type operator *() const typename detail::node_value_type<T>::type operator *() const
{ {
return node_->value(); return node_->value();
@ -87,4 +115,16 @@ namespace psemek::react
std::shared_ptr<detail::node<T>> node_; std::shared_ptr<detail::node<T>> node_;
}; };
template <typename T>
value<T> make_value(T x)
{
return value<T>(std::move(x));
}
template <typename T>
value<T> make_value(value<T> const & v)
{
return v;
}
} }