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
{
template <typename F, typename ... Args>
auto map(F func, value<Args> ... args)
namespace detail
{
using R = decltype(func(std::declval<Args const &>()...));
auto node = std::make_shared<detail::node<R>>();
auto weak_node = std::weak_ptr{node};
template <typename F, typename ... Args>
auto map_impl(F func, value<Args> ... args)
{
using R = decltype(func(std::declval<Args const &>()...));
auto internal_subscriber = [weak_node]{
if (auto node = weak_node.lock())
{
node->cached_value = std::nullopt;
node->internal_signal();
}
};
auto node = std::make_shared<detail::node<R>>();
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<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
: value<T>
{
source(source const &) = default;
source(source &&) = default;
source()
: source(T{})
{}
source(T x)
: value<T>(detail::internal_tag{}, std::make_shared<detail::node<T>>())
{
@ -22,12 +29,41 @@ namespace psemek::react
this->node_->internal_signal();
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 <>
struct source<void>
: value<void>
{
source(source const &) = default;
source(source &&) = default;
source()
: 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 <memory>
#include <initializer_list>
namespace psemek::react
{
@ -21,11 +22,12 @@ namespace psemek::react
util::signal<T> external_signal;
util::function<T()> getter;
std::optional<T> cached_value;
std::vector<std::shared_ptr<void>> 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 <typename C>
constexpr bool is_container = requires { typename C::value_type; };
}
template <typename T>
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)
: node_(node)
{}
explicit operator bool() const
{
return static_cast<bool>(node_);
}
typename detail::node_value_type<T>::type operator *() const
{
return node_->value();
@ -87,4 +115,16 @@ namespace psemek::react
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;
}
}