Add reactive expressions library
This commit is contained in:
parent
15b08aeb47
commit
9b304ead00
4 changed files with 184 additions and 0 deletions
6
libs/react/CMakeLists.txt
Normal file
6
libs/react/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
file(GLOB_RECURSE PSEMEK_REACT_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "include/*.hpp")
|
||||
file(GLOB_RECURSE PSEMEK_REACT_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "source/*.cpp")
|
||||
|
||||
psemek_add_library(psemek-react ${PSEMEK_REACT_HEADERS} ${PSEMEK_REACT_SOURCES})
|
||||
target_include_directories(psemek-react PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(psemek-react PUBLIC psemek-util)
|
||||
46
libs/react/include/psemek/react/map.hpp
Normal file
46
libs/react/include/psemek/react/map.hpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/react/value.hpp>
|
||||
|
||||
namespace psemek::react
|
||||
{
|
||||
|
||||
template <typename F, typename ... Args>
|
||||
auto map(F func, value<Args> ... args)
|
||||
{
|
||||
using R = decltype(func(std::declval<Args const &>()...));
|
||||
|
||||
auto node = std::make_shared<detail::node<R>>();
|
||||
auto weak_node = std::weak_ptr{node};
|
||||
|
||||
auto internal_subscriber = [weak_node]{
|
||||
if (auto node = weak_node.lock())
|
||||
{
|
||||
node->cached_value = std::nullopt;
|
||||
node->internal_signal();
|
||||
}
|
||||
};
|
||||
|
||||
auto external_subscriber = [weak_node](auto const &){
|
||||
if (auto node = weak_node.lock())
|
||||
{
|
||||
if (!node->cached_value)
|
||||
node->external_signal(node->value());
|
||||
}
|
||||
};
|
||||
|
||||
auto internal_tokens = std::tuple{args.subscribe(detail::internal_tag{}, internal_subscriber)...};
|
||||
auto external_tokens = std::tuple{args.subscribe(external_subscriber)...};
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
42
libs/react/include/psemek/react/source.hpp
Normal file
42
libs/react/include/psemek/react/source.hpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/react/value.hpp>
|
||||
|
||||
namespace psemek::react
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct source
|
||||
: value<T>
|
||||
{
|
||||
source(T x)
|
||||
: value<T>(detail::internal_tag{}, std::make_shared<detail::node<T>>())
|
||||
{
|
||||
set(std::move(x));
|
||||
}
|
||||
|
||||
void set(T x) const
|
||||
{
|
||||
this->node_->cached_value = std::nullopt;
|
||||
this->node_->getter = [x = std::move(x)]() mutable { return std::move(x); };
|
||||
this->node_->internal_signal();
|
||||
this->node_->external_signal(this->node_->value());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct source<void>
|
||||
: value<void>
|
||||
{
|
||||
source()
|
||||
: value<void>(detail::internal_tag{}, std::make_shared<detail::node<void>>())
|
||||
{}
|
||||
|
||||
void set() const
|
||||
{
|
||||
this->node_->internal_signal();
|
||||
this->node_->external_signal();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
90
libs/react/include/psemek/react/value.hpp
Normal file
90
libs/react/include/psemek/react/value.hpp
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/util/signal.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
|
||||
namespace psemek::react
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
struct internal_tag
|
||||
{};
|
||||
|
||||
template <typename T>
|
||||
struct node
|
||||
{
|
||||
util::signal<void> internal_signal;
|
||||
util::signal<T> external_signal;
|
||||
util::function<T()> getter;
|
||||
std::optional<T> cached_value;
|
||||
|
||||
T const & value()
|
||||
{
|
||||
if (!cached_value)
|
||||
cached_value = getter();
|
||||
return *cached_value;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct node<void>
|
||||
{
|
||||
util::signal<void> internal_signal;
|
||||
util::signal<void> external_signal;
|
||||
|
||||
void value()
|
||||
{}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct node_value_type
|
||||
{
|
||||
using type = T const &;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct node_value_type<void>
|
||||
{
|
||||
using type = void;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct value
|
||||
{
|
||||
value(detail::internal_tag, std::shared_ptr<detail::node<T>> node)
|
||||
: node_(node)
|
||||
{}
|
||||
|
||||
typename detail::node_value_type<T>::type operator *() const
|
||||
{
|
||||
return node_->value();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto subscribe(typename util::signal<T>::subscriber subscriber, bool call = false) const
|
||||
{
|
||||
if (call)
|
||||
{
|
||||
if constexpr (std::is_same_v<T, void>)
|
||||
subscriber();
|
||||
else
|
||||
subscriber(node_->value());
|
||||
}
|
||||
return node_->external_signal.subscribe(std::move(subscriber));
|
||||
}
|
||||
|
||||
[[nodiscard]] auto subscribe(detail::internal_tag, std::function<void()> subscriber) const
|
||||
{
|
||||
return node_->internal_signal.subscribe(std::move(subscriber));
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<detail::node<T>> node_;
|
||||
};
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue