// Copyright Sebastian Jeckel 2014. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef REACT_DETAIL_GRAPH_ALGORITHMNODES_H_INCLUDED #define REACT_DETAIL_GRAPH_ALGORITHMNODES_H_INCLUDED #pragma once #include "react/detail/Defs.h" #include #include #include "EventNodes.h" #include "SignalNodes.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// /// AddIterateRangeWrapper /////////////////////////////////////////////////////////////////////////////////////////////////// template struct AddIterateRangeWrapper { AddIterateRangeWrapper(const AddIterateRangeWrapper& other) = default; AddIterateRangeWrapper(AddIterateRangeWrapper&& other) : MyFunc( std::move(other.MyFunc) ) {} template < typename FIn, class = typename DisableIfSame::type > explicit AddIterateRangeWrapper(FIn&& func) : MyFunc( std::forward(func) ) {} S operator()(EventRange range, S value, const TArgs& ... args) { for (const auto& e : range) value = MyFunc(e, value, args ...); return value; } F MyFunc; }; template struct AddIterateByRefRangeWrapper { AddIterateByRefRangeWrapper(const AddIterateByRefRangeWrapper& other) = default; AddIterateByRefRangeWrapper(AddIterateByRefRangeWrapper&& other) : MyFunc( std::move(other.MyFunc) ) {} template < typename FIn, class = typename DisableIfSame::type > explicit AddIterateByRefRangeWrapper(FIn&& func) : MyFunc( std::forward(func) ) {} void operator()(EventRange range, S& valueRef, const TArgs& ... args) { for (const auto& e : range) MyFunc(e, valueRef, args ...); } F MyFunc; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// IterateNode /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename S, typename E, typename TFunc > class IterateNode : public SignalNode { using Engine = typename IterateNode::Engine; public: template IterateNode(T&& init, const std::shared_ptr>& events, F&& func) : IterateNode::SignalNode( std::forward(init) ), events_( events ), func_( std::forward(func) ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *events); } ~IterateNode() { Engine::OnNodeDetach(*this, *events_); Engine::OnNodeDestroy(*this); } virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); bool changed = false; {// timer using TimerT = typename IterateNode::ScopedUpdateTimer; TimerT scopedTimer( *this, events_->Events().size() ); S newValue = func_(EventRange( events_->Events() ), this->value_); if (! Equals(newValue, this->value_)) { this->value_ = std::move(newValue); changed = true; } }// ~timer REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); if (changed) Engine::OnNodePulse(*this, turn); else Engine::OnNodeIdlePulse(*this, turn); } virtual const char* GetNodeType() const override { return "IterateNode"; } virtual int DependencyCount() const override { return 1; } private: std::shared_ptr> events_; TFunc func_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// IterateByRefNode /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename S, typename E, typename TFunc > class IterateByRefNode : public SignalNode { using Engine = typename IterateByRefNode::Engine; public: template IterateByRefNode(T&& init, const std::shared_ptr>& events, F&& func) : IterateByRefNode::SignalNode( std::forward(init) ), func_( std::forward(func) ), events_( events ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *events); } ~IterateByRefNode() { Engine::OnNodeDetach(*this, *events_); Engine::OnNodeDestroy(*this); } virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); {// timer using TimerT = typename IterateByRefNode::ScopedUpdateTimer; TimerT scopedTimer( *this, events_->Events().size() ); func_(EventRange( events_->Events() ), this->value_); }// ~timer REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); // Always assume change Engine::OnNodePulse(*this, turn); } virtual const char* GetNodeType() const override { return "IterateByRefNode"; } virtual int DependencyCount() const override { return 1; } protected: TFunc func_; std::shared_ptr> events_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// SyncedIterateNode /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename S, typename E, typename TFunc, typename ... TDepValues > class SyncedIterateNode : public SignalNode { using Engine = typename SyncedIterateNode::Engine; public: template SyncedIterateNode(T&& init, const std::shared_ptr>& events, F&& func, const std::shared_ptr>& ... deps) : SyncedIterateNode::SignalNode( std::forward(init) ), events_( events ), func_( std::forward(func) ), deps_( deps ... ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *events); REACT_EXPAND_PACK(Engine::OnNodeAttach(*this, *deps)); } ~SyncedIterateNode() { Engine::OnNodeDetach(*this, *events_); apply( DetachFunctor>...>( *this ), deps_); Engine::OnNodeDestroy(*this); } virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); events_->SetCurrentTurn(turn); bool changed = false; REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); if (! events_->Events().empty()) {// timer using TimerT = typename SyncedIterateNode::ScopedUpdateTimer; TimerT scopedTimer( *this, events_->Events().size() ); S newValue = apply( [this] (const std::shared_ptr>& ... args) { return func_(EventRange( events_->Events() ), this->value_, args->ValueRef() ...); }, deps_); if (! Equals(newValue, this->value_)) { changed = true; this->value_ = std::move(newValue); } }// ~timer REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); if (changed) Engine::OnNodePulse(*this, turn); else Engine::OnNodeIdlePulse(*this, turn); } virtual const char* GetNodeType() const override { return "SyncedIterateNode"; } virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } private: using DepHolderT = std::tuple>...>; std::shared_ptr> events_; TFunc func_; DepHolderT deps_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// SyncedIterateByRefNode /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename S, typename E, typename TFunc, typename ... TDepValues > class SyncedIterateByRefNode : public SignalNode { using Engine = typename SyncedIterateByRefNode::Engine; public: template SyncedIterateByRefNode(T&& init, const std::shared_ptr>& events, F&& func, const std::shared_ptr>& ... deps) : SyncedIterateByRefNode::SignalNode( std::forward(init) ), events_( events ), func_( std::forward(func) ), deps_( deps ... ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *events); REACT_EXPAND_PACK(Engine::OnNodeAttach(*this, *deps)); } ~SyncedIterateByRefNode() { Engine::OnNodeDetach(*this, *events_); apply( DetachFunctor>...>( *this ), deps_); Engine::OnNodeDestroy(*this); } virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); events_->SetCurrentTurn(turn); bool changed = false; REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); if (! events_->Events().empty()) {// timer using TimerT = typename SyncedIterateByRefNode::ScopedUpdateTimer; TimerT scopedTimer( *this, events_->Events().size() ); apply( [this] (const std::shared_ptr>& ... args) { func_(EventRange( events_->Events() ), this->value_, args->ValueRef() ...); }, deps_); changed = true; }// ~timer REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); if (changed) Engine::OnNodePulse(*this, turn); else Engine::OnNodeIdlePulse(*this, turn); } virtual const char* GetNodeType() const override { return "SyncedIterateByRefNode"; } virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } private: using DepHolderT = std::tuple>...>; std::shared_ptr> events_; TFunc func_; DepHolderT deps_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// HoldNode /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename S > class HoldNode : public SignalNode { using Engine = typename HoldNode::Engine; public: template HoldNode(T&& init, const std::shared_ptr>& events) : HoldNode::SignalNode( std::forward(init) ), events_( events ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *events_); } ~HoldNode() { Engine::OnNodeDetach(*this, *events_); Engine::OnNodeDestroy(*this); } virtual const char* GetNodeType() const override { return "HoldNode"; } virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); bool changed = false; if (! events_->Events().empty()) { const S& newValue = events_->Events().back(); if (! Equals(newValue, this->value_)) { changed = true; this->value_ = newValue; } } REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); if (changed) Engine::OnNodePulse(*this, turn); else Engine::OnNodeIdlePulse(*this, turn); } virtual int DependencyCount() const override { return 1; } private: const std::shared_ptr> events_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// SnapshotNode /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename S, typename E > class SnapshotNode : public SignalNode { using Engine = typename SnapshotNode::Engine; public: SnapshotNode(const std::shared_ptr>& target, const std::shared_ptr>& trigger) : SnapshotNode::SignalNode( target->ValueRef() ), target_( target ), trigger_( trigger ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *target_); Engine::OnNodeAttach(*this, *trigger_); } ~SnapshotNode() { Engine::OnNodeDetach(*this, *target_); Engine::OnNodeDetach(*this, *trigger_); Engine::OnNodeDestroy(*this); } virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); trigger_->SetCurrentTurn(turn); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); bool changed = false; if (! trigger_->Events().empty()) { const S& newValue = target_->ValueRef(); if (! Equals(newValue, this->value_)) { changed = true; this->value_ = newValue; } } REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); if (changed) Engine::OnNodePulse(*this, turn); else Engine::OnNodeIdlePulse(*this, turn); } virtual const char* GetNodeType() const override { return "SnapshotNode"; } virtual int DependencyCount() const override { return 2; } private: const std::shared_ptr> target_; const std::shared_ptr> trigger_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// MonitorNode /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename E > class MonitorNode : public EventStreamNode { using Engine = typename MonitorNode::Engine; public: MonitorNode(const std::shared_ptr>& target) : MonitorNode::EventStreamNode( ), target_( target ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *target_); } ~MonitorNode() { Engine::OnNodeDetach(*this, *target_); Engine::OnNodeDestroy(*this); } virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); this->SetCurrentTurn(turn, true); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); this->events_.push_back(target_->ValueRef()); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); if (! this->events_.empty()) Engine::OnNodePulse(*this, turn); else Engine::OnNodeIdlePulse(*this, turn); } virtual const char* GetNodeType() const override { return "MonitorNode"; } virtual int DependencyCount() const override { return 1; } private: const std::shared_ptr> target_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// PulseNode /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename S, typename E > class PulseNode : public EventStreamNode { using Engine = typename PulseNode::Engine; public: PulseNode(const std::shared_ptr>& target, const std::shared_ptr>& trigger) : PulseNode::EventStreamNode( ), target_( target ), trigger_( trigger ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *target_); Engine::OnNodeAttach(*this, *trigger_); } ~PulseNode() { Engine::OnNodeDetach(*this, *target_); Engine::OnNodeDetach(*this, *trigger_); Engine::OnNodeDestroy(*this); } virtual void Tick(void* turnPtr) override { typedef typename D::Engine::TurnT TurnT; TurnT& turn = *reinterpret_cast(turnPtr); this->SetCurrentTurn(turn, true); trigger_->SetCurrentTurn(turn); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); for (uint i=0; iEvents().size(); i++) this->events_.push_back(target_->ValueRef()); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); if (! this->events_.empty()) Engine::OnNodePulse(*this, turn); else Engine::OnNodeIdlePulse(*this, turn); } virtual const char* GetNodeType() const override { return "PulseNode"; } virtual int DependencyCount() const override { return 2; } private: const std::shared_ptr> target_; const std::shared_ptr> trigger_; }; /****************************************/ REACT_IMPL_END /***************************************/ #endif // REACT_DETAIL_GRAPH_ALGORITHMNODES_H_INCLUDED