From 2a63821fb97cf46f4fd2b749c4dffcbf11ee0a86 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 21 Apr 2014 21:42:56 +0200 Subject: [PATCH 001/266] Removed ELM and SourceSet engines from master branch. Focus is on remaining engines for now. --- include/react/engine/ELMEngine.h | 116 ----------- include/react/engine/SourceSetEngine.h | 161 --------------- project/msvc/CppReact.vcxproj | 4 - project/msvc/CppReact.vcxproj.filters | 12 -- project/msvc/CppReactSandbox.vcxproj | 1 - src/benchmark/Main.cpp | 28 ++- src/engine/ELMEngine.cpp | 169 --------------- src/engine/SourceSetEngine.cpp | 275 ------------------------- src/test/EventStreamTest.cpp | 4 - src/test/EventStreamTestQ.cpp | 4 - src/test/ObserverTest.cpp | 4 - src/test/ObserverTestQ.cpp | 4 - src/test/OperationsTest.cpp | 4 - src/test/OperationsTestQ.cpp | 4 - src/test/SignalTest.cpp | 4 - src/test/SignalTestQ.cpp | 4 - src/test/TransactionTest.cpp | 4 - 17 files changed, 12 insertions(+), 790 deletions(-) delete mode 100644 include/react/engine/ELMEngine.h delete mode 100644 include/react/engine/SourceSetEngine.h delete mode 100644 src/engine/ELMEngine.cpp delete mode 100644 src/engine/SourceSetEngine.cpp diff --git a/include/react/engine/ELMEngine.h b/include/react/engine/ELMEngine.h deleted file mode 100644 index 38d91751..00000000 --- a/include/react/engine/ELMEngine.h +++ /dev/null @@ -1,116 +0,0 @@ - -// 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) - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include - -#include "tbb/task_group.h" -#include "tbb/spin_mutex.h" - -#include "react/common/Containers.h" -#include "react/common/Types.h" -#include "react/detail/EngineBase.h" -#include "react/detail/ReactiveDomain.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ -namespace elm { - -using std::atomic; -using std::set; -using tbb::task_group; -using tbb::spin_mutex; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Turn -/////////////////////////////////////////////////////////////////////////////////////////////////// -class Turn : public TurnBase -{ -public: - Turn(TurnIdT id, TurnFlagsT flags); -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Node -/////////////////////////////////////////////////////////////////////////////////////////////////// -class Node : public IReactiveNode -{ -public: - using ShiftMutexT = spin_mutex; - - Node(); - - ShiftMutexT ShiftMutex; - NodeVector Successors; - - atomic Counter; - atomic ShouldUpdate; - - TurnIdT LastTurnId; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Engine -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EngineBase : public IReactiveEngine -{ -public: - using NodeShiftMutexT = Node::ShiftMutexT; - using NodeSetT = set; - - void OnNodeCreate(Node& node); - void OnNodeDestroy(Node& node); - - void OnNodeAttach(Node& node, Node& parent); - void OnNodeDetach(Node& node, Node& parent); - - void OnTurnInputChange(Node& node, TTurn& turn); - void OnTurnPropagate(TTurn& turn); - - void OnNodePulse(Node& node, TTurn& turn); - void OnNodeIdlePulse(Node& node, TTurn& turn); - - void OnDynamicNodeAttach(Node& node, Node& parent, TTurn& turn); - void OnDynamicNodeDetach(Node& node, Node& parent, TTurn& turn); - -private: - void processChild(Node& node, bool update, TTurn& turn); - void nudgeChildren(Node& parent, bool update, TTurn& turn); - - task_group tasks_; - NodeSetT inputNodes_; -}; - -class BasicEngine : public EngineBase {}; -class QueuingEngine : public DefaultQueuingEngine {}; - -} // ~namespace elm -/****************************************/ REACT_IMPL_END /***************************************/ - -/*****************************************/ REACT_BEGIN /*****************************************/ - -struct parallel; -struct parallel_queue; - -template -class ELMEngine; - -template <> class ELMEngine : public REACT_IMPL::elm::BasicEngine {}; -template <> class ELMEngine : public REACT_IMPL::elm::QueuingEngine {}; - -/******************************************/ REACT_END /******************************************/ - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -template struct EnableParallelUpdating; -template <> struct EnableParallelUpdating> : std::true_type {}; -template <> struct EnableParallelUpdating> : std::true_type {}; - -/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file diff --git a/include/react/engine/SourceSetEngine.h b/include/react/engine/SourceSetEngine.h deleted file mode 100644 index e83ec43e..00000000 --- a/include/react/engine/SourceSetEngine.h +++ /dev/null @@ -1,161 +0,0 @@ - -// 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) - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include - -#include "tbb/queuing_mutex.h" -#include "tbb/spin_mutex.h" -#include "tbb/task_group.h" - -#include "react/common/Containers.h" -#include "react/common/SourceIdSet.h" -#include "react/common/Types.h" -#include "react/detail/EngineBase.h" -#include "react/detail/ReactiveDomain.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ -namespace sourceset { - -class Node; - -using std::atomic; -using std::vector; -using tbb::task_group; -using tbb::queuing_mutex; -using tbb::spin_mutex; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Turn -/////////////////////////////////////////////////////////////////////////////////////////////////// -class Turn : public TurnBase -{ -public: - using SourceIdSetT = SourceIdSet; - - Turn(TurnIdT id, TurnFlagsT flags); - - void AddSourceId(ObjectId id); - - SourceIdSetT& Sources() { return sources_; } - -private: - SourceIdSetT sources_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Node -/////////////////////////////////////////////////////////////////////////////////////////////////// -class Node : public IReactiveNode -{ -public: - using SourceIdSetT = Turn::SourceIdSetT; - - using NudgeMutexT = queuing_mutex; - using ShiftMutexT = spin_mutex; - - Node(); - - void AddSourceId(ObjectId id); - - void AttachSuccessor(Node& node); - void DetachSuccessor(Node& node); - - void DynamicAttachTo(Node& parent, Turn& turn); - void DynamicDetachFrom(Node& parent, Turn& turn); - - void Destroy(); - - void Pulse(Turn& turn, bool updated); - - bool IsDependency(Turn& turn); - bool CheckCurrentTurn(Turn& turn); - - void Nudge(Turn& turn, bool update, bool invalidate); - - void CheckForCycles(ObjectId startId) const; - -private: - enum - { - kFlag_Visited = 1 << 0, - kFlag_Updated = 1 << 1, - kFlag_Invaliated = 1 << 2 - }; - - NodeVector predecessors_; - NodeVector successors_; - - SourceIdSetT sources_; - - uint curTurnId_; - - short tickThreshold_; - uchar flags_; - - NudgeMutexT nudgeMutex_; - ShiftMutexT shiftMutex_; - - void invalidateSources(); -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EngineBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EngineBase : public IReactiveEngine -{ -public: - void OnNodeCreate(Node& node); - - void OnNodeAttach(Node& node, Node& parent); - void OnNodeDetach(Node& node, Node& parent); - - void OnNodeDestroy(Node& node); - - void OnTurnInputChange(Node& node, TTurn& turn); - void OnTurnPropagate(TTurn& turn); - - void OnNodePulse(Node& node, TTurn& turn); - void OnNodeIdlePulse(Node& node, TTurn& turn); - - void OnDynamicNodeAttach(Node& node, Node& parent, TTurn& turn); - void OnDynamicNodeDetach(Node& node, Node& parent, TTurn& turn); - -private: - vector changedInputs_; -}; - -class BasicEngine : public EngineBase {}; -class QueuingEngine : public DefaultQueuingEngine {}; - -} // ~namespace sourceset -/****************************************/ REACT_IMPL_END /***************************************/ - -/*****************************************/ REACT_BEGIN /*****************************************/ - -struct parallel; -struct parallel_queue; - -template -class SourceSetEngine; - -template <> class SourceSetEngine : public REACT_IMPL::sourceset::BasicEngine {}; -template <> class SourceSetEngine : public REACT_IMPL::sourceset::QueuingEngine {}; - -/******************************************/ REACT_END /******************************************/ - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -template struct EnableParallelUpdating; -template <> struct EnableParallelUpdating> : std::true_type {}; -template <> struct EnableParallelUpdating> : std::true_type {}; - -/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index 32190a9d..3f2dbe8a 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -92,9 +92,7 @@ - - @@ -116,9 +114,7 @@ - - diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index 820c90e5..17d8c18f 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -75,15 +75,9 @@ Header Files - - Header Files\engine - Header Files\engine - - Header Files\engine - Header Files\engine @@ -155,15 +149,9 @@ Source Files\logging - - Source Files\engine - Source Files\engine - - Source Files\engine - Source Files\engine diff --git a/project/msvc/CppReactSandbox.vcxproj b/project/msvc/CppReactSandbox.vcxproj index b061b28c..c1646c99 100644 --- a/project/msvc/CppReactSandbox.vcxproj +++ b/project/msvc/CppReactSandbox.vcxproj @@ -80,7 +80,6 @@ - diff --git a/src/benchmark/Main.cpp b/src/benchmark/Main.cpp index e0255d0a..c083ea6a 100644 --- a/src/benchmark/Main.cpp +++ b/src/benchmark/Main.cpp @@ -24,8 +24,6 @@ #include "react/engine/TopoSortEngine.h" #include "react/engine/PulseCountEngine.h" -#include "react/engine/SourceSetEngine.h" -#include "react/engine/ELMEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -36,23 +34,21 @@ using namespace react; REACTIVE_DOMAIN(TopoSortSTDomain, TopoSortEngine); REACTIVE_DOMAIN(TopoSortDomain, TopoSortEngine); REACTIVE_DOMAIN(PulseCountDomain, PulseCountEngine); -REACTIVE_DOMAIN(SourceSetDomain, SourceSetEngine); -REACTIVE_DOMAIN(ELMDomain, ELMEngine); REACTIVE_DOMAIN(SubtreeDomain, SubtreeEngine); void runBenchmarkGrid(std::ostream& out) { RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(20, 10000), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + TopoSortSTDomain, TopoSortDomain, PulseCountDomain); RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(30, 10000), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + TopoSortSTDomain, TopoSortDomain, PulseCountDomain); RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(40, 10000), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + TopoSortSTDomain, TopoSortDomain, PulseCountDomain); RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(50, 10000), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + TopoSortSTDomain, TopoSortDomain, PulseCountDomain); } void runBenchmarkRandom(std::ostream& out) @@ -94,13 +90,13 @@ void runBenchmarkFanout(std::ostream& out) // TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); RUN_BENCHMARK(out, 3, Benchmark_Fanout, BenchmarkParams_Fanout(10, 10, 10), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + TopoSortSTDomain, TopoSortDomain, PulseCountDomain); RUN_BENCHMARK(out, 3, Benchmark_Fanout, BenchmarkParams_Fanout(100, 10, 10), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + TopoSortSTDomain, TopoSortDomain, PulseCountDomain); RUN_BENCHMARK(out, 3, Benchmark_Fanout, BenchmarkParams_Fanout(1000, 10, 10), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + TopoSortSTDomain, TopoSortDomain, PulseCountDomain); } void runBenchmarkSequence(std::ostream& out) @@ -115,13 +111,13 @@ void runBenchmarkSequence(std::ostream& out) // TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(10, 10, 10), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + TopoSortSTDomain, TopoSortDomain, PulseCountDomain); RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(100, 10, 10), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + TopoSortSTDomain, TopoSortDomain, PulseCountDomain); RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(1000, 10, 10), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + TopoSortSTDomain, TopoSortDomain, PulseCountDomain); } void runBenchmarkLifeSim(std::ostream& out) @@ -133,7 +129,7 @@ void runBenchmarkLifeSim(std::ostream& out) // SourceSetDomain, PulseCountDomain); RUN_BENCHMARK(out, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 15, 10000), - TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + TopoSortSTDomain, TopoSortDomain, PulseCountDomain); //RUN_BENCHMARK(out, 3, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 50, 100), // PulseCountDomain, PulseCountDomain); @@ -225,7 +221,7 @@ void profileBenchmark() //RUN_BENCHMARK(std::cout, 1, Benchmark_Random, BenchmarkParams_Random(20, 11, 100, 0, 5, 80, 20, false, 41556, 21624), //SubtreeDomain); - //TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SubtreeDomain, SourceSetDomain); + //TopoSortSTDomain, TopoSortDomain, PulseCountDomain, SubtreeDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 15, 10000), // TopoSortDomain); diff --git a/src/engine/ELMEngine.cpp b/src/engine/ELMEngine.cpp deleted file mode 100644 index 97807822..00000000 --- a/src/engine/ELMEngine.cpp +++ /dev/null @@ -1,169 +0,0 @@ - -// 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) - -#include "react/engine/ELMEngine.h" - -#include - -#include "tbb/parallel_for.h" -#include "tbb/blocked_range.h" - -#include "react/common/Types.h" -#include "react/common/GraphData.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ -namespace elm { - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Turn -/////////////////////////////////////////////////////////////////////////////////////////////////// -Turn::Turn(TurnIdT id, TurnFlagsT flags) : - TurnBase(id, flags) -{ -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Node -/////////////////////////////////////////////////////////////////////////////////////////////////// -Node::Node() : - Counter(0), - ShouldUpdate(false) -{ -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EngineBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -void EngineBase::OnNodeCreate(Node& node) -{ - if (node.IsInputNode()) - inputNodes_.insert(&node); -} - -template -void EngineBase::OnNodeDestroy(Node& node) -{ - if (node.IsInputNode()) - inputNodes_.erase(&node); -} - -template -void EngineBase::OnNodeAttach(Node& node, Node& parent) -{ - parent.Successors.Add(node); -} - -template -void EngineBase::OnNodeDetach(Node& node, Node& parent) -{ - parent.Successors.Remove(node); -} - -template -void EngineBase::OnTurnInputChange(Node& node, TTurn& turn) -{ - node.LastTurnId = turn.Id(); -} - -template -void EngineBase::OnTurnPropagate(TTurn& turn) -{ - for (auto* node : inputNodes_) - nudgeChildren(*node, node->LastTurnId == turn.Id(), turn); - - tasks_.wait(); -} - -template -void EngineBase::OnNodePulse(Node& node, TTurn& turn) -{ - nudgeChildren(node, true, turn); -} - -template -void EngineBase::OnNodeIdlePulse(Node& node, TTurn& turn) -{ - nudgeChildren(node, false, turn); -} - -template -void EngineBase::OnDynamicNodeAttach(Node& node, Node& parent, TTurn& turn) -{ - bool shouldTick = false; - - {// parent.ShiftMutex - NodeShiftMutexT::scoped_lock lock(parent.ShiftMutex); - - parent.Successors.Add(node); - - if (parent.LastTurnId == turn.Id()) - { - shouldTick = true; - } - else - { - node.ShouldUpdate = true; - node.Counter.store(node.DependencyCount() - 1, std::memory_order_relaxed); - } - }// ~parent.ShiftMutex - - if (shouldTick) - node.Tick(&turn); -} - -template -void EngineBase::OnDynamicNodeDetach(Node& node, Node& parent, TTurn& turn) -{// parent.ShiftMutex - NodeShiftMutexT::scoped_lock lock(parent.ShiftMutex); - - parent.Successors.Remove(node); -}// ~parent.ShiftMutex - -template -void EngineBase::processChild(Node& node, bool update, TTurn& turn) -{ - // Invalidated, this node has to be ticked - if (node.ShouldUpdate) - { - // Reset flag - node.ShouldUpdate = false; - node.Tick(&turn); - } - // No tick required - else - { - nudgeChildren(node, false, turn); - } -} - -template -void EngineBase::nudgeChildren(Node& node, bool update, TTurn& turn) -{// node.ShiftMutex - NodeShiftMutexT::scoped_lock lock(node.ShiftMutex); - - for (auto* succ : node.Successors) - { - if (update) - succ->ShouldUpdate = true; - - // Delay tick? - if (succ->Counter.fetch_add(1, std::memory_order_relaxed) < succ->DependencyCount() - 1) - continue; - - succ->Counter.store(0, std::memory_order_relaxed); - tasks_.run(std::bind(&EngineBase::processChild, this, std::ref(*succ), update, std::ref(turn))); - } - - node.LastTurnId = turn.Id(); -}// ~node.ShiftMutex - -// Explicit instantiation -template class EngineBase; -template class EngineBase>; - -} // ~namespace elm -/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file diff --git a/src/engine/SourceSetEngine.cpp b/src/engine/SourceSetEngine.cpp deleted file mode 100644 index fc432a34..00000000 --- a/src/engine/SourceSetEngine.cpp +++ /dev/null @@ -1,275 +0,0 @@ - -// 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) - -#include "react/engine/SourceSetEngine.h" - -#include "tbb/task_group.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ -namespace sourceset { - -// Todo -tbb::task_group tasks; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Turn -/////////////////////////////////////////////////////////////////////////////////////////////////// -Turn::Turn(TurnIdT id, TurnFlagsT flags) : - TurnBase(id, flags) -{ -} - -void Turn::AddSourceId(ObjectId id) -{ - sources_.Insert(id); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Node -/////////////////////////////////////////////////////////////////////////////////////////////////// -Node::Node() : - curTurnId_(UINT_MAX), - tickThreshold_(0), - flags_(0) -{ -} - -void Node::AddSourceId(ObjectId id) -{ - sources_.Insert(id); -} - -void Node::AttachSuccessor(Node& node) -{ - successors_.Add(node); - node.predecessors_.Add(*this); - - node.sources_.Insert(sources_); -} - -void Node::DetachSuccessor(Node& node) -{ - successors_.Remove(node); - node.predecessors_.Remove(*this); - - node.invalidateSources(); -} - -void Node::Destroy() -{ - auto predIt = predecessors_.begin(); - while (predIt != predecessors_.end()) - { - (*predIt)->DetachSuccessor(*this); - predIt = predecessors_.begin(); - } - - auto succIt = successors_.begin(); - while (succIt != successors_.end()) - { - DetachSuccessor(**succIt); - succIt = successors_.begin(); - } -} - -void Node::Pulse(Turn& turn, bool updated) -{ - bool invalidate = (flags_ & kFlag_Invaliated) != 0; - flags_ &= ~(kFlag_Invaliated | kFlag_Updated | kFlag_Visited); - - // shiftMutex_ - { - ShiftMutexT::scoped_lock lock(shiftMutex_); - - curTurnId_ = turn.Id(); - - for (auto succ : successors_) - tasks.run(std::bind(&Node::Nudge, succ, std::ref(turn), updated, invalidate)); - } - // ~shiftMutex_ -} - -bool Node::IsDependency(Turn& turn) -{ - return turn.Sources().IntersectsWith(sources_); -} - -bool Node::CheckCurrentTurn(Turn& turn) -{ - return curTurnId_ == turn.Id(); -} - -void Node::Nudge(Turn& turn, bool update, bool invalidate) -{ - bool shouldTick = false; - - // nudgeMutex_ - { - NudgeMutexT::scoped_lock lock(nudgeMutex_); - - if (update) - flags_ |= kFlag_Updated; - - if (invalidate) - flags_ |= kFlag_Invaliated; - - // First nudge initializes threshold counter for this turn - if (! (flags_ & kFlag_Visited)) - { - flags_ |= kFlag_Visited; - tickThreshold_ = 0; - - // Count unprocessed dependencies - for (auto pred : predecessors_) - if (pred->IsDependency(turn)) - ++tickThreshold_; - } - - // Wait for other predecessors? - if (--tickThreshold_ > 0) - return; - } - // ~nudgeMutex_ - - if (flags_ & kFlag_Updated) - shouldTick = true; - - if (flags_ & kFlag_Invaliated) - invalidateSources(); - - flags_ &= ~(kFlag_Visited | kFlag_Updated); - if (IsOutputNode()) - flags_ &= ~kFlag_Invaliated; - - if (shouldTick) - Tick(&turn); - else - Pulse(turn, false); -} - -void Node::DynamicAttachTo(Node& parent, Turn& turn) -{ - bool shouldTick = false; - - // parent.shiftMutex_ - { - ShiftMutexT::scoped_lock lock(parent.shiftMutex_); - - parent.AttachSuccessor(*this); - - flags_ |= kFlag_Invaliated; - - // Has new parent been processed yet? - if (parent.IsDependency(turn) && !parent.CheckCurrentTurn(turn)) - { - tickThreshold_ = 1; - flags_ |= kFlag_Visited | kFlag_Updated; - } - else - { - shouldTick = true; - } - } - // ~parent.shiftMutex_ - - // Re-tick? - if (shouldTick) - Tick(&turn); -} - -void Node::DynamicDetachFrom(Node& parent, Turn& turn) -// parent.shiftMutex_ -{ - ShiftMutexT::scoped_lock lock(parent.shiftMutex_); - - parent.DetachSuccessor(*this); -} -// ~parent.shiftMutex_ - -void Node::invalidateSources() -{ - // Recalc union - sources_.Clear(); - - for (auto pred : predecessors_) - sources_.Insert(pred->sources_); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SourceSetEngine -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -void EngineBase::OnNodeCreate(Node& node) -{ - if (node.IsInputNode()) - node.AddSourceId(GetObjectId(node)); -} - -template -void EngineBase::OnNodeAttach(Node& node, Node& parent) -{ - parent.AttachSuccessor(node); -} - -template -void EngineBase::OnNodeDetach(Node& node, Node& parent) -{ - parent.DetachSuccessor(node); -} - -template -void EngineBase::OnNodeDestroy(Node& node) -{ - node.Destroy(); -} - -template -void EngineBase::OnTurnInputChange(Node& node, TTurn& turn) -{ - turn.AddSourceId(GetObjectId(node)); - changedInputs_.push_back(&node); -} - -template -void EngineBase::OnTurnPropagate(TTurn& turn) -{ - for (auto* node : changedInputs_) - node->Pulse(turn, true); - tasks.wait(); - - changedInputs_.clear(); -} - -template -void EngineBase::OnNodePulse(Node& node, TTurn& turn) -{ - node.Pulse(turn, true); -} - -template -void EngineBase::OnNodeIdlePulse(Node& node, TTurn& turn) -{ - node.Pulse(turn, false); -} - -template -void EngineBase::OnDynamicNodeAttach(Node& node, Node& parent, TTurn& turn) -{ - node.DynamicAttachTo(parent, turn); -} - -template -void EngineBase::OnDynamicNodeDetach(Node& node, Node& parent, TTurn& turn) -{ - node.DynamicDetachFrom(parent, turn); -} - -// Explicit instantiation -template class EngineBase; -template class EngineBase>; - -} // ~namespace sourceset -/****************************************/ REACT_IMPL_END /***************************************/ diff --git a/src/test/EventStreamTest.cpp b/src/test/EventStreamTest.cpp index 3c74abe0..d9306b8c 100644 --- a/src/test/EventStreamTest.cpp +++ b/src/test/EventStreamTest.cpp @@ -8,8 +8,6 @@ #include "react/engine/PulseCountEngine.h" #include "react/engine/TopoSortEngine.h" -#include "react/engine/SourceSetEngine.h" -#include "react/engine/ELMEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -19,9 +17,7 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqTopoSort, EventStreamTest, TopoSortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSort, EventStreamTest, TopoSortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ELM, EventStreamTest, ELMEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulseCount, EventStreamTest, PulseCountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(SourceSet, EventStreamTest, SourceSetEngine); INSTANTIATE_TYPED_TEST_CASE_P(Subtree, EventStreamTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/src/test/EventStreamTestQ.cpp b/src/test/EventStreamTestQ.cpp index 4438c1d6..ae608a80 100644 --- a/src/test/EventStreamTestQ.cpp +++ b/src/test/EventStreamTestQ.cpp @@ -8,8 +8,6 @@ #include "react/engine/PulseCountEngine.h" #include "react/engine/TopoSortEngine.h" -#include "react/engine/SourceSetEngine.h" -#include "react/engine/ELMEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -19,9 +17,7 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqTopoSortQ, EventStreamTest, TopoSortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSortQ, EventStreamTest, TopoSortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ELMQ, EventStreamTest, ELMEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulseCountQ, EventStreamTest, PulseCountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(SourceSetQ, EventStreamTest, SourceSetEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSortP, EventStreamTest, TopoSortEngine); INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, EventStreamTest, SubtreeEngine); diff --git a/src/test/ObserverTest.cpp b/src/test/ObserverTest.cpp index 36c5f4ff..eec6d900 100644 --- a/src/test/ObserverTest.cpp +++ b/src/test/ObserverTest.cpp @@ -8,8 +8,6 @@ #include "react/engine/PulseCountEngine.h" #include "react/engine/TopoSortEngine.h" -#include "react/engine/SourceSetEngine.h" -#include "react/engine/ELMEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -19,9 +17,7 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqTopoSort, ObserverTest, TopoSortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSort, ObserverTest, TopoSortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ELM, ObserverTest, ELMEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulseCount, ObserverTest, PulseCountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(SourceSet, ObserverTest, SourceSetEngine); INSTANTIATE_TYPED_TEST_CASE_P(Subtree, ObserverTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/src/test/ObserverTestQ.cpp b/src/test/ObserverTestQ.cpp index 80a5f1c2..d964655d 100644 --- a/src/test/ObserverTestQ.cpp +++ b/src/test/ObserverTestQ.cpp @@ -8,8 +8,6 @@ #include "react/engine/PulseCountEngine.h" #include "react/engine/TopoSortEngine.h" -#include "react/engine/SourceSetEngine.h" -#include "react/engine/ELMEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -19,9 +17,7 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqTopoSortQ, ObserverTest, TopoSortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSortQ, ObserverTest, TopoSortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ELMQ, ObserverTest, ELMEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulseCountQ, ObserverTest, PulseCountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(SourceSetQ, ObserverTest, SourceSetEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSortP, ObserverTest, TopoSortEngine); INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, ObserverTest, SubtreeEngine); diff --git a/src/test/OperationsTest.cpp b/src/test/OperationsTest.cpp index c65f2e57..ac13e827 100644 --- a/src/test/OperationsTest.cpp +++ b/src/test/OperationsTest.cpp @@ -8,8 +8,6 @@ #include "react/engine/PulseCountEngine.h" #include "react/engine/TopoSortEngine.h" -#include "react/engine/SourceSetEngine.h" -#include "react/engine/ELMEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -19,9 +17,7 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqTopoSort, OperationsTest, TopoSortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSort, OperationsTest, TopoSortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ELM, OperationsTest, ELMEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulseCount, OperationsTest, PulseCountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(SourceSet, OperationsTest, SourceSetEngine); INSTANTIATE_TYPED_TEST_CASE_P(Subtree, OperationsTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/src/test/OperationsTestQ.cpp b/src/test/OperationsTestQ.cpp index 8149c626..571e8cce 100644 --- a/src/test/OperationsTestQ.cpp +++ b/src/test/OperationsTestQ.cpp @@ -8,8 +8,6 @@ #include "react/engine/PulseCountEngine.h" #include "react/engine/TopoSortEngine.h" -#include "react/engine/SourceSetEngine.h" -#include "react/engine/ELMEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -19,9 +17,7 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqTopoSortQ, OperationsTest, TopoSortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSortQ, OperationsTest, TopoSortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ELMQ, OperationsTest, ELMEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulseCountQ, OperationsTest, PulseCountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(SourceSetQ, OperationsTest, SourceSetEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSortP, OperationsTest, TopoSortEngine); INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, OperationsTest, SubtreeEngine); diff --git a/src/test/SignalTest.cpp b/src/test/SignalTest.cpp index 5cc9bcc3..c903c592 100644 --- a/src/test/SignalTest.cpp +++ b/src/test/SignalTest.cpp @@ -8,8 +8,6 @@ #include "react/engine/PulseCountEngine.h" #include "react/engine/TopoSortEngine.h" -#include "react/engine/SourceSetEngine.h" -#include "react/engine/ELMEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -19,9 +17,7 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqTopoSort, SignalTest, TopoSortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSort, SignalTest, TopoSortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ELM, SignalTest, ELMEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulseCount, SignalTest, PulseCountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(SourceSet, SignalTest, SourceSetEngine); INSTANTIATE_TYPED_TEST_CASE_P(Subtree, SignalTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/src/test/SignalTestQ.cpp b/src/test/SignalTestQ.cpp index 8f9b4dc6..9115c184 100644 --- a/src/test/SignalTestQ.cpp +++ b/src/test/SignalTestQ.cpp @@ -8,8 +8,6 @@ #include "react/engine/PulseCountEngine.h" #include "react/engine/TopoSortEngine.h" -#include "react/engine/SourceSetEngine.h" -#include "react/engine/ELMEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -19,9 +17,7 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqTopoSortQ, SignalTest, TopoSortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSortQ, SignalTest, TopoSortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ELMQ, SignalTest, ELMEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulseCountQ, SignalTest, PulseCountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(SourceSetQ, SignalTest, SourceSetEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSortP, SignalTest, TopoSortEngine); INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, SignalTest, SubtreeEngine); diff --git a/src/test/TransactionTest.cpp b/src/test/TransactionTest.cpp index c0101cfb..3e3cbe13 100644 --- a/src/test/TransactionTest.cpp +++ b/src/test/TransactionTest.cpp @@ -8,8 +8,6 @@ #include "react/engine/PulseCountEngine.h" #include "react/engine/TopoSortEngine.h" -#include "react/engine/SourceSetEngine.h" -#include "react/engine/ELMEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -19,9 +17,7 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqTopoSort, TransactionTest, TopoSortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSort, TransactionTest, TopoSortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ELM, TransactionTest, ELMEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulseCount, TransactionTest, PulseCountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(SourceSet, TransactionTest, SourceSetEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSortP, TransactionTest, TopoSortEngine); INSTANTIATE_TYPED_TEST_CASE_P(Subtree, TransactionTest, SubtreeEngine); From 4a92db796f050f3128b9fcf13bf5ccb1e080e5d9 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 22 Apr 2014 12:32:18 +0200 Subject: [PATCH 002/266] Reactive domain cleanup. --- include/react/Observer.h | 12 +-- include/react/detail/EngineBase.h | 3 + include/react/detail/Options.h | 2 +- include/react/detail/ReactiveDomain.h | 87 ++++++++-------------- include/react/detail/graph/ObserverNodes.h | 9 ++- src/test/TransactionTest.h | 12 +-- 6 files changed, 50 insertions(+), 75 deletions(-) diff --git a/include/react/Observer.h b/include/react/Observer.h index 2095d9de..de847248 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -41,14 +41,12 @@ class Observer Observer() : ptr_{ nullptr }, subject_{ nullptr } - { - } + {} Observer(ObserverNodeT* ptr, const std::shared_ptr& subject) : ptr_{ ptr }, subject_{ subject } - { - } + {} const ObserverNodeT* GetPtr() const { @@ -92,14 +90,12 @@ class ObserverRegistry Entry_(std::unique_ptr&& obs, SubjectT* aSubject) : obs_{ std::move(obs) }, Subject{ aSubject } - { - } + {} Entry_(Entry_&& other) : obs_(std::move(other.obs_)), Subject(other.Subject) - { - } + {} SubjectT* Subject; diff --git a/include/react/detail/EngineBase.h b/include/react/detail/EngineBase.h index d2195700..520ed0b5 100644 --- a/include/react/detail/EngineBase.h +++ b/include/react/detail/EngineBase.h @@ -47,6 +47,9 @@ class TurnBase template friend class DomainBase; + template + friend class ContinuationHolder; + private: using ObsVectT = tbb::concurrent_vector; diff --git a/include/react/detail/Options.h b/include/react/detail/Options.h index 34a9ad21..c591e537 100644 --- a/include/react/detail/Options.h +++ b/include/react/detail/Options.h @@ -11,7 +11,7 @@ /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// -/// CommitFlags +/// ETurnFlags /////////////////////////////////////////////////////////////////////////////////////////////////// enum ETurnFlags { diff --git a/include/react/detail/ReactiveDomain.h b/include/react/detail/ReactiveDomain.h index d93328a7..06c6e5db 100644 --- a/include/react/detail/ReactiveDomain.h +++ b/include/react/detail/ReactiveDomain.h @@ -162,15 +162,16 @@ class DomainBase /////////////////////////////////////////////////////////////////////////////////////////////////// template < - typename F, - typename ... TArgs + typename FIn, + typename ... TArgs, + typename F = std::decay::type, + typename S = std::result_of::type, + typename TOp = REACT_IMPL::FunctionOp ...> > - static auto MakeSignal(F&& func, const SignalT& ... args) - -> SignalT::type> + static auto MakeSignal(FIn&& func, const SignalT& ... args) + -> TempSignal { - using S = typename std::result_of::type; - - return REACT::MakeSignal(std::forward(func), args ...); + return REACT::MakeSignal(std::forward(func), args ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -195,7 +196,7 @@ class DomainBase template static void DoTransaction(F&& func) { - DoTransaction(turnFlags_, std::forward(func)); + DoTransaction(0, std::forward(func)); } template @@ -238,7 +239,7 @@ class DomainBase template static void AddInput(R&& r, V&& v) { - if (! ContinuationHolder_::IsNull()) + if (ContinuationHolder::Get() != nullptr) { addContinuationInput(std::forward(r), std::forward(v)); } @@ -252,48 +253,7 @@ class DomainBase } } - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// Set/Clear continuation - /////////////////////////////////////////////////////////////////////////////////////////////////// - static void SetCurrentContinuation(TurnT& turn) - { - ContinuationHolder_::Set(&turn.continuation_); - } - - static void ClearCurrentContinuation() - { - ContinuationHolder_::Reset(); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// Options - TODO: This sucks - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - static void Set(uint v) { static_assert(false, "Set option not implemented."); } - - template - static bool IsSet(uint v) { static_assert(false, "IsSet option not implemented."); } - - template - static void Unset(uint v) { static_assert(false, "Unset option not implemented."); } - - template - static void Reset() { static_assert(false, "Reset option not implemented."); } - - template <> static void Set(uint v) { turnFlags_ |= v; } - template <> static bool IsSet(uint v) { return (turnFlags_ & v) != 0; } - template <> static void Unset(uint v) { turnFlags_ &= ~v; } - template <> static void Reset() { turnFlags_ = 0; } - private: - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// Transaction input continuation - /////////////////////////////////////////////////////////////////////////////////////////////////// - struct ContinuationHolder_ : public ThreadLocalStaticPtr {}; - - static __declspec(thread) TurnFlagsT turnFlags_; - static std::atomic nextTurnId_; static TurnIdT nextTurnId() @@ -350,7 +310,7 @@ class DomainBase static void addContinuationInput(R&& r, V&& v) { // Copy v - ContinuationHolder_::Get()->Add( + ContinuationHolder::Get()->Add( [&r,v] { addTransactionInput(r, std::move(v)); } ); } @@ -403,9 +363,6 @@ class DomainBase template std::atomic DomainBase::nextTurnId_( 0 ); -template -TurnFlagsT DomainBase::turnFlags_( 0 ); - template typename DomainBase::TransactionState DomainBase::transactionState_; @@ -426,6 +383,28 @@ class DomainInitializer } }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ContinuationHolder +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class ContinuationHolder +{ +public: + using TurnT = typename D::TurnT; + + ContinuationHolder() = delete; + + static void SetTurn(TurnT& turn) { ptr_ = &turn.continuation_; } + static void Clear() { ptr_ = nullptr; } + static ContinuationInput* Get() { return ptr_; } + +private: + static __declspec(thread) ContinuationInput* ptr_; +}; + +template +ContinuationInput* ContinuationHolder::ptr_(nullptr); + /****************************************/ REACT_IMPL_END /***************************************/ #define REACTIVE_DOMAIN(name, ...) \ diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index 933604df..f0e41004 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -17,6 +17,7 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ +// tbb tasks are non-preemptible, thread local flag for each worker namespace current_observer_state_ { static __declspec(thread) bool shouldDetach = false; @@ -87,12 +88,12 @@ class SignalObserverNode : public ObserverNode current_observer_state_::shouldDetach = false; - D::SetCurrentContinuation(turn); + ContinuationHolder::SetTurn(turn); if (auto p = subject_.lock()) func_(p->ValueRef()); - D::ClearCurrentContinuation(); + ContinuationHolder::Clear(); if (current_observer_state_::shouldDetach) turn.QueueForDetach(*this); @@ -162,7 +163,7 @@ class EventObserverNode : public ObserverNode current_observer_state_::shouldDetach = false; - D::SetCurrentContinuation(turn); + ContinuationHolder::SetTurn(turn); if (auto p = subject_.lock()) { @@ -170,7 +171,7 @@ class EventObserverNode : public ObserverNode func_(e); } - D::ClearCurrentContinuation(); + ContinuationHolder::Clear(); if (current_observer_state_::shouldDetach) turn.QueueForDetach(*this); diff --git a/src/test/TransactionTest.h b/src/test/TransactionTest.h index 923fd661..e8e54a1f 100644 --- a/src/test/TransactionTest.h +++ b/src/test/TransactionTest.h @@ -256,29 +256,25 @@ TYPED_TEST_P(TransactionTest, Merging1) // Todo: improve this as it'll fail occasionally shouldSpin = true; std::thread t1([&] { - MyDomain::Set(enable_input_merging); - MyDomain::DoTransaction([&] { + MyDomain::DoTransaction(enable_input_merging, [&] { n1 <<= 2; }); }); std::this_thread::sleep_for(std::chrono::milliseconds(2000)); std::thread t2([&] { - MyDomain::Set(enable_input_merging); - MyDomain::DoTransaction([&] { + MyDomain::DoTransaction(enable_input_merging, [&] { n1 <<= 3; }); }); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::thread t3([&] { - MyDomain::Set(enable_input_merging); - MyDomain::DoTransaction([&] { + MyDomain::DoTransaction(enable_input_merging, [&] { n1 <<= 4; }); }); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::thread t4([&] { - MyDomain::Set(enable_input_merging); - MyDomain::DoTransaction([&] { + MyDomain::DoTransaction(enable_input_merging, [&] { n1 <<= 5; }); From 70a67e89fd9f8ba719197f14aa85422bfc185743 Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 24 Apr 2014 13:41:26 +0200 Subject: [PATCH 003/266] WIP cleanup (build currently broken) --- include/react/EventStream.h | 5 +- include/react/Observer.h | 78 +------- include/react/Signal.h | 3 +- include/react/detail/ContinuationInput.h | 61 ------ include/react/detail/Defs.h | 3 + include/react/detail/EngineBase.h | 6 +- include/react/detail/ObserverBase.h | 117 +++++++++++ include/react/detail/ReactiveDomain.h | 203 ++----------------- include/react/detail/ReactiveInput.h | 237 +++++++++++++++++++++++ include/react/detail/graph/GraphBase.h | 26 +-- include/react/detail/graph/SignalNodes.h | 4 +- project/msvc/CppReact.vcxproj | 3 +- project/msvc/CppReact.vcxproj.filters | 9 +- 13 files changed, 403 insertions(+), 352 deletions(-) delete mode 100644 include/react/detail/ContinuationInput.h create mode 100644 include/react/detail/ObserverBase.h create mode 100644 include/react/detail/ReactiveInput.h diff --git a/include/react/EventStream.h b/include/react/EventStream.h index 5f72a195..8ede6615 100644 --- a/include/react/EventStream.h +++ b/include/react/EventStream.h @@ -15,7 +15,7 @@ #include "Observer.h" #include "react/detail/ReactiveBase.h" -#include "react/detail/ReactiveDomain.h" +#include "react/detail/ReactiveInput.h" #include "react/detail/graph/EventStreamNodes.h" /*****************************************/ REACT_BEGIN /*****************************************/ @@ -108,7 +108,8 @@ class EventSource : public Events template void Emit(V&& v) const { - D::AddInput(*std::static_pointer_cast(ptr_), std::forward(v)); + REACT_IMPL::InputManager::AddInput( + *std::static_pointer_cast(ptr_), std::forward(v)); } template ::value>::type> diff --git a/include/react/Observer.h b/include/react/Observer.h index de847248..5f5dae5e 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -8,11 +8,7 @@ #include "react/detail/Defs.h" -#include -#include - #include "react/detail/IReactiveNode.h" - #include "react/detail/graph/ObserverNodes.h" /*****************************************/ REACT_BEGIN /*****************************************/ @@ -70,78 +66,6 @@ class Observer std::shared_ptr subject_; }; -/******************************************/ REACT_END /******************************************/ - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ObserverRegistry -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ObserverRegistry -{ -public: - using SubjectT = NodeBase; - -private: - class Entry_ - { - public: - Entry_(std::unique_ptr&& obs, SubjectT* aSubject) : - obs_{ std::move(obs) }, - Subject{ aSubject } - {} - - Entry_(Entry_&& other) : - obs_(std::move(other.obs_)), - Subject(other.Subject) - {} - - SubjectT* Subject; - - private: - // Manage lifetime - std::unique_ptr obs_; - }; - -public: - void Register(std::unique_ptr&& obs, SubjectT* subject) - { - auto* raw = obs.get(); - observerMap_.emplace(raw, Entry_(std::move(obs),subject)); - } - - void Unregister(IObserverNode* obs) - { - obs->Detach(); - observerMap_.erase(obs); - } - - void UnregisterFrom(SubjectT* subject) - { - auto it = observerMap_.begin(); - while (it != observerMap_.end()) - { - if (it->second.Subject == subject) - { - it->first->Detach(); - it = observerMap_.erase(it); - } - else - { - ++it; - } - } - } - -private: - std::unordered_map observerMap_; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -/*****************************************/ REACT_BEGIN /*****************************************/ - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Observe /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -160,7 +84,7 @@ auto Observe(const Signal& subject, F&& func) auto* raw = pUnique.get(); - D::Observers().Register(std::move(pUnique), subject.GetPtr().get()); + DomainSpecificData::Observers().Register(std::move(pUnique), subject.GetPtr().get()); return Observer(raw, subject.GetPtr()); } diff --git a/include/react/Signal.h b/include/react/Signal.h index a7f77987..225561ca 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -95,7 +95,8 @@ class VarSignal : public Signal template void Set(V&& newValue) const { - D::AddInput(*std::static_pointer_cast(ptr_), std::forward(newValue)); + REACT_IMPL::InputManager::AddInput( + *std::static_pointer_cast(ptr_), std::forward(newValue)); } template diff --git a/include/react/detail/ContinuationInput.h b/include/react/detail/ContinuationInput.h deleted file mode 100644 index 74672305..00000000 --- a/include/react/detail/ContinuationInput.h +++ /dev/null @@ -1,61 +0,0 @@ - -// 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) - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include -#include - -#include "tbb/concurrent_vector.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ContinuationInput -/////////////////////////////////////////////////////////////////////////////////////////////////// -class ContinuationInput -{ -public: - using InputClosureT = std::function; - using InputVectT = tbb::concurrent_vector; - - // Note: Shouldn't this be generated by default? Apparently it isn't. - inline ContinuationInput& operator=(ContinuationInput&& other) - { - bufferedInputsPtr_ = std::move(other.bufferedInputsPtr_); - return *this; - } - - inline bool IsEmpty() const { return bufferedInputsPtr_ == nullptr; } - - template - void Add(F&& input) - { - std::call_once(bufferedInputsInit_, [this] { - bufferedInputsPtr_.reset(new InputVectT()); - }); - bufferedInputsPtr_->push_back(std::forward(input)); - } - - inline void Execute() - { - if (bufferedInputsPtr_ != nullptr) - { - for (auto f : *bufferedInputsPtr_) - f(); - bufferedInputsPtr_->clear(); - } - } - -private: - std::once_flag bufferedInputsInit_; - std::unique_ptr bufferedInputsPtr_ = nullptr; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file diff --git a/include/react/detail/Defs.h b/include/react/detail/Defs.h index acfe651b..a45a0964 100644 --- a/include/react/detail/Defs.h +++ b/include/react/detail/Defs.h @@ -32,6 +32,9 @@ #define REACT_LOG(...) #endif +// Thread local storage +#define REACT_TLS __declspec(thread) + /*****************************************/ REACT_BEGIN /*****************************************/ // Type aliases diff --git a/include/react/detail/EngineBase.h b/include/react/detail/EngineBase.h index 520ed0b5..30e0715b 100644 --- a/include/react/detail/EngineBase.h +++ b/include/react/detail/EngineBase.h @@ -15,7 +15,7 @@ #include "react/common/Concurrency.h" #include "react/common/Types.h" -#include "react/detail/ContinuationInput.h" +#include "react/detail/ReactiveInput.h" #include "react/detail/IReactiveNode.h" #include "react/detail/IReactiveEngine.h" #include "react/detail/Options.h" @@ -44,8 +44,8 @@ class TurnBase detachedObserversPtr_->push_back(&obs); } - template - friend class DomainBase; + template + friend class InputManager; template friend class ContinuationHolder; diff --git a/include/react/detail/ObserverBase.h b/include/react/detail/ObserverBase.h new file mode 100644 index 00000000..1ecc7f64 --- /dev/null +++ b/include/react/detail/ObserverBase.h @@ -0,0 +1,117 @@ + +// 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) + +#pragma once + +#include "react/detail/Defs.h" + +#include +#include +#include +#include + +#include "IReactiveNode.h" + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ObserverRegistry +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class ObserverRegistry +{ +public: + using SubjectT = NodeBase; + +private: + class Entry + { + public: + Entry(std::unique_ptr&& obs, SubjectT* aSubject) : + obs_{ std::move(obs) }, + Subject{ aSubject } + {} + + Entry(Entry&& other) : + obs_(std::move(other.obs_)), + Subject(other.Subject) + {} + + SubjectT* Subject; + + private: + // Manage lifetime + std::unique_ptr obs_; + }; + +public: + void Register(std::unique_ptr&& obs, SubjectT* subject) + { + auto* raw = obs.get(); + observerMap_.emplace(raw, Entry_(std::move(obs),subject)); + } + + void Unregister(IObserverNode* obs) + { + obs->Detach(); + observerMap_.erase(obs); + } + + void UnregisterFrom(SubjectT* subject) + { + auto it = observerMap_.begin(); + while (it != observerMap_.end()) + { + if (it->second.Subject == subject) + { + it->first->Detach(); + it = observerMap_.erase(it); + } + else + { + ++it; + } + } + } + +private: + std::unordered_map observerMap_; +}; + +template +class DomainSpecificObserverRegistry +{ +public: + DomainSpecificObserverRegistry() = delete; + + static ObserverRegistry& Instance() + { + static ObserverRegistry instance; + return instance; + } +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Observable +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class Observable +{ + void IncObsCount() { obsCount_.fetch_add(1, std::memory_order_relaxed); } + void DecObsCount() { obsCount_.fetch_sub(1, std::memory_order_relaxed); } + uint GetObsCount() const { return obsCount_.load(std::memory_order_relaxed); } + + ~Observable() + { + if (GetObsCount() > 0) + DomainSpecificObserverRegistry::Instance().UnregisterFrom(this); + } + +private: + std::atomic obsCount_ = 0; +}; + +/****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/ReactiveDomain.h b/include/react/detail/ReactiveDomain.h index 06c6e5db..fa13bb81 100644 --- a/include/react/detail/ReactiveDomain.h +++ b/include/react/detail/ReactiveDomain.h @@ -20,7 +20,7 @@ #include "react/Observer.h" #include "react/common/Types.h" -#include "react/detail/ContinuationInput.h" +#include "react/detail/ReactiveInput.h" #include "react/detail/Options.h" #include "react/detail/Traits.h" @@ -90,26 +90,6 @@ class DomainBase using ReactiveLoopT = ReactiveLoop; - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// ObserverRegistry - /////////////////////////////////////////////////////////////////////////////////////////////////// - static ObserverRegistry& Observers() - { - static ObserverRegistry registry; - return registry; - } - -#ifdef REACT_ENABLE_LOGGING - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// Log - /////////////////////////////////////////////////////////////////////////////////////////////////// - static EventLog& Log() - { - static EventLog log; - return log; - } -#endif //REACT_ENABLE_LOGGING - /////////////////////////////////////////////////////////////////////////////////////////////////// /// MakeVar /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -196,187 +176,40 @@ class DomainBase template static void DoTransaction(F&& func) { - DoTransaction(0, std::forward(func)); + InputManager::DoTransaction(0, std::forward(func)); } template static void DoTransaction(TurnFlagsT flags, F&& func) { - // Attempt to add input to another turn. - // If successful, blocks until other turn is done and returns. - if (Engine::TryMerge(std::forward(func))) - return; - - bool shouldPropagate = false; - - auto turn = makeTurn(flags); - - // Phase 1 - Input admission - transactionState_.Active = true; - Engine::OnTurnAdmissionStart(turn); - func(); - Engine::OnTurnAdmissionEnd(turn); - transactionState_.Active = false; - - // Phase 2 - Apply input node changes - for (auto* p : transactionState_.Inputs) - if (p->ApplyInput(&turn)) - shouldPropagate = true; - transactionState_.Inputs.clear(); - - // Phase 3 - Propagate changes - if (shouldPropagate) - Engine::OnTurnPropagate(turn); - - Engine::OnTurnEnd(turn); - - postProcessTurn(turn); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// AddInput - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - static void AddInput(R&& r, V&& v) - { - if (ContinuationHolder::Get() != nullptr) - { - addContinuationInput(std::forward(r), std::forward(v)); - } - else if (transactionState_.Active) - { - addTransactionInput(std::forward(r), std::forward(v)); - } - else - { - addSimpleInput(std::forward(r), std::forward(v)); - } + InputManager::DoTransaction(flags, std::forward(func)); } -private: - static std::atomic nextTurnId_; - - static TurnIdT nextTurnId() - { - auto curId = nextTurnId_.fetch_add(1, std::memory_order_relaxed); - - if (curId == INT_MAX) - nextTurnId_.fetch_sub(INT_MAX); - - return curId; - } - - struct TransactionState - { - bool Active = false; - std::vector Inputs; - }; - - static TransactionState transactionState_; - - static TurnT makeTurn(TurnFlagsT flags) - { - return TurnT(nextTurnId(), flags); - } - - // Create a turn with a single input - template - static void addSimpleInput(R&& r, V&& v) - { - auto turn = makeTurn(0); - - Engine::OnTurnAdmissionStart(turn); - r.AddInput(std::forward(v)); - Engine::OnTurnAdmissionEnd(turn); - - if (r.ApplyInput(&turn)) - Engine::OnTurnPropagate(turn); - - Engine::OnTurnEnd(turn); - - postProcessTurn(turn); - } - - // This input is part of an active transaction - template - static void addTransactionInput(R&& r, V&& v) - { - r.AddInput(std::forward(v)); - transactionState_.Inputs.push_back(&r); - } - - // Input happened during a turn - buffer in continuation - template - static void addContinuationInput(R&& r, V&& v) - { - // Copy v - ContinuationHolder::Get()->Add( - [&r,v] { addTransactionInput(r, std::move(v)); } - ); - } - - static void postProcessTurn(TurnT& turn) - { - turn.detachObservers(Observers()); - - // Steal continuation from current turn - if (! turn.continuation_.IsEmpty()) - processContinuations(std::move(turn.continuation_), 0); - } - - static void processContinuations(ContinuationInput&& cont, TurnFlagsT flags) +#ifdef REACT_ENABLE_LOGGING + /////////////////////////////////////////////////////////////////////////////////////////////// + /// Log + /////////////////////////////////////////////////////////////////////////////////////////////// + static EventLog& Log() { - // No merging for continuations - flags &= ~enable_input_merging; - - while (true) - { - bool shouldPropagate = false; - auto turn = makeTurn(flags); - - transactionState_.Active = true; - Engine::OnTurnAdmissionStart(turn); - cont.Execute(); - Engine::OnTurnAdmissionEnd(turn); - transactionState_.Active = false; - - for (auto* p : transactionState_.Inputs) - if (p->ApplyInput(&turn)) - shouldPropagate = true; - transactionState_.Inputs.clear(); - - if (shouldPropagate) - Engine::OnTurnPropagate(turn); - - Engine::OnTurnEnd(turn); - - turn.detachObservers(Observers()); - - if (turn.continuation_.IsEmpty()) - break; - - cont = std::move(turn.continuation_); - } + static ObserverRegistry instance; + return instance; } +#endif //REACT_ENABLE_LOGGING }; -template -std::atomic DomainBase::nextTurnId_( 0 ); - -template -typename DomainBase::TransactionState DomainBase::transactionState_; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Ensure singletons are created immediately after domain declaration (TODO hax) -/////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///// Ensure singletons are created immediately after domain declaration (TODO hax) +///////////////////////////////////////////////////////////////////////////////////////////////////// template class DomainInitializer { public: DomainInitializer() { + //DomainSpecificData::Observers(); + #ifdef REACT_ENABLE_LOGGING - D::Log(); + DomainSpecificData::Log(); #endif //REACT_ENABLE_LOGGING typename D::Engine::Engine(); @@ -399,7 +232,7 @@ class ContinuationHolder static ContinuationInput* Get() { return ptr_; } private: - static __declspec(thread) ContinuationInput* ptr_; + static REACT_TLS ContinuationInput* ptr_; }; template diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h new file mode 100644 index 00000000..3b7d1122 --- /dev/null +++ b/include/react/detail/ReactiveInput.h @@ -0,0 +1,237 @@ + +// 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) + +#pragma once + +#include "react/detail/Defs.h" + +#include +#include +#include + +#include "tbb/concurrent_vector.h" + +#include "react/detail/IReactiveNode.h." + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ContinuationInput +/////////////////////////////////////////////////////////////////////////////////////////////////// +class ContinuationInput +{ +public: + using InputClosureT = std::function; + using InputVectT = tbb::concurrent_vector; + + // Note: Shouldn't this be generated by default? Apparently it isn't. + inline ContinuationInput& operator=(ContinuationInput&& other) + { + bufferedInputsPtr_ = std::move(other.bufferedInputsPtr_); + return *this; + } + + inline bool IsEmpty() const { return bufferedInputsPtr_ == nullptr; } + + template + void Add(F&& input) + { + std::call_once(bufferedInputsInit_, [this] { + bufferedInputsPtr_.reset(new InputVectT()); + }); + bufferedInputsPtr_->push_back(std::forward(input)); + } + + inline void Execute() + { + if (bufferedInputsPtr_ != nullptr) + { + for (auto f : *bufferedInputsPtr_) + f(); + bufferedInputsPtr_->clear(); + } + } + +private: + std::once_flag bufferedInputsInit_; + std::unique_ptr bufferedInputsPtr_ = nullptr; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// InputManager +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class InputManager +{ +public: + using TurnT = typename D::TurnT; + using Engine = typename D::Engine; + + template + static void DoTransaction(TurnFlagsT flags, F&& func) + { + // Attempt to add input to another turn. + // If successful, blocks until other turn is done and returns. + if (Engine::TryMerge(std::forward(func))) + return; + + bool shouldPropagate = false; + + auto turn = makeTurn(flags); + + // Phase 1 - Input admission + transactionState_.Active = true; + Engine::OnTurnAdmissionStart(turn); + func(); + Engine::OnTurnAdmissionEnd(turn); + transactionState_.Active = false; + + // Phase 2 - Apply input node changes + for (auto* p : transactionState_.Inputs) + if (p->ApplyInput(&turn)) + shouldPropagate = true; + transactionState_.Inputs.clear(); + + // Phase 3 - Propagate changes + if (shouldPropagate) + Engine::OnTurnPropagate(turn); + + Engine::OnTurnEnd(turn); + + postProcessTurn(turn); + } + + template + static void AddInput(R&& r, V&& v) + { + if (ContinuationHolder::Get() != nullptr) + { + addContinuationInput(std::forward(r), std::forward(v)); + } + else if (transactionState_.Active) + { + addTransactionInput(std::forward(r), std::forward(v)); + } + else + { + addSimpleInput(std::forward(r), std::forward(v)); + } + } + +private: + static std::atomic nextTurnId_; + + static TurnIdT nextTurnId() + { + auto curId = nextTurnId_.fetch_add(1, std::memory_order_relaxed); + + if (curId == INT_MAX) + nextTurnId_.fetch_sub(INT_MAX); + + return curId; + } + + struct TransactionState + { + bool Active = false; + std::vector Inputs; + }; + + static TransactionState transactionState_; + + static TurnT makeTurn(TurnFlagsT flags) + { + return TurnT(nextTurnId(), flags); + } + + // Create a turn with a single input + template + static void addSimpleInput(R&& r, V&& v) + { + auto turn = makeTurn(0); + + Engine::OnTurnAdmissionStart(turn); + r.AddInput(std::forward(v)); + Engine::OnTurnAdmissionEnd(turn); + + if (r.ApplyInput(&turn)) + Engine::OnTurnPropagate(turn); + + Engine::OnTurnEnd(turn); + + postProcessTurn(turn); + } + + // This input is part of an active transaction + template + static void addTransactionInput(R&& r, V&& v) + { + r.AddInput(std::forward(v)); + transactionState_.Inputs.push_back(&r); + } + + // Input happened during a turn - buffer in continuation + template + static void addContinuationInput(R&& r, V&& v) + { + // Copy v + ContinuationHolder::Get()->Add( + [&r,v] { addTransactionInput(r, std::move(v)); } + ); + } + + static void postProcessTurn(TurnT& turn) + { + turn.detachObservers(Observers()); + + // Steal continuation from current turn + if (! turn.continuation_.IsEmpty()) + processContinuations(std::move(turn.continuation_), 0); + } + + static void processContinuations(ContinuationInput&& cont, TurnFlagsT flags) + { + // No merging for continuations + flags &= ~enable_input_merging; + + while (true) + { + bool shouldPropagate = false; + auto turn = makeTurn(flags); + + transactionState_.Active = true; + Engine::OnTurnAdmissionStart(turn); + cont.Execute(); + Engine::OnTurnAdmissionEnd(turn); + transactionState_.Active = false; + + for (auto* p : transactionState_.Inputs) + if (p->ApplyInput(&turn)) + shouldPropagate = true; + transactionState_.Inputs.clear(); + + if (shouldPropagate) + Engine::OnTurnPropagate(turn); + + Engine::OnTurnEnd(turn); + + turn.detachObservers(Observers()); + + if (turn.continuation_.IsEmpty()) + break; + + cont = std::move(turn.continuation_); + } + } +}; + +template +std::atomic InputManager::nextTurnId_( 0 ); + +template +typename InputManager::TransactionState InputManager::transactionState_; + +/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index 23d23a6f..52ba066e 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -25,11 +25,11 @@ /// NodeUpdateTimer /////////////////////////////////////////////////////////////////////////////////////////////////// template -class NodeUpdateTimer; +class NodeUpdateTimerBase; // Defines guard that does nothing template -class NodeUpdateTimer +class NodeUpdateTimerBase { public: template @@ -40,7 +40,7 @@ class NodeUpdateTimer }; template -class NodeUpdateTimer +class NodeUpdateTimerBase { public: template @@ -77,6 +77,10 @@ class NodeUpdateTimer bool shouldMeasure_ = true; }; +template +class NodeUpdateTimer : + public NodeUpdateTimerBase::value> {}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// NodeBase /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -118,28 +122,14 @@ template typename V > class ReactiveNode : - public NodeBase, - public NodeUpdateTimer::value> + public NodeBase { public: using PtrT = std::shared_ptr; ReactiveNode() = default; - ~ReactiveNode() - { - if (GetObsCount() > 0) - D::Observers().UnregisterFrom(this); - } - virtual const char* GetNodeType() const override { return "ReactiveNode"; } - - void IncObsCount() { obsCount_.fetch_add(1, std::memory_order_relaxed); } - void DecObsCount() { obsCount_.fetch_sub(1, std::memory_order_relaxed); } - uint GetObsCount() const { return obsCount_.load(std::memory_order_relaxed); } - -private: - std::atomic obsCount_ = 0; }; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index 60215e23..bbcc60c6 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -201,7 +201,9 @@ template typename S, typename TOp > -class SignalOpNode : public SignalNode +class SignalOpNode : + public SignalNode, + public NodeUpdateTimer { public: template diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index 3f2dbe8a..f3c0a8dd 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -77,7 +77,8 @@ - + + diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index 17d8c18f..64a3dc38 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -84,9 +84,6 @@ Header Files\engine - - Header Files\detail - Header Files\detail @@ -141,6 +138,12 @@ Header Files + + Header Files\detail + + + Header Files\detail + From 142ff0a88af562fcb43915855e312088affb310c Mon Sep 17 00:00:00 2001 From: schlangster Date: Sat, 26 Apr 2014 01:24:11 +0200 Subject: [PATCH 004/266] Started refactoring of ReactiveDomain and observer registry. --- include/react/EventStream.h | 1 + include/react/Observer.h | 50 ++++++++++------------ include/react/Signal.h | 1 + include/react/detail/ObserverBase.h | 37 +++++++++++----- include/react/detail/ReactiveBase.h | 1 + include/react/detail/ReactiveInput.h | 4 +- include/react/detail/graph/GraphBase.h | 4 +- include/react/detail/graph/ObserverNodes.h | 2 +- 8 files changed, 59 insertions(+), 41 deletions(-) diff --git a/include/react/EventStream.h b/include/react/EventStream.h index 8ede6615..6d5e136a 100644 --- a/include/react/EventStream.h +++ b/include/react/EventStream.h @@ -15,6 +15,7 @@ #include "Observer.h" #include "react/detail/ReactiveBase.h" +#include "react/detail/ReactiveDomain.h" #include "react/detail/ReactiveInput.h" #include "react/detail/graph/EventStreamNodes.h" diff --git a/include/react/Observer.h b/include/react/Observer.h index 5f5dae5e..653ac0d8 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -9,6 +9,7 @@ #include "react/detail/Defs.h" #include "react/detail/IReactiveNode.h" +#include "react/detail/ObserverBase.h" #include "react/detail/graph/ObserverNodes.h" /*****************************************/ REACT_BEGIN /*****************************************/ @@ -32,7 +33,7 @@ class Observer { public: using SubjectT = REACT_IMPL::NodeBase; - using ObserverNodeT = REACT_IMPL::ObserverNode; + using ObserverNodeT = REACT_IMPL::IObserverNode; Observer() : ptr_{ nullptr }, @@ -54,7 +55,7 @@ class Observer if (ptr_ == nullptr) return false; - D::Observers().Unregister(ptr_); + REACT_IMPL::DomainSpecificObserverRegistry::Instance().Unregister(ptr_); return true; } @@ -72,19 +73,17 @@ class Observer template < typename D, - typename F, + typename FIn, typename TArg > -auto Observe(const Signal& subject, F&& func) +auto Observe(const Signal& subject, FIn&& func) -> Observer { - std::unique_ptr< REACT_IMPL::ObserverNode> pUnique( - new REACT_IMPL::SignalObserverNode( - subject.GetPtr(), std::forward(func))); + using F = std::decay::type; + using TNode = REACT_IMPL::SignalObserverNode; - auto* raw = pUnique.get(); - - DomainSpecificData::Observers().Register(std::move(pUnique), subject.GetPtr().get()); + auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). + template Register(subject, std::forward(func)); return Observer(raw, subject.GetPtr()); } @@ -92,21 +91,19 @@ auto Observe(const Signal& subject, F&& func) template < typename D, - typename F, + typename FIn, typename TArg, typename = std::enable_if< ! std::is_same::value>::type > -auto Observe(const Events& subject, F&& func) +auto Observe(const Events& subject, FIn&& func) -> Observer { - std::unique_ptr< REACT_IMPL::ObserverNode> pUnique( - new REACT_IMPL::EventObserverNode( - subject.GetPtr(), std::forward(func))); - - auto* raw = pUnique.get(); + using F = std::decay::type; + using TNode = REACT_IMPL::EventObserverNode; - D::Observers().Register(std::move(pUnique), subject.GetPtr().get()); + auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). + template Register(subject, std::forward(func)); return Observer(raw, subject.GetPtr()); } @@ -114,19 +111,17 @@ auto Observe(const Events& subject, F&& func) template < typename D, - typename F + typename FIn > -auto Observe(const Events& subject, F&& func) +auto Observe(const Events& subject, FIn&& func) -> Observer { - auto f = [func] (EventToken _) { func(); }; - std::unique_ptr< REACT_IMPL::ObserverNode> pUnique( - new REACT_IMPL::EventObserverNode( - subject.GetPtr(), std::move(f))); + auto wrapper = [func] (EventToken _) { func(); }; - auto* raw = pUnique.get(); + using TNode = REACT_IMPL::EventObserverNode; - D::Observers().Register(std::move(pUnique), subject.GetPtr().get()); + auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). + template Register(subject, std::move(wrapper)); return Observer(raw, subject.GetPtr()); } @@ -142,7 +137,8 @@ template > void DetachAllObservers(const Reactive>& subject) { - D::Observers().UnregisterFrom(subject.GetPtr().get()); + REACT_IMPL::DomainSpecificObserverRegistry::Instance().UnregisterFrom( + subject.GetPtr().get()); } /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/Signal.h b/include/react/Signal.h index 225561ca..cb899d5c 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -17,6 +17,7 @@ #include "react/common/Util.h" #include "react/detail/ReactiveBase.h" #include "react/detail/ReactiveDomain.h" +#include "react/detail/ReactiveInput.h" #include "react/detail/graph/SignalNodes.h" /*****************************************/ REACT_BEGIN /*****************************************/ diff --git a/include/react/detail/ObserverBase.h b/include/react/detail/ObserverBase.h index 1ecc7f64..ffa67d48 100644 --- a/include/react/detail/ObserverBase.h +++ b/include/react/detail/ObserverBase.h @@ -17,6 +17,9 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ +template +class Observable; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// ObserverRegistry /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -24,15 +27,15 @@ template class ObserverRegistry { public: - using SubjectT = NodeBase; + using SubjectT = Observable; private: class Entry { public: - Entry(std::unique_ptr&& obs, SubjectT* aSubject) : + Entry(std::unique_ptr&& obs, const SubjectT* subject) : obs_{ std::move(obs) }, - Subject{ aSubject } + Subject{ subject } {} Entry(Entry&& other) : @@ -40,18 +43,31 @@ class ObserverRegistry Subject(other.Subject) {} - SubjectT* Subject; + const SubjectT* Subject; private: // Manage lifetime - std::unique_ptr obs_; + std::unique_ptr obs_; }; public: - void Register(std::unique_ptr&& obs, SubjectT* subject) + template + < + typename TNode, + typename TSubject, + typename F + > + IObserverNode* Register(const TSubject& subject, F&& func) { - auto* raw = obs.get(); - observerMap_.emplace(raw, Entry_(std::move(obs),subject)); + std::unique_ptr ptr + { + new TNode(subject.GetPtr(), std::forward(func)) + }; + + auto* raw = ptr.get(); + observerMap_.emplace(raw, Entry(std::move(ptr), subject.GetPtr().get() )); + + return raw; } void Unregister(IObserverNode* obs) @@ -60,7 +76,7 @@ class ObserverRegistry observerMap_.erase(obs); } - void UnregisterFrom(SubjectT* subject) + void UnregisterFrom(const SubjectT* subject) { auto it = observerMap_.begin(); while (it != observerMap_.end()) @@ -78,7 +94,7 @@ class ObserverRegistry } private: - std::unordered_map observerMap_; + std::unordered_map observerMap_; }; template @@ -100,6 +116,7 @@ class DomainSpecificObserverRegistry template class Observable { +public: void IncObsCount() { obsCount_.fetch_add(1, std::memory_order_relaxed); } void DecObsCount() { obsCount_.fetch_sub(1, std::memory_order_relaxed); } uint GetObsCount() const { return obsCount_.load(std::memory_order_relaxed); } diff --git a/include/react/detail/ReactiveBase.h b/include/react/detail/ReactiveBase.h index 0a1d220d..6ce67000 100644 --- a/include/react/detail/ReactiveBase.h +++ b/include/react/detail/ReactiveBase.h @@ -10,6 +10,7 @@ #include #include +#include /*****************************************/ REACT_BEGIN /*****************************************/ diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index 3b7d1122..2fd3ab24 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -185,7 +185,7 @@ class InputManager static void postProcessTurn(TurnT& turn) { - turn.detachObservers(Observers()); + turn.detachObservers(DomainSpecificObserverRegistry::Instance()); // Steal continuation from current turn if (! turn.continuation_.IsEmpty()) @@ -218,7 +218,7 @@ class InputManager Engine::OnTurnEnd(turn); - turn.detachObservers(Observers()); + turn.detachObservers(DomainSpecificObserverRegistry::Instance()); if (turn.continuation_.IsEmpty()) break; diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index 52ba066e..1a70c2bd 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -18,6 +18,7 @@ #include "react/common/Util.h" #include "react/common/Types.h" #include "react/detail/IReactiveEngine.h" +#include "react/detail/ObserverBase.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -87,7 +88,8 @@ class NodeUpdateTimer : template class NodeBase : public std::enable_shared_from_this>, - public D::Policy::Engine::NodeT + public D::Policy::Engine::NodeT, + public Observable { public: using PtrT = std::shared_ptr; diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index f0e41004..4d3d10ba 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -35,7 +35,7 @@ class ObserverNode : using PtrT = std::shared_ptr; ObserverNode() : - ReactiveNode() + ReactiveNode() { } From 5a109ea439298f784f3c54a0c0e61e12f593141a Mon Sep 17 00:00:00 2001 From: schlangster Date: Sat, 26 Apr 2014 11:02:51 +0200 Subject: [PATCH 005/266] Removed deprectated classes. --- include/react/common/GraphData.h | 449 -------------------------- include/react/common/Singleton.h | 32 -- project/msvc/CppReact.vcxproj | 2 - project/msvc/CppReact.vcxproj.filters | 6 - 4 files changed, 489 deletions(-) delete mode 100644 include/react/common/GraphData.h delete mode 100644 include/react/common/Singleton.h diff --git a/include/react/common/GraphData.h b/include/react/common/GraphData.h deleted file mode 100644 index d7077161..00000000 --- a/include/react/common/GraphData.h +++ /dev/null @@ -1,449 +0,0 @@ - -// 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) - -#pragma once - -#include -#include - -#include "tbb/parallel_for.h" -#include "tbb/blocked_range.h" - -#include "Types.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// GraphData -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename TNode, - typename TChunk, - int INIT_NODE_COUNT -> -class GraphData -{ -public: - enum - { - kNodesPerChunk = sizeof(TChunk) * 8, - kOffsetMask = kNodesPerChunk - 1 - }; - - static_assert(kNodesPerChunk != 0, "kNodesPerChunk is 0"); - static_assert((INIT_NODE_COUNT % kNodesPerChunk) == 0, "INIT_NODE_COUNT must be multiple of kNodesPerChunk"); - -private: - class Chunk - { - public: - Chunk() : data_(0) {} - - inline bool IsSet(uint offset) const { return (data_ >> offset) & 1; } - inline void Set(uint offset) { data_ |= (((TChunk)1) << offset); } - inline void Clear(uint offset) { data_ &= ~(((TChunk)1) << offset); } - - inline void Reset() { data_ = 0; } - - Chunk& operator=(const TChunk data) { data_ = data; return *this; } - operator TChunk() const { return data_; } - - void Dump() const - { - TChunk t = data_; - for (int i=0; i>= 1; - } - printf(" "); - } - - private: - TChunk data_; - }; - -public: - - enum class EEntryType - { - Free = 0, - Node, - Buffer - }; - - class Entry - { - public: - Entry() : - Node(nullptr), - type_(EEntryType::Free) - { - } - - TNode* Node; - - private: - EEntryType type_; - - friend class GraphData; - }; - - class Iterator - { - public: - Iterator(const Chunk* curChunk) : - curIndex_(0), - curOffset_(0), - curChunk_(curChunk) - { - } - - Iterator(const Chunk* curChunk, uint index, uint offset) : - curIndex_(index), - curOffset_(offset), - curChunk_(curChunk) - { - } - - inline bool operator<(const Iterator& other) const - { - return (curChunk_ < other.curChunk_) || - (curChunk_ == other.curChunk_ && curOffset_ < other.curOffset_); - } - - inline Iterator& operator+=(const Iterator& other) - { - curChunk_ += other.curChunk_; - curOffset_+= other.curOffset_; - curIndex_ += other.curIndex_; - - if (curOffset_ >= kNodesPerChunk) - { - curOffset_ -= kNodesPerChunk; - ++curChunk_; - } - - return *this; - } - - inline Iterator operator+(const Iterator& other) - { - Iterator result; - result += other; - return result; - } - - inline Iterator& operator++() - { - curIndex_++; - - if (curOffset_ < kNodesPerChunk - 1) - { - curOffset_++; - } - else - { - curOffset_ = 0; - ++curChunk_; - } - return *this; - } - - inline Iterator operator++(int) - { - Iterator tmp(*this); - operator++(); - return tmp; - } - - inline bool IsReachable() const - { - return curChunk_->IsSet(curOffset_); - } - - inline uint Index() const - { - return curIndex_; - } - - private: - uint curIndex_; - uint curOffset_; - - const Chunk* curChunk_; - }; - - GraphData() : - entryCapacity_(INIT_NODE_COUNT), - chunksPerEntry_(entryCapacity_ / kNodesPerChunk), - chunks_(new Chunk[entryCapacity_ * chunksPerEntry_]), - nextIndex_(0), - entries_(entryCapacity_) - { - } - - ~GraphData() - { - delete[] chunks_; - } - - uint EntryCapacity() const - { - return entryCapacity_; - } - - bool IsNode(uint index) const - { - return entries_[index].type_ == EEntryType::Node; - } - - bool IsBuffer(uint index) const - { - return entries_[index].type_ == EEntryType::Buffer; - } - - inline bool IsReachable(uint from, uint to) const - { - uint vIndex = (from * entryCapacity_ + to); - return chunks_[vIndex / kNodesPerChunk].IsSet(vIndex & kOffsetMask); - } - - inline void SetReachable(uint from, uint to) - { - uint vIndex = (from * entryCapacity_ + to); - chunks_[vIndex / kNodesPerChunk].Set(vIndex & kOffsetMask); - } - - inline void ClearReachable(uint from, uint to) - { - uint vIndex = (from * entryCapacity_ + to); - chunks_[vIndex / kNodesPerChunk].Clear(vIndex & kOffsetMask); - } - - inline Entry& GetEntry(uint index) - { - return entries_[index]; - } - - uint InitNode(TNode& node) - { - // New index - uint index = requestIndex(); - - entries_[index].type_ = EEntryType::Node; - entries_[index].Node = &node; - - SetReachable(index,index); - - return index; - } - - uint InitBuffer() - { - int index = requestIndex(); - - entries_[index].type_ = EEntryType::Buffer; - - return index; - } - - void ReleaseNode(uint index) - { - clearIndex(index); - - entries_[index].Node = nullptr; - } - - void ReleaseBuffer(uint index) - { - clearIndex(index); - } - - // Single-threaded - template - void BufferOp(uint dstIndex, uint srcIndex, const F& op) - { - auto* srcStart = &chunks_[srcIndex * chunksPerEntry_]; - auto* srcEnd = srcStart + chunksPerEntry_; - - auto* dst = &chunks_[dstIndex * chunksPerEntry_]; - - while (srcStart != srcEnd) - { - *dst = op(*dst, *srcStart); - srcStart++; dst++; - } - } - - // Parallel - template - void ParallelBufferOp(uint dstIndex, uint srcIndex, uint grainSize, const F& op) - { - Chunk* srcStart = &chunks_[srcIndex * chunksPerEntry_]; - Chunk* srcEnd = srcStart + chunksPerEntry_; - - const int offset = chunksPerEntry_ * (dstIndex - srcIndex); - - tbb::parallel_for(tbb::blocked_range(srcStart, srcEnd, grainSize), - [&] (const tbb::blocked_range& range) - { - const Chunk* src = range.begin(); - Chunk* dst = range.begin() + offset; - - while (src != range.end()) - { - *dst = op(*dst, *src); - src++; dst++; - } - } - ); - } - - typedef std::pair Range; - - Range ReachableRange(uint nodeIndex) const - { - return std::make_pair( - Iterator(&chunks_[chunksPerEntry_ * nodeIndex]), - Iterator(&chunks_[chunksPerEntry_ * (nodeIndex+1)])); - } - - Range ReachableRange(uint nodeIndex, uint startIndex, uint endIndex) const - { - Chunk* startChunk = &chunks_[chunksPerEntry_ * nodeIndex + (startIndex / kNodesPerChunk)]; - uint startOffset = startIndex & kOffsetMask; - - Chunk* endChunk = &chunks_[chunksPerEntry_ * nodeIndex + (endIndex / kNodesPerChunk)]; - uint endOffset = endIndex & kOffsetMask; - - return std::make_pair( - Iterator(startChunk, startIndex, startOffset), - Iterator(endChunk, endIndex, endOffset)); - } - - void Dump() - { - printf("Dump graph:\n"); - for (uint i=0; i EntryVector; - - uint entryCapacity_; - uint chunksPerEntry_; - - uint nextIndex_; - - EntryVector entries_; - Chunk* chunks_; - - uint requestIndex() - { - int cycleMarker = (nextIndex_ - 1) % entryCapacity_; - if (cycleMarker < 0) - cycleMarker += entryCapacity_; - - auto it = entries_.begin() + nextIndex_; - - while (cycleMarker != nextIndex_) - { - if (it->type_ == EEntryType::Free) - return nextIndex_; - - if (it != entries_.end() - 1) - { - nextIndex_++; - it++; - } - else - { - nextIndex_ = 0; - it = entries_.begin(); - } - } - - // All in use => grow - nextIndex_ = entryCapacity_; - grow(); - - entries_.resize(entryCapacity_); - - return nextIndex_; - } - - void clearIndex(uint index) - { - // Entry - entries_[index].type_ = EEntryType::Free; - - // Matrix row - { - Chunk* start = &chunks_[index * chunksPerEntry_]; - Chunk* end = start + chunksPerEntry_; - while (start != end) - { - start->Reset(); - start++; - } - } - - // Matrix col - { - const uint offset = index & kOffsetMask; - - Chunk* p = &chunks_[index / kNodesPerChunk]; - for (uint i=0; iClear(offset); - p += chunksPerEntry_; - } - } - } - - void grow() - { - const uint newEntryCount = entryCapacity_ << 1; - const uint newChunksPerEntry = newEntryCount / kNodesPerChunk; - const uint dataCountDelta = newChunksPerEntry - chunksPerEntry_; - - Chunk* newChunks = new Chunk[newEntryCount * newChunksPerEntry]; - - uint insertIndex = 0; - - for (uint i=0; i -class BasicSingleton -{ -public: - static T& Instance() - { - static T instance; - return instance; - } - -protected: - BasicSingleton() {} - BasicSingleton(const BasicSingleton& other) {} - BasicSingleton(BasicSingleton&& other) {} -}; - -/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index f3c0a8dd..eb036f43 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -98,8 +98,6 @@ - - diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index 64a3dc38..23d30d88 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -39,12 +39,6 @@ Header Files\common - - Header Files\common - - - Header Files\common - Header Files\common From 907268fac29ba49250d32058b7a9db5079d8f42e Mon Sep 17 00:00:00 2001 From: schlangster Date: Sat, 26 Apr 2014 11:03:36 +0200 Subject: [PATCH 006/266] Cleanup. --- include/react/EventStream.h | 12 ++++-------- include/react/Reactor.h | 3 +-- include/react/detail/EngineBase.h | 9 +++------ include/react/detail/ReactiveBase.h | 9 +++------ include/react/detail/graph/ConversionNodes.h | 3 +-- include/react/detail/graph/ObserverNodes.h | 3 +-- include/react/detail/graph/SignalNodes.h | 6 ++---- 7 files changed, 15 insertions(+), 30 deletions(-) diff --git a/include/react/EventStream.h b/include/react/EventStream.h index 6d5e136a..39b55543 100644 --- a/include/react/EventStream.h +++ b/include/react/EventStream.h @@ -41,13 +41,11 @@ class Events : public Reactive> Events() : Reactive() - { - } + {} explicit Events(const std::shared_ptr& ptr) : Reactive(ptr) - { - } + {} template Events Filter(F&& f) @@ -98,13 +96,11 @@ class EventSource : public Events public: EventSource() : Events() - { - } + {} explicit EventSource(const std::shared_ptr& ptr) : Events(ptr) - { - } + {} template void Emit(V&& v) const diff --git a/include/react/Reactor.h b/include/react/Reactor.h index c756f715..c3d41e48 100644 --- a/include/react/Reactor.h +++ b/include/react/Reactor.h @@ -35,8 +35,7 @@ class ReactiveLoop public: Context(NodeT& node) : node_{ node } - { - } + {} template E& Await(const Events& evn) diff --git a/include/react/detail/EngineBase.h b/include/react/detail/EngineBase.h index 30e0715b..0c963e0d 100644 --- a/include/react/detail/EngineBase.h +++ b/include/react/detail/EngineBase.h @@ -31,8 +31,7 @@ class TurnBase public: inline TurnBase(TurnIdT id, TurnFlagsT flags) : id_{ id } - { - } + {} inline TurnIdT Id() const { return id_; } @@ -79,8 +78,7 @@ class TurnQueueManager public: explicit QueueEntry(TurnFlagsT flags) : isMergeable_{ (flags & enable_input_merging) != 0 } - { - } + {} inline void Append(QueueEntry& tr) { @@ -198,8 +196,7 @@ class DefaultQueueableTurn : DefaultQueueableTurn(TurnIdT id, TurnFlagsT flags) : TTurnBase(id, flags), TurnQueueManager::QueueEntry(flags) - { - } + {} }; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/ReactiveBase.h b/include/react/detail/ReactiveBase.h index 6ce67000..08ad7144 100644 --- a/include/react/detail/ReactiveBase.h +++ b/include/react/detail/ReactiveBase.h @@ -26,18 +26,15 @@ class Reactive Reactive() : ptr_{ nullptr } - { - } + {} explicit Reactive(const std::shared_ptr& ptr) : ptr_{ ptr } - { - } + {} explicit Reactive(std::shared_ptr&& ptr) : ptr_{ std::move(ptr) } - { - } + {} const std::shared_ptr& GetPtr() const { diff --git a/include/react/detail/graph/ConversionNodes.h b/include/react/detail/graph/ConversionNodes.h index 4f7868cc..da5d0a66 100644 --- a/include/react/detail/graph/ConversionNodes.h +++ b/include/react/detail/graph/ConversionNodes.h @@ -35,8 +35,7 @@ class FoldBaseNode : public SignalNode FoldBaseNode(T&& init, const EventStreamNodePtr& events) : SignalNode(std::forward(init)), events_{ events } - { - } + {} virtual void Tick(void* turnPtr) override { diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index 4d3d10ba..b92b8b3a 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -36,8 +36,7 @@ class ObserverNode : ObserverNode() : ReactiveNode() - { - } + {} virtual const char* GetNodeType() const { return "ObserverNode"; } virtual bool IsOutputNode() const { return true; } diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index bbcc60c6..768c9f89 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -39,15 +39,13 @@ class SignalNode : public ReactiveNode SignalNode() : ReactiveNode() - { - } + {} template SignalNode(T&& value) : ReactiveNode(), value_{ std::forward(value) } - { - } + {} virtual const char* GetNodeType() const override { return "SignalNode"; } From bf989c54e2927582c7777151446c7035315048a4 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 28 Apr 2014 12:51:43 +0200 Subject: [PATCH 007/266] Comprehensive refactoring + cleanup. --- include/react/Algorithm.h | 23 +- include/react/{EventStream.h => Event.h} | 153 +++++++--- include/react/Observer.h | 116 ++----- include/react/ReactiveObject.h | 11 +- include/react/Reactor.h | 3 +- include/react/Signal.h | 146 +++++---- include/react/common/Concurrency.h | 23 +- include/react/common/Containers.h | 1 + include/react/common/Types.h | 3 +- include/react/common/Util.h | 27 -- include/react/detail/Defs.h | 2 + include/react/detail/EngineBase.h | 12 +- include/react/detail/EventBase.h | 46 +++ include/react/detail/IReactiveEngine.h | 2 + include/react/detail/IReactiveNode.h | 14 +- include/react/detail/ObserverBase.h | 61 ++-- include/react/detail/ReactiveBase.h | 284 ++++++++++++++++-- include/react/detail/ReactiveDomain.h | 245 --------------- include/react/detail/ReactiveInput.h | 1 + include/react/detail/SignalBase.h | 52 ++++ include/react/detail/Traits.h | 20 -- .../{ConversionNodes.h => AlgorithmNodes.h} | 147 ++------- .../{EventStreamNodes.h => EventNodes.h} | 135 +++++++-- include/react/detail/graph/GraphBase.h | 36 +-- include/react/detail/graph/ObserverNodes.h | 70 ++--- include/react/detail/graph/ReactorNodes.h | 10 +- include/react/detail/graph/SignalNodes.h | 73 ++--- include/react/engine/PulseCountEngine.h | 2 +- include/react/engine/SubtreeEngine.h | 3 +- project/msvc/CppReact.vcxproj | 11 +- project/msvc/CppReact.vcxproj.filters | 39 +-- src/benchmark/BenchmarkGrid.h | 7 +- src/benchmark/BenchmarkLifeSim.h | 5 +- src/logging/EventRecords.cpp | 1 + src/sandbox/Main.cpp | 4 +- src/test/EventStreamTest.h | 30 +- src/test/TransactionTest.h | 3 +- 37 files changed, 891 insertions(+), 930 deletions(-) rename include/react/{EventStream.h => Event.h} (70%) create mode 100644 include/react/detail/EventBase.h delete mode 100644 include/react/detail/ReactiveDomain.h create mode 100644 include/react/detail/SignalBase.h rename include/react/detail/graph/{ConversionNodes.h => AlgorithmNodes.h} (70%) rename include/react/detail/graph/{EventStreamNodes.h => EventNodes.h} (76%) diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index d4396e8f..baab3038 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -8,15 +8,14 @@ #include "react/detail/Defs.h" -#include #include -#include #include #include +#include "Event.h" #include "Signal.h" -#include "EventStream.h" -#include "react/detail/graph/ConversionNodes.h" + +#include "react/detail/graph/AlgorithmNodes.h" /*****************************************/ REACT_BEGIN /*****************************************/ @@ -159,22 +158,6 @@ auto Pulse(const Signal& target, const Events& trigger) target.GetPtr(), trigger.GetPtr())); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Flatten -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TInnerValue -> -auto Flatten(const Signal>& node) - -> Events -{ - return Events( - std::make_shared, TInnerValue>>( - node.GetPtr(), node().GetPtr())); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Incrementer /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/EventStream.h b/include/react/Event.h similarity index 70% rename from include/react/EventStream.h rename to include/react/Event.h index 39b55543..fc23db60 100644 --- a/include/react/EventStream.h +++ b/include/react/Event.h @@ -13,11 +13,8 @@ #include #include -#include "Observer.h" -#include "react/detail/ReactiveBase.h" -#include "react/detail/ReactiveDomain.h" -#include "react/detail/ReactiveInput.h" -#include "react/detail/graph/EventStreamNodes.h" +#include "react/Observer.h" +#include "react/detail/EventBase.h" /*****************************************/ REACT_BEGIN /*****************************************/ @@ -31,38 +28,39 @@ template typename D, typename E = EventToken > -class Events : public Reactive> +class Events : public REACT_IMPL::EventStreamBase { protected: - using NodeT = REACT_IMPL::EventStreamNode; + using BaseT = REACT_IMPL::EventStreamBase; -public: - using ValueT = E; +private: + using NodeT = REACT_IMPL::EventStreamNode; + using NodePtrT = REACT_IMPL::SharedPtrT; - Events() : - Reactive() - {} +public: + Events() = default; + Events(const Events&) = default; - explicit Events(const std::shared_ptr& ptr) : - Reactive(ptr) + explicit Events(NodePtrT&& nodePtr) : + EventStreamBase{ std::move(nodePtr) } {} template Events Filter(F&& f) { - return react::Filter(*this, std::forward(f)); + return REACT::Filter(*this, std::forward(f)); } template Events Transform(F&& f) { - return react::Transform(*this, std::forward(f)); + return REACT::Transform(*this, std::forward(f)); } template Observer Observe(F&& f) { - return react::Observe(*this, std::forward(f)); + return REACT::Observe(*this, std::forward(f)); } }; @@ -81,7 +79,7 @@ bool Equals(const Events& lhs, const Events& rhs) /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Eventsource +/// EventSource /////////////////////////////////////////////////////////////////////////////////////////////////// template < @@ -91,33 +89,35 @@ template class EventSource : public Events { private: - using NodeT = REACT_IMPL::EventSourceNode; + using NodeT = REACT_IMPL::EventSourceNode; + using NodePtrT = REACT_IMPL::SharedPtrT; public: - EventSource() : - Events() - {} + using ValueT = E; + + EventSource() = default; + EventSource(const EventSource&) = default; - explicit EventSource(const std::shared_ptr& ptr) : - Events(ptr) + explicit EventSource(NodePtrT&& nodePtr) : + Events{ std::move(nodePtr) } {} - template - void Emit(V&& v) const + template + void Emit(T&& e) const { - REACT_IMPL::InputManager::AddInput( - *std::static_pointer_cast(ptr_), std::forward(v)); + BaseT::emit(std::forward(e)); } - template ::value>::type> + template ::value>::type> void Emit() const { - Emit(EventToken::token); + BaseT::emit(EventToken::token); } - const EventSource& operator<<(const E& e) const + template + const EventSource& operator<<(T&& e) const { - Emit(e); + BaseT::emit(std::forward(e)); return *this; } }; @@ -134,19 +134,15 @@ template class TempEvents : public Events { protected: - using NodeT = REACT_IMPL::EventOpNode; + using NodeT = REACT_IMPL::EventOpNode; + using NodePtrT = REACT_IMPL::SharedPtrT; public: - TempEvents() : - Events() - {} - - explicit TempEvents(const std::shared_ptr& ptr) : - Events(ptr) - {} + TempEvents() = default; + TempEvents(const TempEvents&) = default; - explicit TempEvents(std::shared_ptr&& ptr) : - Events(std::move(ptr)) + explicit TempEvents(NodePtrT&& nodePtr) : + Events{ std::move(nodePtr) } {} TOp StealOp() @@ -184,8 +180,8 @@ template typename ... TArgs, typename E = TArg1, typename TOp = REACT_IMPL::EventMergeOp, - REACT_IMPL::EventStreamNodePtr ...> + REACT_IMPL::EventStreamNodePtrT, + REACT_IMPL::EventStreamNodePtrT ...> > auto Merge(const Events& arg1, const Events& ... args) -> TempEvents @@ -207,8 +203,8 @@ template typename TRightVal = TRightEvents::ValueT, typename E = TLeftVal, typename TOp = REACT_IMPL::EventMergeOp, - REACT_IMPL::EventStreamNodePtr>, + REACT_IMPL::EventStreamNodePtrT, + REACT_IMPL::EventStreamNodePtrT>, class = std::enable_if< IsEvent::value>::type, class = std::enable_if< @@ -250,7 +246,7 @@ template typename E = TLeftVal, typename TOp = REACT_IMPL::EventMergeOp>, + REACT_IMPL::EventStreamNodePtrT>, class = std::enable_if< IsEvent::value>::type > @@ -271,7 +267,7 @@ template typename TLeftVal = TLeftEvents::ValueT, typename E = TLeftVal, typename TOp = REACT_IMPL::EventMergeOp, + REACT_IMPL::EventStreamNodePtrT, TRightOp>, class = std::enable_if< IsEvent::value>::type @@ -294,7 +290,7 @@ template typename FIn, typename F = std::decay::type, typename TOp = REACT_IMPL::EventFilterOp> + REACT_IMPL::EventStreamNodePtrT> > auto Filter(const Events& src, FIn&& filter) -> TempEvents @@ -344,7 +340,7 @@ template typename FIn, typename F = std::decay::type, typename TOp = REACT_IMPL::EventTransformOp> + REACT_IMPL::EventStreamNodePtrT> > auto Transform(const Events& src, FIn&& func) -> TempEvents @@ -384,4 +380,61 @@ auto operator->*(TEvents&& src, F&& func) return Transform(std::forward(src), std::forward(func)); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Flatten +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename TInnerValue +> +auto Flatten(const Signal>& node) + -> Events +{ + return Events( + std::make_shared, TInnerValue>>( + node.GetPtr(), node().GetPtr())); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Observe +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename FIn, + typename TArg, + class = std::enable_if< + ! std::is_same::value>::type +> +auto Observe(const Events& subject, FIn&& func) + -> Observer +{ + using F = std::decay::type; + using TNode = REACT_IMPL::EventObserverNode; + + auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). + template Register(subject, std::forward(func)); + + return Observer(raw, subject.GetPtr()); +} + +template +< + typename D, + typename FIn +> +auto Observe(const Events& subject, FIn&& func) + -> Observer +{ + auto wrapper = [func] (EventToken _) { func(); }; + + using TNode = REACT_IMPL::EventObserverNode; + + auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). + template Register(subject, std::move(wrapper)); + + return Observer(raw, subject.GetPtr()); +} + /******************************************/ REACT_END /******************************************/ diff --git a/include/react/Observer.h b/include/react/Observer.h index 653ac0d8..d0fec0f5 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -14,17 +14,6 @@ /*****************************************/ REACT_BEGIN /*****************************************/ -template -class Reactive; - -template -class Signal; - -template -class Events; - -enum class EventToken; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Observer /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -32,111 +21,44 @@ template class Observer { public: - using SubjectT = REACT_IMPL::NodeBase; - using ObserverNodeT = REACT_IMPL::IObserverNode; + using SubjectT = REACT_IMPL::NodeBasePtrT; + using NodeT = REACT_IMPL::IObserver; - Observer() : - ptr_{ nullptr }, - subject_{ nullptr } + Observer() = default; + Observer(const Observer&) = delete; + + Observer(Observer&& other) : + nodePtr_{ std::move(other.nodePtr_) }, + subject_{ std::move(other.subject_) } {} - Observer(ObserverNodeT* ptr, const std::shared_ptr& subject) : - ptr_{ ptr }, + Observer(NodeT* nodePtr, const SubjectT& subject) : + nodePtr_{ nodePtr }, subject_{ subject } {} - const ObserverNodeT* GetPtr() const - { - return ptr_; - } + bool IsValid() const { return nodePtr_ != nullptr; } - bool Detach() + void Detach() { - if (ptr_ == nullptr) - return false; - - REACT_IMPL::DomainSpecificObserverRegistry::Instance().Unregister(ptr_); - return true; + REACT_IMPL::DomainSpecificObserverRegistry::Instance().Unregister(nodePtr_); } private: // Ownership managed by registry - ObserverNodeT* ptr_; + NodeT* nodePtr_; // While the observer handle exists, the subject is not destroyed - std::shared_ptr subject_; + SubjectT subject_; }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Observe -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename FIn, - typename TArg -> -auto Observe(const Signal& subject, FIn&& func) - -> Observer -{ - using F = std::decay::type; - using TNode = REACT_IMPL::SignalObserverNode; - - auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). - template Register(subject, std::forward(func)); - - return Observer(raw, subject.GetPtr()); -} - -template -< - typename D, - typename FIn, - typename TArg, - typename = std::enable_if< - ! std::is_same::value>::type -> -auto Observe(const Events& subject, FIn&& func) - -> Observer -{ - using F = std::decay::type; - using TNode = REACT_IMPL::EventObserverNode; - - auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). - template Register(subject, std::forward(func)); - - return Observer(raw, subject.GetPtr()); -} - -template -< - typename D, - typename FIn -> -auto Observe(const Events& subject, FIn&& func) - -> Observer -{ - auto wrapper = [func] (EventToken _) { func(); }; - - using TNode = REACT_IMPL::EventObserverNode; - - auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). - template Register(subject, std::move(wrapper)); - - return Observer(raw, subject.GetPtr()); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// DetachAllObservers /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - template class TNode, - typename TArg -> -void DetachAllObservers(const Reactive>& subject) +template +void DetachAllObservers(const TSubject& subject) { + using D = typename TSubject::DomainT; REACT_IMPL::DomainSpecificObserverRegistry::Instance().UnregisterFrom( subject.GetPtr().get()); } @@ -149,4 +71,4 @@ inline void DetachThisObserver() REACT_IMPL::current_observer_state_::shouldDetach = true; } -/******************************************/ REACT_END /******************************************/ +/******************************************/ REACT_END /******************************************/ \ No newline at end of file diff --git a/include/react/ReactiveObject.h b/include/react/ReactiveObject.h index 639dd11c..5ae08c9f 100644 --- a/include/react/ReactiveObject.h +++ b/include/react/ReactiveObject.h @@ -8,7 +8,10 @@ #include "react/detail/Defs.h" -#include "react/detail/ReactiveDomain.h" +#include +#include + +#include "react/detail/ReactiveBase.h" /*****************************************/ REACT_BEGIN /*****************************************/ @@ -30,12 +33,6 @@ class ReactiveObject template using VarSignalT = VarSignal; - template - using RefSignalT = RefSignal; - - template - using VarRefSignalT = VarRefSignal; - template using EventsT = Events; diff --git a/include/react/Reactor.h b/include/react/Reactor.h index c3d41e48..5e98f289 100644 --- a/include/react/Reactor.h +++ b/include/react/Reactor.h @@ -15,9 +15,8 @@ #include "react/common/Util.h" -#include "react/EventStream.h" +#include "react/Event.h" #include "react/detail/ReactiveBase.h" -#include "react/detail/ReactiveDomain.h" #include "react/detail/graph/ReactorNodes.h" /*****************************************/ REACT_BEGIN /*****************************************/ diff --git a/include/react/Signal.h b/include/react/Signal.h index cb899d5c..48eb1a03 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -8,17 +8,13 @@ #include "react/detail/Defs.h" -#include #include -#include +#include #include #include -#include "react/common/Util.h" -#include "react/detail/ReactiveBase.h" -#include "react/detail/ReactiveDomain.h" -#include "react/detail/ReactiveInput.h" -#include "react/detail/graph/SignalNodes.h" +#include "react/Observer.h" +#include "react/detail/SignalBase.h" /*****************************************/ REACT_BEGIN /*****************************************/ @@ -30,43 +26,49 @@ template typename D, typename S > -class Signal : public Reactive> +class Signal : public REACT_IMPL::SignalBase { protected: - using NodeT = REACT_IMPL::SignalNode; + using BaseT = REACT_IMPL::SignalBase; + +private: + using NodeT = REACT_IMPL::SignalNode; + using NodePtrT = REACT_IMPL::SharedPtrT; public: - using ValueT = S; + using ValueT = S; - Signal() : - Reactive() - {} - - explicit Signal(const std::shared_ptr& ptr) : - Reactive(ptr) - {} + Signal() = default; - explicit Signal(std::shared_ptr&& ptr) : - Reactive(std::move(ptr)) + explicit Signal(NodePtrT&& nodePtr) : + SignalBase{ std::move(nodePtr) } {} const S& Value() const { - return ptr_->ValueRef(); + return BaseT::getValue(); } - const S& operator()(void) const + const S& operator()() const { - return Value(); + return BaseT::getValue(); } template Observer Observe(F&& f) { - return react::Observe(*this, std::forward(f)); + return REACT::Observe(*this, std::forward(f)); } }; +template +< + typename D, + typename S +> +class Signal : public REACT_IMPL::SignalBase> +{}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// VarSignal /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -77,37 +79,35 @@ template > class VarSignal : public Signal { -protected: - using NodeT = REACT_IMPL::VarNode; +private: + using NodeT = REACT_IMPL::VarNode; + using NodePtrT = REACT_IMPL::SharedPtrT; public: - VarSignal() : - Signal() - {} - - explicit VarSignal(const std::shared_ptr& ptr) : - Signal(ptr) - {} + VarSignal() = default; - explicit VarSignal(std::shared_ptr&& ptr) : - Signal(std::move(ptr)) + explicit VarSignal(NodePtrT&& ptr) : + Signal{ std::move(ptr) } {} - template - void Set(V&& newValue) const + template + void Set(T&& newValue) const { - REACT_IMPL::InputManager::AddInput( - *std::static_pointer_cast(ptr_), std::forward(newValue)); + BaseT::setValue(std::forward(newValue)); } - template - const VarSignal& operator<<=(V&& newValue) const + template + const VarSignal& operator<<=(T&& newValue) const { - Set(std::forward(newValue)); + BaseT::setValue(std::forward(newValue)); return *this; } }; +template +class VarSignal : public VarSignal> +{}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// TempSignal /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -119,20 +119,15 @@ template > class TempSignal : public Signal { -protected: - using NodeT = REACT_IMPL::SignalOpNode; +private: + using NodeT = REACT_IMPL::SignalOpNode; + using NodePtrT = REACT_IMPL::SharedPtrT; public: - TempSignal() : - Signal() - {} + TempSignal() = default; - explicit TempSignal(const std::shared_ptr& ptr) : - Signal(ptr) - {} - - explicit TempSignal(std::shared_ptr&& ptr) : - Signal(std::move(ptr)) + explicit TempSignal(NodePtrT&& ptr) : + Signal{ std::move(ptr) } {} TOp StealOp() @@ -225,9 +220,9 @@ template typename D, typename FIn, typename ... TArgs, - typename F, //= std::decay::type, - typename S, //= std::result_of::type, - typename TOp //= REACT_IMPL::FunctionOp ...> + typename F = std::decay::type, + typename S = std::result_of::type, + typename TOp = REACT_IMPL::FunctionOp ...> > auto MakeSignal(FIn&& func, const Signal& ... args) -> TempSignal @@ -257,7 +252,7 @@ template typename TVal = TSignal::ValueT, \ typename F = name ## OpFunctor, \ typename S = std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp>, \ + typename TOp = REACT_IMPL::FunctionOp>, \ class = std::enable_if< \ IsSignal::value>::type \ > \ @@ -347,8 +342,8 @@ template typename F = name ## OpFunctor, \ typename S = std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp, \ - REACT_IMPL::SignalNodePtr>, \ + REACT_IMPL::SignalNodePtrT, \ + REACT_IMPL::SignalNodePtrT>, \ class = std::enable_if< \ IsSignal::value>::type, \ class = std::enable_if< \ @@ -372,7 +367,7 @@ template typename F = name ## OpLFunctor, \ typename S = std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp>, \ + REACT_IMPL::SignalNodePtrT>, \ class = std::enable_if< \ IsSignal::value>::type, \ class = std::enable_if< \ @@ -396,7 +391,7 @@ template typename F = name ## OpRFunctor, \ typename S = std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp>, \ + REACT_IMPL::SignalNodePtrT>, \ class = std::enable_if< \ ! IsSignal::value>::type, \ class = std::enable_if< \ @@ -440,7 +435,7 @@ template typename S = std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp>, \ + REACT_IMPL::SignalNodePtrT>, \ class = std::enable_if< \ IsSignal::value>::type \ > \ @@ -464,7 +459,7 @@ template typename S = std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp>, \ + REACT_IMPL::SignalNodePtrT>, \ class = std::enable_if< \ IsSignal::value>::type \ > \ @@ -550,13 +545,13 @@ struct InputPack template InputPack(const Signal& first, const Signal& second) : - Data(std::tie(first, second)) + Data{ std::tie(first, second) } { } template InputPack(const InputPack& curArgs, const Signal& newArg) : - Data(std::tuple_cat(curArgs.Data, std::tie(newArg))) + Data{ std::tuple_cat(curArgs.Data, std::tie(newArg)) } { } }; @@ -604,7 +599,7 @@ template struct ApplyHelper { static inline auto MakeSignal(F&& func, const Signal& ... args) - -> Signal() ...))> + -> Signal::type> { return D::MakeSignal(std::forward(func), args ...); } @@ -664,4 +659,25 @@ auto Flatten(const Signal>& node) node.GetPtr(), node.Value().GetPtr())); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Observe +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename FIn, + typename S +> +auto Observe(const Signal& subject, FIn&& func) + -> Observer +{ + using F = std::decay::type; + using TNode = REACT_IMPL::SignalObserverNode; + + auto* obs = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). + template Register(subject, std::forward(func)); + + return Observer{ obs, subject.GetPtr() }; +} + /******************************************/ REACT_END /******************************************/ diff --git a/include/react/common/Concurrency.h b/include/react/common/Concurrency.h index e67811fa..c3d9bb43 100644 --- a/include/react/common/Concurrency.h +++ b/include/react/common/Concurrency.h @@ -8,8 +8,8 @@ #include "react/detail/Defs.h" -#include #include +#include /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -79,27 +79,6 @@ class BlockingCondition bool blocked_ = false; }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ThreadLocalPtr -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ThreadLocalStaticPtr -{ -public: - static T* Get() { return ptr_; } - static void Set(T* ptr) { ptr_ = ptr; } - static void Reset() { ptr_ = nullptr; } - static bool IsNull() { return ptr_ == nullptr; } - - ThreadLocalStaticPtr() = delete; - -private: - static __declspec(thread) T* ptr_; -}; - -template -T* ThreadLocalStaticPtr::ptr_(nullptr); - /////////////////////////////////////////////////////////////////////////////////////////////////// /// ConditionalCriticalSection /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/common/Containers.h b/include/react/common/Containers.h index df190369..385c1a41 100644 --- a/include/react/common/Containers.h +++ b/include/react/common/Containers.h @@ -10,6 +10,7 @@ #include #include +#include #include /***************************************/ REACT_IMPL_BEGIN /**************************************/ diff --git a/include/react/common/Types.h b/include/react/common/Types.h index b7bd415b..07c3e3b8 100644 --- a/include/react/common/Types.h +++ b/include/react/common/Types.h @@ -7,8 +7,9 @@ #pragma once #include "react/detail/Defs.h" -#include + #include +#include /***************************************/ REACT_IMPL_BEGIN /**************************************/ diff --git a/include/react/common/Util.h b/include/react/common/Util.h index f82d3d1e..e347553a 100644 --- a/include/react/common/Util.h +++ b/include/react/common/Util.h @@ -8,13 +8,8 @@ #include "react/detail/Defs.h" -#include -#include #include #include -#include -#include -#include #include #include #include @@ -92,28 +87,6 @@ inline auto applyMemberFn(O obj, F f, T&& t) template inline void pass(TArgs&& ...) {} -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Format print bits -/////////////////////////////////////////////////////////////////////////////////////////////////// -//assumes little endian -inline void PrintBits(size_t const size, void const* const p) -{ - unsigned char *b = (unsigned char*) p; - unsigned char byte; - - for (int i=size-1; i>=0; i--) - { - for (int j=7; j>=0; j--) - { - byte = b[i] & (1<>= j; - printf("%u", byte); - if (!(j & 0x1)) - printf(" "); - } - } -} - /////////////////////////////////////////////////////////////////////////////////////////////////// // Identity (workaround to enable enable decltype()::X) /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/Defs.h b/include/react/detail/Defs.h index a45a0964..3d69a6da 100644 --- a/include/react/detail/Defs.h +++ b/include/react/detail/Defs.h @@ -6,6 +6,8 @@ #pragma once +#include + /////////////////////////////////////////////////////////////////////////////////////////////////// #define REACT_BEGIN namespace react { #define REACT_END } diff --git a/include/react/detail/EngineBase.h b/include/react/detail/EngineBase.h index 0c963e0d..7219764d 100644 --- a/include/react/detail/EngineBase.h +++ b/include/react/detail/EngineBase.h @@ -8,7 +8,10 @@ #include "react/detail/Defs.h" +#include #include +#include +#include #include "tbb/concurrent_vector.h" #include "tbb/queuing_mutex.h" @@ -25,6 +28,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// TurnBase /////////////////////////////////////////////////////////////////////////////////////////////////// +class IObserver; class TurnBase { @@ -35,7 +39,7 @@ class TurnBase inline TurnIdT Id() const { return id_; } - inline void QueueForDetach(IObserverNode& obs) + inline void QueueForDetach(IObserver& obs) { if (detachedObserversPtr_ == nullptr) detachedObserversPtr_ = std::unique_ptr(new ObsVectT()); @@ -50,7 +54,7 @@ class TurnBase friend class ContinuationHolder; private: - using ObsVectT = tbb::concurrent_vector; + using ObsVectT = tbb::concurrent_vector; TurnIdT id_; @@ -194,8 +198,8 @@ class DefaultQueueableTurn : { public: DefaultQueueableTurn(TurnIdT id, TurnFlagsT flags) : - TTurnBase(id, flags), - TurnQueueManager::QueueEntry(flags) + TTurnBase{ id, flags }, + TurnQueueManager::QueueEntry{ flags } {} }; diff --git a/include/react/detail/EventBase.h b/include/react/detail/EventBase.h new file mode 100644 index 00000000..cdb7c3d4 --- /dev/null +++ b/include/react/detail/EventBase.h @@ -0,0 +1,46 @@ + +// 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) + +#pragma once + +#include "react/detail/Defs.h" + +#include +#include + +#include "react/detail/ReactiveBase.h" +#include "react/detail/graph/EventNodes.h" + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventStreamBase +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename E +> +class EventStreamBase : public ReactiveBase> +{ +public: + EventStreamBase() = default; + + template + explicit EventStreamBase(T&& ptr) : + ReactiveBase{ std::forward(ptr) } + {} + +protected: + template + void emit(T&& e) const + { + InputManager::AddInput( + *std::static_pointer_cast>(ptr_), std::forward(e)); + } +}; + +/****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/IReactiveEngine.h b/include/react/detail/IReactiveEngine.h index 394551a1..752da259 100644 --- a/include/react/detail/IReactiveEngine.h +++ b/include/react/detail/IReactiveEngine.h @@ -6,6 +6,8 @@ #pragma once +#include + #include "react/detail/Defs.h" #include "react/common/Types.h" diff --git a/include/react/detail/IReactiveNode.h b/include/react/detail/IReactiveNode.h index 681629a3..f5f8264a 100644 --- a/include/react/detail/IReactiveNode.h +++ b/include/react/detail/IReactiveNode.h @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// struct IReactiveNode { - virtual ~IReactiveNode() {} + virtual ~IReactiveNode() = default; // Unique type identifier virtual const char* GetNodeType() const = 0; @@ -36,22 +36,12 @@ struct IReactiveNode virtual int DependencyCount() const = 0; }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IObserverNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -struct IObserverNode -{ - virtual ~IObserverNode() {} - - virtual void Detach() = 0; -}; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// IInputNode /////////////////////////////////////////////////////////////////////////////////////////////////// struct IInputNode { - virtual ~IInputNode() {} + virtual ~IInputNode() = default; virtual bool ApplyInput(void* turnPtr) = 0; }; diff --git a/include/react/detail/ObserverBase.h b/include/react/detail/ObserverBase.h index ffa67d48..8bf9bbda 100644 --- a/include/react/detail/ObserverBase.h +++ b/include/react/detail/ObserverBase.h @@ -20,34 +20,41 @@ template class Observable; +class IObserver; + +// tbb tasks are non-preemptible, thread local flag for each worker +namespace current_observer_state_ +{ + static REACT_TLS bool shouldDetach = false; +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// ObserverRegistry /////////////////////////////////////////////////////////////////////////////////////////////////// template class ObserverRegistry { -public: - using SubjectT = Observable; - private: class Entry { public: - Entry(std::unique_ptr&& obs, const SubjectT* subject) : + Entry(std::unique_ptr&& obs, const Observable* subject) : obs_{ std::move(obs) }, Subject{ subject } {} + Entry(const Entry&) = delete; + Entry(Entry&& other) : - obs_(std::move(other.obs_)), - Subject(other.Subject) + obs_{ std::move(other.obs_) }, + Subject{ other.Subject } {} - const SubjectT* Subject; + const Observable* Subject; private: // Manage lifetime - std::unique_ptr obs_; + std::unique_ptr obs_; }; public: @@ -57,33 +64,34 @@ class ObserverRegistry typename TSubject, typename F > - IObserverNode* Register(const TSubject& subject, F&& func) + IObserver* Register(const TSubject& subject, F&& func) { - std::unique_ptr ptr + std::unique_ptr ptr { new TNode(subject.GetPtr(), std::forward(func)) }; - auto* raw = ptr.get(); - observerMap_.emplace(raw, Entry(std::move(ptr), subject.GetPtr().get() )); + auto* obsPtr = ptr.get(); - return raw; + observerMap_.emplace(obsPtr, Entry(std::move(ptr), subject.GetPtr().get() )); + + return obsPtr; } - void Unregister(IObserverNode* obs) + void Unregister(IObserver* obs) { - obs->Detach(); + obs->detachObserver(); observerMap_.erase(obs); } - void UnregisterFrom(const SubjectT* subject) + void UnregisterFrom(const Observable* subject) { auto it = observerMap_.begin(); while (it != observerMap_.end()) { if (it->second.Subject == subject) { - it->first->Detach(); + it->first->detachObserver(); it = observerMap_.erase(it); } else @@ -94,7 +102,7 @@ class ObserverRegistry } private: - std::unordered_map observerMap_; + std::unordered_map observerMap_; }; template @@ -131,4 +139,19 @@ class Observable std::atomic obsCount_ = 0; }; -/****************************************/ REACT_IMPL_END /***************************************/ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// IObserver +/////////////////////////////////////////////////////////////////////////////////////////////////// +class IObserver +{ +public: + virtual ~IObserver() = default; + +private: + virtual void detachObserver() = 0; + + template + friend class ObserverRegistry; +}; + +/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file diff --git a/include/react/detail/ReactiveBase.h b/include/react/detail/ReactiveBase.h index 08ad7144..75853fdb 100644 --- a/include/react/detail/ReactiveBase.h +++ b/include/react/detail/ReactiveBase.h @@ -12,58 +12,292 @@ #include #include -/*****************************************/ REACT_BEGIN /*****************************************/ +#include "react/common/Types.h" +#include "react/detail/ReactiveInput.h" +#include "react/detail/Options.h" +#include "react/detail/Traits.h" +#include "react/detail/graph/GraphBase.h" + +#ifdef REACT_ENABLE_LOGGING + #include "react/detail/logging/EventLog.h" + #include "react/detail/logging/EventRecords.h" +#endif //REACT_ENABLE_LOGGING + +#include "react/engine/TopoSortEngine.h" + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +template +bool Equals(const L& lhs, const R& rhs) +{ + return lhs == rhs; +} + +template +bool Equals(const std::reference_wrapper& lhs, const std::reference_wrapper& rhs) +{ + return lhs.get() == rhs.get(); +} /////////////////////////////////////////////////////////////////////////////////////////////////// /// Reactive /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Reactive +template +class ReactiveBase { public: - using Domain = typename T::Domain; - using DomainT = typename T::Domain; + using DomainT = typename TNode::DomainT; - Reactive() : - ptr_{ nullptr } - {} - - explicit Reactive(const std::shared_ptr& ptr) : - ptr_{ ptr } - {} + ReactiveBase() = default; + ReactiveBase(const ReactiveBase&) = default; - explicit Reactive(std::shared_ptr&& ptr) : - ptr_{ std::move(ptr) } + template + explicit ReactiveBase(T&& ptr) : + ptr_{ std::forward(ptr) } {} - const std::shared_ptr& GetPtr() const + const SharedPtrT& GetPtr() const { return ptr_; } - bool Equals(const Reactive& other) const + bool Equals(const ReactiveBase& other) const { return ptr_ == other.ptr_; } protected: - std::shared_ptr ptr_; + SharedPtrT ptr_; }; +/****************************************/ REACT_IMPL_END /***************************************/ + + + +/*****************************************/ REACT_BEGIN /*****************************************/ + +enum class EventToken; + +template +class ReactiveLoop; + +template +class Observer; + +template +class Signal; + +template +class VarSignal; + +template +class TempSignal; + +template +class Events; + +template +class EventSource; + +template +class TempEvents; + /******************************************/ REACT_END /******************************************/ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -template -bool Equals(const L& lhs, const R& rhs) +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Domain +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename TEngine = TopoSortEngine +> +struct DomainPolicy { - return lhs == rhs; -} + using Engine = TEngine; +}; -template -bool Equals(const std::reference_wrapper& lhs, const std::reference_wrapper& rhs) +template +class DomainBase { - return lhs.get() == rhs.get(); -} +public: + using TurnT = typename TPolicy::Engine::TurnT; + + DomainBase() = delete; + + using Policy = TPolicy; + using Engine = EngineInterface; + + /////////////////////////////////////////////////////////////////////////////////////////////////// + /// Aliases for reactives of current domain + /////////////////////////////////////////////////////////////////////////////////////////////////// + template + using SignalT = Signal; + + template + using VarSignalT = VarSignal; + + template + using EventsT = Events; + + template + using EventSourceT = EventSource; + + using ObserverT = Observer; + + using ReactiveLoopT = ReactiveLoop; + + /////////////////////////////////////////////////////////////////////////////////////////////////// + /// MakeVar + /////////////////////////////////////////////////////////////////////////////////////////////////// + template + < + typename V, + typename S = std::decay::type, + class = std::enable_if< + !IsSignal::value>::type + > + static auto MakeVar(V&& value) + -> VarSignalT + { + return REACT::MakeVar(std::forward(value)); + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + // MakeVar (higher order signal) + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + template + < + typename V, + typename S = std::decay::type, + typename TInner = S::ValueT, + class = std::enable_if< + IsSignal::value>::type + > + static auto MakeVar(V&& value) + -> VarSignalT> + { + return REACT::MakeVar(std::forward(value)); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + /// MakeVal + /////////////////////////////////////////////////////////////////////////////////////////////////// + template + < + typename V, + typename S = std::decay::type + > + static auto MakeVal(V&& value) + -> SignalT + { + return REACT::MakeVal(std::forward(value)); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + /// MakeSignal + /////////////////////////////////////////////////////////////////////////////////////////////////// + template + < + typename FIn, + typename ... TArgs, + typename F = std::decay::type, + typename S = std::result_of::type, + typename TOp = REACT_IMPL::FunctionOp ...> + > + static auto MakeSignal(FIn&& func, const SignalT& ... args) + -> TempSignal + { + return REACT::MakeSignal(std::forward(func), args ...); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + /// MakeEventSource + /////////////////////////////////////////////////////////////////////////////////////////////////// + template + static auto MakeEventSource() + -> EventSourceT + { + return REACT::MakeEventSource(); + } + + static auto MakeEventSource() + -> EventSourceT + { + return REACT::MakeEventSource(); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + /// DoTransaction + /////////////////////////////////////////////////////////////////////////////////////////////////// + template + static void DoTransaction(F&& func) + { + InputManager::DoTransaction(0, std::forward(func)); + } + + template + static void DoTransaction(TurnFlagsT flags, F&& func) + { + InputManager::DoTransaction(flags, std::forward(func)); + } + +#ifdef REACT_ENABLE_LOGGING + /////////////////////////////////////////////////////////////////////////////////////////////// + /// Log + /////////////////////////////////////////////////////////////////////////////////////////////// + static EventLog& Log() + { + static ObserverRegistry instance; + return instance; + } +#endif //REACT_ENABLE_LOGGING +}; + +///////////////////////////////////////////////////////////////////////////////////////////////////// +///// Ensure singletons are created immediately after domain declaration (TODO hax) +///////////////////////////////////////////////////////////////////////////////////////////////////// +template +class DomainInitializer +{ +public: + DomainInitializer() + { + //DomainSpecificData::Observers(); + +#ifdef REACT_ENABLE_LOGGING + DomainSpecificData::Log(); +#endif //REACT_ENABLE_LOGGING + + typename D::Engine::Engine(); + } +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ContinuationHolder +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class ContinuationHolder +{ +public: + using TurnT = typename D::TurnT; + + ContinuationHolder() = delete; + + static void SetTurn(TurnT& turn) { ptr_ = &turn.continuation_; } + static void Clear() { ptr_ = nullptr; } + static ContinuationInput* Get() { return ptr_; } + +private: + static REACT_TLS ContinuationInput* ptr_; +}; + +template +ContinuationInput* ContinuationHolder::ptr_(nullptr); /****************************************/ REACT_IMPL_END /***************************************/ + +#define REACTIVE_DOMAIN(name, ...) \ + struct name : public REACT_IMPL::DomainBase> {}; \ + REACT_IMPL::DomainInitializer< name > name ## _initializer_; diff --git a/include/react/detail/ReactiveDomain.h b/include/react/detail/ReactiveDomain.h deleted file mode 100644 index fa13bb81..00000000 --- a/include/react/detail/ReactiveDomain.h +++ /dev/null @@ -1,245 +0,0 @@ - -// 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) - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include -#include -#include - -#include "tbb/concurrent_vector.h" -#include "tbb/queuing_mutex.h" -#include "tbb/spin_mutex.h" - - -#include "react/Observer.h" -#include "react/common/Types.h" -#include "react/detail/ReactiveInput.h" -#include "react/detail/Options.h" -#include "react/detail/Traits.h" - -#ifdef REACT_ENABLE_LOGGING - #include "react/detail/logging/EventLog.h" - #include "react/detail/logging/EventRecords.h" -#endif //REACT_ENABLE_LOGGING - -#include "react/engine/TopoSortEngine.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -enum class EventToken; - -template -class ReactiveLoop; - -/******************************************/ REACT_END /******************************************/ - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Domain -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename TEngine = TopoSortEngine -> -struct DomainPolicy -{ - using Engine = TEngine; -}; - -template -class DomainBase -{ -public: - using TurnT = typename TPolicy::Engine::TurnT; - - DomainBase() = delete; - - using Policy = TPolicy; - using Engine = EngineInterface; - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// Aliases for reactives of current domain - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - using SignalT = Signal; - - template - using VarSignalT = VarSignal; - - template - using RefSignalT = RefSignal; - - template - using VarRefSignalT = VarRefSignal; - - template - using EventsT = Events; - - template - using EventSourceT = EventSource; - - using ObserverT = Observer; - - using ReactiveLoopT = ReactiveLoop; - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// MakeVar - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - < - typename V, - typename S = std::decay::type, - class = std::enable_if< - !IsSignal::value>::type - > - static auto MakeVar(V&& value) - -> VarSignalT - { - return REACT::MakeVar(std::forward(value)); - } - - ////////////////////////////////////////////////////////////////////////////////////////////////////////// - // MakeVar (higher order signal) - ////////////////////////////////////////////////////////////////////////////////////////////////////////// - template - < - typename V, - typename S = std::decay::type, - typename TInner = S::ValueT, - class = std::enable_if< - IsSignal::value>::type - > - static auto MakeVar(V&& value) - -> VarSignalT> - { - return REACT::MakeVar(std::forward(value)); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// MakeVal - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - < - typename V, - typename S = std::decay::type - > - static auto MakeVal(V&& value) - -> SignalT - { - return REACT::MakeVal(std::forward(value)); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// MakeSignal - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - < - typename FIn, - typename ... TArgs, - typename F = std::decay::type, - typename S = std::result_of::type, - typename TOp = REACT_IMPL::FunctionOp ...> - > - static auto MakeSignal(FIn&& func, const SignalT& ... args) - -> TempSignal - { - return REACT::MakeSignal(std::forward(func), args ...); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// MakeEventSource - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - static auto MakeEventSource() - -> EventSourceT - { - return REACT::MakeEventSource(); - } - - static auto MakeEventSource() - -> EventSourceT - { - return REACT::MakeEventSource(); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// DoTransaction - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - static void DoTransaction(F&& func) - { - InputManager::DoTransaction(0, std::forward(func)); - } - - template - static void DoTransaction(TurnFlagsT flags, F&& func) - { - InputManager::DoTransaction(flags, std::forward(func)); - } - -#ifdef REACT_ENABLE_LOGGING - /////////////////////////////////////////////////////////////////////////////////////////////// - /// Log - /////////////////////////////////////////////////////////////////////////////////////////////// - static EventLog& Log() - { - static ObserverRegistry instance; - return instance; - } -#endif //REACT_ENABLE_LOGGING -}; - -///////////////////////////////////////////////////////////////////////////////////////////////////// -///// Ensure singletons are created immediately after domain declaration (TODO hax) -///////////////////////////////////////////////////////////////////////////////////////////////////// -template -class DomainInitializer -{ -public: - DomainInitializer() - { - //DomainSpecificData::Observers(); - -#ifdef REACT_ENABLE_LOGGING - DomainSpecificData::Log(); -#endif //REACT_ENABLE_LOGGING - - typename D::Engine::Engine(); - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ContinuationHolder -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ContinuationHolder -{ -public: - using TurnT = typename D::TurnT; - - ContinuationHolder() = delete; - - static void SetTurn(TurnT& turn) { ptr_ = &turn.continuation_; } - static void Clear() { ptr_ = nullptr; } - static ContinuationInput* Get() { return ptr_; } - -private: - static REACT_TLS ContinuationInput* ptr_; -}; - -template -ContinuationInput* ContinuationHolder::ptr_(nullptr); - -/****************************************/ REACT_IMPL_END /***************************************/ - -#define REACTIVE_DOMAIN(name, ...) \ - struct name : public REACT_IMPL::DomainBase> {}; \ - REACT_IMPL::DomainInitializer< name > name ## _initializer_; diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index 2fd3ab24..6999b0c3 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -8,6 +8,7 @@ #include "react/detail/Defs.h" +#include #include #include #include diff --git a/include/react/detail/SignalBase.h b/include/react/detail/SignalBase.h new file mode 100644 index 00000000..7c1bdea1 --- /dev/null +++ b/include/react/detail/SignalBase.h @@ -0,0 +1,52 @@ + +// 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) + +#pragma once + +#include "react/detail/Defs.h" + +#include +#include + +#include "react/detail/ReactiveBase.h" +#include "react/detail/graph/SignalNodes.h" + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SignalBase +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename S +> +class SignalBase : public ReactiveBase> +{ +public: + SignalBase() = default; + SignalBase(const SignalBase&) = default; + + template + explicit SignalBase(T&& ptr) : + ReactiveBase{ std::forward(ptr) } + {} + +protected: + const S& getValue() const + { + return ptr_->ValueRef(); + } + + template + void setValue(T&& newValue) const + { + InputManager::AddInput( + *std::static_pointer_cast>(ptr_), std::forward(newValue)); + } +}; + +/****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/Traits.h b/include/react/detail/Traits.h index 7c4a2ff9..778aacb1 100644 --- a/include/react/detail/Traits.h +++ b/include/react/detail/Traits.h @@ -19,12 +19,6 @@ class VarSignal; template class TempSignal; -template -using RefSignal = Signal>; - -template -using VarRefSignal = VarSignal>; - template class Events; @@ -34,20 +28,6 @@ class EventSource; template class TempEvents; -enum class EventToken; - -template -< - typename D, - typename FIn, - typename ... TArgs, - typename F = std::decay::type, - typename S = std::result_of::type, - typename TOp = REACT_IMPL::FunctionOp ...> -> -auto MakeSignal(FIn&& func, const Signal& ... args) - -> TempSignal; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// IsSignal /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/graph/ConversionNodes.h b/include/react/detail/graph/AlgorithmNodes.h similarity index 70% rename from include/react/detail/graph/ConversionNodes.h rename to include/react/detail/graph/AlgorithmNodes.h index da5d0a66..76992847 100644 --- a/include/react/detail/graph/ConversionNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -8,13 +8,9 @@ #include "react/detail/Defs.h" -#include -#include -#include #include -#include -#include "EventStreamNodes.h" +#include "EventNodes.h" #include "SignalNodes.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -32,8 +28,8 @@ class FoldBaseNode : public SignalNode { public: template - FoldBaseNode(T&& init, const EventStreamNodePtr& events) : - SignalNode(std::forward(init)), + FoldBaseNode(T&& init, const SharedPtrT>& events) : + SignalNode(std::forward(init)), events_{ events } {} @@ -64,7 +60,7 @@ class FoldBaseNode : public SignalNode virtual int DependencyCount() const override { return 1; } protected: - EventStreamNodePtr events_; + SharedPtrT> events_; virtual S calcNewValue() const = 0; }; @@ -83,8 +79,8 @@ class FoldNode : public FoldBaseNode { public: template - FoldNode(T&& init, const EventStreamNodePtr& events, F&& func) : - FoldBaseNode(std::forward(init), events), + FoldNode(T&& init, const SharedPtrT>& events, F&& func) : + FoldBaseNode(std::forward(init), events), func_{ std::forward(func) } { Engine::OnNodeCreate(*this); @@ -126,8 +122,8 @@ class IterateNode : public FoldBaseNode { public: template - IterateNode(T&& init, const EventStreamNodePtr& events, F&& func) : - FoldBaseNode(std::forward(init), events), + IterateNode(T&& init, const SharedPtrT>& events, F&& func) : + FoldBaseNode(std::forward(init), events), func_{ std::forward(func) } { Engine::OnNodeCreate(*this); @@ -166,8 +162,8 @@ class HoldNode : public SignalNode { public: template - HoldNode(T&& init, const EventStreamNodePtr& events) : - SignalNode(std::forward(init)), + HoldNode(T&& init, const SharedPtrT>& events) : + SignalNode(std::forward(init)), events_{ events } { Engine::OnNodeCreate(*this); @@ -218,7 +214,7 @@ class HoldNode : public SignalNode virtual int DependencyCount() const override { return 1; } private: - const EventStreamNodePtr events_; + const SharedPtrT> events_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -233,8 +229,9 @@ template class SnapshotNode : public SignalNode { public: - SnapshotNode(const SignalNodePtr& target, const EventStreamNodePtr& trigger) : - SignalNode(target->ValueRef()), + SnapshotNode(const SharedPtrT>& target, + const SharedPtrT>& trigger) : + SignalNode{ target->ValueRef() }, target_{ target }, trigger_{ trigger } { @@ -250,7 +247,8 @@ class SnapshotNode : public SignalNode Engine::OnNodeDestroy(*this); } - virtual const char* GetNodeType() const override { return "SnapshotNode"; } + virtual const char* GetNodeType() const override { return "SnapshotNode"; } + virtual int DependencyCount() const override { return 2; } virtual void Tick(void* turnPtr) override { @@ -287,11 +285,9 @@ class SnapshotNode : public SignalNode } } - virtual int DependencyCount() const override { return 2; } - private: - const SignalNodePtr target_; - const EventStreamNodePtr trigger_; + const SharedPtrT> target_; + const SharedPtrT> trigger_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -305,8 +301,8 @@ template class MonitorNode : public EventStreamNode { public: - MonitorNode(const SignalNodePtr& target) : - EventStreamNode(), + MonitorNode(const SharedPtrT>& target) : + EventStreamNode{ }, target_{ target } { Engine::OnNodeCreate(*this); @@ -319,7 +315,8 @@ class MonitorNode : public EventStreamNode Engine::OnNodeDestroy(*this); } - virtual const char* GetNodeType() const override { return "MonitorNode"; } + virtual const char* GetNodeType() const override { return "MonitorNode"; } + virtual int DependencyCount() const override { return 1; } virtual void Tick(void* turnPtr) override { @@ -346,10 +343,8 @@ class MonitorNode : public EventStreamNode } } - virtual int DependencyCount() const override { return 1; } - private: - const SignalNodePtr target_; + const SharedPtrT> target_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -364,8 +359,9 @@ template class PulseNode : public EventStreamNode { public: - PulseNode(const SignalNodePtr& target, const EventStreamNodePtr& trigger) : - EventStreamNode(), + PulseNode(const SharedPtrT>& target, + const SharedPtrT>& trigger) : + EventStreamNode{ }, target_{ target }, trigger_{ trigger } { @@ -381,7 +377,8 @@ class PulseNode : public EventStreamNode Engine::OnNodeDestroy(*this); } - virtual const char* GetNodeType() const override { return "PulseNode"; } + virtual const char* GetNodeType() const override { return "PulseNode"; } + virtual int DependencyCount() const override { return 2; } virtual void Tick(void* turnPtr) override { @@ -410,93 +407,9 @@ class PulseNode : public EventStreamNode } } - virtual int DependencyCount() const { return 2; } - -private: - const SignalNodePtr target_; - const EventStreamNodePtr trigger_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventFlattenNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TOuter, - typename TInner -> -class EventFlattenNode : public EventStreamNode -{ -public: - EventFlattenNode(const SignalNodePtr& outer, const EventStreamNodePtr& inner) : - EventStreamNode(), - outer_{ outer }, - inner_{ inner } - { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *outer_); - Engine::OnNodeAttach(*this, *inner_); - } - - ~EventFlattenNode() - { - Engine::OnNodeDetach(*this, *outer_); - Engine::OnNodeDetach(*this, *inner_); - Engine::OnNodeDestroy(*this); - } - - virtual const char* GetNodeType() const override { return "EventFlattenNode"; } - - virtual bool IsDynamicNode() const override { return true; } - - virtual void Tick(void* turnPtr) override - { - typedef typename D::Engine::TurnT TurnT; - TurnT& turn = *static_cast(turnPtr); - - SetCurrentTurn(turn, true); - inner_->SetCurrentTurn(turn); - - auto newInner = outer_->ValueRef().GetPtr(); - - if (newInner != inner_) - { - newInner->SetCurrentTurn(turn); - - // Topology has been changed - auto oldInner = inner_; - inner_ = newInner; - - Engine::OnDynamicNodeDetach(*this, *oldInner, turn); - Engine::OnDynamicNodeAttach(*this, *newInner, turn); - - return; - } - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - events_.insert(events_.end(), inner_->Events().begin(), inner_->Events().end()); - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - if (events_.size() > 0) - { - Engine::OnNodePulse(*this, *static_cast(turnPtr)); - } - else - { - Engine::OnNodeIdlePulse(*this, *static_cast(turnPtr)); - } - } - - virtual int DependencyCount() const override { return 2; } - private: - SignalNodePtr outer_; - EventStreamNodePtr inner_; + const SharedPtrT> target_; + const SharedPtrT> trigger_; }; /****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file diff --git a/include/react/detail/graph/EventStreamNodes.h b/include/react/detail/graph/EventNodes.h similarity index 76% rename from include/react/detail/graph/EventStreamNodes.h rename to include/react/detail/graph/EventNodes.h index db702f84..b614efd0 100644 --- a/include/react/detail/graph/EventStreamNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -9,12 +9,8 @@ #include "react/detail/Defs.h" #include -#include #include -#include #include -#include -#include #include "tbb/spin_mutex.h" @@ -49,16 +45,11 @@ class EventStreamNode : { public: using DataT = std::deque; - using PtrT = std::shared_ptr; - using WeakPtrT = std::weak_ptr; - using EngineT = typename D::Engine; using TurnT = typename EngineT::TurnT; EventStreamNode() = default; - virtual const char* GetNodeType() const override { return "EventStreamNode"; } - void SetCurrentTurn(const TurnT& turn, bool forceUpdate = false, bool noClear = false) { AccessCriticalSection([&] { @@ -79,10 +70,7 @@ class EventStreamNode : }; template -using EventStreamNodePtr = typename EventStreamNode::PtrT; - -template -using EventStreamNodeWeakPtr = typename EventStreamNode::WeakPtrT; +using EventStreamNodePtrT = SharedPtrT>; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventSourceNode @@ -98,7 +86,7 @@ class EventSourceNode : { public: EventSourceNode() : - EventStreamNode() + EventStreamNode{ } { Engine::OnNodeCreate(*this); } @@ -108,15 +96,15 @@ class EventSourceNode : Engine::OnNodeDestroy(*this); } - virtual const char* GetNodeType() const override { return "EventSourceNode"; } + virtual const char* GetNodeType() const override { return "EventSourceNode"; } + virtual bool IsInputNode() const override { return true; } + virtual int DependencyCount() const override { return 0; } virtual void Tick(void* turnPtr) override { - REACT_ASSERT(false, "Don't tick the EventSourceNode\n"); + REACT_ASSERT(false, "Ticked EventSourceNode\n"); } - virtual bool IsInputNode() const override { return true; } - template void AddInput(V&& v) { @@ -205,7 +193,7 @@ class EventMergeOp : public ReactiveOpBase } template - void collect(const NodeHolderT& depPtr) const + void collect(const SharedPtrT& depPtr) const { depPtr->SetCurrentTurn(MyTurn); @@ -232,7 +220,7 @@ class EventFilterOp : public ReactiveOpBase public: template EventFilterOp(TFilterIn&& filter, TDepIn&& dep) : - ReactiveOpBase(0u, std::forward(dep)), + ReactiveOpBase{ 0u, std::forward(dep) }, filter_{ std::forward(filter) } {} @@ -283,7 +271,7 @@ class EventFilterOp : public ReactiveOpBase } template - static void collectImpl(const TTurn& turn, const TCollector& collector, const NodeHolderT& depPtr) + static void collectImpl(const TTurn& turn, const TCollector& collector, const SharedPtrT& depPtr) { depPtr->SetCurrentTurn(turn); @@ -309,7 +297,7 @@ class EventTransformOp : public ReactiveOpBase public: template EventTransformOp(TFuncIn&& func, TDepIn&& dep) : - ReactiveOpBase(0u, std::forward(dep)), + ReactiveOpBase{ 0u, std::forward(dep) }, func_{ std::forward(func) } {} @@ -358,7 +346,7 @@ class EventTransformOp : public ReactiveOpBase } template - static void collectImpl(const TTurn& turn, const TCollector& collector, const NodeHolderT& depPtr) + static void collectImpl(const TTurn& turn, const TCollector& collector, const SharedPtrT& depPtr) { depPtr->SetCurrentTurn(turn); @@ -383,7 +371,7 @@ class EventOpNode : public EventStreamNode public: template EventOpNode(TArgs&& ... args) : - EventStreamNode(), + EventStreamNode{ }, op_{ std::forward(args) ... } { Engine::OnNodeCreate(*this); @@ -397,7 +385,8 @@ class EventOpNode : public EventStreamNode Engine::OnNodeDestroy(*this); } - virtual const char* GetNodeType() const override { return "EventOpNode"; } + virtual const char* GetNodeType() const override { return "EventOpNode"; } + virtual int DependencyCount() const override { return TOp::dependency_count; } virtual void Tick(void* turnPtr) override { @@ -424,11 +413,6 @@ class EventOpNode : public EventStreamNode } } - virtual int DependencyCount() const override - { - return TOp::dependency_count; - } - TOp StealOp() { REACT_ASSERT(wasOpStolen_ == false, "Op was already stolen."); @@ -440,8 +424,7 @@ class EventOpNode : public EventStreamNode private: struct EventCollector { - EventCollector(DataT& events) : MyEvents{ events } - {} + EventCollector(DataT& events) : MyEvents{ events } {} void operator()(const E& e) const { MyEvents.push_back(e); } @@ -452,4 +435,92 @@ class EventOpNode : public EventStreamNode bool wasOpStolen_ = false; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventFlattenNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename S +> +class SignalNode; + +template +< + typename D, + typename TOuter, + typename TInner +> +class EventFlattenNode : public EventStreamNode +{ +public: + EventFlattenNode(const SharedPtrT>& outer, + const SharedPtrT>& inner) : + EventStreamNode{ }, + outer_{ outer }, + inner_{ inner } + { + Engine::OnNodeCreate(*this); + Engine::OnNodeAttach(*this, *outer_); + Engine::OnNodeAttach(*this, *inner_); + } + + ~EventFlattenNode() + { + Engine::OnNodeDetach(*this, *outer_); + Engine::OnNodeDetach(*this, *inner_); + Engine::OnNodeDestroy(*this); + } + + virtual const char* GetNodeType() const override { return "EventFlattenNode"; } + virtual bool IsDynamicNode() const override { return true; } + virtual int DependencyCount() const override { return 2; } + + virtual void Tick(void* turnPtr) override + { + typedef typename D::Engine::TurnT TurnT; + TurnT& turn = *static_cast(turnPtr); + + SetCurrentTurn(turn, true); + inner_->SetCurrentTurn(turn); + + auto newInner = outer_->ValueRef().GetPtr(); + + if (newInner != inner_) + { + newInner->SetCurrentTurn(turn); + + // Topology has been changed + auto oldInner = inner_; + inner_ = newInner; + + Engine::OnDynamicNodeDetach(*this, *oldInner, turn); + Engine::OnDynamicNodeAttach(*this, *newInner, turn); + + return; + } + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + + events_.insert(events_.end(), inner_->Events().begin(), inner_->Events().end()); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + + if (events_.size() > 0) + { + Engine::OnNodePulse(*this, *static_cast(turnPtr)); + } + else + { + Engine::OnNodeIdlePulse(*this, *static_cast(turnPtr)); + } + } + +private: + SharedPtrT> outer_; + SharedPtrT> inner_; +}; + /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index 1a70c2bd..78dbb24a 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -10,7 +10,6 @@ #include #include -#include #include #include "tbb/tick_count.h" // vc12 chrono clock too inaccurate @@ -22,6 +21,12 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ +template +using SharedPtrT = std::shared_ptr; + +template +using WeakPtrT = std::weak_ptr; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// NodeUpdateTimer /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -92,28 +97,25 @@ class NodeBase : public Observable { public: - using PtrT = std::shared_ptr; - - using Domain = D; + using DomainT = D; using Policy = typename D::Policy; using Engine = typename D::Engine; using NodeT = typename Engine::NodeT; using TurnT = typename Engine::TurnT; - - virtual const char* GetNodeType() const override { return "NodeBase"; } virtual bool IsInputNode() const override { return false; } virtual bool IsOutputNode() const override { return false; } virtual bool IsDynamicNode() const override { return false; } - virtual int DependencyCount() const { return 0; } - - PtrT GetSharedPtr() const + SharedPtrT GetSharedPtr() const { return shared_from_this(); } }; +template +using NodeBasePtrT = SharedPtrT>; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// ReactiveNode /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -123,15 +125,10 @@ template typename P, typename V > -class ReactiveNode : - public NodeBase +class ReactiveNode : public NodeBase { public: - using PtrT = std::shared_ptr; - ReactiveNode() = default; - - virtual const char* GetNodeType() const override { return "ReactiveNode"; } }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -181,9 +178,6 @@ class ReactiveOpBase } protected: - template - using NodeHolderT = std::shared_ptr; - // Attach template struct AttachFunctor @@ -203,7 +197,7 @@ class ReactiveOpBase } template - void attach(const NodeHolderT& depPtr) const + void attach(const SharedPtrT& depPtr) const { D::Engine::OnNodeAttach(MyNode, *depPtr); } @@ -230,7 +224,7 @@ class ReactiveOpBase } template - void detach(const NodeHolderT& depPtr) const + void detach(const SharedPtrT& depPtr) const { D::Engine::OnNodeDetach(MyNode, *depPtr); } @@ -243,7 +237,7 @@ class ReactiveOpBase struct CountHelper { static const int value = T::dependency_count; }; template - struct CountHelper> { static const int value = 1; }; + struct CountHelper> { static const int value = 1; }; template struct DepCounter; diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index b92b8b3a..5089b31d 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -8,46 +8,32 @@ #include "react/detail/Defs.h" -#include -#include +#include -#include "EventStreamNodes.h" #include "GraphBase.h" -#include "SignalNodes.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ -// tbb tasks are non-preemptible, thread local flag for each worker -namespace current_observer_state_ -{ - static __declspec(thread) bool shouldDetach = false; -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// ObserverNode /////////////////////////////////////////////////////////////////////////////////////////////////// template class ObserverNode : public ReactiveNode, - public IObserverNode + public IObserver { public: - using PtrT = std::shared_ptr; - - ObserverNode() : - ReactiveNode() - {} + ObserverNode() = default; - virtual const char* GetNodeType() const { return "ObserverNode"; } - virtual bool IsOutputNode() const { return true; } + virtual bool IsOutputNode() const { return true; } }; -template -using ObserverNodePtr = typename ObserverNode::PtrT; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// SignalObserverNode /////////////////////////////////////////////////////////////////////////////////////////////////// +template +class SignalNode; + template < typename D, @@ -58,15 +44,13 @@ class SignalObserverNode : public ObserverNode { public: template - SignalObserverNode(const SignalNodePtr& subject, F&& func) : - ObserverNode(), + SignalObserverNode(const SharedPtrT>& subject, F&& func) : + ObserverNode{ }, subject_{ subject }, func_{ std::forward(func) } { Engine::OnNodeCreate(*this); - subject->IncObsCount(); - Engine::OnNodeAttach(*this, *subject); } @@ -75,7 +59,8 @@ class SignalObserverNode : public ObserverNode Engine::OnNodeDestroy(*this); } - virtual const char* GetNodeType() const override { return "SignalObserverNode"; } + virtual const char* GetNodeType() const override { return "SignalObserverNode"; } + virtual int DependencyCount() const override { return 1; } virtual void Tick(void* turnPtr) override { @@ -101,9 +86,8 @@ class SignalObserverNode : public ObserverNode GetObjectId(*this), turn.Id())); } - virtual int DependencyCount() const { return 1; } - - virtual void Detach() +private: + virtual void detachObserver() override { if (auto p = subject_.lock()) { @@ -114,15 +98,16 @@ class SignalObserverNode : public ObserverNode } } -private: - SignalNodeWeakPtr subject_; - - TFunc func_; + WeakPtrT> subject_; + TFunc func_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventObserverNode /////////////////////////////////////////////////////////////////////////////////////////////////// +template +class EventStreamNode; + template < typename D, @@ -133,15 +118,13 @@ class EventObserverNode : public ObserverNode { public: template - EventObserverNode(const EventStreamNodePtr& subject, F&& func) : - ObserverNode(), + EventObserverNode(const SharedPtrT>& subject, F&& func) : + ObserverNode{ }, subject_{ subject }, func_{ std::forward(func) } { Engine::OnNodeCreate(*this); - subject->IncObsCount(); - Engine::OnNodeAttach(*this, *subject); } @@ -150,7 +133,8 @@ class EventObserverNode : public ObserverNode Engine::OnNodeDestroy(*this); } - virtual const char* GetNodeType() const override { return "EventObserverNode"; } + virtual const char* GetNodeType() const override { return "EventObserverNode"; } + virtual int DependencyCount() const override { return 1; } virtual void Tick(void* turnPtr) override { @@ -179,9 +163,11 @@ class EventObserverNode : public ObserverNode GetObjectId(*this), turn.Id())); } - virtual int DependencyCount() const { return 1; } +private: + WeakPtrT> subject_; + TFunc func_; - virtual void Detach() + virtual void detachObserver() { if (auto p = subject_.lock()) { @@ -191,10 +177,6 @@ class EventObserverNode : public ObserverNode subject_.reset(); } } - -private: - EventStreamNodeWeakPtr subject_; - TFunc func_; }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/graph/ReactorNodes.h b/include/react/detail/graph/ReactorNodes.h index 15809331..570df4c9 100644 --- a/include/react/detail/graph/ReactorNodes.h +++ b/include/react/detail/graph/ReactorNodes.h @@ -19,7 +19,7 @@ #include #include "GraphBase.h" -#include "EventStreamNodes.h" +#include "EventNodes.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -35,7 +35,7 @@ class ReactorNode : public ReactiveNode { public: - using NodeBasePtrT = NodeBase::PtrT; + using NodeBasePtrT = SharedPtrT>; using CoroutineT = boost::coroutines::coroutine; using LoopT = typename CoroutineT::pull_type; @@ -115,7 +115,7 @@ class ReactorNode : } template - E& Await(const EventStreamNodePtr& events) + E& Await(const SharedPtrT>& events) { // First attach to target event node (*curOutPtr_)(&std::static_pointer_cast>(events)); @@ -132,7 +132,7 @@ class ReactorNode : } template - void RepeatUntil(const EventStreamNodePtr& events, F func) + void RepeatUntil(const SharedPtrT>& events, F func) { // First attach to target event node if (turnPtr_ != nullptr) @@ -189,7 +189,7 @@ class ReactorNode : private: template - bool checkEvent(const EventStreamNodePtr& events) + bool checkEvent(const SharedPtrT>& events) { if (turnPtr_ == nullptr) return false; diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index 768c9f89..9637406b 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -8,14 +8,8 @@ #include "react/detail/Defs.h" -#include -#include -#include #include - -#include - #include "GraphBase.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -34,26 +28,14 @@ template class SignalNode : public ReactiveNode { public: - using PtrT = std::shared_ptr; - using WeakPtrT = std::weak_ptr; - - SignalNode() : - ReactiveNode() - {} + SignalNode() = default; template SignalNode(T&& value) : - ReactiveNode(), + ReactiveNode{ }, value_{ std::forward(value) } {} - virtual const char* GetNodeType() const override { return "SignalNode"; } - - virtual void Tick(void* turnPtr) override - { - REACT_ASSERT(false, "Don't tick SignalNode\n"); - } - const S& ValueRef() const { return value_; @@ -64,10 +46,7 @@ class SignalNode : public ReactiveNode }; template -using SignalNodePtr = typename SignalNode::PtrT; - -template -using SignalNodeWeakPtr = typename SignalNode::WeakPtrT; +using SignalNodePtrT = SharedPtrT>; /////////////////////////////////////////////////////////////////////////////////////////////////// /// VarNode @@ -84,7 +63,7 @@ class VarNode : public: template VarNode(T&& value) : - SignalNode(std::forward(value)), + SignalNode{ std::forward(value) }, newValue_{ value_ } { Engine::OnNodeCreate(*this); @@ -95,15 +74,15 @@ class VarNode : Engine::OnNodeDestroy(*this); } - virtual const char* GetNodeType() const override { return "VarNode"; } + virtual const char* GetNodeType() const override { return "VarNode"; } + virtual bool IsInputNode() const override { return true; } + virtual int DependencyCount() const override { return 0; } virtual void Tick(void* turnPtr) override { - REACT_ASSERT(false, "Don't tick the VarNode\n"); + REACT_ASSERT(false, "Ticked VarNode\n"); } - virtual bool IsInputNode() const override { return true; } - template void AddInput(V&& newValue) { @@ -145,12 +124,12 @@ class FunctionOp : public ReactiveOpBase public: template FunctionOp(FIn&& func, TDepsIn&& ... deps) : - ReactiveOpBase(0u, std::forward(deps) ...), + ReactiveOpBase{ 0u, std::forward(deps) ... }, func_{ std::forward(func) } {} FunctionOp(FunctionOp&& other) : - ReactiveOpBase(std::move(other)), + ReactiveOpBase{ std::move(other) }, func_{ std::move(other.func_) } {} @@ -163,8 +142,7 @@ class FunctionOp : public ReactiveOpBase // Eval struct EvalFunctor { - EvalFunctor(const F& f) : MyFunc{ f } - {} + EvalFunctor(const F& f) : MyFunc{ f } {} S operator()(const TDeps& ... args) const { @@ -178,7 +156,7 @@ class FunctionOp : public ReactiveOpBase } template - static auto eval(const NodeHolderT& depPtr) -> decltype(depPtr->ValueRef()) + static auto eval(const SharedPtrT& depPtr) -> decltype(depPtr->ValueRef()) { return depPtr->ValueRef(); } @@ -206,7 +184,7 @@ class SignalOpNode : public: template SignalOpNode(TArgs&& ... args) : - SignalNode(), + SignalNode{ }, op_{ std::forward(args) ... } { value_ = op_.Evaluate(); @@ -222,7 +200,8 @@ class SignalOpNode : Engine::OnNodeDestroy(*this); } - virtual const char* GetNodeType() const override { return "SignalOpNode"; } + virtual const char* GetNodeType() const override { return "SignalOpNode"; } + virtual int DependencyCount() const override { return TOp::dependency_count; } virtual void Tick(void* turnPtr) override { @@ -259,11 +238,6 @@ class SignalOpNode : } } - virtual int DependencyCount() const override - { - return TOp::dependency_count; - } - TOp StealOp() { REACT_ASSERT(wasOpStolen_ == false, "Op was already stolen."); @@ -289,8 +263,9 @@ template class FlattenNode : public SignalNode { public: - FlattenNode(const SignalNodePtr& outer, const SignalNodePtr& inner) : - SignalNode(inner->ValueRef()), + FlattenNode(const SharedPtrT>& outer, + const SharedPtrT>& inner) : + SignalNode{ inner->ValueRef() }, outer_{ outer }, inner_{ inner } { @@ -306,9 +281,9 @@ class FlattenNode : public SignalNode Engine::OnNodeDestroy(*this); } - virtual const char* GetNodeType() const override { return "FlattenNode"; } - - virtual bool IsDynamicNode() const override { return true; } + virtual const char* GetNodeType() const override { return "FlattenNode"; } + virtual bool IsDynamicNode() const override { return true; } + virtual int DependencyCount() const override { return 2; } virtual void Tick(void* turnPtr) override { @@ -348,11 +323,9 @@ class FlattenNode : public SignalNode } } - virtual int DependencyCount() const override { return 2; } - private: - SignalNodePtr outer_; - SignalNodePtr inner_; + SharedPtrT> outer_; + SharedPtrT> inner_; }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/engine/PulseCountEngine.h b/include/react/engine/PulseCountEngine.h index 9d50fdba..5ccbef17 100644 --- a/include/react/engine/PulseCountEngine.h +++ b/include/react/engine/PulseCountEngine.h @@ -9,6 +9,7 @@ #include "react/detail/Defs.h" #include +#include #include #include "tbb/task_group.h" @@ -18,7 +19,6 @@ #include "react/common/Containers.h" #include "react/common/Types.h" #include "react/detail/EngineBase.h" -#include "react/detail/ReactiveDomain.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ namespace pulsecount { diff --git a/include/react/engine/SubtreeEngine.h b/include/react/engine/SubtreeEngine.h index 3906380b..291c7044 100644 --- a/include/react/engine/SubtreeEngine.h +++ b/include/react/engine/SubtreeEngine.h @@ -9,15 +9,16 @@ #include "react/detail/Defs.h" #include +#include #include #include "tbb/spin_rw_mutex.h" #include "tbb/task.h" #include "react/common/Containers.h" +#include "react/common/TopoQueue.h" #include "react/common/Types.h" #include "react/detail/EngineBase.h" -#include "react/detail/ReactiveDomain.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ namespace subtree { diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index eb036f43..2d9d3afc 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -77,21 +77,22 @@ + + - - + + - - + @@ -106,7 +107,7 @@ - + diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index 23d30d88..3a1b5e06 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -51,12 +51,6 @@ Header Files\common - - Header Files - - - Header Files - Header Files @@ -96,24 +90,12 @@ Header Files\detail - - Header Files\detail - Header Files\detail - - Header Files\detail\graph - - - Header Files\detail\graph - Header Files\detail\graph - - Header Files\detail\graph - Header Files\detail\graph @@ -138,6 +120,27 @@ Header Files\detail + + Header Files\detail\graph + + + Header Files\detail\graph + + + Header Files\detail + + + Header Files + + + Header Files\detail + + + Header Files + + + Header Files\detail\graph + diff --git a/src/benchmark/BenchmarkGrid.h b/src/benchmark/BenchmarkGrid.h index 80ffa53d..b64d6f27 100644 --- a/src/benchmark/BenchmarkGrid.h +++ b/src/benchmark/BenchmarkGrid.h @@ -8,9 +8,10 @@ #include #include -#include #include +#include "tbb/tick_count.h" + #include "BenchmarkBase.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -26,10 +27,10 @@ class GridGraphGenerator public: typedef typename D::template SignalT MyHandle; - typedef std::function Func1T; + typedef std::function Func1T; typedef std::function Func2T; - typedef std::vector HandleVect; + typedef std::vector HandleVect; typedef std::vector WidthVect; HandleVect InputSignals; diff --git a/src/benchmark/BenchmarkLifeSim.h b/src/benchmark/BenchmarkLifeSim.h index 66614698..263449e7 100644 --- a/src/benchmark/BenchmarkLifeSim.h +++ b/src/benchmark/BenchmarkLifeSim.h @@ -6,10 +6,12 @@ #pragma once +#include #include #include #include #include +#include #include #include "BenchmarkBase.h" @@ -18,8 +20,9 @@ using namespace react; -using std::vector; using std::atomic; +using std::vector; + using std::tuple; using std::unique_ptr; using std::move; diff --git a/src/logging/EventRecords.cpp b/src/logging/EventRecords.cpp index 6483a149..ce250e22 100644 --- a/src/logging/EventRecords.cpp +++ b/src/logging/EventRecords.cpp @@ -4,6 +4,7 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#include #include #include "react/logging/EventRecords.h" diff --git a/src/sandbox/Main.cpp b/src/sandbox/Main.cpp index dd246cae..cc010cf0 100644 --- a/src/sandbox/Main.cpp +++ b/src/sandbox/Main.cpp @@ -13,7 +13,7 @@ //#define REACT_ENABLE_LOGGING #include "react/Signal.h" -#include "react/EventStream.h" +#include "react/Event.h" #include "react/Algorithm.h" #include "react/ReactiveObject.h" @@ -215,7 +215,7 @@ class Manager : public ReactiveObject ObserverT nameObs; public: - VarRefSignalT CurrentCompany; + VarSignalT CurrentCompany; Manager(Company& c) : CurrentCompany{ MakeVar(std::ref(c)) } diff --git a/src/test/EventStreamTest.h b/src/test/EventStreamTest.h index e3b128fb..be0552d7 100644 --- a/src/test/EventStreamTest.h +++ b/src/test/EventStreamTest.h @@ -11,7 +11,7 @@ #include "gtest/gtest.h" -#include "react/EventStream.h" +#include "react/Event.h" /////////////////////////////////////////////////////////////////////////////////////////////////// namespace { @@ -193,22 +193,24 @@ TYPED_TEST_P(EventStreamTest, EventMerge3) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(EventStreamTest, EventFilter) { - std::queue results; + using std::string; - auto in = MyDomain::MakeEventSource(); + std::queue results; - auto filtered = Filter(in, [] (const std::string& s) + auto in = MyDomain::MakeEventSource(); + + auto filtered = Filter(in, [] (const string& s) { return s == "Hello World"; }); - Observe(filtered, [&] (const std::string& s) + Observe(filtered, [&] (const string& s) { results.push(s); }); - in << "Hello Worlt" << "Hello World" << "Hello Vorld"; + in << string("Hello Worlt") << string("Hello World") << string("Hello Vorld"); ASSERT_FALSE(results.empty()); ASSERT_EQ(results.front(), "Hello World"); @@ -222,27 +224,29 @@ TYPED_TEST_P(EventStreamTest, EventFilter) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(EventStreamTest, EventTransform) { - std::vector results; + using std::string; + + std::vector results; - auto in1 = MyDomain::MakeEventSource(); - auto in2 = MyDomain::MakeEventSource(); + auto in1 = MyDomain::MakeEventSource(); + auto in2 = MyDomain::MakeEventSource(); auto merged = Merge(in1, in2); - auto transformed = Transform(merged, [] (std::string s) -> std::string + auto transformed = Transform(merged, [] (string s) -> string { std::transform(s.begin(), s.end(), s.begin(), ::toupper); return s; }); - Observe(transformed, [&] (const std::string& s) + Observe(transformed, [&] (const string& s) { results.push_back(s); }); - in1 << "Hello Worlt" << "Hello World"; - in2 << "Hello Vorld"; + in1 << string("Hello Worlt") << string("Hello World"); + in2 << string("Hello Vorld"); ASSERT_EQ(results.size(), 3); ASSERT_TRUE(std::find(results.begin(), results.end(), "HELLO WORLT") != results.end()); diff --git a/src/test/TransactionTest.h b/src/test/TransactionTest.h index e8e54a1f..f1e4cf66 100644 --- a/src/test/TransactionTest.h +++ b/src/test/TransactionTest.h @@ -8,8 +8,9 @@ #include "gtest/gtest.h" -#include #include +#include +#include #include "react/Signal.h" From 73cb0ad90b6bae0429bca4ddcbd140baae290329 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 29 Apr 2014 18:51:52 +0200 Subject: [PATCH 008/266] Misc cleanup. --- include/react/Algorithm.h | 16 +-- include/react/Event.h | 137 +++++++++++++++++++---- include/react/Observer.h | 2 +- include/react/ReactiveObject.h | 10 ++ include/react/Reactor.h | 8 +- include/react/Signal.h | 135 ++++++++++++++++++---- include/react/detail/ObserverBase.h | 4 +- include/react/detail/ReactiveBase.h | 41 ++++--- include/react/detail/graph/EventNodes.h | 2 +- include/react/detail/graph/SignalNodes.h | 2 +- src/sandbox/Main.cpp | 87 ++++---------- 11 files changed, 298 insertions(+), 146 deletions(-) diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index baab3038..e8becd1c 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -36,7 +36,7 @@ auto Fold(V&& init, const Events& events, FIn&& func) { return Signal( std::make_shared>( - std::forward(init), events.GetPtr(), std::forward(func))); + std::forward(init), events.NodePtr(), std::forward(func))); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -56,7 +56,7 @@ auto Iterate(V&& init, const Events& events, FIn&& func) { return Signal( std::make_shared>( - std::forward(init), events.GetPtr(), std::forward(func))); + std::forward(init), events.NodePtr(), std::forward(func))); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -73,7 +73,7 @@ auto Hold(V&& init, const Events& events) { return Signal( std::make_shared>( - std::forward(init), events.GetPtr())); + std::forward(init), events.NodePtr())); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -90,7 +90,7 @@ auto Snapshot(const Signal& target, const Events& trigger) { return Signal( std::make_shared>( - target.GetPtr(), trigger.GetPtr())); + target.NodePtr(), trigger.NodePtr())); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -106,7 +106,7 @@ auto Monitor(const Signal& target) { return Events( std::make_shared>( - target.GetPtr())); + target.NodePtr())); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -155,14 +155,14 @@ auto Pulse(const Signal& target, const Events& trigger) { return Events( std::make_shared>( - target.GetPtr(), trigger.GetPtr())); + target.NodePtr(), trigger.NodePtr())); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Incrementer /////////////////////////////////////////////////////////////////////////////////////////////////// template -struct Incrementer : public std::unary_function +struct Incrementer { T operator() (T v) const { return v+1; } }; @@ -171,7 +171,7 @@ struct Incrementer : public std::unary_function /// Decrementer /////////////////////////////////////////////////////////////////////////////////////////////////// template -struct Decrementer : public std::unary_function +struct Decrementer { T operator() (T v) const { return v-1; } }; diff --git a/include/react/Event.h b/include/react/Event.h index fc23db60..8316cec8 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -38,9 +38,15 @@ class Events : public REACT_IMPL::EventStreamBase using NodePtrT = REACT_IMPL::SharedPtrT; public: + using ValueT = E; + Events() = default; Events(const Events&) = default; + Events(Events&& other) : + EventStreamBase{ std::move(other) } + {} + explicit Events(NodePtrT&& nodePtr) : EventStreamBase{ std::move(nodePtr) } {} @@ -64,19 +70,53 @@ class Events : public REACT_IMPL::EventStreamBase } }; -/******************************************/ REACT_END /******************************************/ +// Specialize for references +template +< + typename D, + typename E +> +class Events : public REACT_IMPL::EventStreamBase> +{ +protected: + using BaseT = REACT_IMPL::EventStreamBase>; -/***************************************/ REACT_IMPL_BEGIN /**************************************/ +private: + using NodeT = REACT_IMPL::EventStreamNode>; + using NodePtrT = REACT_IMPL::SharedPtrT; -template -bool Equals(const Events& lhs, const Events& rhs) -{ - return lhs.Equals(rhs); -} +public: + using ValueT = std::reference_wrapper; -/****************************************/ REACT_IMPL_END /***************************************/ + Events() = default; + Events(const Events&) = default; -/*****************************************/ REACT_BEGIN /*****************************************/ + Events(Events&& other) : + EventStreamBase{ std::move(other) } + {} + + explicit Events(NodePtrT&& nodePtr) : + EventStreamBase{ std::move(nodePtr) } + {} + + template + Events Filter(F&& f) + { + return REACT::Filter(*this, std::forward(f)); + } + + template + Events Transform(F&& f) + { + return REACT::Transform(*this, std::forward(f)); + } + + template + Observer Observe(F&& f) + { + return REACT::Observe(*this, std::forward(f)); + } +}; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventSource @@ -93,11 +133,13 @@ class EventSource : public Events using NodePtrT = REACT_IMPL::SharedPtrT; public: - using ValueT = E; - EventSource() = default; EventSource(const EventSource&) = default; + EventSource(EventSource&& other) : + Events{ std::move(other) } + {} + explicit EventSource(NodePtrT&& nodePtr) : Events{ std::move(nodePtr) } {} @@ -122,6 +164,43 @@ class EventSource : public Events } }; +// Specialize for references +template +< + typename D, + typename E +> +class EventSource : public Events> +{ +private: + using NodeT = REACT_IMPL::EventSourceNode>; + using NodePtrT = REACT_IMPL::SharedPtrT; + +public: + EventSource() = default; + EventSource(const EventSource&) = default; + + EventSource(EventSource&& other) : + Events{ std::move(other) } + {} + + explicit EventSource(NodePtrT&& nodePtr) : + Events{ std::move(nodePtr) } + {} + + void Emit(std::reference_wrapper e) const + { + BaseT::emit(e); + } + + template + const EventSource& operator<<(std::reference_wrapper e) const + { + BaseT::emit(e); + return *this; + } +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// TempEvents /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -141,6 +220,10 @@ class TempEvents : public Events TempEvents() = default; TempEvents(const TempEvents&) = default; + TempEvents(TempEvents&& other) : + Events{ std::move(other) } + {} + explicit TempEvents(NodePtrT&& nodePtr) : Events{ std::move(nodePtr) } {} @@ -151,6 +234,20 @@ class TempEvents : public Events } }; +/******************************************/ REACT_END /******************************************/ + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +template +bool Equals(const Events& lhs, const Events& rhs) +{ + return lhs.Equals(rhs); +} + +/****************************************/ REACT_IMPL_END /***************************************/ + +/*****************************************/ REACT_BEGIN /*****************************************/ + /////////////////////////////////////////////////////////////////////////////////////////////////// /// MakeEventSource /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -191,7 +288,7 @@ auto Merge(const Events& arg1, const Events& ... args) return TempEvents( std::make_shared>( - arg1.GetPtr(), args.GetPtr() ...)); + arg1.NodePtr(), args.NodePtr() ...)); } template @@ -215,7 +312,7 @@ auto operator|(const TLeftEvents& lhs, const TRightEvents& rhs) { return TempEvents( std::make_shared>( - lhs.GetPtr(), rhs.GetPtr())); + lhs.NodePtr(), rhs.NodePtr())); } template @@ -255,7 +352,7 @@ auto operator|(TempEvents&& lhs, const TRightEvents& rhs) { return TempEvents( std::make_shared>( - lhs.StealOp(), rhs.GetPtr())); + lhs.StealOp(), rhs.NodePtr())); } template @@ -277,7 +374,7 @@ auto operator|(const TLeftEvents& lhs, TempEvents&& rhs) { return TempEvents( std::make_shared>( - lhs.GetPtr(), rhs.StealOp())); + lhs.NodePtr(), rhs.StealOp())); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -297,7 +394,7 @@ auto Filter(const Events& src, FIn&& filter) { return TempEvents( std::make_shared>( - std::forward(filter), src.GetPtr())); + std::forward(filter), src.NodePtr())); } template @@ -347,7 +444,7 @@ auto Transform(const Events& src, FIn&& func) { return TempEvents( std::make_shared>( - std::forward(func), src.GetPtr())); + std::forward(func), src.NodePtr())); } template @@ -393,7 +490,7 @@ auto Flatten(const Signal>& node) { return Events( std::make_shared, TInnerValue>>( - node.GetPtr(), node().GetPtr())); + node.NodePtr(), node().NodePtr())); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -416,7 +513,7 @@ auto Observe(const Events& subject, FIn&& func) auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). template Register(subject, std::forward(func)); - return Observer(raw, subject.GetPtr()); + return Observer(raw, subject.NodePtr()); } template @@ -434,7 +531,7 @@ auto Observe(const Events& subject, FIn&& func) auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). template Register(subject, std::move(wrapper)); - return Observer(raw, subject.GetPtr()); + return Observer(raw, subject.NodePtr()); } /******************************************/ REACT_END /******************************************/ diff --git a/include/react/Observer.h b/include/react/Observer.h index d0fec0f5..d7a458f8 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -60,7 +60,7 @@ void DetachAllObservers(const TSubject& subject) { using D = typename TSubject::DomainT; REACT_IMPL::DomainSpecificObserverRegistry::Instance().UnregisterFrom( - subject.GetPtr().get()); + subject.NodePtr().get()); } /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/ReactiveObject.h b/include/react/ReactiveObject.h index 5ae08c9f..0008a96d 100644 --- a/include/react/ReactiveObject.h +++ b/include/react/ReactiveObject.h @@ -59,6 +59,16 @@ class ReactiveObject return REACT::MakeVar(std::forward(value)); } + template + < + typename S + > + static auto MakeVar(std::reference_wrapper value) + -> VarSignalT + { + return REACT::MakeVar(value); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////// // MakeVar (higher order) ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/Reactor.h b/include/react/Reactor.h index 5e98f289..ec5ae9b5 100644 --- a/include/react/Reactor.h +++ b/include/react/Reactor.h @@ -6,7 +6,7 @@ #pragma once -#ifndef REACT_DISABLE_REACTORS +#ifdef REACT_ENABLE_REACTORS #include "react/detail/Defs.h" @@ -39,13 +39,13 @@ class ReactiveLoop template E& Await(const Events& evn) { - return node_.Await(evn.GetPtr()); + return node_.Await(evn.NodePtr()); } template void RepeatUntil(const Events& evn, F func) { - node_.RepeatUntil(evn.GetPtr(), func); + node_.RepeatUntil(evn.NodePtr(), func); } private: @@ -65,4 +65,4 @@ class ReactiveLoop /******************************************/ REACT_END /******************************************/ -#endif //REACT_DISABLE_REACTORS \ No newline at end of file +#endif //REACT_ENABLE_REACTORS \ No newline at end of file diff --git a/include/react/Signal.h b/include/react/Signal.h index 48eb1a03..f2b23694 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -36,9 +36,14 @@ class Signal : public REACT_IMPL::SignalBase using NodePtrT = REACT_IMPL::SharedPtrT; public: - using ValueT = S; + using ValueT = S; Signal() = default; + Signal(const Signal&) = default; + + Signal(Signal&& other) : + SignalBase{ std::move(other) } + {} explicit Signal(NodePtrT&& nodePtr) : SignalBase{ std::move(nodePtr) } @@ -61,13 +66,51 @@ class Signal : public REACT_IMPL::SignalBase } }; +// Specialize for references template < typename D, typename S > class Signal : public REACT_IMPL::SignalBase> -{}; +{ +protected: + using BaseT = REACT_IMPL::SignalBase>; + +private: + using NodeT = REACT_IMPL::SignalNode>; + using NodePtrT = REACT_IMPL::SharedPtrT; + +public: + using ValueT = std::reference_wrapper; + + Signal() = default; + Signal(const Signal&) = default; + + Signal(Signal&& other) : + SignalBase{ std::move(other) } + {} + + explicit Signal(NodePtrT&& nodePtr) : + SignalBase{ std::move(nodePtr) } + {} + + const S& Value() const + { + return BaseT::getValue(); + } + + const S& operator()() const + { + return BaseT::getValue(); + } + + template + Observer Observe(F&& f) + { + return REACT::Observe(*this, std::forward(f)); + } +}; /////////////////////////////////////////////////////////////////////////////////////////////////// /// VarSignal @@ -85,9 +128,14 @@ class VarSignal : public Signal public: VarSignal() = default; + VarSignal(const VarSignal&) = default; - explicit VarSignal(NodePtrT&& ptr) : - Signal{ std::move(ptr) } + VarSignal(VarSignal&& other) : + Signal{ std::move(other) } + {} + + explicit VarSignal(NodePtrT&& nodePtr) : + Signal{ std::move(nodePtr) } {} template @@ -104,9 +152,41 @@ class VarSignal : public Signal } }; -template -class VarSignal : public VarSignal> -{}; +// Specialize for references +template +< + typename D, + typename S +> +class VarSignal : public Signal> +{ +private: + using NodeT = REACT_IMPL::VarNode>; + using NodePtrT = REACT_IMPL::SharedPtrT; + +public: + VarSignal() = default; + VarSignal(const VarSignal&) = default; + + VarSignal(VarSignal&& other) : + Signal{ std::move(other) } + {} + + explicit VarSignal(NodePtrT&& nodePtr) : + Signal{ std::move(nodePtr) } + {} + + void Set(std::reference_wrapper newValue) const + { + BaseT::setValue(newValue); + } + + const VarSignal& operator<<=(std::reference_wrapper newValue) const + { + BaseT::setValue(newValue); + return *this; + } +}; /////////////////////////////////////////////////////////////////////////////////////////////////// /// TempSignal @@ -125,6 +205,11 @@ class TempSignal : public Signal public: TempSignal() = default; + TempSignal(const TempSignal&) = default; + + TempSignal(TempSignal&& other) : + Signal{ std::move(other) } + {} explicit TempSignal(NodePtrT&& ptr) : Signal{ std::move(ptr) } @@ -146,12 +231,6 @@ bool Equals(const Signal& lhs, const Signal& rhs) return lhs.Equals(rhs); } -template -bool Equals(const VarSignal& lhs, const VarSignal& rhs) -{ - return lhs.Equals(rhs); -} - /****************************************/ REACT_IMPL_END /***************************************/ /*****************************************/ REACT_BEGIN /*****************************************/ @@ -175,6 +254,18 @@ auto MakeVar(V&& value) std::forward(value))); } +template +< + typename D, + typename S +> +auto MakeVar(std::reference_wrapper value) + -> VarSignal +{ + return VarSignal( + std::make_shared>>(value)); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// MakeVar (higher order reactives) /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -232,7 +323,7 @@ auto MakeSignal(FIn&& func, const Signal& ... args) return TempSignal( std::make_shared>( - std::forward(func), args.GetPtr() ... )); + std::forward(func), args.NodePtr() ... )); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -261,7 +352,7 @@ auto operator ## op(const TSignal& arg) { \ return TempSignal( \ std::make_shared>( \ - F(), arg.GetPtr())); \ + F(), arg.NodePtr())); \ } DECLARE_OP(+, UnaryPlus); @@ -354,7 +445,7 @@ auto operator ## op(const TLeftSignal& lhs, const TRightSignal& rhs) { \ return TempSignal( \ std::make_shared>( \ - F(), lhs.GetPtr(), rhs.GetPtr())); \ + F(), lhs.NodePtr(), rhs.NodePtr())); \ } \ \ template \ @@ -378,7 +469,7 @@ auto operator ## op(const TLeftSignal& lhs, TRightValIn&& rhs) { \ return TempSignal( \ std::make_shared>( \ - F(std::forward(rhs)), lhs.GetPtr())); \ + F(std::forward(rhs)), lhs.NodePtr())); \ } \ \ template \ @@ -402,7 +493,7 @@ auto operator ## op(TLeftValIn&& lhs, const TRightSignal& rhs) { \ return TempSignal( \ std::make_shared>( \ - F(std::forward(lhs)), rhs.GetPtr())); \ + F(std::forward(lhs)), rhs.NodePtr())); \ } \ template \ < \ @@ -445,7 +536,7 @@ template { \ return TempSignal( \ std::make_shared>( \ - F(), lhs.StealOp(), rhs.GetPtr())); \ + F(), lhs.StealOp(), rhs.NodePtr())); \ } \ \ template \ @@ -468,7 +559,7 @@ auto operator ## op(const TLeftSignal& lhs, TempSignal&& r { \ return TempSignal( \ std::make_shared>( \ - F(), lhs.GetPtr(), rhs.StealOp())); \ + F(), lhs.NodePtr(), rhs.StealOp())); \ } \ \ template \ @@ -656,7 +747,7 @@ auto Flatten(const Signal>& node) { return Signal( std::make_shared, TInnerValue>>( - node.GetPtr(), node.Value().GetPtr())); + node.NodePtr(), node.Value().NodePtr())); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -677,7 +768,7 @@ auto Observe(const Signal& subject, FIn&& func) auto* obs = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). template Register(subject, std::forward(func)); - return Observer{ obs, subject.GetPtr() }; + return Observer{ obs, subject.NodePtr() }; } /******************************************/ REACT_END /******************************************/ diff --git a/include/react/detail/ObserverBase.h b/include/react/detail/ObserverBase.h index 8bf9bbda..42f75053 100644 --- a/include/react/detail/ObserverBase.h +++ b/include/react/detail/ObserverBase.h @@ -68,12 +68,12 @@ class ObserverRegistry { std::unique_ptr ptr { - new TNode(subject.GetPtr(), std::forward(func)) + new TNode(subject.NodePtr(), std::forward(func)) }; auto* obsPtr = ptr.get(); - observerMap_.emplace(obsPtr, Entry(std::move(ptr), subject.GetPtr().get() )); + observerMap_.emplace(obsPtr, Entry(std::move(ptr), subject.NodePtr().get() )); return obsPtr; } diff --git a/include/react/detail/ReactiveBase.h b/include/react/detail/ReactiveBase.h index 75853fdb..aba2231c 100644 --- a/include/react/detail/ReactiveBase.h +++ b/include/react/detail/ReactiveBase.h @@ -46,17 +46,25 @@ template class ReactiveBase { public: - using DomainT = typename TNode::DomainT; + using DomainT = typename TNode::DomainT; + using NodePtrT = SharedPtrT; ReactiveBase() = default; ReactiveBase(const ReactiveBase&) = default; - template - explicit ReactiveBase(T&& ptr) : - ptr_{ std::forward(ptr) } + ReactiveBase(ReactiveBase&& other) : + ptr_{ std::move(other.ptr_) } {} - const SharedPtrT& GetPtr() const + explicit ReactiveBase(const NodePtrT& ptr) : + ptr_{ ptr } + {} + + explicit ReactiveBase(NodePtrT&& ptr) : + ptr_{ std::move(ptr) } + {} + + const SharedPtrT& NodePtr() const { return ptr_; } @@ -66,6 +74,11 @@ class ReactiveBase return ptr_ == other.ptr_; } + bool IsValid() const + { + return ptr_ != nullptr; + } + protected: SharedPtrT ptr_; }; @@ -165,7 +178,7 @@ class DomainBase } ////////////////////////////////////////////////////////////////////////////////////////////////////////// - // MakeVar (higher order signal) + // MakeVar (higher order) ////////////////////////////////////////////////////////////////////////////////////////////////////////// template < @@ -181,20 +194,6 @@ class DomainBase return REACT::MakeVar(std::forward(value)); } - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// MakeVal - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - < - typename V, - typename S = std::decay::type - > - static auto MakeVal(V&& value) - -> SignalT - { - return REACT::MakeVal(std::forward(value)); - } - /////////////////////////////////////////////////////////////////////////////////////////////////// /// MakeSignal /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -264,8 +263,6 @@ class DomainInitializer public: DomainInitializer() { - //DomainSpecificData::Observers(); - #ifdef REACT_ENABLE_LOGGING DomainSpecificData::Log(); #endif //REACT_ENABLE_LOGGING diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index b614efd0..bbd4e401 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -484,7 +484,7 @@ class EventFlattenNode : public EventStreamNode SetCurrentTurn(turn, true); inner_->SetCurrentTurn(turn); - auto newInner = outer_->ValueRef().GetPtr(); + auto newInner = outer_->ValueRef().NodePtr(); if (newInner != inner_) { diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index 9637406b..68ef6a11 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -290,7 +290,7 @@ class FlattenNode : public SignalNode using TurnT = typename D::Engine::TurnT; TurnT& turn = *static_cast(turnPtr); - auto newInner = outer_->ValueRef().GetPtr(); + auto newInner = outer_->ValueRef().NodePtr(); if (newInner != inner_) { diff --git a/src/sandbox/Main.cpp b/src/sandbox/Main.cpp index cc010cf0..47ec3240 100644 --- a/src/sandbox/Main.cpp +++ b/src/sandbox/Main.cpp @@ -12,13 +12,15 @@ //#define REACT_ENABLE_LOGGING +// Experimental. Requires boost::coroutine. +//#define REACT_ENABLE_REACTORS + #include "react/Signal.h" #include "react/Event.h" #include "react/Algorithm.h" #include "react/ReactiveObject.h" -//#include "react/engine/SubtreeEngine.h" -//#include "react/engine/PulseCountEngine.h" + using namespace std; using namespace react; @@ -27,6 +29,11 @@ using namespace react; // Each domain represents a separate dependency graph, managed by a dedicated propagation engine. // Reactives of different domains can not be combined. REACTIVE_DOMAIN(D); + +// Explicit engine: +//#include "react/engine/SubtreeEngine.h" +//#include "react/engine/PulseCountEngine.h" + //REACTIVE_DOMAIN(D, PulseCountEngine); void SignalExample1() @@ -148,14 +155,12 @@ void EventExample2() class Person : public ReactiveObject { public: - VarSignalT Age = MakeVar(1); - SignalT Health = 100 - Age; - SignalT Wisdom = Age * Age / 100; + VarSignalT Age = MakeVar(1); + SignalT Health = 100 - Age; + SignalT Wisdom = Age * Age / 100; - // Note ICC compiler bug: - // Initializing them directly uses the same lambda for both signals - ObserverT wisdomObs; - ObserverT weaknessObs; + ObserverT wisdomObs; + ObserverT weaknessObs; Person() { @@ -201,8 +206,7 @@ class Company : public ReactiveObject Company(const char* name) : Name{ MakeVar(string(name)) } - { - } + {} inline bool operator==(const Company& other) const { @@ -218,7 +222,7 @@ class Manager : public ReactiveObject VarSignalT CurrentCompany; Manager(Company& c) : - CurrentCompany{ MakeVar(std::ref(c)) } + CurrentCompany{ MakeVar(ref(c)) } { nameObs = REACTIVE_REF(CurrentCompany, Name).Observe([] (string name) { cout << "Manager: Now managing " << name << endl; @@ -238,7 +242,7 @@ void ObjectExample2() company1.Name <<= string("BT Cellnet"); company2.Name <<= string("Inprise"); - manager.CurrentCompany <<= std::ref(company2); + manager.CurrentCompany <<= ref(company2); company1.Name <<= string("O2"); company2.Name <<= string("Borland"); @@ -261,7 +265,7 @@ void FoldExample1() cout << fold1() << endl; auto charSrc = D::MakeEventSource(); - auto strFold = Fold(std::string(""), charSrc, [] (std::string s, char c) { + auto strFold = Fold(string(""), charSrc, [] (string s, char c) { return s + c; }); @@ -269,55 +273,8 @@ void FoldExample1() cout << "Str: " << strFold() << endl; } -// -//#include "tbb/tick_count.h" -// -//void Debug() -//{ -// cout << "A" << endl; -// { -// int x = 0; -// -// auto t0 = tbb::tick_count::now(); -// -// for (int i=0; i<10000000; i++) -// { -// x += i; -// } -// -// auto t1 = tbb::tick_count::now(); -// -// auto d = (t1 - t0).seconds(); -// -// //cout << x << endl; -// cout << d << endl; -// } -// -// cout << "B" << endl; -// { -// auto a = D::MakeVar(0); -// auto b = D::MakeVar(1); -// auto c = D::MakeVar(2); -// auto d = D::MakeVar(4); -// auto e = D::MakeVar(5); -// -// auto x = a + b + c + d + e; -// -// auto t0 = tbb::tick_count::now(); -// -// for (int i=0; i<10000000; i++) -// a <<= i; -// -// auto t1 = tbb::tick_count::now(); -// -// auto td = (t1 - t0).seconds(); -// -// cout << x() << endl; -// cout << td << endl; -// } -//} - -#ifndef REACT_DISABLE_REACTORS + +#ifdef REACT_ENABLE_REACTORS #include "react/Reactor.h" void LoopTest() @@ -369,7 +326,7 @@ void LoopTest() cout << endl; } } -#endif //REACT_DISABLE_REACTORS +#endif //REACT_ENABLE_REACTORS int main() { @@ -385,7 +342,7 @@ int main() FoldExample1(); -#ifndef REACT_DISABLE_REACTORS +#ifdef REACT_ENABLE_REACTORS LoopTest(); #endif From d3a5458d5f9a88611a6865a8470adb265c666057 Mon Sep 17 00:00:00 2001 From: Sebastian Jeckel Date: Tue, 29 Apr 2014 19:12:39 +0200 Subject: [PATCH 009/266] Update README.md --- README.md | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fe6e8266..92bc079e 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ cout << "area: " << area() << endl; // => area: 20 For more information, see the [Signal guide](SignalGuide) -#### Event streams +#### Events Event streams represent flows of discrete values as first-class objects, based on ideas found in [Deprecating the Observer Pattern](http://infoscience.epfl.ch/record/176887/files/DeprecatingObservers2012.pdf). @@ -94,6 +94,7 @@ REACTIVE_DOMAIN(MyDomain, TopoSortEngine); ```C++ #include "react/Reactor.h" //... +using namespace std; using namespace react; REACTIVE_DOMAIN(D); @@ -144,10 +145,11 @@ mouseUp << PointT(30,30); ```C++ #include "react/ReactiveObject.h" //... -REACTIVE_DOMAIN(D); - +using namespace std; using namespace react; +REACTIVE_DOMAIN(D); + class Company : public ReactiveObject { public: @@ -155,8 +157,7 @@ public: Company(const char* name) : Name{ MakeVar(string(name)) } - { - } + {} inline bool operator==(const Company& other) const { /* ... */ } }; @@ -166,16 +167,32 @@ class Manager : public ReactiveObject ObserverT nameObs; public: - VarRefSignalT CurrentCompany; + VarSignalT CurrentCompany; Manager(initialCompany& company) : - CurrentCompany{ MakeVar(std::ref(company)) } + CurrentCompany{ MakeVar(ref(company)) } { nameObs = REACTIVE_REF(CurrentCompany, Name).Observe([] (string name) { cout << "Manager: Now managing " << name << endl; }); } }; + +void main() +{ + Company company1{ "Cellnet" }; + Company company2{ "Borland" }; + + Manager manager{ company1 }; + + company1.Name <<= string("BT Cellnet"); + company2.Name <<= string("Inprise"); + + manager.CurrentCompany <<= ref(company2); + + company1.Name <<= string("O2"); + company2.Name <<= string("Borland"); +} ``` ### More examples From 2192c45bafa026ce4ef58c51112666b916e19ddd Mon Sep 17 00:00:00 2001 From: Sebastian Jeckel Date: Tue, 29 Apr 2014 19:14:17 +0200 Subject: [PATCH 010/266] Update README.md --- README.md | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 92bc079e..82ce612f 100644 --- a/README.md +++ b/README.md @@ -178,21 +178,18 @@ public: } }; -void main() -{ - Company company1{ "Cellnet" }; - Company company2{ "Borland" }; +Company company1{ "Cellnet" }; +Company company2{ "Borland" }; - Manager manager{ company1 }; +Manager manager{ company1 }; - company1.Name <<= string("BT Cellnet"); - company2.Name <<= string("Inprise"); +company1.Name <<= string("BT Cellnet"); +company2.Name <<= string("Inprise"); - manager.CurrentCompany <<= ref(company2); +manager.CurrentCompany <<= ref(company2); - company1.Name <<= string("O2"); - company2.Name <<= string("Borland"); -} +company1.Name <<= string("O2"); +company2.Name <<= string("Borland"); ``` ### More examples From ce71721ceb56c13e7e9fb40e15f2d4a174e10342 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 30 Apr 2014 01:56:39 +0200 Subject: [PATCH 011/266] Added toposort image. --- doc/media/toposort1.graphml | 613 ++++++++++++++++++++++++++++++++++++ doc/media/toposort1.png | Bin 0 -> 25940 bytes 2 files changed, 613 insertions(+) create mode 100644 doc/media/toposort1.graphml create mode 100644 doc/media/toposort1.png diff --git a/doc/media/toposort1.graphml b/doc/media/toposort1.graphml new file mode 100644 index 00000000..e2b6e872 --- /dev/null +++ b/doc/media/toposort1.graphml @@ -0,0 +1,613 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + a + + + + + + + + + + + + + + + + + + b + + + + + + + + + + + + + + + + + + c + + + + + + + + + + + + + + + + + + t1 + + + + + + + + + + + + + + + + + + x + + + + + + + + + + + + + + + + + + a + + + + + + + + + + + + + + + + + + b + + + + + + + + + + + + + + + + + + c + + + + + + + + + + + + + + + + + + t1 + + + + + + + + + + + + + + + + + + x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + a + + + + + + + + + + + + + + + + + + b + + + + + + + + + + + + + + + + + + c + + + + + + + + + + + + + + + + + + t1 + + + + + + + + + + + + + + + + + + x + + + + + + + + + + + + + + + + + + b <<= K + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + t=0 + + + + + + + + + + + + + + + + + + t=1 + + + + + + + + + + + + + + + + + + t=2 + + + + + + + + + + + + + + + + + + x=(a+b)*c + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/toposort1.png b/doc/media/toposort1.png new file mode 100644 index 0000000000000000000000000000000000000000..c0cc73bfaee050287e41f835a746592bc501c35b GIT binary patch literal 25940 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 Date: Wed, 30 Apr 2014 13:55:24 +0200 Subject: [PATCH 012/266] TopoSortEngine -> ToposortEngine (step1) --- include/react/detail/ReactiveBase.h | 4 +- .../{TopoSortEngine.h => Topo_SortEngine.h} | 24 ++++---- project/msvc/CppReact.vcxproj | 2 +- project/msvc/CppReact.vcxproj.filters | 2 +- src/benchmark/Main.cpp | 58 +++++++++---------- ...TopoSortEngine.cpp => Topo_SortEngine.cpp} | 2 +- src/sandbox/Main.cpp | 2 +- src/test/EventStreamTest.cpp | 6 +- src/test/EventStreamTestQ.cpp | 8 +-- src/test/MoveTest.cpp | 4 +- src/test/ObserverTest.cpp | 6 +- src/test/ObserverTestQ.cpp | 8 +-- src/test/OperationsTest.cpp | 6 +- src/test/OperationsTestQ.cpp | 8 +-- src/test/SignalTest.cpp | 6 +- src/test/SignalTestQ.cpp | 8 +-- src/test/TransactionTest.cpp | 8 +-- 17 files changed, 81 insertions(+), 81 deletions(-) rename include/react/engine/{TopoSortEngine.h => Topo_SortEngine.h} (94%) rename src/engine/{TopoSortEngine.cpp => Topo_SortEngine.cpp} (99%) diff --git a/include/react/detail/ReactiveBase.h b/include/react/detail/ReactiveBase.h index aba2231c..19c98e21 100644 --- a/include/react/detail/ReactiveBase.h +++ b/include/react/detail/ReactiveBase.h @@ -23,7 +23,7 @@ #include "react/detail/logging/EventRecords.h" #endif //REACT_ENABLE_LOGGING -#include "react/engine/TopoSortEngine.h" +#include "react/engine/ToposortEngine.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -124,7 +124,7 @@ class TempEvents; /////////////////////////////////////////////////////////////////////////////////////////////////// template < - typename TEngine = TopoSortEngine + typename TEngine = ToposortEngine > struct DomainPolicy { diff --git a/include/react/engine/TopoSortEngine.h b/include/react/engine/Topo_SortEngine.h similarity index 94% rename from include/react/engine/TopoSortEngine.h rename to include/react/engine/Topo_SortEngine.h index 603de690..5b963add 100644 --- a/include/react/engine/TopoSortEngine.h +++ b/include/react/engine/Topo_SortEngine.h @@ -330,21 +330,21 @@ struct parallel_queue; struct parallel_pipeline; template -class TopoSortEngine; +class ToposortEngine; -template <> class TopoSortEngine : +template <> class ToposortEngine : public REACT_IMPL::toposort::BasicSeqEngine {}; -template <> class TopoSortEngine : +template <> class ToposortEngine : public REACT_IMPL::toposort::QueuingSeqEngine {}; -template <> class TopoSortEngine : +template <> class ToposortEngine : public REACT_IMPL::toposort::BasicParEngine {}; -template <> class TopoSortEngine : +template <> class ToposortEngine : public REACT_IMPL::toposort::QueuingParEngine {}; -template <> class TopoSortEngine : +template <> class ToposortEngine : public REACT_IMPL::toposort::PipeliningEngine {}; /******************************************/ REACT_END /******************************************/ @@ -352,13 +352,13 @@ template <> class TopoSortEngine : /***************************************/ REACT_IMPL_BEGIN /**************************************/ template struct EnableNodeUpdateTimer; -template <> struct EnableNodeUpdateTimer> : std::true_type {}; -template <> struct EnableNodeUpdateTimer> : std::true_type {}; -template <> struct EnableNodeUpdateTimer> : std::true_type {}; +template <> struct EnableNodeUpdateTimer> : std::true_type {}; +template <> struct EnableNodeUpdateTimer> : std::true_type {}; +template <> struct EnableNodeUpdateTimer> : std::true_type {}; template struct EnableParallelUpdating; -template <> struct EnableParallelUpdating> : std::true_type {}; -template <> struct EnableParallelUpdating> : std::true_type {}; -template <> struct EnableParallelUpdating> : std::true_type {}; +template <> struct EnableParallelUpdating> : std::true_type {}; +template <> struct EnableParallelUpdating> : std::true_type {}; +template <> struct EnableParallelUpdating> : std::true_type {}; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index 2d9d3afc..40c049fa 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -116,7 +116,7 @@ - + diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index 3a1b5e06..d076e40d 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -155,7 +155,7 @@ Source Files\engine - + Source Files\engine diff --git a/src/benchmark/Main.cpp b/src/benchmark/Main.cpp index c083ea6a..da0fc0ef 100644 --- a/src/benchmark/Main.cpp +++ b/src/benchmark/Main.cpp @@ -22,7 +22,7 @@ #include "react/common/Util.h" #include "react/logging/EventLog.h" -#include "react/engine/TopoSortEngine.h" +#include "react/engine/ToposortEngine.h" #include "react/engine/PulseCountEngine.h" #include "react/engine/SubtreeEngine.h" @@ -31,24 +31,24 @@ namespace { using namespace react; -REACTIVE_DOMAIN(TopoSortSTDomain, TopoSortEngine); -REACTIVE_DOMAIN(TopoSortDomain, TopoSortEngine); +REACTIVE_DOMAIN(ToposortSTDomain, ToposortEngine); +REACTIVE_DOMAIN(ToposortDomain, ToposortEngine); REACTIVE_DOMAIN(PulseCountDomain, PulseCountEngine); REACTIVE_DOMAIN(SubtreeDomain, SubtreeEngine); void runBenchmarkGrid(std::ostream& out) { RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(20, 10000), - TopoSortSTDomain, TopoSortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulseCountDomain); RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(30, 10000), - TopoSortSTDomain, TopoSortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulseCountDomain); RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(40, 10000), - TopoSortSTDomain, TopoSortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulseCountDomain); RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(50, 10000), - TopoSortSTDomain, TopoSortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulseCountDomain); } void runBenchmarkRandom(std::ostream& out) @@ -67,10 +67,10 @@ void runBenchmarkRandom(std::ostream& out) { int x = (slowPercent * (w*(h-1))) / 100; //RUN_BENCHMARK(out, 5, Benchmark_Random, BenchmarkParams_Random(w, h, 20, 0, 10, 40, x, true, seed1, seed2), - // TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); RUN_BENCHMARK(out, 5, Benchmark_Random, BenchmarkParams_Random(w, h, 20, 0, 10, 40, x, true, seed1, seed2), - TopoSortSTDomain); + ToposortSTDomain); } seed1 *= 2; @@ -81,43 +81,43 @@ void runBenchmarkRandom(std::ostream& out) void runBenchmarkFanout(std::ostream& out) { //RUN_BENCHMARK(out, 5, Benchmark_Fanout, BenchmarkParams_Fanout(10, 10000, 0), - // TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortSTDomain, ToposortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); //RUN_BENCHMARK(out, 5, Benchmark_Fanout, BenchmarkParams_Fanout(100, 10000, 0), - // TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortSTDomain, ToposortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); //RUN_BENCHMARK(out, 5, Benchmark_Fanout, BenchmarkParams_Fanout(1000, 10000, 0), - // TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortSTDomain, ToposortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); RUN_BENCHMARK(out, 3, Benchmark_Fanout, BenchmarkParams_Fanout(10, 10, 10), - TopoSortSTDomain, TopoSortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulseCountDomain); RUN_BENCHMARK(out, 3, Benchmark_Fanout, BenchmarkParams_Fanout(100, 10, 10), - TopoSortSTDomain, TopoSortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulseCountDomain); RUN_BENCHMARK(out, 3, Benchmark_Fanout, BenchmarkParams_Fanout(1000, 10, 10), - TopoSortSTDomain, TopoSortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulseCountDomain); } void runBenchmarkSequence(std::ostream& out) { //RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(10, 10000, 0), - // TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortSTDomain, ToposortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); //RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(100, 10000, 0), - // TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortSTDomain, ToposortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); //RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(1000, 10000, 0), - // TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortSTDomain, ToposortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(10, 10, 10), - TopoSortSTDomain, TopoSortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulseCountDomain); RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(100, 10, 10), - TopoSortSTDomain, TopoSortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulseCountDomain); RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(1000, 10, 10), - TopoSortSTDomain, TopoSortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulseCountDomain); } void runBenchmarkLifeSim(std::ostream& out) @@ -129,7 +129,7 @@ void runBenchmarkLifeSim(std::ostream& out) // SourceSetDomain, PulseCountDomain); RUN_BENCHMARK(out, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 15, 10000), - TopoSortSTDomain, TopoSortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulseCountDomain); //RUN_BENCHMARK(out, 3, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 50, 100), // PulseCountDomain, PulseCountDomain); @@ -179,7 +179,7 @@ void debugBenchmarks() // TestDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_Random, BenchmarkParams_Random(10, 25, 10, 0, 10, 25, 25, false), - // TopoSortDomain); + // ToposortDomain); //const auto w = 10; //const auto h = 11; @@ -193,13 +193,13 @@ void debugBenchmarks() // { // int x = (slowPercent * (w*(h-1))) / 100; // RUN_BENCHMARK(std::cout, 1, Benchmark_Random, BenchmarkParams_Random(w, h, 100, 0, 5, 25, x, false, seed1 / j, seed2 / j), - // TopoSortDomain); + // ToposortDomain); // } //} //RunBenchmark<1>(Benchmark2()); //RunBenchmark<1>(Benchmark2()); - //RunBenchmark<3>(Benchmark2()); + //RunBenchmark<3>(Benchmark2()); #ifdef REACT_ENABLE_LOGGING std::ofstream logfile; @@ -213,18 +213,18 @@ void debugBenchmarks() void profileBenchmark() { RUN_BENCHMARK(std::cout, 3, Benchmark_Grid, BenchmarkParams_Grid(30, 10000), - TopoSortSTDomain); - //TopoSortSTDomain, TopoSortDomain, ELMDomain, PulseCountDomain, SubtreeDomain, SourceSetDomain); + ToposortSTDomain); + //ToposortSTDomain, ToposortDomain, ELMDomain, PulseCountDomain, SubtreeDomain, SourceSetDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_Grid, BenchmarkParams_Grid(30, 10000), // SourceSetDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_Random, BenchmarkParams_Random(20, 11, 100, 0, 5, 80, 20, false, 41556, 21624), //SubtreeDomain); - //TopoSortSTDomain, TopoSortDomain, PulseCountDomain, SubtreeDomain); + //ToposortSTDomain, ToposortDomain, PulseCountDomain, SubtreeDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 15, 10000), - // TopoSortDomain); + // ToposortDomain); } } // ~anonymous namespace diff --git a/src/engine/TopoSortEngine.cpp b/src/engine/Topo_SortEngine.cpp similarity index 99% rename from src/engine/TopoSortEngine.cpp rename to src/engine/Topo_SortEngine.cpp index 2d057373..f27e3694 100644 --- a/src/engine/TopoSortEngine.cpp +++ b/src/engine/Topo_SortEngine.cpp @@ -4,7 +4,7 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#include "react/engine/TopoSortEngine.h" +#include "react/engine/ToposortEngine.h" #include "tbb/parallel_for.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ diff --git a/src/sandbox/Main.cpp b/src/sandbox/Main.cpp index 47ec3240..a79f3658 100644 --- a/src/sandbox/Main.cpp +++ b/src/sandbox/Main.cpp @@ -13,7 +13,7 @@ //#define REACT_ENABLE_LOGGING // Experimental. Requires boost::coroutine. -//#define REACT_ENABLE_REACTORS +#define REACT_ENABLE_REACTORS #include "react/Signal.h" #include "react/Event.h" diff --git a/src/test/EventStreamTest.cpp b/src/test/EventStreamTest.cpp index d9306b8c..8613bb4f 100644 --- a/src/test/EventStreamTest.cpp +++ b/src/test/EventStreamTest.cpp @@ -7,7 +7,7 @@ #include "EventStreamTest.h" #include "react/engine/PulseCountEngine.h" -#include "react/engine/TopoSortEngine.h" +#include "react/engine/ToposortEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -15,8 +15,8 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqTopoSort, EventStreamTest, TopoSortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSort, EventStreamTest, TopoSortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, EventStreamTest, ToposortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, EventStreamTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulseCount, EventStreamTest, PulseCountEngine); INSTANTIATE_TYPED_TEST_CASE_P(Subtree, EventStreamTest, SubtreeEngine); diff --git a/src/test/EventStreamTestQ.cpp b/src/test/EventStreamTestQ.cpp index ae608a80..c71afb7a 100644 --- a/src/test/EventStreamTestQ.cpp +++ b/src/test/EventStreamTestQ.cpp @@ -7,7 +7,7 @@ #include "EventStreamTest.h" #include "react/engine/PulseCountEngine.h" -#include "react/engine/TopoSortEngine.h" +#include "react/engine/ToposortEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -15,10 +15,10 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqTopoSortQ, EventStreamTest, TopoSortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSortQ, EventStreamTest, TopoSortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, EventStreamTest, ToposortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, EventStreamTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulseCountQ, EventStreamTest, PulseCountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSortP, EventStreamTest, TopoSortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposortP, EventStreamTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, EventStreamTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/src/test/MoveTest.cpp b/src/test/MoveTest.cpp index 7a4029bd..73c17b70 100644 --- a/src/test/MoveTest.cpp +++ b/src/test/MoveTest.cpp @@ -6,13 +6,13 @@ #include "MoveTest.h" -#include "react/engine/TopoSortEngine.h" +#include "react/engine/ToposortEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqTopoSort, MoveTest, TopoSortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, MoveTest, ToposortEngine); } // ~namespace \ No newline at end of file diff --git a/src/test/ObserverTest.cpp b/src/test/ObserverTest.cpp index eec6d900..1eb78fa4 100644 --- a/src/test/ObserverTest.cpp +++ b/src/test/ObserverTest.cpp @@ -7,7 +7,7 @@ #include "ObserverTest.h" #include "react/engine/PulseCountEngine.h" -#include "react/engine/TopoSortEngine.h" +#include "react/engine/ToposortEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -15,8 +15,8 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqTopoSort, ObserverTest, TopoSortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSort, ObserverTest, TopoSortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, ObserverTest, ToposortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, ObserverTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulseCount, ObserverTest, PulseCountEngine); INSTANTIATE_TYPED_TEST_CASE_P(Subtree, ObserverTest, SubtreeEngine); diff --git a/src/test/ObserverTestQ.cpp b/src/test/ObserverTestQ.cpp index d964655d..f1a6782b 100644 --- a/src/test/ObserverTestQ.cpp +++ b/src/test/ObserverTestQ.cpp @@ -7,7 +7,7 @@ #include "ObserverTest.h" #include "react/engine/PulseCountEngine.h" -#include "react/engine/TopoSortEngine.h" +#include "react/engine/ToposortEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -15,10 +15,10 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqTopoSortQ, ObserverTest, TopoSortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSortQ, ObserverTest, TopoSortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, ObserverTest, ToposortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, ObserverTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulseCountQ, ObserverTest, PulseCountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSortP, ObserverTest, TopoSortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposortP, ObserverTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, ObserverTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/src/test/OperationsTest.cpp b/src/test/OperationsTest.cpp index ac13e827..31901df5 100644 --- a/src/test/OperationsTest.cpp +++ b/src/test/OperationsTest.cpp @@ -7,7 +7,7 @@ #include "OperationsTest.h" #include "react/engine/PulseCountEngine.h" -#include "react/engine/TopoSortEngine.h" +#include "react/engine/ToposortEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -15,8 +15,8 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqTopoSort, OperationsTest, TopoSortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSort, OperationsTest, TopoSortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, OperationsTest, ToposortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, OperationsTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulseCount, OperationsTest, PulseCountEngine); INSTANTIATE_TYPED_TEST_CASE_P(Subtree, OperationsTest, SubtreeEngine); diff --git a/src/test/OperationsTestQ.cpp b/src/test/OperationsTestQ.cpp index 571e8cce..9fc3cedc 100644 --- a/src/test/OperationsTestQ.cpp +++ b/src/test/OperationsTestQ.cpp @@ -7,7 +7,7 @@ #include "OperationsTest.h" #include "react/engine/PulseCountEngine.h" -#include "react/engine/TopoSortEngine.h" +#include "react/engine/ToposortEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -15,10 +15,10 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqTopoSortQ, OperationsTest, TopoSortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSortQ, OperationsTest, TopoSortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, OperationsTest, ToposortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, OperationsTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulseCountQ, OperationsTest, PulseCountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSortP, OperationsTest, TopoSortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposortP, OperationsTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, OperationsTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/src/test/SignalTest.cpp b/src/test/SignalTest.cpp index c903c592..ac8fa32d 100644 --- a/src/test/SignalTest.cpp +++ b/src/test/SignalTest.cpp @@ -7,7 +7,7 @@ #include "SignalTest.h" #include "react/engine/PulseCountEngine.h" -#include "react/engine/TopoSortEngine.h" +#include "react/engine/ToposortEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -15,8 +15,8 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqTopoSort, SignalTest, TopoSortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSort, SignalTest, TopoSortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, SignalTest, ToposortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, SignalTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulseCount, SignalTest, PulseCountEngine); INSTANTIATE_TYPED_TEST_CASE_P(Subtree, SignalTest, SubtreeEngine); diff --git a/src/test/SignalTestQ.cpp b/src/test/SignalTestQ.cpp index 9115c184..020391a2 100644 --- a/src/test/SignalTestQ.cpp +++ b/src/test/SignalTestQ.cpp @@ -7,7 +7,7 @@ #include "SignalTest.h" #include "react/engine/PulseCountEngine.h" -#include "react/engine/TopoSortEngine.h" +#include "react/engine/ToposortEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -15,10 +15,10 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqTopoSortQ, SignalTest, TopoSortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSortQ, SignalTest, TopoSortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, SignalTest, ToposortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, SignalTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulseCountQ, SignalTest, PulseCountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSortP, SignalTest, TopoSortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposortP, SignalTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, SignalTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/src/test/TransactionTest.cpp b/src/test/TransactionTest.cpp index 3e3cbe13..43ea882c 100644 --- a/src/test/TransactionTest.cpp +++ b/src/test/TransactionTest.cpp @@ -7,7 +7,7 @@ #include "TransactionTest.h" #include "react/engine/PulseCountEngine.h" -#include "react/engine/TopoSortEngine.h" +#include "react/engine/ToposortEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -15,10 +15,10 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqTopoSort, TransactionTest, TopoSortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSort, TransactionTest, TopoSortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, TransactionTest, ToposortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, TransactionTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulseCount, TransactionTest, PulseCountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParTopoSortP, TransactionTest, TopoSortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposortP, TransactionTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(Subtree, TransactionTest, SubtreeEngine); } // ~namespace \ No newline at end of file From 83167f6cb820e7ee094c3902857bfefa0c1ae3ed Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 30 Apr 2014 13:56:06 +0200 Subject: [PATCH 013/266] ToposortEngine rename step 2. --- include/react/engine/{Topo_SortEngine.h => ToposortEngine.h} | 0 src/engine/{Topo_SortEngine.cpp => ToposortEngine.cpp} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename include/react/engine/{Topo_SortEngine.h => ToposortEngine.h} (100%) rename src/engine/{Topo_SortEngine.cpp => ToposortEngine.cpp} (100%) diff --git a/include/react/engine/Topo_SortEngine.h b/include/react/engine/ToposortEngine.h similarity index 100% rename from include/react/engine/Topo_SortEngine.h rename to include/react/engine/ToposortEngine.h diff --git a/src/engine/Topo_SortEngine.cpp b/src/engine/ToposortEngine.cpp similarity index 100% rename from src/engine/Topo_SortEngine.cpp rename to src/engine/ToposortEngine.cpp From 47026f66104e06c4e43c7268ebe8e3ad3a27937d Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 2 May 2014 16:06:59 +0200 Subject: [PATCH 014/266] More images. --- doc/media/toposort1.graphml | 70 ++--- doc/media/toposort1.png | Bin 25940 -> 25492 bytes doc/media/toposort2.graphml | 570 ++++++++++++++++++++++++++++++++++++ doc/media/toposort2.png | Bin 0 -> 10369 bytes doc/media/toposort3.graphml | 156 ++++++++++ doc/media/toposort3.png | Bin 0 -> 7912 bytes 6 files changed, 744 insertions(+), 52 deletions(-) create mode 100644 doc/media/toposort2.graphml create mode 100644 doc/media/toposort2.png create mode 100644 doc/media/toposort3.graphml create mode 100644 doc/media/toposort3.png diff --git a/doc/media/toposort1.graphml b/doc/media/toposort1.graphml index e2b6e872..1cf13a90 100644 --- a/doc/media/toposort1.graphml +++ b/doc/media/toposort1.graphml @@ -15,7 +15,6 @@ - @@ -34,7 +33,6 @@ - @@ -53,7 +51,6 @@ - @@ -71,12 +68,11 @@ - - + b @@ -89,7 +85,6 @@ - @@ -107,13 +102,13 @@ - - t1 + + @@ -125,7 +120,6 @@ - @@ -143,7 +137,6 @@ - @@ -161,12 +154,11 @@ - - + b @@ -179,7 +171,6 @@ - @@ -197,13 +188,13 @@ - - - t1 + + + @@ -215,7 +206,6 @@ - @@ -233,7 +223,6 @@ - @@ -252,7 +241,6 @@ - @@ -270,12 +258,11 @@ - - + b @@ -288,7 +275,6 @@ - @@ -306,13 +292,13 @@ - - - t1 + + + @@ -324,12 +310,11 @@ - - + x @@ -342,7 +327,6 @@ - @@ -360,7 +344,6 @@ - @@ -379,13 +362,12 @@ - - t=0 + level 0 @@ -397,13 +379,12 @@ - - t=1 + level 1 @@ -415,13 +396,12 @@ - - t=2 + level 2 @@ -433,7 +413,6 @@ - @@ -452,7 +431,6 @@ - @@ -464,7 +442,6 @@ - @@ -476,7 +453,6 @@ - @@ -488,7 +464,6 @@ - @@ -500,7 +475,6 @@ - @@ -512,11 +486,10 @@ - - + @@ -524,7 +497,6 @@ - @@ -536,7 +508,6 @@ - @@ -548,7 +519,6 @@ - @@ -560,11 +530,10 @@ - - + @@ -572,7 +541,6 @@ - @@ -584,11 +552,10 @@ - - + @@ -596,7 +563,6 @@ - diff --git a/doc/media/toposort1.png b/doc/media/toposort1.png index c0cc73bfaee050287e41f835a746592bc501c35b..bf7270ee9f51d7e86cb20e82814425079b0b835c 100644 GIT binary patch literal 25492 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + task 1 + + + + + + + + + + + + + + + + + P= { + + + + + + + + + + + + + + + + + } + + + + + + + + + + + + + + + + + task 2 + + + + + + + + + + + + + + + + + task 3 + + + + + + + + + + + + + + + + + task 4 + + + + + + + + + + + + + + + + + + heavy + + + + + + + + + + + + + + + + + + light + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/toposort2.png b/doc/media/toposort2.png new file mode 100644 index 0000000000000000000000000000000000000000..87f36465eaf0d199bfe8364893890938fe5b203a GIT binary patch literal 10369 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 ac4_4C)HTriFTm_%WGV{kcM9YlJ^No2rQ@ss literal 0 HcmV?d00001 diff --git a/doc/media/toposort3.graphml b/doc/media/toposort3.graphml new file mode 100644 index 00000000..69529a72 --- /dev/null +++ b/doc/media/toposort3.graphml @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + op2 + + + + + + + + + + + + + + + + + op1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + delayed by op2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/toposort3.png b/doc/media/toposort3.png new file mode 100644 index 0000000000000000000000000000000000000000..d60b848a68ad6010db2027f2ecb78e3f8c2f89bb GIT binary patch literal 7912 zcmZvh2UJr{xAzf6=^#?1qx9aShN7T=fb=R&5Qs<#y(NHB1q7uAqzMWrRp~VdNJkKr zUP7n>hCpaZfaE(q?{mL(@49!^Iwx6^GiN3C#biP?M37(cRa(YX*EC zk&#`Xrz8iyE$#Q)lacY%-oJatB6JR25anezi6^xOJJ>aTaDFUNVPaWaHPR+$)>vV} zV#%7!_n7_CRg+s`gIZIr@mp=_sUPZenhbZ!UIz80TNn=_Zo-9zyhg*CfHFKj86rL!k^qq1F9R2n3?5t7~UBpDy0B zL&vEYt5s|x5xo56d7!F;2=mO7wfVqrV`F7zAD=&e?pf37eqVun|NQ*Cg?A}*l}?c# zuV`d$Zr&b(nMw)tepv5L_mc3@ynN>U+B~IOz3ufs#*x?zdNb7X<}+ckDUytitpi(2KJ5Ed z39?ib#exV64ZRL_HqXH5&`dRVb~=D%nG(NbPvaX~?dgd_!&%}%(_F@6ipqGO*VJ+3 zCR5)O&lsyW zL1+>TDv>kkJ>Nv-i}DTi@fpK_DI}WnuhOe--@Y`|EbZI-kmf1n70wNr_hzE|mL>_g zx$I_o2=!otwzf8b%)7p`%hX?JfVoJq@lzFBn>P@?1M~TUF6u?s1gc8D=w$=LHSote zX?k}T();av5?I)DDV8*C1&~M7w`t7wTJs!vaV!4y+$#RD%ITQ41s`@%3aE$P<+j>d zEi%U)owbD=^>thHqHRrOFJ2_#LSoujykQ8ZTeI z{Qh)tZF?d%15=@(piuf!i5L!Q`YOJD5EjU^+%FeKL5I zL5ll^5c%aF_Yos{;lW8jx5wnHEuAvYX7h){z zitT7r28PdoAe}Mvo`r>nmlsSD#MfBJ5x-_%E&puua@BY+q;v&$2g~j1ccz4=Z&8j) z09xfo-ABY!&Cc4|Ce9?`4?ak4j%Q%RT>A*Z=_=hc{`V1;jH68wPm0Dj>8ftfHhJ+6 z9#sy8piRqih3pGuUP=swNWRI$Fa z#W$GlBRn$^Q86mA`F~!_PEGYMZViPvyvfP=e$wF;9??Rg#~<7D_V!-hExIgj#5&Zh zw2ek9c#{mo`yeZ_O7uj4s`yZQWXm;PH$Nd=)Ucm#Zxw$#6n4bDsFJ-Q53CC~I;OP3_tF3{6Rw@w*|DTmNjpc zCQt-B>@r%!Gp9qG)G~zV)3V}L`P8^5Z{m+{@7-$ho-g~by|v}B`$3((Ur#y?27Pya zdLo@I@6q81MJw!^DHHQx)0IEVjYl|W$rfY9hR(QK3|s9}0W+Tq`uRXfG-t0TAAd>i z3r*@Zu*z#pxa9g91<#icW1PdoA9gTHf%FibhlYmkdf6#`ow`5=*23!GACr@AZyDZB zzjma?*G#D1Y`GPDZliu&3fDr2XBEcC3xMl#1VX5!`$q5G9%}YWcT)gdsTVQ8$zODV z>op8Mjp|J(Wl+oe_QuKi65LH!!EEi6w3VW?^m_90wZvG-$;h=d-8Z1)WSHGXZeUQ5 z?X6tv@zTTNVuCxls;a6I1-�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 O-M?paw?^wx^#23rgiR&@ literal 0 HcmV?d00001 From 275ccf30b5d03b64bc9c4d8256c2c7c7c1f6f251 Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 2 May 2014 16:08:00 +0200 Subject: [PATCH 015/266] Moved traits.h --- include/react/{detail => }/Traits.h | 0 project/msvc/CppReact.vcxproj | 4 ++-- project/msvc/CppReact.vcxproj.filters | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) rename include/react/{detail => }/Traits.h (100%) diff --git a/include/react/detail/Traits.h b/include/react/Traits.h similarity index 100% rename from include/react/detail/Traits.h rename to include/react/Traits.h diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index 40c049fa..e8abbb68 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -93,10 +93,9 @@ - - + @@ -112,6 +111,7 @@ + diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index d076e40d..b9dc05c9 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -69,9 +69,6 @@ Header Files\engine - - Header Files\engine - Header Files\detail @@ -90,9 +87,6 @@ Header Files\detail - - Header Files\detail - Header Files\detail\graph @@ -141,6 +135,12 @@ Header Files\detail\graph + + Header Files\engine + + + Header Files + From 27761314403213f4860bb1de3231c1a3a83d3531 Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 2 May 2014 16:09:56 +0200 Subject: [PATCH 016/266] Improved Filter/Transform member variants. --- include/react/Event.h | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index 8316cec8..4ae70c46 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -14,6 +14,7 @@ #include #include "react/Observer.h" +#include "react/Traits.h" #include "react/detail/EventBase.h" /*****************************************/ REACT_BEGIN /*****************************************/ @@ -52,21 +53,23 @@ class Events : public REACT_IMPL::EventStreamBase {} template - Events Filter(F&& f) + Observer Observe(F&& f) const { - return REACT::Filter(*this, std::forward(f)); + return REACT::Observe(*this, std::forward(f)); } template - Events Transform(F&& f) + auto Filter(F&& f) const + -> decltype(REACT::Filter(std::declval(), std::forward(f))) { - return REACT::Transform(*this, std::forward(f)); + return REACT::Filter(*this, std::forward(f)); } template - Observer Observe(F&& f) + auto Transform(F&& f) const + -> decltype(REACT::Transform(std::declval(), std::forward(f))) { - return REACT::Observe(*this, std::forward(f)); + return REACT::Transform(*this, std::forward(f)); } }; @@ -86,7 +89,7 @@ class Events : public REACT_IMPL::EventStreamBase; public: - using ValueT = std::reference_wrapper; + using ValueT = E; Events() = default; Events(const Events&) = default; @@ -100,19 +103,21 @@ class Events : public REACT_IMPL::EventStreamBase - Events Filter(F&& f) + auto Filter(F&& f) const + -> decltype(REACT::Filter(std::declval(), std::forward(f))) { return REACT::Filter(*this, std::forward(f)); } template - Events Transform(F&& f) + auto Transform(F&& f) const + -> decltype(REACT::Transform(std::declval(), std::forward(f))) { return REACT::Transform(*this, std::forward(f)); } template - Observer Observe(F&& f) + Observer Observe(F&& f) const { return REACT::Observe(*this, std::forward(f)); } @@ -232,6 +237,20 @@ class TempEvents : public Events { return std::move(std::static_pointer_cast(ptr_)->StealOp()); } + + template + auto Filter(F&& f) const + -> decltype(REACT::Filter(std::declval(), std::forward(f))) + { + return REACT::Filter(*this, std::forward(f)); + } + + template + auto Transform(F&& f) const + -> decltype(REACT::Transform(std::declval(), std::forward(f))) + { + return REACT::Transform(*this, std::forward(f)); + } }; /******************************************/ REACT_END /******************************************/ From 9d6e4c73d2dbc9a863db3fe9bab493858a9392b3 Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 2 May 2014 16:10:41 +0200 Subject: [PATCH 017/266] Using reinterpret_cast on turnPtr isntead of static_cast. --- include/react/detail/graph/AlgorithmNodes.h | 10 +++++----- include/react/detail/graph/EventNodes.h | 10 +++++----- include/react/detail/graph/ObserverNodes.h | 4 ++-- include/react/detail/graph/ReactorNodes.h | 2 +- include/react/detail/graph/SignalNodes.h | 6 +++--- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index 76992847..8b24dbe0 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -36,7 +36,7 @@ class FoldBaseNode : public SignalNode virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; - TurnT& turn = *static_cast(turnPtr); + TurnT& turn = *reinterpret_cast(turnPtr); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); @@ -181,7 +181,7 @@ class HoldNode : public SignalNode virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; - TurnT& turn = *static_cast(turnPtr); + TurnT& turn = *reinterpret_cast(turnPtr); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); @@ -253,7 +253,7 @@ class SnapshotNode : public SignalNode virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; - TurnT& turn = *static_cast(turnPtr); + TurnT& turn = *reinterpret_cast(turnPtr); trigger_->SetCurrentTurn(turn); @@ -321,7 +321,7 @@ class MonitorNode : public EventStreamNode virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; - TurnT& turn = *static_cast(turnPtr); + TurnT& turn = *reinterpret_cast(turnPtr); SetCurrentTurn(turn, true); @@ -383,7 +383,7 @@ class PulseNode : public EventStreamNode virtual void Tick(void* turnPtr) override { typedef typename D::Engine::TurnT TurnT; - TurnT& turn = *static_cast(turnPtr); + TurnT& turn = *reinterpret_cast(turnPtr); SetCurrentTurn(turn, true); trigger_->SetCurrentTurn(turn); diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index bbd4e401..d7deced7 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -123,7 +123,7 @@ class EventSourceNode : if (events_.size() > 0 && !changedFlag_) { using TurnT = typename D::Engine::TurnT; - TurnT& turn = *static_cast(turnPtr); + TurnT& turn = *reinterpret_cast(turnPtr); SetCurrentTurn(turn, true, true); changedFlag_ = true; @@ -391,7 +391,7 @@ class EventOpNode : public EventStreamNode virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; - TurnT& turn = *static_cast(turnPtr); + TurnT& turn = *reinterpret_cast(turnPtr); SetCurrentTurn(turn, true); @@ -479,7 +479,7 @@ class EventFlattenNode : public EventStreamNode virtual void Tick(void* turnPtr) override { typedef typename D::Engine::TurnT TurnT; - TurnT& turn = *static_cast(turnPtr); + TurnT& turn = *reinterpret_cast(turnPtr); SetCurrentTurn(turn, true); inner_->SetCurrentTurn(turn); @@ -510,11 +510,11 @@ class EventFlattenNode : public EventStreamNode if (events_.size() > 0) { - Engine::OnNodePulse(*this, *static_cast(turnPtr)); + Engine::OnNodePulse(*this, turn); } else { - Engine::OnNodeIdlePulse(*this, *static_cast(turnPtr)); + Engine::OnNodeIdlePulse(*this, turn); } } diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index 5089b31d..b94aa869 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -65,7 +65,7 @@ class SignalObserverNode : public ObserverNode virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; - TurnT& turn = *static_cast(turnPtr); + TurnT& turn = *reinterpret_cast(turnPtr); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); @@ -139,7 +139,7 @@ class EventObserverNode : public ObserverNode virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; - TurnT& turn = *static_cast(turnPtr); + TurnT& turn = *reinterpret_cast(turnPtr); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); diff --git a/include/react/detail/graph/ReactorNodes.h b/include/react/detail/graph/ReactorNodes.h index 570df4c9..c54d6e8d 100644 --- a/include/react/detail/graph/ReactorNodes.h +++ b/include/react/detail/graph/ReactorNodes.h @@ -92,7 +92,7 @@ class ReactorNode : virtual void Tick(void* turnPtr) override { - turnPtr_ = static_cast(turnPtr); + turnPtr_ = reinterpret_cast(turnPtr); REACT_SCOPE_EXIT{ turnPtr_ = nullptr; }; mainLoop_(); diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index 68ef6a11..723291e0 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -94,7 +94,7 @@ class VarNode : if (! impl::Equals(value_, newValue_)) { using TurnT = typename D::Engine::TurnT; - TurnT& turn = *static_cast(turnPtr); + TurnT& turn = *reinterpret_cast(turnPtr); value_ = std::move(newValue_); Engine::OnTurnInputChange(*this, turn); @@ -206,7 +206,7 @@ class SignalOpNode : virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; - TurnT& turn = *static_cast(turnPtr); + TurnT& turn = *reinterpret_cast(turnPtr); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); @@ -288,7 +288,7 @@ class FlattenNode : public SignalNode virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; - TurnT& turn = *static_cast(turnPtr); + TurnT& turn = *reinterpret_cast(turnPtr); auto newInner = outer_->ValueRef().NodePtr(); From c74a5f751775a5bb8f78149dc8901d748d7f4b66 Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 2 May 2014 16:11:38 +0200 Subject: [PATCH 018/266] Misc refactoring. --- include/react/Algorithm.h | 19 +++++------ include/react/ReactiveObject.h | 2 +- include/react/Signal.h | 49 +++++++++++++---------------- include/react/detail/ReactiveBase.h | 2 +- 4 files changed, 33 insertions(+), 39 deletions(-) diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index e8becd1c..940b7228 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -118,9 +118,10 @@ template typename S > auto Changed(const Signal& target) - -> Events + -> Events { - return Transform(Monitor(target), [] (const S& v) { return true; }); + return Monitor(target) + .Transform([] (const S& v) { return EventToken::token; }); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -133,12 +134,12 @@ template typename S = std::decay::type > auto ChangedTo(const Signal& target, V&& value) - -> Events + -> Events { - auto transformFunc = [=] (const S& v) { return v == value; }; - auto filterFunc = [=] (bool v) { return v == true; }; - - return Filter(Transform(Monitor(target), transformFunc), filterFunc); + return Monitor(target) + .Transform([=] (const S& v) { return v == value; }) + .Filter([] (bool v) { return v == true; }) + .Transform([=] (const S& v) { return EventToken::token; }) } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -164,7 +165,7 @@ auto Pulse(const Signal& target, const Events& trigger) template struct Incrementer { - T operator() (T v) const { return v+1; } + T operator()(T v) const { return v+1; } }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -173,7 +174,7 @@ struct Incrementer template struct Decrementer { - T operator() (T v) const { return v-1; } + T operator()(T v) const { return v-1; } }; /******************************************/ REACT_END /******************************************/ diff --git a/include/react/ReactiveObject.h b/include/react/ReactiveObject.h index 0008a96d..d81ae47e 100644 --- a/include/react/ReactiveObject.h +++ b/include/react/ReactiveObject.h @@ -121,7 +121,7 @@ class ReactiveObject #define REACTIVE_REF(obj, name) \ Flatten( \ MakeSignal( \ - [] (REACT_IMPL::Identity::Type::ValueT::type r) \ + [] (const REACT_IMPL::Identity::Type::ValueT& r) \ { \ return static_cast::Type>(r.name); \ }, \ diff --git a/include/react/Signal.h b/include/react/Signal.h index f2b23694..9bca8b5f 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -14,6 +14,7 @@ #include #include "react/Observer.h" +#include "react/Traits.h" #include "react/detail/SignalBase.h" /*****************************************/ REACT_BEGIN /*****************************************/ @@ -39,7 +40,7 @@ class Signal : public REACT_IMPL::SignalBase using ValueT = S; Signal() = default; - Signal(const Signal&) = default; + Signal(const Signal& other) = default; Signal(Signal&& other) : SignalBase{ std::move(other) } @@ -49,20 +50,19 @@ class Signal : public REACT_IMPL::SignalBase SignalBase{ std::move(nodePtr) } {} - const S& Value() const - { - return BaseT::getValue(); - } + const S& Value() const { return BaseT::getValue(); } + const S& operator()() const { return BaseT::getValue(); } - const S& operator()() const + template + Observer Observe(F&& f) const { - return BaseT::getValue(); + return REACT::Observe(*this, std::forward(f)); } - template - Observer Observe(F&& f) + template ::value>::type> + S Flatten() const { - return REACT::Observe(*this, std::forward(f)); + return REACT::Flatten(*this); } }; @@ -82,7 +82,7 @@ class Signal : public REACT_IMPL::SignalBase> using NodePtrT = REACT_IMPL::SharedPtrT; public: - using ValueT = std::reference_wrapper; + using ValueT = S; Signal() = default; Signal(const Signal&) = default; @@ -95,18 +95,11 @@ class Signal : public REACT_IMPL::SignalBase> SignalBase{ std::move(nodePtr) } {} - const S& Value() const - { - return BaseT::getValue(); - } - - const S& operator()() const - { - return BaseT::getValue(); - } + const S& Value() const { return BaseT::getValue(); } + const S& operator()() const { return BaseT::getValue(); } template - Observer Observe(F&& f) + Observer Observe(F&& f) const { return REACT::Observe(*this, std::forward(f)); } @@ -126,7 +119,7 @@ class VarSignal : public Signal using NodeT = REACT_IMPL::VarNode; using NodePtrT = REACT_IMPL::SharedPtrT; -public: +public: VarSignal() = default; VarSignal(const VarSignal&) = default; @@ -164,7 +157,9 @@ class VarSignal : public Signal> using NodeT = REACT_IMPL::VarNode>; using NodePtrT = REACT_IMPL::SharedPtrT; -public: +public: + using ValueT = S; + VarSignal() = default; VarSignal(const VarSignal&) = default; @@ -637,18 +632,16 @@ struct InputPack template InputPack(const Signal& first, const Signal& second) : Data{ std::tie(first, second) } - { - } + {} template InputPack(const InputPack& curArgs, const Signal& newArg) : Data{ std::tuple_cat(curArgs.Data, std::tie(newArg)) } - { - } + {} }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Comma operator overload to create input pack from 2 nodes. +/// Comma operator overload to create input pack from 2 signals. /////////////////////////////////////////////////////////////////////////////////////////////////// template < diff --git a/include/react/detail/ReactiveBase.h b/include/react/detail/ReactiveBase.h index 19c98e21..7db8f8ca 100644 --- a/include/react/detail/ReactiveBase.h +++ b/include/react/detail/ReactiveBase.h @@ -12,10 +12,10 @@ #include #include +#include "react/Traits.h" #include "react/common/Types.h" #include "react/detail/ReactiveInput.h" #include "react/detail/Options.h" -#include "react/detail/Traits.h" #include "react/detail/graph/GraphBase.h" #ifdef REACT_ENABLE_LOGGING From f0691a65097ebeb18ab2aa6aceb3744b473bef04 Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 2 May 2014 16:12:08 +0200 Subject: [PATCH 019/266] Test cases for Signal member variants of Observe and Flatten. --- src/test/SignalTest.h | 45 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/test/SignalTest.h b/src/test/SignalTest.h index f29085f5..3f69d9fc 100644 --- a/src/test/SignalTest.h +++ b/src/test/SignalTest.h @@ -509,6 +509,48 @@ TYPED_TEST_P(SignalTest, Flatten4) ASSERT_TRUE(std::find(results.begin(), results.end(), 600) != results.end()); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Member1 test +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(SignalTest, Member1) +{ + auto in1 = MyDomain::MakeVar(10); + auto in2 = MyDomain::MakeVar(std::string{ "a" }); + + auto x = in1 + 1; + + in1.Observe([] (int v) { + ASSERT_EQ(v, 20); + }); + + x.Observe([] (int v) { + ASSERT_EQ(v, 21); + }); + + in2.Observe([] (const std::string& v) { + ASSERT_EQ(v, "b"); + }); + + in1 <<= 20; + in2 <<= std::string{ "b"}; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Member2 test +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(SignalTest, Member2) +{ + auto outer = MyDomain::MakeVar(10); + auto inner = MyDomain::MakeVar(outer); + auto flattened = inner.Flatten(); + + flattened.Observe([] (int v) { + ASSERT_EQ(v, 30); + }); + + outer <<= 30; +} + /////////////////////////////////////////////////////////////////////////////////////////////////// REGISTER_TYPED_TEST_CASE_P ( @@ -516,7 +558,8 @@ REGISTER_TYPED_TEST_CASE_P MakeVars, Signals1, Signals2, Signals3, Signals4, FunctionBind1, FunctionBind2, - Flatten1, Flatten2, Flatten3, Flatten4 + Flatten1, Flatten2, Flatten3, Flatten4, + Member1, Member2 ); } // ~namespace From 627f2e631c54332562f3e5565a3ed042fe913a37 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 5 May 2014 02:31:28 +0200 Subject: [PATCH 020/266] Added graph images. --- doc/media/reactives1.graphml | 151 +++++++++++++++++ doc/media/reactives1.png | Bin 0 -> 6920 bytes doc/media/reactives2.graphml | 308 +++++++++++++++++++++++++++++++++++ doc/media/reactives2.png | Bin 0 -> 13167 bytes 4 files changed, 459 insertions(+) create mode 100644 doc/media/reactives1.graphml create mode 100644 doc/media/reactives1.png create mode 100644 doc/media/reactives2.graphml create mode 100644 doc/media/reactives2.png diff --git a/doc/media/reactives1.graphml b/doc/media/reactives1.graphml new file mode 100644 index 00000000..aa855cad --- /dev/null +++ b/doc/media/reactives1.graphml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + a + + + + + + + + + + + + + + + + + b + + + + + + + + + + + + + + + + + c + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/reactives1.png b/doc/media/reactives1.png new file mode 100644 index 0000000000000000000000000000000000000000..705e0557670b2940842656351e01bfc6e6a01245 GIT binary patch literal 6920 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 OFjOHLil5}o!~O@&A!(xk literal 0 HcmV?d00001 diff --git a/doc/media/reactives2.graphml b/doc/media/reactives2.graphml new file mode 100644 index 00000000..1c902632 --- /dev/null +++ b/doc/media/reactives2.graphml @@ -0,0 +1,308 @@ + + + + + + + + + + + + + + + + + + + + + + + + 2 + + + + + + + + + + + + + + + + + + 2 + + + + + + + + + + + + + + + + + + 2 + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + a + + + + + + + + + + + + + + + + + + b + + + + + + + + + + + + + + + + + + c + + + + + + + + + + + + + + + + + + x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/reactives2.png b/doc/media/reactives2.png new file mode 100644 index 0000000000000000000000000000000000000000..f8fab071c131a69d684a81baa5fe33a032682f43 GIT binary patch literal 13167 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 literal 0 HcmV?d00001 From 5ae85afc713dfd446b317e749854393b35e4d48c Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 5 May 2014 02:32:38 +0200 Subject: [PATCH 021/266] Got rid of INT_MAX and some other refactoring. --- include/react/common/Containers.h | 6 +++--- include/react/common/SourceIdSet.h | 4 ++-- include/react/common/TopoQueue.h | 18 +++++++++--------- include/react/detail/Defs.h | 10 ++++++++++ 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/include/react/common/Containers.h b/include/react/common/Containers.h index 385c1a41..a1f778b3 100644 --- a/include/react/common/Containers.h +++ b/include/react/common/Containers.h @@ -75,7 +75,7 @@ class NodeVector /////////////////////////////////////////////////////////////////////////////////////////////////// struct SplitTag {}; -template +template class NodeBuffer { public: @@ -83,7 +83,7 @@ class NodeBuffer using iterator = typename DataT::iterator; using const_iterator = typename DataT::const_iterator; - static const int split_size = N / 2; + static const uint split_size = N / 2; NodeBuffer() : size_{ 0 }, @@ -167,7 +167,7 @@ class NodeBuffer } DataT nodes_; - int size_; + uint size_; iterator front_; iterator back_; }; diff --git a/include/react/common/SourceIdSet.h b/include/react/common/SourceIdSet.h index d02bf23a..aeef0d25 100644 --- a/include/react/common/SourceIdSet.h +++ b/include/react/common/SourceIdSet.h @@ -113,8 +113,8 @@ class SourceIdSet } private: - MutexT mutex_; - DataT data_; + MutexT mutex_; + DataT data_; bool isSorted_ = false; void sort() diff --git a/include/react/common/TopoQueue.h b/include/react/common/TopoQueue.h index 222bfe66..a46bdfef 100644 --- a/include/react/common/TopoQueue.h +++ b/include/react/common/TopoQueue.h @@ -10,6 +10,7 @@ #include #include +#include #include #include "tbb/enumerable_thread_specific.h" @@ -48,7 +49,7 @@ class TopoQueue { next_.clear(); - minLevel_ = INT_MAX; + minLevel_ = std::numeric_limits::max(); for (const auto& e : data_) { auto l = LevelFunctorT{}(e); @@ -76,7 +77,7 @@ class TopoQueue DataT next_; DataT data_; - int minLevel_ = INT_MAX; + int minLevel_ = std::numeric_limits::max(); }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -107,14 +108,13 @@ class WeightedRange using WeightFunctorT = NodeWeightHelper; WeightedRange() = default; - WeightedRange(const WeightedRange& other) = default; + WeightedRange(const WeightedRange&) = default; WeightedRange(const TIt& a, const TIt& b, uint weight) : begin_{ a }, end_{ b }, weight_{ weight } - { - } + {} WeightedRange(WeightedRange& source, tbb::split) { @@ -188,7 +188,7 @@ class ConcurrentTopoQueue uint totalWeight = 0; // Determine current min level - minLevel_ = INT_MAX; + minLevel_ = std::numeric_limits::max(); for (const auto& buf : collectBuffer_) if (minLevel_ > buf.MinLevel) minLevel_ = buf.MinLevel; @@ -208,7 +208,7 @@ class ConcurrentTopoQueue v.resize(std::distance(v.begin(), p)); // Calc new min level and weight for this buffer - buf.MinLevel = INT_MAX; + buf.MinLevel = std::numeric_limits::max(); int oldWeight = buf.Weight; buf.Weight = 0; for (const T& x : v) @@ -245,11 +245,11 @@ class ConcurrentTopoQueue struct ThreadLocalBuffer { DataT Data; - int MinLevel = INT_MAX; + int MinLevel = std::numeric_limits::max(); uint Weight = 0; }; - int minLevel_ = INT_MAX; + int minLevel_ = std::numeric_limits::max(); DataT nodes_; RangeT range_; diff --git a/include/react/detail/Defs.h b/include/react/detail/Defs.h index 3d69a6da..2be17696 100644 --- a/include/react/detail/Defs.h +++ b/include/react/detail/Defs.h @@ -6,6 +6,16 @@ #pragma once +#define WINDOWS_LEAN_AND_MEAN +#define NOMINMAX + +#ifdef max + #undef max +#endif +#ifdef min + #undef min +#endif + #include /////////////////////////////////////////////////////////////////////////////////////////////////// From a47c5b2f3e82d6fa90d7ab6ea41800ad91e24f83 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 5 May 2014 02:33:42 +0200 Subject: [PATCH 022/266] One more INT_MAX. --- include/react/engine/ToposortEngine.h | 6 ++++-- src/engine/ToposortEngine.cpp | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/include/react/engine/ToposortEngine.h b/include/react/engine/ToposortEngine.h index 5b963add..ee9433cb 100644 --- a/include/react/engine/ToposortEngine.h +++ b/include/react/engine/ToposortEngine.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,7 @@ using std::atomic; using std::condition_variable; using std::function; using std::mutex; +using std::numeric_limits; using std::pair; using std::set; using std::vector; @@ -245,8 +247,8 @@ class PipeliningTurn : public TurnBase PipeliningTurn* successor_ = nullptr; int currentLevel_ = -1; - int maxLevel_ = INT_MAX; /// This turn may only advance up to maxLevel - int minLevel_ = -1; /// successor.maxLevel = this.minLevel - 1 + int maxLevel_ = numeric_limits::max(); /// This turn may only advance up to maxLevel + int minLevel_ = -1; /// successor.maxLevel = this.minLevel - 1 int curUpperBound_ = -1; diff --git a/src/engine/ToposortEngine.cpp b/src/engine/ToposortEngine.cpp index f27e3694..62930b0c 100644 --- a/src/engine/ToposortEngine.cpp +++ b/src/engine/ToposortEngine.cpp @@ -331,7 +331,7 @@ void PipeliningTurn::Remove() } else if (successor_) { - successor_->SetMaxLevel(INT_MAX); + successor_->SetMaxLevel(numeric_limits::max()); successor_->predecessor_ = nullptr; } @@ -492,7 +492,7 @@ void PipeliningEngine::OnDynamicNodeDetach(ParNode& node, ParNode& parent, Pipel void PipeliningEngine::applyDynamicAttach(ParNode& node, ParNode& parent, PipeliningTurn& turn) { - turn.WaitForMaxLevel(INT_MAX); + turn.WaitForMaxLevel(numeric_limits::max()); OnNodeAttach(node, parent); From f4e913835e3448a509ae6387353f289cb2a362f4 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 5 May 2014 02:34:12 +0200 Subject: [PATCH 023/266] Traits -> TypeTraits --- include/react/{Traits.h => TypeTraits.h} | 3 +++ 1 file changed, 3 insertions(+) rename include/react/{Traits.h => TypeTraits.h} (93%) diff --git a/include/react/Traits.h b/include/react/TypeTraits.h similarity index 93% rename from include/react/Traits.h rename to include/react/TypeTraits.h index 778aacb1..09f84092 100644 --- a/include/react/Traits.h +++ b/include/react/TypeTraits.h @@ -10,6 +10,9 @@ /*****************************************/ REACT_BEGIN /*****************************************/ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Forward declarations +/////////////////////////////////////////////////////////////////////////////////////////////////// template class Signal; From 2a9aa232d72858c6f68b573f4f9d3685e4c3ec60 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 5 May 2014 02:36:12 +0200 Subject: [PATCH 024/266] Moved DomainBase out of detail namespace to Domain.h, restructured some includes. --- include/react/Algorithm.h | 64 ++++--- include/react/Domain.h | 222 +++++++++++++++++++++++ include/react/Event.h | 2 +- include/react/ReactiveObject.h | 47 ++++- include/react/Signal.h | 8 +- include/react/common/Types.h | 2 - include/react/detail/ReactiveBase.h | 226 ------------------------ include/react/detail/ReactiveInput.h | 30 +++- include/react/detail/graph/EventNodes.h | 2 +- project/msvc/CppReact.vcxproj | 3 +- project/msvc/CppReact.vcxproj.filters | 5 +- 11 files changed, 341 insertions(+), 270 deletions(-) create mode 100644 include/react/Domain.h diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index 940b7228..f8d18fa3 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -12,13 +12,27 @@ #include #include -#include "Event.h" -#include "Signal.h" - #include "react/detail/graph/AlgorithmNodes.h" /*****************************************/ REACT_BEGIN /*****************************************/ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Forward declarations +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class Signal; + +template +class VarSignal; + +template +class Events; + +template +class EventSource; + +enum class EventToken; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Fold /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -28,12 +42,13 @@ template typename V, typename E, typename FIn, - typename S = std::decay::type, - typename F = std::decay::type + typename S = std::decay::type > auto Fold(V&& init, const Events& events, FIn&& func) -> Signal { + using F = std::decay::type; + return Signal( std::make_shared>( std::forward(init), events.NodePtr(), std::forward(func))); @@ -48,12 +63,13 @@ template typename V, typename E, typename FIn, - typename S = std::decay::type, - typename F = std::decay::type + typename S = std::decay::type > auto Iterate(V&& init, const Events& events, FIn&& func) -> Signal { + using F = std::decay::type; + return Signal( std::make_shared>( std::forward(init), events.NodePtr(), std::forward(func))); @@ -109,6 +125,23 @@ auto Monitor(const Signal& target) target.NodePtr())); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Pulse +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename S, + typename E +> +auto Pulse(const Signal& target, const Events& trigger) + -> Events +{ + return Events( + std::make_shared>( + target.NodePtr(), trigger.NodePtr())); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Changed /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -142,23 +175,6 @@ auto ChangedTo(const Signal& target, V&& value) .Transform([=] (const S& v) { return EventToken::token; }) } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Pulse -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename E -> -auto Pulse(const Signal& target, const Events& trigger) - -> Events -{ - return Events( - std::make_shared>( - target.NodePtr(), trigger.NodePtr())); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Incrementer /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/Domain.h b/include/react/Domain.h new file mode 100644 index 00000000..5050b4c7 --- /dev/null +++ b/include/react/Domain.h @@ -0,0 +1,222 @@ + +// 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) + +#pragma once + +#include "react/detail/Defs.h" + +#include "react/TypeTraits.h" +#include "react/detail/ReactiveInput.h" +#include "react/detail/Options.h" + +#ifdef REACT_ENABLE_LOGGING + #include "react/detail/logging/EventLog.h" + #include "react/detail/logging/EventRecords.h" +#endif //REACT_ENABLE_LOGGING + +#include "react/detail/IReactiveEngine.h" +#include "react/engine/ToposortEngine.h" + +/*****************************************/ REACT_BEGIN /*****************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Forward declarations +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class ReactiveLoop; + +template +class Observer; + +template +class Signal; + +template +class VarSignal; + +template +class TempSignal; + +template +class Events; + +template +class EventSource; + +template +class TempEvents; + +enum class EventToken; + +using REACT_IMPL::TurnFlagsT; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// DomainBase +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class DomainBase +{ +public: + using TurnT = typename TPolicy::Engine::TurnT; + + DomainBase() = delete; + + using Policy = TPolicy; + using Engine = REACT_IMPL::EngineInterface; + + /////////////////////////////////////////////////////////////////////////////////////////////////// + /// Aliases for reactives of current domain + /////////////////////////////////////////////////////////////////////////////////////////////////// + template + using SignalT = Signal; + + template + using VarSignalT = VarSignal; + + template + using EventsT = Events; + + template + using EventSourceT = EventSource; + + using ObserverT = Observer; + + using ReactiveLoopT = ReactiveLoop; + + /////////////////////////////////////////////////////////////////////////////////////////////////// + /// MakeVar + /////////////////////////////////////////////////////////////////////////////////////////////////// + template + < + typename V, + typename S = std::decay::type, + class = std::enable_if< + !IsSignal::value>::type + > + static auto MakeVar(V&& value) + -> VarSignalT + { + return REACT::MakeVar(std::forward(value)); + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + /// MakeVar (higher order) + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + template + < + typename V, + typename S = std::decay::type, + typename TInner = S::ValueT, + class = std::enable_if< + IsSignal::value>::type + > + static auto MakeVar(V&& value) + -> VarSignalT> + { + return REACT::MakeVar(std::forward(value)); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + /// MakeSignal + /////////////////////////////////////////////////////////////////////////////////////////////////// + template + < + typename FIn, + typename ... TArgs, + typename F = std::decay::type, + typename S = std::result_of::type, + typename TOp = REACT_IMPL::FunctionOp ...> + > + static auto MakeSignal(FIn&& func, const SignalT& ... args) + -> TempSignal + { + return REACT::MakeSignal(std::forward(func), args ...); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + /// MakeEventSource + /////////////////////////////////////////////////////////////////////////////////////////////////// + template + static auto MakeEventSource() + -> EventSourceT + { + return REACT::MakeEventSource(); + } + + static auto MakeEventSource() + -> EventSourceT + { + return REACT::MakeEventSource(); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + /// DoTransaction + /////////////////////////////////////////////////////////////////////////////////////////////////// + template + static void DoTransaction(F&& func) + { + REACT_IMPL::InputManager::DoTransaction(0, std::forward(func)); + } + + template + static void DoTransaction(TurnFlagsT flags, F&& func) + { + REACT_IMPL::InputManager::DoTransaction(flags, std::forward(func)); + } + +#ifdef REACT_ENABLE_LOGGING + /////////////////////////////////////////////////////////////////////////////////////////////// + /// Log + /////////////////////////////////////////////////////////////////////////////////////////////// + static EventLog& Log() + { + static ObserverRegistry instance; + return instance; + } +#endif //REACT_ENABLE_LOGGING +}; + +/******************************************/ REACT_END /******************************************/ + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Policy +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename TEngine = ToposortEngine +> +struct DomainPolicy +{ + using Engine = TEngine; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Ensure singletons are created immediately after domain declaration (TODO hax) +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class DomainInitializer +{ +public: + DomainInitializer() + { +#ifdef REACT_ENABLE_LOGGING + DomainSpecificData::Log(); +#endif //REACT_ENABLE_LOGGING + + typename D::Engine::Engine(); + } +}; + +/****************************************/ REACT_IMPL_END /***************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Domain definition macro +/////////////////////////////////////////////////////////////////////////////////////////////////// +#define REACTIVE_DOMAIN(name, ...) \ + struct name : public REACT::DomainBase> {}; \ + REACT_IMPL::DomainInitializer< name > name ## _initializer_; \ No newline at end of file diff --git a/include/react/Event.h b/include/react/Event.h index 4ae70c46..510b8f19 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -14,7 +14,7 @@ #include #include "react/Observer.h" -#include "react/Traits.h" +#include "react/TypeTraits.h" #include "react/detail/EventBase.h" /*****************************************/ REACT_BEGIN /*****************************************/ diff --git a/include/react/ReactiveObject.h b/include/react/ReactiveObject.h index d81ae47e..90f1c456 100644 --- a/include/react/ReactiveObject.h +++ b/include/react/ReactiveObject.h @@ -11,10 +11,41 @@ #include #include -#include "react/detail/ReactiveBase.h" +#include "react/TypeTraits.h" +#include "react/common/Util.h" + /*****************************************/ REACT_BEGIN /*****************************************/ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Forward declarations +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class Signal; + +template +class VarSignal; + +template +class TempSignal; + +template +class Events; + +template +class EventSource; + +template +class TempEvents; + +template +class ReactiveLoop; + +template +class Observer; + +enum class EventToken; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// ReactiveObject /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -91,15 +122,15 @@ class ReactiveObject /////////////////////////////////////////////////////////////////////////////////////////////////// template < - typename F, - typename ... TArgs + typename FIn, + typename ... TArgs, + typename F = std::decay::type, + typename S = std::result_of::type > - static auto MakeSignal(F&& func, const SignalT& ... args) - -> SignalT::type> + static auto MakeSignal(FIn&& func, const SignalT& ... args) + -> SignalT { - using S = typename std::result_of::type; - - return REACT::MakeSignal(std::forward(func), args ...); + return REACT::MakeSignal(std::forward(func), args ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/Signal.h b/include/react/Signal.h index 9bca8b5f..49de5993 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -14,7 +14,7 @@ #include #include "react/Observer.h" -#include "react/Traits.h" +#include "react/TypeTraits.h" #include "react/detail/SignalBase.h" /*****************************************/ REACT_BEGIN /*****************************************/ @@ -306,8 +306,8 @@ template typename D, typename FIn, typename ... TArgs, - typename F = std::decay::type, - typename S = std::result_of::type, + typename F = std::decay::type, + typename S = std::result_of::type, typename TOp = REACT_IMPL::FunctionOp ...> > auto MakeSignal(FIn&& func, const Signal& ... args) @@ -453,7 +453,7 @@ template typename F = name ## OpLFunctor, \ typename S = std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp>, \ + REACT_IMPL::SignalNodePtrT>, \ class = std::enable_if< \ IsSignal::value>::type, \ class = std::enable_if< \ diff --git a/include/react/common/Types.h b/include/react/common/Types.h index 07c3e3b8..37b0f217 100644 --- a/include/react/common/Types.h +++ b/include/react/common/Types.h @@ -21,8 +21,6 @@ ObjectId GetObjectId(const O& obj) return (ObjectId)&obj; } -using TurnIdT = uint; -using TurnFlagsT = uint; using UpdateDurationT = std::chrono::duration; /****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file diff --git a/include/react/detail/ReactiveBase.h b/include/react/detail/ReactiveBase.h index 7db8f8ca..09aaac7a 100644 --- a/include/react/detail/ReactiveBase.h +++ b/include/react/detail/ReactiveBase.h @@ -8,23 +8,11 @@ #include "react/detail/Defs.h" -#include #include #include -#include "react/Traits.h" -#include "react/common/Types.h" -#include "react/detail/ReactiveInput.h" -#include "react/detail/Options.h" #include "react/detail/graph/GraphBase.h" -#ifdef REACT_ENABLE_LOGGING - #include "react/detail/logging/EventLog.h" - #include "react/detail/logging/EventRecords.h" -#endif //REACT_ENABLE_LOGGING - -#include "react/engine/ToposortEngine.h" - /***************************************/ REACT_IMPL_BEGIN /**************************************/ template @@ -84,217 +72,3 @@ class ReactiveBase }; /****************************************/ REACT_IMPL_END /***************************************/ - - - -/*****************************************/ REACT_BEGIN /*****************************************/ - -enum class EventToken; - -template -class ReactiveLoop; - -template -class Observer; - -template -class Signal; - -template -class VarSignal; - -template -class TempSignal; - -template -class Events; - -template -class EventSource; - -template -class TempEvents; - -/******************************************/ REACT_END /******************************************/ - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Domain -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename TEngine = ToposortEngine -> -struct DomainPolicy -{ - using Engine = TEngine; -}; - -template -class DomainBase -{ -public: - using TurnT = typename TPolicy::Engine::TurnT; - - DomainBase() = delete; - - using Policy = TPolicy; - using Engine = EngineInterface; - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// Aliases for reactives of current domain - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - using SignalT = Signal; - - template - using VarSignalT = VarSignal; - - template - using EventsT = Events; - - template - using EventSourceT = EventSource; - - using ObserverT = Observer; - - using ReactiveLoopT = ReactiveLoop; - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// MakeVar - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - < - typename V, - typename S = std::decay::type, - class = std::enable_if< - !IsSignal::value>::type - > - static auto MakeVar(V&& value) - -> VarSignalT - { - return REACT::MakeVar(std::forward(value)); - } - - ////////////////////////////////////////////////////////////////////////////////////////////////////////// - // MakeVar (higher order) - ////////////////////////////////////////////////////////////////////////////////////////////////////////// - template - < - typename V, - typename S = std::decay::type, - typename TInner = S::ValueT, - class = std::enable_if< - IsSignal::value>::type - > - static auto MakeVar(V&& value) - -> VarSignalT> - { - return REACT::MakeVar(std::forward(value)); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// MakeSignal - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - < - typename FIn, - typename ... TArgs, - typename F = std::decay::type, - typename S = std::result_of::type, - typename TOp = REACT_IMPL::FunctionOp ...> - > - static auto MakeSignal(FIn&& func, const SignalT& ... args) - -> TempSignal - { - return REACT::MakeSignal(std::forward(func), args ...); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// MakeEventSource - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - static auto MakeEventSource() - -> EventSourceT - { - return REACT::MakeEventSource(); - } - - static auto MakeEventSource() - -> EventSourceT - { - return REACT::MakeEventSource(); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// DoTransaction - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - static void DoTransaction(F&& func) - { - InputManager::DoTransaction(0, std::forward(func)); - } - - template - static void DoTransaction(TurnFlagsT flags, F&& func) - { - InputManager::DoTransaction(flags, std::forward(func)); - } - -#ifdef REACT_ENABLE_LOGGING - /////////////////////////////////////////////////////////////////////////////////////////////// - /// Log - /////////////////////////////////////////////////////////////////////////////////////////////// - static EventLog& Log() - { - static ObserverRegistry instance; - return instance; - } -#endif //REACT_ENABLE_LOGGING -}; - -///////////////////////////////////////////////////////////////////////////////////////////////////// -///// Ensure singletons are created immediately after domain declaration (TODO hax) -///////////////////////////////////////////////////////////////////////////////////////////////////// -template -class DomainInitializer -{ -public: - DomainInitializer() - { -#ifdef REACT_ENABLE_LOGGING - DomainSpecificData::Log(); -#endif //REACT_ENABLE_LOGGING - - typename D::Engine::Engine(); - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ContinuationHolder -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ContinuationHolder -{ -public: - using TurnT = typename D::TurnT; - - ContinuationHolder() = delete; - - static void SetTurn(TurnT& turn) { ptr_ = &turn.continuation_; } - static void Clear() { ptr_ = nullptr; } - static ContinuationInput* Get() { return ptr_; } - -private: - static REACT_TLS ContinuationInput* ptr_; -}; - -template -ContinuationInput* ContinuationHolder::ptr_(nullptr); - -/****************************************/ REACT_IMPL_END /***************************************/ - -#define REACTIVE_DOMAIN(name, ...) \ - struct name : public REACT_IMPL::DomainBase> {}; \ - REACT_IMPL::DomainInitializer< name > name ## _initializer_; diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index 6999b0c3..f9dee44e 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -19,6 +20,9 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ +using TurnIdT = uint; +using TurnFlagsT = uint; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// ContinuationInput /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -61,6 +65,28 @@ class ContinuationInput std::unique_ptr bufferedInputsPtr_ = nullptr; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ContinuationHolder +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class ContinuationHolder +{ +public: + using TurnT = typename D::TurnT; + + ContinuationHolder() = delete; + + static void SetTurn(TurnT& turn) { ptr_ = &turn.continuation_; } + static void Clear() { ptr_ = nullptr; } + static ContinuationInput* Get() { return ptr_; } + +private: + static REACT_TLS ContinuationInput* ptr_; +}; + +template +ContinuationInput* ContinuationHolder::ptr_(nullptr); + /////////////////////////////////////////////////////////////////////////////////////////////////// /// InputManager /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -129,8 +155,8 @@ class InputManager { auto curId = nextTurnId_.fetch_add(1, std::memory_order_relaxed); - if (curId == INT_MAX) - nextTurnId_.fetch_sub(INT_MAX); + if (curId == std::numeric_limits::max()) + nextTurnId_.fetch_sub(std::numeric_limits::max()); return curId; } diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index d7deced7..0a1c6aa1 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -66,7 +66,7 @@ class EventStreamNode : protected: DataT events_; - uint curTurnId_ = INT_MAX; + uint curTurnId_ = std::numeric_limits::max(); }; template diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index e8abbb68..d492b2c8 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -93,6 +93,7 @@ + @@ -111,7 +112,7 @@ - + diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index b9dc05c9..afd8ec79 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -138,7 +138,10 @@ Header Files\engine - + + Header Files + + Header Files From 1d1b58cb4211d79e4612d3c13ad1e2db794260e1 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 5 May 2014 02:37:40 +0200 Subject: [PATCH 025/266] Added Domain.h include to tests etc. --- src/benchmark/BenchmarkLifeSim.h | 3 ++ src/benchmark/Main.cpp | 1 + src/sandbox/Main.cpp | 65 +++++++++++++++++++++++++++++++- src/test/EventStreamTest.h | 1 + src/test/MoveTest.h | 1 + src/test/ObserverTest.h | 1 + src/test/OperationsTest.h | 3 ++ src/test/SignalTest.h | 1 + src/test/TransactionTest.h | 1 + 9 files changed, 75 insertions(+), 2 deletions(-) diff --git a/src/benchmark/BenchmarkLifeSim.h b/src/benchmark/BenchmarkLifeSim.h index 263449e7..28858331 100644 --- a/src/benchmark/BenchmarkLifeSim.h +++ b/src/benchmark/BenchmarkLifeSim.h @@ -15,6 +15,9 @@ #include #include "BenchmarkBase.h" + +#include "react/Signal.h" +#include "react/Event.h" #include "react/ReactiveObject.h" #include "react/logging/EventLog.h" diff --git a/src/benchmark/Main.cpp b/src/benchmark/Main.cpp index da0fc0ef..bd066a4c 100644 --- a/src/benchmark/Main.cpp +++ b/src/benchmark/Main.cpp @@ -17,6 +17,7 @@ #include "BenchmarkSequence.h" #include "BenchmarkLifeSim.h" +#include "react/Domain.h" #include "react/Signal.h" #include "react/Algorithm.h" #include "react/common/Util.h" diff --git a/src/sandbox/Main.cpp b/src/sandbox/Main.cpp index a79f3658..1c73775a 100644 --- a/src/sandbox/Main.cpp +++ b/src/sandbox/Main.cpp @@ -15,13 +15,12 @@ // Experimental. Requires boost::coroutine. #define REACT_ENABLE_REACTORS +#include "react/Domain.h" #include "react/Signal.h" #include "react/Event.h" #include "react/Algorithm.h" #include "react/ReactiveObject.h" - - using namespace std; using namespace react; @@ -36,6 +35,66 @@ REACTIVE_DOMAIN(D); //REACTIVE_DOMAIN(D, PulseCountEngine); +void SignalExample0() +{ + // Sequential + { + REACTIVE_DOMAIN(D, ToposortEngine); + + auto a = D::MakeVar(1); + auto b = D::MakeVar(2); + auto c = D::MakeVar(3); + + auto x = (a + b) * c; + + b <<= 20; + } + + // Parallel + { + REACTIVE_DOMAIN(D, ToposortEngine); + + auto in = D::MakeVar(0); + + auto op1 = in ->* [] (int in) + { + int result = in /* Costly operation #1 */; + return result; + }; + + auto op2 = in ->* [] (int in) + { + int result = in /* Costly operation #2 */; + return result; + }; + + auto out = op1 + op2; + + in <<= 123456789; + } + + // Queuing + { + REACTIVE_DOMAIN(D, ToposortEngine); + + auto a = D::MakeVar(1); + auto b = D::MakeVar(2); + auto c = D::MakeVar(3); + + auto x = (a + b) * c; + + std::thread t1{ [&] { a <<= 10; } }; + std::thread t2{ [&] { b <<= 100; } }; + std::thread t3{ [&] { c <<= 1000; } }; + std::thread t4{ [&] { a <<= 10000; } }; + + t1.join(); + t2.join(); + t3.join(); + t4.join(); + } +} + void SignalExample1() { cout << "Signal Example 1" << endl; @@ -330,6 +389,8 @@ void LoopTest() int main() { + SignalExample0(); + SignalExample1(); SignalExample2(); SignalExample3(); diff --git a/src/test/EventStreamTest.h b/src/test/EventStreamTest.h index be0552d7..5e7ca7bd 100644 --- a/src/test/EventStreamTest.h +++ b/src/test/EventStreamTest.h @@ -11,6 +11,7 @@ #include "gtest/gtest.h" +#include "react/Domain.h" #include "react/Event.h" /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/MoveTest.h b/src/test/MoveTest.h index 522ee473..7848126e 100644 --- a/src/test/MoveTest.h +++ b/src/test/MoveTest.h @@ -8,6 +8,7 @@ #include "gtest/gtest.h" +#include "react/Domain.h" #include "react/Signal.h" /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/ObserverTest.h b/src/test/ObserverTest.h index 977abb54..89ee8f96 100644 --- a/src/test/ObserverTest.h +++ b/src/test/ObserverTest.h @@ -8,6 +8,7 @@ #include "gtest/gtest.h" +#include "react/Domain.h" #include "react/Signal.h" /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/OperationsTest.h b/src/test/OperationsTest.h index 35b43663..d7b92ec9 100644 --- a/src/test/OperationsTest.h +++ b/src/test/OperationsTest.h @@ -11,6 +11,9 @@ #include #include +#include "react/Domain.h" +#include "react/Signal.h" +#include "react/Event.h" #include "react/Algorithm.h" /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/SignalTest.h b/src/test/SignalTest.h index 3f69d9fc..80cb4635 100644 --- a/src/test/SignalTest.h +++ b/src/test/SignalTest.h @@ -10,6 +10,7 @@ #include +#include "react/Domain.h" #include "react/Signal.h" /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/TransactionTest.h b/src/test/TransactionTest.h index f1e4cf66..484536ae 100644 --- a/src/test/TransactionTest.h +++ b/src/test/TransactionTest.h @@ -12,6 +12,7 @@ #include #include +#include "react/Domain.h" #include "react/Signal.h" /////////////////////////////////////////////////////////////////////////////////////////////////// From ee88bc7ce975d7a0fdea7102c866481b6852cf26 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 5 May 2014 02:58:52 +0200 Subject: [PATCH 026/266] Fixed Transform. --- include/react/Event.h | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index 510b8f19..7a25d2ab 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -452,34 +452,36 @@ auto operator&(TEvents&& src, F&& filter) template < typename D, - typename E, + typename EIn, typename FIn, typename F = std::decay::type, - typename TOp = REACT_IMPL::EventTransformOp> + typename EOut = std::result_of::type, + typename TOp = REACT_IMPL::EventTransformOp> > -auto Transform(const Events& src, FIn&& func) - -> TempEvents +auto Transform(const Events& src, FIn&& func) + -> TempEvents { - return TempEvents( - std::make_shared>( + return TempEvents( + std::make_shared>( std::forward(func), src.NodePtr())); } template < typename D, - typename E, + typename EIn, typename TOpIn, typename FIn, typename F = std::decay::type, - typename TOpOut = REACT_IMPL::EventTransformOp + typename EOut = std::result_of::type, + typename TOpOut = REACT_IMPL::EventTransformOp > -auto Transform(TempEvents&& src, FIn&& func) - -> TempEvents +auto Transform(TempEvents&& src, FIn&& func) + -> TempEvents { - return TempEvents( - std::make_shared>( + return TempEvents( + std::make_shared>( std::forward(func), src.StealOp())); } From 46f73cf7dcaed66b620cb6a6f3d554b0ecaebfda Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 5 May 2014 03:07:09 +0200 Subject: [PATCH 027/266] Consistent naming scheme for engines. --- include/react/engine/PulseCountEngine.h | 14 ++--- src/benchmark/Main.cpp | 52 +++++++++---------- ...eCountEngine.cpp => PulseCountEngine_.cpp} | 4 +- src/engine/SubtreeEngine.cpp | 2 +- src/sandbox/Main.cpp | 4 +- src/test/EventStreamTest.cpp | 4 +- src/test/EventStreamTestQ.cpp | 4 +- src/test/ObserverTest.cpp | 4 +- src/test/ObserverTestQ.cpp | 4 +- src/test/OperationsTest.cpp | 4 +- src/test/OperationsTestQ.cpp | 4 +- src/test/SignalTest.cpp | 4 +- src/test/SignalTestQ.cpp | 4 +- src/test/TransactionTest.cpp | 4 +- 14 files changed, 56 insertions(+), 56 deletions(-) rename src/engine/{PulseCountEngine.cpp => PulseCountEngine_.cpp} (99%) diff --git a/include/react/engine/PulseCountEngine.h b/include/react/engine/PulseCountEngine.h index 5ccbef17..733a7032 100644 --- a/include/react/engine/PulseCountEngine.h +++ b/include/react/engine/PulseCountEngine.h @@ -134,12 +134,12 @@ struct parallel; struct parallel_queue; template -class PulseCountEngine; +class PulsecountEngine; -template <> class PulseCountEngine : +template <> class PulsecountEngine : public REACT_IMPL::pulsecount::BasicEngine {}; -template <> class PulseCountEngine : +template <> class PulsecountEngine : public REACT_IMPL::pulsecount::QueuingEngine {}; /******************************************/ REACT_END /******************************************/ @@ -147,11 +147,11 @@ template <> class PulseCountEngine : /***************************************/ REACT_IMPL_BEGIN /**************************************/ template struct EnableNodeUpdateTimer; -template <> struct EnableNodeUpdateTimer> : std::true_type {}; -template <> struct EnableNodeUpdateTimer> : std::true_type {}; +template <> struct EnableNodeUpdateTimer> : std::true_type {}; +template <> struct EnableNodeUpdateTimer> : std::true_type {}; template struct EnableParallelUpdating; -template <> struct EnableParallelUpdating> : std::true_type {}; -template <> struct EnableParallelUpdating> : std::true_type {}; +template <> struct EnableParallelUpdating> : std::true_type {}; +template <> struct EnableParallelUpdating> : std::true_type {}; /****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file diff --git a/src/benchmark/Main.cpp b/src/benchmark/Main.cpp index bd066a4c..b72424bf 100644 --- a/src/benchmark/Main.cpp +++ b/src/benchmark/Main.cpp @@ -24,7 +24,7 @@ #include "react/logging/EventLog.h" #include "react/engine/ToposortEngine.h" -#include "react/engine/PulseCountEngine.h" +#include "react/engine/PulsecountEngine.h" #include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -34,22 +34,22 @@ using namespace react; REACTIVE_DOMAIN(ToposortSTDomain, ToposortEngine); REACTIVE_DOMAIN(ToposortDomain, ToposortEngine); -REACTIVE_DOMAIN(PulseCountDomain, PulseCountEngine); +REACTIVE_DOMAIN(PulsecountDomain, PulsecountEngine); REACTIVE_DOMAIN(SubtreeDomain, SubtreeEngine); void runBenchmarkGrid(std::ostream& out) { RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(20, 10000), - ToposortSTDomain, ToposortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulsecountDomain); RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(30, 10000), - ToposortSTDomain, ToposortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulsecountDomain); RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(40, 10000), - ToposortSTDomain, ToposortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulsecountDomain); RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(50, 10000), - ToposortSTDomain, ToposortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulsecountDomain); } void runBenchmarkRandom(std::ostream& out) @@ -68,7 +68,7 @@ void runBenchmarkRandom(std::ostream& out) { int x = (slowPercent * (w*(h-1))) / 100; //RUN_BENCHMARK(out, 5, Benchmark_Random, BenchmarkParams_Random(w, h, 20, 0, 10, 40, x, true, seed1, seed2), - // ToposortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortDomain, ELMDomain, PulsecountDomain, SourceSetDomain); RUN_BENCHMARK(out, 5, Benchmark_Random, BenchmarkParams_Random(w, h, 20, 0, 10, 40, x, true, seed1, seed2), ToposortSTDomain); @@ -82,43 +82,43 @@ void runBenchmarkRandom(std::ostream& out) void runBenchmarkFanout(std::ostream& out) { //RUN_BENCHMARK(out, 5, Benchmark_Fanout, BenchmarkParams_Fanout(10, 10000, 0), - // ToposortSTDomain, ToposortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortSTDomain, ToposortDomain, ELMDomain, PulsecountDomain, SourceSetDomain); //RUN_BENCHMARK(out, 5, Benchmark_Fanout, BenchmarkParams_Fanout(100, 10000, 0), - // ToposortSTDomain, ToposortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortSTDomain, ToposortDomain, ELMDomain, PulsecountDomain, SourceSetDomain); //RUN_BENCHMARK(out, 5, Benchmark_Fanout, BenchmarkParams_Fanout(1000, 10000, 0), - // ToposortSTDomain, ToposortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortSTDomain, ToposortDomain, ELMDomain, PulsecountDomain, SourceSetDomain); RUN_BENCHMARK(out, 3, Benchmark_Fanout, BenchmarkParams_Fanout(10, 10, 10), - ToposortSTDomain, ToposortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulsecountDomain); RUN_BENCHMARK(out, 3, Benchmark_Fanout, BenchmarkParams_Fanout(100, 10, 10), - ToposortSTDomain, ToposortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulsecountDomain); RUN_BENCHMARK(out, 3, Benchmark_Fanout, BenchmarkParams_Fanout(1000, 10, 10), - ToposortSTDomain, ToposortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulsecountDomain); } void runBenchmarkSequence(std::ostream& out) { //RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(10, 10000, 0), - // ToposortSTDomain, ToposortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortSTDomain, ToposortDomain, ELMDomain, PulsecountDomain, SourceSetDomain); //RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(100, 10000, 0), - // ToposortSTDomain, ToposortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortSTDomain, ToposortDomain, ELMDomain, PulsecountDomain, SourceSetDomain); //RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(1000, 10000, 0), - // ToposortSTDomain, ToposortDomain, ELMDomain, PulseCountDomain, SourceSetDomain); + // ToposortSTDomain, ToposortDomain, ELMDomain, PulsecountDomain, SourceSetDomain); RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(10, 10, 10), - ToposortSTDomain, ToposortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulsecountDomain); RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(100, 10, 10), - ToposortSTDomain, ToposortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulsecountDomain); RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(1000, 10, 10), - ToposortSTDomain, ToposortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulsecountDomain); } void runBenchmarkLifeSim(std::ostream& out) @@ -127,13 +127,13 @@ void runBenchmarkLifeSim(std::ostream& out) // ELMDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(250, 30, 10000), - // SourceSetDomain, PulseCountDomain); + // SourceSetDomain, PulsecountDomain); RUN_BENCHMARK(out, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 15, 10000), - ToposortSTDomain, ToposortDomain, PulseCountDomain); + ToposortSTDomain, ToposortDomain, PulsecountDomain); //RUN_BENCHMARK(out, 3, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 50, 100), - // PulseCountDomain, PulseCountDomain); + // PulsecountDomain, PulsecountDomain); } void runBenchmarks() @@ -165,10 +165,10 @@ void runBenchmarks() void debugBenchmarks() { - using TestDomain = PulseCountDomain; + using TestDomain = PulsecountDomain; RUN_BENCHMARK(std::cout, 3, Benchmark_Grid, BenchmarkParams_Grid(30, 1), - PulseCountDomain); + PulsecountDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_Fanout, BenchmarkParams_Fanout(1000, 5, 0), // TestDomain); @@ -215,14 +215,14 @@ void profileBenchmark() { RUN_BENCHMARK(std::cout, 3, Benchmark_Grid, BenchmarkParams_Grid(30, 10000), ToposortSTDomain); - //ToposortSTDomain, ToposortDomain, ELMDomain, PulseCountDomain, SubtreeDomain, SourceSetDomain); + //ToposortSTDomain, ToposortDomain, ELMDomain, PulsecountDomain, SubtreeDomain, SourceSetDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_Grid, BenchmarkParams_Grid(30, 10000), // SourceSetDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_Random, BenchmarkParams_Random(20, 11, 100, 0, 5, 80, 20, false, 41556, 21624), //SubtreeDomain); - //ToposortSTDomain, ToposortDomain, PulseCountDomain, SubtreeDomain); + //ToposortSTDomain, ToposortDomain, PulsecountDomain, SubtreeDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 15, 10000), // ToposortDomain); diff --git a/src/engine/PulseCountEngine.cpp b/src/engine/PulseCountEngine_.cpp similarity index 99% rename from src/engine/PulseCountEngine.cpp rename to src/engine/PulseCountEngine_.cpp index af298221..f38bddf5 100644 --- a/src/engine/PulseCountEngine.cpp +++ b/src/engine/PulseCountEngine_.cpp @@ -4,7 +4,7 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#include "react/engine/PulseCountEngine.h" +#include "react/engine/PulsecountEngine.h" #include #include @@ -182,7 +182,7 @@ Turn::Turn(TurnIdT id, TurnFlagsT flags) : } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// PulseCountEngine +/// PulsecountEngine /////////////////////////////////////////////////////////////////////////////////////////////////// template diff --git a/src/engine/SubtreeEngine.cpp b/src/engine/SubtreeEngine.cpp index 2e4ac6c0..2edea3a1 100644 --- a/src/engine/SubtreeEngine.cpp +++ b/src/engine/SubtreeEngine.cpp @@ -30,7 +30,7 @@ Turn::Turn(TurnIdT id, TurnFlagsT flags) : } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// PulseCountEngine +/// PulsecountEngine /////////////////////////////////////////////////////////////////////////////////////////////////// template diff --git a/src/sandbox/Main.cpp b/src/sandbox/Main.cpp index 1c73775a..ec4b28b3 100644 --- a/src/sandbox/Main.cpp +++ b/src/sandbox/Main.cpp @@ -31,9 +31,9 @@ REACTIVE_DOMAIN(D); // Explicit engine: //#include "react/engine/SubtreeEngine.h" -//#include "react/engine/PulseCountEngine.h" +//#include "react/engine/PulsecountEngine.h" -//REACTIVE_DOMAIN(D, PulseCountEngine); +//REACTIVE_DOMAIN(D, PulsecountEngine); void SignalExample0() { diff --git a/src/test/EventStreamTest.cpp b/src/test/EventStreamTest.cpp index 8613bb4f..ade5f6ae 100644 --- a/src/test/EventStreamTest.cpp +++ b/src/test/EventStreamTest.cpp @@ -6,7 +6,7 @@ #include "EventStreamTest.h" -#include "react/engine/PulseCountEngine.h" +#include "react/engine/PulsecountEngine.h" #include "react/engine/ToposortEngine.h" #include "react/engine/SubtreeEngine.h" @@ -17,7 +17,7 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, EventStreamTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, EventStreamTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(PulseCount, EventStreamTest, PulseCountEngine); +INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, EventStreamTest, PulsecountEngine); INSTANTIATE_TYPED_TEST_CASE_P(Subtree, EventStreamTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/src/test/EventStreamTestQ.cpp b/src/test/EventStreamTestQ.cpp index c71afb7a..4a625420 100644 --- a/src/test/EventStreamTestQ.cpp +++ b/src/test/EventStreamTestQ.cpp @@ -6,7 +6,7 @@ #include "EventStreamTest.h" -#include "react/engine/PulseCountEngine.h" +#include "react/engine/PulsecountEngine.h" #include "react/engine/ToposortEngine.h" #include "react/engine/SubtreeEngine.h" @@ -17,7 +17,7 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, EventStreamTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, EventStreamTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(PulseCountQ, EventStreamTest, PulseCountEngine); +INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, EventStreamTest, PulsecountEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParToposortP, EventStreamTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, EventStreamTest, SubtreeEngine); diff --git a/src/test/ObserverTest.cpp b/src/test/ObserverTest.cpp index 1eb78fa4..4c068f00 100644 --- a/src/test/ObserverTest.cpp +++ b/src/test/ObserverTest.cpp @@ -6,7 +6,7 @@ #include "ObserverTest.h" -#include "react/engine/PulseCountEngine.h" +#include "react/engine/PulsecountEngine.h" #include "react/engine/ToposortEngine.h" #include "react/engine/SubtreeEngine.h" @@ -17,7 +17,7 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, ObserverTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, ObserverTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(PulseCount, ObserverTest, PulseCountEngine); +INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, ObserverTest, PulsecountEngine); INSTANTIATE_TYPED_TEST_CASE_P(Subtree, ObserverTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/src/test/ObserverTestQ.cpp b/src/test/ObserverTestQ.cpp index f1a6782b..4d1d36f6 100644 --- a/src/test/ObserverTestQ.cpp +++ b/src/test/ObserverTestQ.cpp @@ -6,7 +6,7 @@ #include "ObserverTest.h" -#include "react/engine/PulseCountEngine.h" +#include "react/engine/PulsecountEngine.h" #include "react/engine/ToposortEngine.h" #include "react/engine/SubtreeEngine.h" @@ -17,7 +17,7 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, ObserverTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, ObserverTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(PulseCountQ, ObserverTest, PulseCountEngine); +INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, ObserverTest, PulsecountEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParToposortP, ObserverTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, ObserverTest, SubtreeEngine); diff --git a/src/test/OperationsTest.cpp b/src/test/OperationsTest.cpp index 31901df5..d1a89f52 100644 --- a/src/test/OperationsTest.cpp +++ b/src/test/OperationsTest.cpp @@ -6,7 +6,7 @@ #include "OperationsTest.h" -#include "react/engine/PulseCountEngine.h" +#include "react/engine/PulsecountEngine.h" #include "react/engine/ToposortEngine.h" #include "react/engine/SubtreeEngine.h" @@ -17,7 +17,7 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, OperationsTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, OperationsTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(PulseCount, OperationsTest, PulseCountEngine); +INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, OperationsTest, PulsecountEngine); INSTANTIATE_TYPED_TEST_CASE_P(Subtree, OperationsTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/src/test/OperationsTestQ.cpp b/src/test/OperationsTestQ.cpp index 9fc3cedc..c77ea687 100644 --- a/src/test/OperationsTestQ.cpp +++ b/src/test/OperationsTestQ.cpp @@ -6,7 +6,7 @@ #include "OperationsTest.h" -#include "react/engine/PulseCountEngine.h" +#include "react/engine/PulsecountEngine.h" #include "react/engine/ToposortEngine.h" #include "react/engine/SubtreeEngine.h" @@ -17,7 +17,7 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, OperationsTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, OperationsTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(PulseCountQ, OperationsTest, PulseCountEngine); +INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, OperationsTest, PulsecountEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParToposortP, OperationsTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, OperationsTest, SubtreeEngine); diff --git a/src/test/SignalTest.cpp b/src/test/SignalTest.cpp index ac8fa32d..fbe9bbe4 100644 --- a/src/test/SignalTest.cpp +++ b/src/test/SignalTest.cpp @@ -6,7 +6,7 @@ #include "SignalTest.h" -#include "react/engine/PulseCountEngine.h" +#include "react/engine/PulsecountEngine.h" #include "react/engine/ToposortEngine.h" #include "react/engine/SubtreeEngine.h" @@ -17,7 +17,7 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, SignalTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, SignalTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(PulseCount, SignalTest, PulseCountEngine); +INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, SignalTest, PulsecountEngine); INSTANTIATE_TYPED_TEST_CASE_P(Subtree, SignalTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/src/test/SignalTestQ.cpp b/src/test/SignalTestQ.cpp index 020391a2..fc45d8ea 100644 --- a/src/test/SignalTestQ.cpp +++ b/src/test/SignalTestQ.cpp @@ -6,7 +6,7 @@ #include "SignalTest.h" -#include "react/engine/PulseCountEngine.h" +#include "react/engine/PulsecountEngine.h" #include "react/engine/ToposortEngine.h" #include "react/engine/SubtreeEngine.h" @@ -17,7 +17,7 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, SignalTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, SignalTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(PulseCountQ, SignalTest, PulseCountEngine); +INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, SignalTest, PulsecountEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParToposortP, SignalTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, SignalTest, SubtreeEngine); diff --git a/src/test/TransactionTest.cpp b/src/test/TransactionTest.cpp index 43ea882c..7b5f4fc0 100644 --- a/src/test/TransactionTest.cpp +++ b/src/test/TransactionTest.cpp @@ -6,7 +6,7 @@ #include "TransactionTest.h" -#include "react/engine/PulseCountEngine.h" +#include "react/engine/PulsecountEngine.h" #include "react/engine/ToposortEngine.h" #include "react/engine/SubtreeEngine.h" @@ -17,7 +17,7 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, TransactionTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, TransactionTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(PulseCount, TransactionTest, PulseCountEngine); +INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, TransactionTest, PulsecountEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParToposortP, TransactionTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(Subtree, TransactionTest, SubtreeEngine); From 4e96369bf88c0a03df5cee924a42bfeed97721a6 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 5 May 2014 03:07:36 +0200 Subject: [PATCH 028/266] PulseCount -> Pulsecount --- src/engine/{PulseCountEngine_.cpp => PulsecountEngine.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/engine/{PulseCountEngine_.cpp => PulsecountEngine.cpp} (100%) diff --git a/src/engine/PulseCountEngine_.cpp b/src/engine/PulsecountEngine.cpp similarity index 100% rename from src/engine/PulseCountEngine_.cpp rename to src/engine/PulsecountEngine.cpp From a236aae0ee868db41599997d96707ba5470ca9b8 Mon Sep 17 00:00:00 2001 From: Sebastian Jeckel Date: Mon, 5 May 2014 03:39:31 +0200 Subject: [PATCH 029/266] Update README.md --- README.md | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 82ce612f..f36cae1a 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,12 @@ Signals are time-varying reactive values, that can be combined to create reactiv These expressions are automatically recalculated whenever one of their dependent values changes. ```C++ +#include "react/Domain.h" #include "react/Signal.h" ///... using namespace react; -REACTIVE_DOMAIN(MyDomain); +REACTIVE_DOMAIN(D); auto width = MyDomain::MakeVar(1); auto height = MyDomain::MakeVar(2); @@ -50,7 +51,8 @@ For more information, see the [Signal guide](SignalGuide) Event streams represent flows of discrete values as first-class objects, based on ideas found in [Deprecating the Observer Pattern](http://infoscience.epfl.ch/record/176887/files/DeprecatingObservers2012.pdf). ```C++ -#include "react/EventStream.h" +#include "react/Domain.h" +#include "react/Event.h" //... using namespace react; @@ -71,27 +73,29 @@ Depending on the selected engine, independent propagation paths are automaticall For more details, see Propagation Engines. ```C++ -#include "react/propagation/TopoSortEngine.h" +#include "react/propagation/ToposortEngine.h" //... using namespace react; // Single-threaded updating -REACTIVE_DOMAIN(MyDomain, TopoSortEngine); +REACTIVE_DOMAIN(MyDomain, ToposortEngine); // Parallel updating -REACTIVE_DOMAIN(MyDomain, TopoSortEngine); +REACTIVE_DOMAIN(MyDomain, ToposortEngine); // Input from multiple threads -REACTIVE_DOMAIN(MyDomain, TopoSortEngine); -REACTIVE_DOMAIN(MyDomain, TopoSortEngine); +REACTIVE_DOMAIN(MyDomain, ToposortEngine); +REACTIVE_DOMAIN(MyDomain, ToposortEngine); // Parallel updating + input from multiple threads + pipelining -REACTIVE_DOMAIN(MyDomain, TopoSortEngine); +REACTIVE_DOMAIN(MyDomain, ToposortEngine); ``` #### Reactive loops ```C++ +#include "react/Domain.h" +#include "react/Event.h" #include "react/Reactor.h" //... using namespace std; @@ -143,6 +147,9 @@ mouseUp << PointT(30,30); #### Reactive objects and dynamic reactives ```C++ +#include "react/Domain.h" +#include "react/Signal.h" +#include "react/Event.h" #include "react/ReactiveObject.h" //... using namespace std; From a23bfe38fe36833b83bfaf662f9d7d55179ef1ae Mon Sep 17 00:00:00 2001 From: Sebastian Jeckel Date: Mon, 5 May 2014 03:45:23 +0200 Subject: [PATCH 030/266] Update README.md --- README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f36cae1a..40bab404 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,9 @@ The general idea is that dependency relations between data/actions are expressed #### Compiling -I mainly tested the build on Windows with Visual Studio 2013. +Currently, the build has only been tested in Visual Studio 2013. The Intel C++ Compiler 14.0 with Visual Studio 2012/13 is theoretically supported as well, but it doesn't compile the current codebase anymore due to [some bugs]() with C++11 support. -Cpp.React uses standard C++11 and the dependencies are portable, so other compilers/platforms should work, too. - ###### Dependencies * [Intel TBB 4.2](https://www.threadingbuildingblocks.org/) (required) * [Google test framework](https://code.google.com/p/googletest/) (optional, to compile the tests) @@ -48,7 +46,7 @@ For more information, see the [Signal guide](SignalGuide) #### Events -Event streams represent flows of discrete values as first-class objects, based on ideas found in [Deprecating the Observer Pattern](http://infoscience.epfl.ch/record/176887/files/DeprecatingObservers2012.pdf). +Event streams represent flows of discrete values as first-class objects. ```C++ #include "react/Domain.h" @@ -68,9 +66,8 @@ Observe(clicked, [] { cout << "button clicked!" << endl; }); #### Implicit parallelism -The change propagation is handled implicitly by a so called propagation engine. +The change propagation is handled implicitly by a _propagation engine_. Depending on the selected engine, independent propagation paths are automatically parallelized. -For more details, see Propagation Engines. ```C++ #include "react/propagation/ToposortEngine.h" From 21f7a19be9972f9a7be887ba504de1da45c433b8 Mon Sep 17 00:00:00 2001 From: Sebastian Jeckel Date: Mon, 5 May 2014 03:45:52 +0200 Subject: [PATCH 031/266] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 40bab404..3f28850d 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,6 @@ width <<= 10; cout << "area: " << area() << endl; // => area: 20 ``` -For more information, see the [Signal guide](SignalGuide) - #### Events Event streams represent flows of discrete values as first-class objects. From 3f61e69df2affde8b2abb18a4c0aabfae9a5f117 Mon Sep 17 00:00:00 2001 From: Sebastian Jeckel Date: Mon, 5 May 2014 03:52:56 +0200 Subject: [PATCH 032/266] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 3f28850d..138a3f7d 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,6 @@ mouseUp << PointT(30,30); ```C++ #include "react/Domain.h" #include "react/Signal.h" -#include "react/Event.h" #include "react/ReactiveObject.h" //... using namespace std; From 93680f4644ba3c01bd17ccd734531e07d1d52d13 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 5 May 2014 13:59:54 +0200 Subject: [PATCH 033/266] Pulsecount cleanup. --- project/msvc/CppReact.vcxproj | 2 +- project/msvc/CppReact.vcxproj.filters | 6 +++--- src/engine/PulsecountEngine.cpp | 9 +++------ 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index d492b2c8..3b121a34 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -115,7 +115,7 @@ - + diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index afd8ec79..3605a740 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -152,14 +152,14 @@ Source Files\logging - - Source Files\engine - Source Files\engine Source Files\engine + + Source Files\engine + \ No newline at end of file diff --git a/src/engine/PulsecountEngine.cpp b/src/engine/PulsecountEngine.cpp index f38bddf5..730759b0 100644 --- a/src/engine/PulsecountEngine.cpp +++ b/src/engine/PulsecountEngine.cpp @@ -32,13 +32,11 @@ class MarkerTask: public task template MarkerTask(TInput srcBegin, TInput srcEnd) : nodes_{ srcBegin, srcEnd } - { - } + {} MarkerTask(MarkerTask& other, SplitTag) : nodes_{ other.nodes_, SplitTag{} } - { - } + {} task* execute() { @@ -178,8 +176,7 @@ class UpdaterTask: public task /////////////////////////////////////////////////////////////////////////////////////////////////// Turn::Turn(TurnIdT id, TurnFlagsT flags) : TurnBase(id, flags) -{ -} +{} /////////////////////////////////////////////////////////////////////////////////////////////////// /// PulsecountEngine From 4a51f2459fa7f0dc0f02ae8104da5cbcf473e46b Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 5 May 2014 14:00:51 +0200 Subject: [PATCH 034/266] Added member variant of react::Merge and replaced universal reference in some signatures with explicit reference types. --- include/react/Event.h | 46 +++++++++++++++++++++++++++++++----------- include/react/Signal.h | 35 ++++++++++++++++++++------------ 2 files changed, 56 insertions(+), 25 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index 7a25d2ab..151eef75 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -102,6 +102,13 @@ class Events : public REACT_IMPL::EventStreamBase + auto Merge(const Events& ... args) + -> decltype(REACT::Merge(std::declval(), std::forward(args) ...)) + { + return REACT::Merge(*this, std::forward(args) ...); + } + template auto Filter(F&& f) const -> decltype(REACT::Filter(std::declval(), std::forward(f))) @@ -149,10 +156,14 @@ class EventSource : public Events Events{ std::move(nodePtr) } {} - template - void Emit(T&& e) const + void Emit(const E& e) const + { + BaseT::emit(e); + } + + void Emit(E&& e) const { - BaseT::emit(std::forward(e)); + BaseT::emit(std::move(e)); } template ::value>::type> @@ -161,10 +172,15 @@ class EventSource : public Events BaseT::emit(EventToken::token); } - template - const EventSource& operator<<(T&& e) const + const EventSource& operator<<(const E& e) const + { + BaseT::emit(e); + return *this; + } + + const EventSource& operator<<(E&& e) const { - BaseT::emit(std::forward(e)); + BaseT::emit(std::move(e)); return *this; } }; @@ -198,7 +214,6 @@ class EventSource : public Events> BaseT::emit(e); } - template const EventSource& operator<<(std::reference_wrapper e) const { BaseT::emit(e); @@ -238,6 +253,13 @@ class TempEvents : public Events return std::move(std::static_pointer_cast(ptr_)->StealOp()); } + template + auto Merge(const Events& ... args) + -> decltype(REACT::Merge(std::declval(), std::forward(args) ...)) + { + return REACT::Merge(*this, std::forward(args) ...); + } + template auto Filter(F&& f) const -> decltype(REACT::Filter(std::declval(), std::forward(f))) @@ -521,15 +543,15 @@ template < typename D, typename FIn, - typename TArg, + typename E, class = std::enable_if< - ! std::is_same::value>::type + ! std::is_same::value>::type > -auto Observe(const Events& subject, FIn&& func) +auto Observe(const Events& subject, FIn&& func) -> Observer { using F = std::decay::type; - using TNode = REACT_IMPL::EventObserverNode; + using TNode = REACT_IMPL::EventObserverNode; auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). template Register(subject, std::forward(func)); @@ -545,7 +567,7 @@ template auto Observe(const Events& subject, FIn&& func) -> Observer { - auto wrapper = [func] (EventToken _) { func(); }; + auto wrapper = [func] (EventToken) { func(); }; using TNode = REACT_IMPL::EventObserverNode; diff --git a/include/react/Signal.h b/include/react/Signal.h index 49de5993..da9e19cc 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -40,7 +40,7 @@ class Signal : public REACT_IMPL::SignalBase using ValueT = S; Signal() = default; - Signal(const Signal& other) = default; + Signal(const Signal&) = default; Signal(Signal&& other) : SignalBase{ std::move(other) } @@ -59,7 +59,7 @@ class Signal : public REACT_IMPL::SignalBase return REACT::Observe(*this, std::forward(f)); } - template ::value>::type> + template ::value>::type> S Flatten() const { return REACT::Flatten(*this); @@ -131,16 +131,25 @@ class VarSignal : public Signal Signal{ std::move(nodePtr) } {} - template - void Set(T&& newValue) const + void Set(const S& newValue) const + { + BaseT::setValue(newValue); + } + + void Set(S&& newValue) const { - BaseT::setValue(std::forward(newValue)); + BaseT::setValue(std::move(newValue)); + } + + const VarSignal& operator<<=(const S& newValue) const + { + BaseT::setValue(newValue); + return *this; } - template - const VarSignal& operator<<=(T&& newValue) const + const VarSignal& operator<<=(S&& newValue) const { - BaseT::setValue(std::forward(newValue)); + BaseT::setValue(std::move(newValue)); return *this; } }; @@ -305,15 +314,15 @@ template < typename D, typename FIn, - typename ... TArgs, + typename ... TValues, typename F = std::decay::type, - typename S = std::result_of::type, - typename TOp = REACT_IMPL::FunctionOp ...> + typename S = std::result_of::type, + typename TOp = REACT_IMPL::FunctionOp ...> > -auto MakeSignal(FIn&& func, const Signal& ... args) +auto MakeSignal(FIn&& func, const Signal& ... args) -> TempSignal { - static_assert(sizeof...(TArgs) > 0, + static_assert(sizeof...(TValues) > 0, "react::MakeSignal requires at least 1 signal dependency."); return TempSignal( From df403cffda637ca33a0ae4cab725af880f33a64e Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 6 May 2014 14:26:47 +0200 Subject: [PATCH 035/266] Added Forward function for events. Could be useful for splitting observers. --- include/react/Event.h | 15 +++++++ include/react/detail/graph/EventNodes.h | 53 +++++++++++++++++++++++++ src/test/EventStreamTest.h | 39 +++++++++++++++++- 3 files changed, 106 insertions(+), 1 deletion(-) diff --git a/include/react/Event.h b/include/react/Event.h index 151eef75..a480e0ee 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -308,6 +308,21 @@ auto MakeEventSource() std::make_shared>()); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Forward +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename E +> +auto Forward(const Events& other) + -> Events +{ + return Events( + std::make_shared>(other.NodePtr())); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Merge /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 0a1c6aa1..f6389f07 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -140,6 +140,59 @@ class EventSourceNode : bool changedFlag_ = false; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventForwardNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename E +> +class EventForwardNode : public EventStreamNode +{ +public: + EventForwardNode(const SharedPtrT>& other) : + EventStreamNode{ }, + other_{ other } + { + Engine::OnNodeCreate(*this); + Engine::OnNodeAttach(*this, *other_); + } + + ~EventForwardNode() + { + Engine::OnNodeDetach(*this, *other_); + Engine::OnNodeDestroy(*this); + } + + virtual const char* GetNodeType() const override { return "EventForwardNode"; } + virtual int DependencyCount() const override { return 1; } + + virtual void Tick(void* turnPtr) override + { + typedef typename D::Engine::TurnT TurnT; + TurnT& turn = *reinterpret_cast(turnPtr); + + SetCurrentTurn(turn, true); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + + events_.insert(events_.end(), other_->Events().begin(), other_->Events().end()); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + + if (events_.size() > 0) + Engine::OnNodePulse(*this, turn); + else + Engine::OnNodeIdlePulse(*this, turn); + } + +private: + SharedPtrT> other_; +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventMergeOp /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/EventStreamTest.h b/src/test/EventStreamTest.h index 5e7ca7bd..cb6a0a94 100644 --- a/src/test/EventStreamTest.h +++ b/src/test/EventStreamTest.h @@ -255,6 +255,42 @@ TYPED_TEST_P(EventStreamTest, EventTransform) ASSERT_TRUE(std::find(results.begin(), results.end(), "HELLO VORLD") != results.end()); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventForward test +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(EventStreamTest, EventForward) +{ + using std::string; + + std::queue results; + + auto in = MyDomain::MakeEventSource(); + auto phase1 = Forward(in); + auto phase2 = Forward(phase1); + auto phase3 = Forward(phase2); + + Observe(phase3, [&] (int v) + { + results.push(v); + }); + + in << 1 << 2 << 3; + + ASSERT_FALSE(results.empty()); + ASSERT_EQ(results.front(),1); + results.pop(); + + ASSERT_FALSE(results.empty()); + ASSERT_EQ(results.front(),2); + results.pop(); + + ASSERT_FALSE(results.empty()); + ASSERT_EQ(results.front(),3); + results.pop(); + + ASSERT_TRUE(results.empty()); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// REGISTER_TYPED_TEST_CASE_P ( @@ -264,7 +300,8 @@ REGISTER_TYPED_TEST_CASE_P EventMerge2, EventMerge3, EventFilter, - EventTransform + EventTransform, + EventForward ); } // ~namespace From 890a87efc353bb7a6567014d4eb8766d20159a00 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 6 May 2014 18:43:16 +0200 Subject: [PATCH 036/266] Added ScopedObserver. --- include/react/Domain.h | 5 +++++ include/react/Observer.h | 36 +++++++++++++++++++++++++++++++--- include/react/ReactiveObject.h | 5 +++++ src/test/ObserverTest.h | 30 +++++++++++++++++++++++++++- 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/include/react/Domain.h b/include/react/Domain.h index 5050b4c7..836541c9 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -31,6 +31,9 @@ class ReactiveLoop; template class Observer; +template +class ScopedObserver; + template class Signal; @@ -84,6 +87,8 @@ class DomainBase using ObserverT = Observer; + using ScopedObserverT = ScopedObserver; + using ReactiveLoopT = ReactiveLoop; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/Observer.h b/include/react/Observer.h index d7a458f8..280cc36b 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -20,11 +20,15 @@ template class Observer { -public: +private: using SubjectT = REACT_IMPL::NodeBasePtrT; using NodeT = REACT_IMPL::IObserver; - Observer() = default; +public: + Observer() : + nodePtr_{ nullptr } + {} + Observer(const Observer&) = delete; Observer(Observer&& other) : @@ -37,10 +41,14 @@ class Observer subject_{ subject } {} - bool IsValid() const { return nodePtr_ != nullptr; } + bool IsValid() const + { + return nodePtr_ != nullptr; + } void Detach() { + REACT_ASSERT(IsValid(), "Trying to detach an invalid Observer"); REACT_IMPL::DomainSpecificObserverRegistry::Instance().Unregister(nodePtr_); } @@ -52,6 +60,27 @@ class Observer SubjectT subject_; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ScopedObserver +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class ScopedObserver +{ +public: + ScopedObserver(Observer&& obs) : + obs_{ std::move(obs) } + {} + + ~ScopedObserver() + { + if (obs_.IsValid()) + obs_.Detach(); + } + +private: + Observer obs_; +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// DetachAllObservers /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -59,6 +88,7 @@ template void DetachAllObservers(const TSubject& subject) { using D = typename TSubject::DomainT; + REACT_IMPL::DomainSpecificObserverRegistry::Instance().UnregisterFrom( subject.NodePtr().get()); } diff --git a/include/react/ReactiveObject.h b/include/react/ReactiveObject.h index 90f1c456..0f0f03a3 100644 --- a/include/react/ReactiveObject.h +++ b/include/react/ReactiveObject.h @@ -44,6 +44,9 @@ class ReactiveLoop; template class Observer; +template +class ScopedObserver; + enum class EventToken; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -72,6 +75,8 @@ class ReactiveObject using ObserverT = Observer; + using ScopedObserverT = ScopedObserver; + using ReactiveLoopT = ReactiveLoop; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/ObserverTest.h b/src/test/ObserverTest.h index 89ee8f96..9a926917 100644 --- a/src/test/ObserverTest.h +++ b/src/test/ObserverTest.h @@ -8,8 +8,11 @@ #include "gtest/gtest.h" +#include + #include "react/Domain.h" #include "react/Signal.h" +#include "react/Observer.h" /////////////////////////////////////////////////////////////////////////////////////////////////// namespace { @@ -101,11 +104,36 @@ TYPED_TEST_P(ObserverTest, Detach) ASSERT_EQ(observeCount3,2); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Detach test +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(ObserverTest, ScopedObserverTest) +{ + std::vector results; + + auto in = MyDomain::MakeVar(1); + + { + MyDomain::ScopedObserverT obs = in.Observe([&] (int v) { + results.push_back(v); + + }); + + in <<=2; + } + + in <<=3; + + ASSERT_EQ(results.size(),1); + ASSERT_EQ(results[0], 2); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// REGISTER_TYPED_TEST_CASE_P ( ObserverTest, - Detach + Detach, + ScopedObserverTest ); } // ~namespace From eee5c5b796d5e0863738b369cb5e6937a81ff3d7 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 6 May 2014 23:55:35 +0200 Subject: [PATCH 037/266] Misc refactoring. --- include/react/Event.h | 13 +++++++++++++ include/react/Observer.h | 2 +- include/react/Signal.h | 10 ++++++++++ include/react/common/Containers.h | 2 -- include/react/detail/Defs.h | 1 - include/react/detail/EventBase.h | 1 + include/react/detail/SignalBase.h | 1 + 7 files changed, 26 insertions(+), 4 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index a480e0ee..0ec7c388 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -19,6 +19,9 @@ /*****************************************/ REACT_BEGIN /*****************************************/ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventToken +/////////////////////////////////////////////////////////////////////////////////////////////////// enum class EventToken { token }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -52,6 +55,16 @@ class Events : public REACT_IMPL::EventStreamBase EventStreamBase{ std::move(nodePtr) } {} + bool Equals(const Events& other) const + { + return BaseT::Equals(other); + } + + bool IsValid() const + { + return BaseT::IsValid(); + } + template Observer Observe(F&& f) const { diff --git a/include/react/Observer.h b/include/react/Observer.h index 280cc36b..811a0b51 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -48,7 +48,7 @@ class Observer void Detach() { - REACT_ASSERT(IsValid(), "Trying to detach an invalid Observer"); + REACT_ASSERT(IsValid(), "Detach on invalid Observer."); REACT_IMPL::DomainSpecificObserverRegistry::Instance().Unregister(nodePtr_); } diff --git a/include/react/Signal.h b/include/react/Signal.h index da9e19cc..f5406fee 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -53,6 +53,16 @@ class Signal : public REACT_IMPL::SignalBase const S& Value() const { return BaseT::getValue(); } const S& operator()() const { return BaseT::getValue(); } + bool Equals(const Signal& other) const + { + return BaseT::Equals(other); + } + + bool IsValid() const + { + return BaseT::IsValid(); + } + template Observer Observe(F&& f) const { diff --git a/include/react/common/Containers.h b/include/react/common/Containers.h index a1f778b3..3b3a1613 100644 --- a/include/react/common/Containers.h +++ b/include/react/common/Containers.h @@ -151,14 +151,12 @@ class NodeBuffer bool IsEmpty() const { return size_ == 0; } private: - //! Increment the pointer. inline void increment(iterator& it) { if (++it == nodes_.end()) it = nodes_.begin(); } - //! Decrement the pointer. inline void decrement(iterator& it) { if (it == nodes_.begin()) diff --git a/include/react/detail/Defs.h b/include/react/detail/Defs.h index 2be17696..b369a83e 100644 --- a/include/react/detail/Defs.h +++ b/include/react/detail/Defs.h @@ -6,7 +6,6 @@ #pragma once -#define WINDOWS_LEAN_AND_MEAN #define NOMINMAX #ifdef max diff --git a/include/react/detail/EventBase.h b/include/react/detail/EventBase.h index cdb7c3d4..d0269bc1 100644 --- a/include/react/detail/EventBase.h +++ b/include/react/detail/EventBase.h @@ -12,6 +12,7 @@ #include #include "react/detail/ReactiveBase.h" +#include "react/detail/ReactiveInput.h" #include "react/detail/graph/EventNodes.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ diff --git a/include/react/detail/SignalBase.h b/include/react/detail/SignalBase.h index 7c1bdea1..c375b69a 100644 --- a/include/react/detail/SignalBase.h +++ b/include/react/detail/SignalBase.h @@ -12,6 +12,7 @@ #include #include "react/detail/ReactiveBase.h" +#include "react/detail/ReactiveInput.h" #include "react/detail/graph/SignalNodes.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ From 1dd414e5d06e6f4a95a1b73a1a60e8a5674fcebf Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 7 May 2014 16:07:43 +0200 Subject: [PATCH 038/266] Added some missing member functions to Signal and Event. --- include/react/Event.h | 35 ++++++++++++++++++++++++++++++++--- include/react/Signal.h | 22 ++++++++++++++++------ 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index 0ec7c388..d119b4ee 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -65,10 +65,17 @@ class Events : public REACT_IMPL::EventStreamBase return BaseT::IsValid(); } - template - Observer Observe(F&& f) const + auto Forward() const + -> decltype(REACT::Forward(std::declval())) { - return REACT::Observe(*this, std::forward(f)); + return REACT::Forward(*this); + } + + template + auto Merge(const Events& ... args) const + -> decltype(REACT::Merge(std::declval(), std::forward(args) ...)) + { + return REACT::Merge(*this, std::forward(args) ...); } template @@ -84,6 +91,12 @@ class Events : public REACT_IMPL::EventStreamBase { return REACT::Transform(*this, std::forward(f)); } + + template + Observer Observe(F&& f) const + { + return REACT::Observe(*this, std::forward(f)); + } }; // Specialize for references @@ -115,6 +128,22 @@ class Events : public REACT_IMPL::EventStreamBase decltype(REACT::Forward(std::declval())) + { + return REACT::Forward(*this); + } + template auto Merge(const Events& ... args) -> decltype(REACT::Merge(std::declval(), std::forward(args) ...)) diff --git a/include/react/Signal.h b/include/react/Signal.h index f5406fee..82e05395 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -63,17 +63,17 @@ class Signal : public REACT_IMPL::SignalBase return BaseT::IsValid(); } - template - Observer Observe(F&& f) const - { - return REACT::Observe(*this, std::forward(f)); - } - template ::value>::type> S Flatten() const { return REACT::Flatten(*this); } + + template + Observer Observe(F&& f) const + { + return REACT::Observe(*this, std::forward(f)); + } }; // Specialize for references @@ -108,6 +108,16 @@ class Signal : public REACT_IMPL::SignalBase> const S& Value() const { return BaseT::getValue(); } const S& operator()() const { return BaseT::getValue(); } + bool Equals(const Signal& other) const + { + return BaseT::Equals(other); + } + + bool IsValid() const + { + return BaseT::IsValid(); + } + template Observer Observe(F&& f) const { From 3d0211de43f2e0cd24aa027fea4a9c536f63c2be Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 7 May 2014 18:01:57 +0200 Subject: [PATCH 039/266] Updated readme. --- README.md | 107 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 81 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 138a3f7d..0ca23658 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,20 @@ ## Introduction -Cpp.React is an experimental [Reactive Programming](http://en.wikipedia.org/wiki/Reactive_programming) framework for C++11. Its purpose is to provide abstractions that simplify the implementation of reactive behaviour. +Cpp.React is an experimental [Reactive Programming](http://en.wikipedia.org/wiki/Reactive_programming) framework for C++11. -The general idea is that dependency relations between data/actions are expressed declarively, while the actual propagation of changes is handled automatically. The benefits: -* Reduction of boilerplate code. -* Updating is always consistent and glitch-free. -* Support for implicit parallelization of updates. +It provides abstractions to simplify the implementation of reactive behaviour required by modern applications. + +As an alternative to callbacks, it offers the following benefits: +* Less boilerplate code; +* consistent updating without redundant calculations or glitches; +* implicit parallelization. #### Compiling -Currently, the build has only been tested in Visual Studio 2013. -The Intel C++ Compiler 14.0 with Visual Studio 2012/13 is theoretically supported as well, but it doesn't compile the current codebase anymore due to [some bugs]() with C++11 support. +So far, the build has only been tested in Visual Studio 2013 as it's the development environment I'm using. +The Intel C++ Compiler 14.0 with Visual Studio 2012/13 is theoretically supported as well, but last I checked, it did not compile anymore due to [some bugs]() with C++11 support. + +You are encouraged to try compiling it with other C++11 compilers and tell me why it didn't work :) ###### Dependencies * [Intel TBB 4.2](https://www.threadingbuildingblocks.org/) (required) @@ -21,8 +25,9 @@ The Intel C++ Compiler 14.0 with Visual Studio 2012/13 is theoretically supporte #### Signals -Signals are time-varying reactive values, that can be combined to create reactive expressions. -These expressions are automatically recalculated whenever one of their dependent values changes. +Signals are time-varying reactive values. +They can be combined to expressions to create new signals, which are automatically recalculated whenever one of their data dependencies changes. +As such, they can be seen as self-updating variables. ```C++ #include "react/Domain.h" @@ -32,8 +37,8 @@ using namespace react; REACTIVE_DOMAIN(D); -auto width = MyDomain::MakeVar(1); -auto height = MyDomain::MakeVar(2); +auto width = D::MakeVar(1); +auto height = D::MakeVar(2); auto area = width * height; @@ -44,7 +49,8 @@ cout << "area: " << area() << endl; // => area: 20 #### Events -Event streams represent flows of discrete values as first-class objects. +Event streams represent flows of discrete values. +They are first-class objects, so they can be merged, filtered, transformed or composed to more complex types. ```C++ #include "react/Domain.h" @@ -52,10 +58,10 @@ Event streams represent flows of discrete values as first-class objects. //... using namespace react; -REACTIVE_DOMAIN(MyDomain); +REACTIVE_DOMAIN(D); -auto leftClicked = MyDomain::MakeEventSource(); -auto rightClicked = MyDomain::MakeEventSource(); +auto leftClicked = D::MakeEventSource(); +auto rightClicked = D::MakeEventSource(); auto clicked = leftClicked | rightClicked; @@ -68,25 +74,74 @@ The change propagation is handled implicitly by a _propagation engine_. Depending on the selected engine, independent propagation paths are automatically parallelized. ```C++ -#include "react/propagation/ToposortEngine.h" -//... +#include "react/Domain.h" +#include "react/Signal.h" +#include "react/engine/ToposortEngine.h" + using namespace react; -// Single-threaded updating -REACTIVE_DOMAIN(MyDomain, ToposortEngine); +int main() +{ + // Sequential + { + REACTIVE_DOMAIN(D, ToposortEngine); + + auto a = D::MakeVar(1); + auto b = D::MakeVar(2); + auto c = D::MakeVar(3); -// Parallel updating -REACTIVE_DOMAIN(MyDomain, ToposortEngine); + auto x = (a + b) * c; -// Input from multiple threads -REACTIVE_DOMAIN(MyDomain, ToposortEngine); -REACTIVE_DOMAIN(MyDomain, ToposortEngine); + b <<= 20; + } + + // Parallel + { + REACTIVE_DOMAIN(D, ToposortEngine); -// Parallel updating + input from multiple threads + pipelining -REACTIVE_DOMAIN(MyDomain, ToposortEngine); + auto in = D::MakeVar(0); + + auto op1 = in ->* [] (int in) + { + int result = in /* Costly operation #1 */; + return result; + }; + + auto op2 = in ->* [] (int in) + { + int result = in /* Costly operation #2 */; + return result; + }; + + auto out = op1 + op2; + + // op1 and op2 can be re-calculated in parallel + in <<= 123456789; + } + + // Queued input + { + REACTIVE_DOMAIN(D, ToposortEngine); + + auto a = D::MakeVar(1); + auto b = D::MakeVar(2); + auto c = D::MakeVar(3); + + auto x = (a + b) * c; + + // Thread-safe input from multiple-threads (Note: unspecified order) + std::thread t1{ [&] { a <<= 10; } }; + std::thread t2{ [&] { b <<= 100; } }; + std::thread t3{ [&] { a <<= 1000; } }; + std::thread t4{ [&] { b <<= 10000; } }; + + t1.join(); t2.join(); t3.join(); t4.join(); + } +} ``` #### Reactive loops +(Note: Experimental) ```C++ #include "react/Domain.h" From 230009bae36a908260a7c92a903c34c2c0ef2a85 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 7 May 2014 18:19:49 +0200 Subject: [PATCH 040/266] Updated readme. --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0ca23658..3399de7f 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,7 @@ Cpp.React is an experimental [Reactive Programming](http://en.wikipedia.org/wiki/Reactive_programming) framework for C++11. It provides abstractions to simplify the implementation of reactive behaviour required by modern applications. - -As an alternative to callbacks, it offers the following benefits: +As an alternative to raw callbacks, it offers the following benefits: * Less boilerplate code; * consistent updating without redundant calculations or glitches; * implicit parallelization. @@ -220,17 +219,17 @@ public: class Manager : public ReactiveObject { - ObserverT nameObs; - public: VarSignalT CurrentCompany; Manager(initialCompany& company) : CurrentCompany{ MakeVar(ref(company)) } { - nameObs = REACTIVE_REF(CurrentCompany, Name).Observe([] (string name) { - cout << "Manager: Now managing " << name << endl; - }); + // Reactive reference to inner event stream of signal + REACTIVE_REF(CurrentCompany, Name) + .Observe([] (string name) { + cout << "Manager: Now managing " << name << endl; + }); } }; From 1b77dab3abb68f2a05958ceec0ac0b5cce814f8f Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 7 May 2014 21:40:48 +0200 Subject: [PATCH 041/266] Added bitwise complement operator overload for signals. Not sure about other bitwise operators yet. --- include/react/Signal.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/react/Signal.h b/include/react/Signal.h index 82e05395..69b68bf7 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -382,6 +382,7 @@ auto operator ## op(const TSignal& arg) DECLARE_OP(+, UnaryPlus); DECLARE_OP(-, UnaryMinus); DECLARE_OP(!, LogicalNegation); +DECLARE_OP(~, BitwiseComplement); DECLARE_OP(++, Increment); DECLARE_OP(--, Decrement); @@ -644,6 +645,12 @@ DECLARE_OP(>=, GreaterEqual); DECLARE_OP(&&, LogicalAnd); DECLARE_OP(||, LogicalOr); +//DECLARE_OP(&, BitwiseAnd); +//DECLARE_OP(|, BitwiseOr); +//DECLARE_OP(^, BitwiseXor); +//DECLARE_OP(<<, BitwiseLeftShift); // MSVC: Internal compiler error +//DECLARE_OP(>>, BitwiseRightShift); + #undef DECLARE_OP /////////////////////////////////////////////////////////////////////////////////////////////////// From cb20d4708e8ff1b48f6989cfbd5722b5e11440ad Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 7 May 2014 22:14:20 +0200 Subject: [PATCH 042/266] Overload for TempSignal argument case of unary ops. --- include/react/Signal.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/include/react/Signal.h b/include/react/Signal.h index 69b68bf7..8e89bf0f 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -377,6 +377,23 @@ auto operator ## op(const TSignal& arg) return TempSignal( \ std::make_shared>( \ F(), arg.NodePtr())); \ +} \ + \ +template \ +< \ + typename D, \ + typename TVal, \ + typename TOpIn, \ + typename F = name ## OpFunctor, \ + typename S = std::result_of::type, \ + typename TOp = REACT_IMPL::FunctionOp \ +> \ +auto operator ## op(TempSignal&& arg) \ + -> TempSignal \ +{ \ + return TempSignal( \ + std::make_shared>( \ + F(), arg.StealOp())); \ } DECLARE_OP(+, UnaryPlus); From f6d53a397d9bd226d4c113d7f91a4ae5fae1e632 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 7 May 2014 22:14:48 +0200 Subject: [PATCH 043/266] Updated readme. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3399de7f..3ff53b62 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ ## Introduction -Cpp.React is an experimental [Reactive Programming](http://en.wikipedia.org/wiki/Reactive_programming) framework for C++11. +Cpp.React is an experimental [Reactive Programming](http://en.wikipedia.org/wiki/Reactive_programming) library for C++11. -It provides abstractions to simplify the implementation of reactive behaviour required by modern applications. +It provides abstractions to simplify the implementation of reactive behaviour As an alternative to raw callbacks, it offers the following benefits: * Less boilerplate code; * consistent updating without redundant calculations or glitches; @@ -13,7 +13,7 @@ As an alternative to raw callbacks, it offers the following benefits: So far, the build has only been tested in Visual Studio 2013 as it's the development environment I'm using. The Intel C++ Compiler 14.0 with Visual Studio 2012/13 is theoretically supported as well, but last I checked, it did not compile anymore due to [some bugs]() with C++11 support. -You are encouraged to try compiling it with other C++11 compilers and tell me why it didn't work :) +You are encouraged to try compiling it with other C++11 compilers and tell me why it doesn't work :) ###### Dependencies * [Intel TBB 4.2](https://www.threadingbuildingblocks.org/) (required) From e84ec64e698d0637f5a535b2012c9312df556ec4 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 7 May 2014 22:16:09 +0200 Subject: [PATCH 044/266] Updated readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ff53b62..43cf09bb 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Cpp.React is an experimental [Reactive Programming](http://en.wikipedia.org/wiki/Reactive_programming) library for C++11. -It provides abstractions to simplify the implementation of reactive behaviour +It provides abstractions to simplify the implementation of reactive behaviour in programs. As an alternative to raw callbacks, it offers the following benefits: * Less boilerplate code; * consistent updating without redundant calculations or glitches; From b8f69d438b2e79a2835ce3f0dd1fa45a87ce6c28 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 7 May 2014 23:53:24 +0200 Subject: [PATCH 045/266] Readme tweaks. --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 43cf09bb..31acdab1 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Cpp.React is an experimental [Reactive Programming](http://en.wikipedia.org/wiki/Reactive_programming) library for C++11. -It provides abstractions to simplify the implementation of reactive behaviour in programs. +It provides abstractions to simplify the implementation of reactive behaviour. As an alternative to raw callbacks, it offers the following benefits: * Less boilerplate code; * consistent updating without redundant calculations or glitches; @@ -24,9 +24,8 @@ You are encouraged to try compiling it with other C++11 compilers and tell me wh #### Signals -Signals are time-varying reactive values. +Signals self-updating reactive variables. They can be combined to expressions to create new signals, which are automatically recalculated whenever one of their data dependencies changes. -As such, they can be seen as self-updating variables. ```C++ #include "react/Domain.h" @@ -49,7 +48,7 @@ cout << "area: " << area() << endl; // => area: 20 #### Events Event streams represent flows of discrete values. -They are first-class objects, so they can be merged, filtered, transformed or composed to more complex types. +They are first-class objects and can be merged, filtered, transformed or composed to more complex types. ```C++ #include "react/Domain.h" From 77c0019da393a256fb961b6f2ab0139ddbd557fc Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 7 May 2014 23:54:22 +0200 Subject: [PATCH 046/266] Examples cleanup. --- src/sandbox/Main.cpp | 62 -------------------------------------------- 1 file changed, 62 deletions(-) diff --git a/src/sandbox/Main.cpp b/src/sandbox/Main.cpp index ec4b28b3..daf74323 100644 --- a/src/sandbox/Main.cpp +++ b/src/sandbox/Main.cpp @@ -35,66 +35,6 @@ REACTIVE_DOMAIN(D); //REACTIVE_DOMAIN(D, PulsecountEngine); -void SignalExample0() -{ - // Sequential - { - REACTIVE_DOMAIN(D, ToposortEngine); - - auto a = D::MakeVar(1); - auto b = D::MakeVar(2); - auto c = D::MakeVar(3); - - auto x = (a + b) * c; - - b <<= 20; - } - - // Parallel - { - REACTIVE_DOMAIN(D, ToposortEngine); - - auto in = D::MakeVar(0); - - auto op1 = in ->* [] (int in) - { - int result = in /* Costly operation #1 */; - return result; - }; - - auto op2 = in ->* [] (int in) - { - int result = in /* Costly operation #2 */; - return result; - }; - - auto out = op1 + op2; - - in <<= 123456789; - } - - // Queuing - { - REACTIVE_DOMAIN(D, ToposortEngine); - - auto a = D::MakeVar(1); - auto b = D::MakeVar(2); - auto c = D::MakeVar(3); - - auto x = (a + b) * c; - - std::thread t1{ [&] { a <<= 10; } }; - std::thread t2{ [&] { b <<= 100; } }; - std::thread t3{ [&] { c <<= 1000; } }; - std::thread t4{ [&] { a <<= 10000; } }; - - t1.join(); - t2.join(); - t3.join(); - t4.join(); - } -} - void SignalExample1() { cout << "Signal Example 1" << endl; @@ -389,8 +329,6 @@ void LoopTest() int main() { - SignalExample0(); - SignalExample1(); SignalExample2(); SignalExample3(); From bad61ad5d711037eefa817af3605b77708cf873b Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 8 May 2014 00:12:33 +0200 Subject: [PATCH 047/266] Added a few more comments to examples. --- README.md | 95 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 31acdab1..3fdba928 100644 --- a/README.md +++ b/README.md @@ -35,13 +35,17 @@ using namespace react; REACTIVE_DOMAIN(D); +// Two variable that can be manipulated imperatively auto width = D::MakeVar(1); auto height = D::MakeVar(2); auto area = width * height; cout << "area: " << area() << endl; // => area: 2 + +// Width changed, so area is re-calculated automatically width <<= 10; + cout << "area: " << area() << endl; // => area: 20 ``` @@ -53,17 +57,20 @@ They are first-class objects and can be merged, filtered, transformed or compose ```C++ #include "react/Domain.h" #include "react/Event.h" +#include "react/Observer.h" //... +using namespace std; using namespace react; REACTIVE_DOMAIN(D); +// Two event sources auto leftClicked = D::MakeEventSource(); auto rightClicked = D::MakeEventSource(); -auto clicked = leftClicked | rightClicked; - -Observe(clicked, [] { cout << "button clicked!" << endl; }); +// Merge both event streams and register an observer +auto clickObserver = (leftClicked | rightClicked) + .Observe([] { cout << "button clicked!" << endl; }); ``` #### Implicit parallelism @@ -78,63 +85,61 @@ Depending on the selected engine, independent propagation paths are automaticall using namespace react; -int main() + +// Sequential { - // Sequential - { - REACTIVE_DOMAIN(D, ToposortEngine); + REACTIVE_DOMAIN(D, ToposortEngine); - auto a = D::MakeVar(1); - auto b = D::MakeVar(2); - auto c = D::MakeVar(3); + auto a = D::MakeVar(1); + auto b = D::MakeVar(2); + auto c = D::MakeVar(3); - auto x = (a + b) * c; + auto x = (a + b) * c; - b <<= 20; - } + b <<= 20; +} - // Parallel - { - REACTIVE_DOMAIN(D, ToposortEngine); +// Parallel +{ + REACTIVE_DOMAIN(D, ToposortEngine); - auto in = D::MakeVar(0); + auto in = D::MakeVar(0); - auto op1 = in ->* [] (int in) - { - int result = in /* Costly operation #1 */; - return result; - }; + auto op1 = in ->* [] (int in) + { + int result = in /* Costly operation #1 */; + return result; + }; - auto op2 = in ->* [] (int in) - { - int result = in /* Costly operation #2 */; - return result; - }; + auto op2 = in ->* [] (int in) + { + int result = in /* Costly operation #2 */; + return result; + }; - auto out = op1 + op2; + auto out = op1 + op2; - // op1 and op2 can be re-calculated in parallel - in <<= 123456789; - } + // op1 and op2 can be re-calculated in parallel + in <<= 123456789; +} - // Queued input - { - REACTIVE_DOMAIN(D, ToposortEngine); +// Queued input +{ + REACTIVE_DOMAIN(D, ToposortEngine); - auto a = D::MakeVar(1); - auto b = D::MakeVar(2); - auto c = D::MakeVar(3); + auto a = D::MakeVar(1); + auto b = D::MakeVar(2); + auto c = D::MakeVar(3); - auto x = (a + b) * c; + auto x = (a + b) * c; - // Thread-safe input from multiple-threads (Note: unspecified order) - std::thread t1{ [&] { a <<= 10; } }; - std::thread t2{ [&] { b <<= 100; } }; - std::thread t3{ [&] { a <<= 1000; } }; - std::thread t4{ [&] { b <<= 10000; } }; + // Thread-safe input from multiple-threads (Note: unspecified order) + std::thread t1{ [&] { a <<= 10; } }; + std::thread t2{ [&] { b <<= 100; } }; + std::thread t3{ [&] { a <<= 1000; } }; + std::thread t4{ [&] { b <<= 10000; } }; - t1.join(); t2.join(); t3.join(); t4.join(); - } + t1.join(); t2.join(); t3.join(); t4.join(); } ``` From e11c816fcc1918bb6ed8f1a8fde73d4a66c2f2cd Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 8 May 2014 02:09:58 +0200 Subject: [PATCH 048/266] Fixed readme example. --- README.md | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 3fdba928..d5d7eda7 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ Depending on the selected engine, independent propagation paths are automaticall #include "react/Domain.h" #include "react/Signal.h" #include "react/engine/ToposortEngine.h" - +//... using namespace react; @@ -133,7 +133,7 @@ using namespace react; auto x = (a + b) * c; - // Thread-safe input from multiple-threads (Note: unspecified order) + // Thread-safe input (Note: unspecified order) std::thread t1{ [&] { a <<= 10; } }; std::thread t2{ [&] { b <<= 100; } }; std::thread t3{ [&] { a <<= 1000; } }; @@ -167,20 +167,20 @@ auto mouseMove = D::MakeEventSource(); D::ReactiveLoopT loop { - [&] (D::ReactiveLoopT::Context& ctx) - { - PathT points; + [&] (D::ReactiveLoopT::Context& ctx) + { + PathT points; - points.emplace_back(ctx.Await(mouseDown)); + points.emplace_back(ctx.Await(mouseDown)); - ctx.RepeatUntil(mouseUp, [&] { - points.emplace_back(ctx.Await(mouseMove)); - }); + ctx.RepeatUntil(mouseUp, [&] { + points.emplace_back(ctx.Await(mouseMove)); + }); - points.emplace_back(ctx.Await(mouseUp)); + points.emplace_back(ctx.Await(mouseUp)); - paths.push_back(points); - } + paths.push_back(points); + } }; mouseDown << PointT(1,1); @@ -218,23 +218,26 @@ public: Name{ MakeVar(string(name)) } {} - inline bool operator==(const Company& other) const { /* ... */ } + bool operator==(const Company&) const; }; class Manager : public ReactiveObject { public: VarSignalT CurrentCompany; + ObserverT NameObserver; Manager(initialCompany& company) : - CurrentCompany{ MakeVar(ref(company)) } - { - // Reactive reference to inner event stream of signal - REACTIVE_REF(CurrentCompany, Name) - .Observe([] (string name) { - cout << "Manager: Now managing " << name << endl; - }); - } + CurrentCompany{ MakeVar(ref(company)) }, + NameObserver + { + // Reactive reference to inner event stream of signal + REACTIVE_REF(CurrentCompany, Name) + .Observe([] (const string& name) { + cout << "Manager: Now managing " << name << endl; + }); + } + {} }; Company company1{ "Cellnet" }; @@ -245,6 +248,7 @@ Manager manager{ company1 }; company1.Name <<= string("BT Cellnet"); company2.Name <<= string("Inprise"); +// Observer moves from company1.Name to company2.Name manager.CurrentCompany <<= ref(company2); company1.Name <<= string("O2"); From 6c885691058d7d91cafc96054518267efd11595a Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 9 May 2014 13:00:19 +0200 Subject: [PATCH 049/266] Got rid of some unnecessary static_pointer_casts. --- include/react/Event.h | 5 ++--- include/react/Signal.h | 2 +- include/react/detail/EventBase.h | 4 ++-- include/react/detail/SignalBase.h | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index d119b4ee..d2e8b5b6 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -8,10 +8,9 @@ #include "react/detail/Defs.h" -#include +#include #include #include -#include #include "react/Observer.h" #include "react/TypeTraits.h" @@ -292,7 +291,7 @@ class TempEvents : public Events TOp StealOp() { - return std::move(std::static_pointer_cast(ptr_)->StealOp()); + return std::move(reinterpret_cast(ptr_.get())->StealOp()); } template diff --git a/include/react/Signal.h b/include/react/Signal.h index 8e89bf0f..6c404d75 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -241,7 +241,7 @@ class TempSignal : public Signal TOp StealOp() { - return std::move(std::static_pointer_cast(ptr_)->StealOp()); + return std::move(reinterpret_cast(ptr_.get())->StealOp()); } }; diff --git a/include/react/detail/EventBase.h b/include/react/detail/EventBase.h index d0269bc1..0bdedd06 100644 --- a/include/react/detail/EventBase.h +++ b/include/react/detail/EventBase.h @@ -8,7 +8,6 @@ #include "react/detail/Defs.h" -#include #include #include "react/detail/ReactiveBase.h" @@ -40,7 +39,8 @@ class EventStreamBase : public ReactiveBase> void emit(T&& e) const { InputManager::AddInput( - *std::static_pointer_cast>(ptr_), std::forward(e)); + *reinterpret_cast*>(ptr_.get()), + std::forward(e)); } }; diff --git a/include/react/detail/SignalBase.h b/include/react/detail/SignalBase.h index c375b69a..93a25a91 100644 --- a/include/react/detail/SignalBase.h +++ b/include/react/detail/SignalBase.h @@ -8,7 +8,6 @@ #include "react/detail/Defs.h" -#include #include #include "react/detail/ReactiveBase.h" @@ -46,7 +45,8 @@ class SignalBase : public ReactiveBase> void setValue(T&& newValue) const { InputManager::AddInput( - *std::static_pointer_cast>(ptr_), std::forward(newValue)); + *reinterpret_cast*>(ptr_.get()), + std::forward(newValue)); } }; From cda44923401fa6073b9ee8c14f52490ea598be81 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 12 May 2014 13:16:10 +0200 Subject: [PATCH 050/266] Moved type aliases to detail namespace. --- include/react/detail/Defs.h | 6 ++++-- src/benchmark/BenchmarkRandom.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/react/detail/Defs.h b/include/react/detail/Defs.h index b369a83e..f181481c 100644 --- a/include/react/detail/Defs.h +++ b/include/react/detail/Defs.h @@ -16,6 +16,7 @@ #endif #include +#include /////////////////////////////////////////////////////////////////////////////////////////////////// #define REACT_BEGIN namespace react { @@ -46,10 +47,11 @@ // Thread local storage #define REACT_TLS __declspec(thread) -/*****************************************/ REACT_BEGIN /*****************************************/ +/***************************************/ REACT_IMPL_BEGIN /**************************************/ // Type aliases using uint = unsigned int; using uchar = unsigned char; +using std::size_t; -/******************************************/ REACT_END /******************************************/ +/****************************************/ REACT_IMPL_END /***************************************/ diff --git a/src/benchmark/BenchmarkRandom.h b/src/benchmark/BenchmarkRandom.h index 9c7691df..6554e0c2 100644 --- a/src/benchmark/BenchmarkRandom.h +++ b/src/benchmark/BenchmarkRandom.h @@ -85,7 +85,7 @@ class RandomGraphGenerator auto edgeNodeIt = edgeNodes.begin(); auto slowNodeIt = slowNodes.begin(); - uint cur = 0; + auto cur = 0; HandleVect nodes(nodeCount); for (int w=0; w Date: Mon, 12 May 2014 13:22:52 +0200 Subject: [PATCH 051/266] Relaxed const-ness of FunctionOp. Before it would only accept functors with operator() const, which does not include those returned by std::bind. Considering that any functor should represent a pure function, enforcing as much const-ness as possible seems like a good idea, but enabling bind is more important. --- include/react/detail/graph/SignalNodes.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index 723291e0..e78d6775 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -133,7 +133,7 @@ class FunctionOp : public ReactiveOpBase func_{ std::move(other.func_) } {} - S Evaluate() const + S Evaluate() { return apply(EvalFunctor{ func_ }, deps_); } @@ -142,15 +142,16 @@ class FunctionOp : public ReactiveOpBase // Eval struct EvalFunctor { - EvalFunctor(const F& f) : MyFunc{ f } {} + EvalFunctor(F& f) : MyFunc{ f } {} - S operator()(const TDeps& ... args) const + template + S operator()(T&& ... args) { return MyFunc(eval(args) ...); } template - static auto eval(const T& op) -> decltype(op.Evaluate()) + static auto eval(T& op) -> decltype(op.Evaluate()) { return op.Evaluate(); } @@ -161,7 +162,7 @@ class FunctionOp : public ReactiveOpBase return depPtr->ValueRef(); } - const F& MyFunc; + F& MyFunc; }; private: From f96fd6bd7c3b1d5d1df877ec2612de16e4fe9a09 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 13 May 2014 03:41:29 +0200 Subject: [PATCH 052/266] Added FoldByRef and IterateByRef to algorithms. Iteration func modifies current state by reference without copy and comparison. Downside: Comparison to previous value is no longer possible, so always assuming a change. --- include/react/Algorithm.h | 42 ++++++++ include/react/detail/graph/AlgorithmNodes.h | 112 ++++++++++++++++++++ src/test/OperationsTest.h | 52 ++++++++- 3 files changed, 205 insertions(+), 1 deletion(-) diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index f8d18fa3..432df60f 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -54,6 +54,27 @@ auto Fold(V&& init, const Events& events, FIn&& func) std::forward(init), events.NodePtr(), std::forward(func))); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// FoldByRef - Pass current value as reference +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename V, + typename E, + typename FIn, + typename S = std::decay::type +> +auto FoldByRef(V&& init, const Events& events, FIn&& func) + -> Signal +{ + using F = std::decay::type; + + return Signal( + std::make_shared>( + std::forward(init), events.NodePtr(), std::forward(func))); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Iterate /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -75,6 +96,27 @@ auto Iterate(V&& init, const Events& events, FIn&& func) std::forward(init), events.NodePtr(), std::forward(func))); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// IterateByRef +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename V, + typename E, + typename FIn, + typename S = std::decay::type +> +auto IterateByRef(V&& init, const Events& events, FIn&& func) + -> Signal +{ + using F = std::decay::type; + + return Signal( + std::make_shared>( + std::forward(init), events.NodePtr(), std::forward(func))); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Hold /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index 8b24dbe0..83da71d5 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -108,6 +108,62 @@ class FoldNode : public FoldBaseNode } }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// FoldByRefNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename S, + typename E, + typename TFunc +> +class FoldByRefNode : public SignalNode +{ +public: + template + FoldByRefNode(T&& init, const SharedPtrT>& events, F&& func) : + SignalNode(std::forward(init)), + func_{ std::forward(func) }, + events_{ events } + { + Engine::OnNodeCreate(*this); + Engine::OnNodeAttach(*this, *events); + } + + ~FoldByRefNode() + { + 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())); + + for (const auto& e : events_->Events()) + func_(value_,e); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + + // Always assume change + Engine::OnNodePulse(*this, turn); + } + + virtual const char* GetNodeType() const override { return "FoldByRefNode"; } + virtual int DependencyCount() const override { return 1; } + +protected: + TFunc func_; + + SharedPtrT> events_; +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// IterateNode /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -150,6 +206,62 @@ class IterateNode : public FoldBaseNode } }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// IterateByRefNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename S, + typename E, + typename TFunc +> +class IterateByRefNode : public SignalNode +{ +public: + template + IterateByRefNode(T&& init, const SharedPtrT>& events, F&& func) : + 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())); + + for (const auto& e : events_->Events()) + func_(value_); + + 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_; + + SharedPtrT> events_; +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// HoldNode /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/OperationsTest.h b/src/test/OperationsTest.h index d7b92ec9..83ecadba 100644 --- a/src/test/OperationsTest.h +++ b/src/test/OperationsTest.h @@ -213,6 +213,54 @@ TYPED_TEST_P(OperationsTest, Snapshot1) ASSERT_EQ(snap(), 20); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// FoldByRef1 test +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(OperationsTest, FoldByRef1) +{ + auto src = MyDomain::MakeEventSource(); + auto f = FoldByRef( + std::vector(), + src, + [] (std::vector& v, int d) { + v.push_back(d); + }); + + // Push + for (auto i=1; i<=100; i++) + src << i; + + ASSERT_EQ(f().size(), 100); + + // Check + for (auto i=1; i<=100; i++) + ASSERT_EQ(f()[i-1], i); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// IterateByRef1 test +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(OperationsTest, IterateByRef1) +{ + auto src = MyDomain::MakeEventSource(); + auto x = IterateByRef( + std::vector(), + src, + [] (std::vector& v) { + v.push_back(123); + }); + + // Push + for (auto i=0; i<100; i++) + src.Emit(); + + ASSERT_EQ(x().size(), 100); + + // Check + for (auto i=0; i<100; i++) + ASSERT_EQ(x()[i], 123); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// REGISTER_TYPED_TEST_CASE_P ( @@ -223,7 +271,9 @@ REGISTER_TYPED_TEST_CASE_P Monitor1, Hold1, Pulse1, - Snapshot1 + Snapshot1, + FoldByRef1, + IterateByRef1 ); } // ~namespace From eb40014c0564d0ccdd68524d6f59db4e04dbf11f Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 13 May 2014 19:07:42 +0200 Subject: [PATCH 053/266] Fixed REACTIVE_REF/PTR macros to work outside of ReactiveObject class definitions. --- include/react/ReactiveObject.h | 71 +++++++++++++++++----------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/include/react/ReactiveObject.h b/include/react/ReactiveObject.h index 0f0f03a3..e9cc08ae 100644 --- a/include/react/ReactiveObject.h +++ b/include/react/ReactiveObject.h @@ -56,9 +56,9 @@ template class ReactiveObject { public: - /////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////// /// Aliases - /////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////// using DomainT = D; template @@ -79,9 +79,9 @@ class ReactiveObject using ReactiveLoopT = ReactiveLoop; - /////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////// /// MakeVar - /////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////// template < typename V, @@ -105,9 +105,9 @@ class ReactiveObject return REACT::MakeVar(value); } - ////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////// // MakeVar (higher order) - ////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////// template < typename V, @@ -122,9 +122,9 @@ class ReactiveObject return REACT::MakeVar(std::forward(value)); } - /////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////// /// MakeSignal - /////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////// template < typename FIn, @@ -138,40 +138,41 @@ class ReactiveObject return REACT::MakeSignal(std::forward(func), args ...); } - /////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////// /// MakeEventSource - /////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////// template static auto MakeEventSource() -> EventSourceT { return REACT::MakeEventSource(); } - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// Flatten macros - /////////////////////////////////////////////////////////////////////////////////////////////////// - // Todo: Add safety wrapper + static assert to check for this for ReactiveObject - // Note: Using static_cast rather than -> return type, because when using lambda for inline class - // initialization, decltype did not recognize the parameter r - #define REACTIVE_REF(obj, name) \ - Flatten( \ - MakeSignal( \ - [] (const REACT_IMPL::Identity::Type::ValueT& r) \ - { \ - return static_cast::Type>(r.name); \ - }, \ - obj)) - - #define REACTIVE_PTR(obj, name) \ - Flatten( \ - MakeSignal( \ - [] (REACT_IMPL::Identity::Type::ValueT r) \ - { \ - REACT_ASSERT(r != nullptr); \ - return static_castname)>::Type>(r->name); \ - }, \ - obj)) }; /******************************************/ REACT_END /******************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Flatten macros +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Note: Using static_cast rather than -> return type, because when using lambda for inline +// class initialization, decltype did not recognize the parameter r +#define REACTIVE_REF(obj, name) \ + Flatten( \ + MakeSignal( \ + [] (const REACT_IMPL::Identity::Type::ValueT& r) \ + { \ + using T = decltype(r.name); \ + return static_cast::Type>(r.name); \ + }, \ + obj)) + +#define REACTIVE_PTR(obj, name) \ + Flatten( \ + MakeSignal( \ + [] (REACT_IMPL::Identity::Type::ValueT r) \ + { \ + REACT_ASSERT(r != nullptr); \ + using T = decltype(r->name); \ + return static_cast::Type>(r->name); \ + }, \ + obj)) From 2ccf60dd5ba8ef58485c8deae8659b8d3e14bc4c Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 14 May 2014 21:56:03 +0200 Subject: [PATCH 054/266] Added missing wrapper function for token streams to ReactiveObject. --- include/react/ReactiveObject.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/react/ReactiveObject.h b/include/react/ReactiveObject.h index e9cc08ae..463d18e5 100644 --- a/include/react/ReactiveObject.h +++ b/include/react/ReactiveObject.h @@ -147,6 +147,12 @@ class ReactiveObject { return REACT::MakeEventSource(); } + + static auto MakeEventSource() + -> EventSource + { + return REACT::MakeEventSource(); + } }; /******************************************/ REACT_END /******************************************/ From aa0e38e2b3d727396114d4e784410afc14c6f93a Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 14 May 2014 21:58:12 +0200 Subject: [PATCH 055/266] Extended ObserverRegistry::Register to support forwarding of additional deps to node ctor. --- include/react/detail/ObserverBase.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/react/detail/ObserverBase.h b/include/react/detail/ObserverBase.h index 42f75053..5a6ea9de 100644 --- a/include/react/detail/ObserverBase.h +++ b/include/react/detail/ObserverBase.h @@ -62,13 +62,14 @@ class ObserverRegistry < typename TNode, typename TSubject, - typename F + typename F, + typename ... TDeps > - IObserver* Register(const TSubject& subject, F&& func) + IObserver* Register(const TSubject& subject, F&& func, const TDeps& ... deps) { std::unique_ptr ptr { - new TNode(subject.NodePtr(), std::forward(func)) + new TNode(subject.NodePtr(), std::forward(func), deps.NodePtr() ...) }; auto* obsPtr = ptr.get(); From ce335c073f98ddb41c51518ed2fb86e0d05bcbf9 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 14 May 2014 22:12:27 +0200 Subject: [PATCH 056/266] Moved Observe functions from Signal/Event.h to Observer.h. Replaced lambda func wrapper for token stream observer with functor to enable move. --- include/react/Event.h | 41 ------------------- include/react/Observer.h | 86 ++++++++++++++++++++++++++++++++++++++++ include/react/Signal.h | 85 ++++++++++++++++++++++----------------- 3 files changed, 134 insertions(+), 78 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index d2e8b5b6..95e0cd0d 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -592,45 +592,4 @@ auto Flatten(const Signal>& node) node.NodePtr(), node().NodePtr())); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Observe -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename FIn, - typename E, - class = std::enable_if< - ! std::is_same::value>::type -> -auto Observe(const Events& subject, FIn&& func) - -> Observer -{ - using F = std::decay::type; - using TNode = REACT_IMPL::EventObserverNode; - - auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). - template Register(subject, std::forward(func)); - - return Observer(raw, subject.NodePtr()); -} - -template -< - typename D, - typename FIn -> -auto Observe(const Events& subject, FIn&& func) - -> Observer -{ - auto wrapper = [func] (EventToken) { func(); }; - - using TNode = REACT_IMPL::EventObserverNode; - - auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). - template Register(subject, std::move(wrapper)); - - return Observer(raw, subject.NodePtr()); -} - /******************************************/ REACT_END /******************************************/ diff --git a/include/react/Observer.h b/include/react/Observer.h index 811a0b51..9e19a481 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -14,6 +14,17 @@ /*****************************************/ REACT_BEGIN /*****************************************/ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Forward declarations +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class Signal; + +template +class Events; + +enum class EventToken; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Observer /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -81,6 +92,81 @@ class ScopedObserver Observer obs_; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Observe - Signals +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename FIn, + typename S +> +auto Observe(const Signal& subject, FIn&& func) + -> Observer +{ + using F = std::decay::type; + using TNode = REACT_IMPL::SignalObserverNode; + + auto* obs = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). + template Register(subject, std::forward(func)); + + return Observer{ obs, subject.NodePtr() }; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Observe - Events +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename FIn, + typename E, + class = std::enable_if< + ! std::is_same::value>::type +> +auto Observe(const Events& subject, FIn&& func) + -> Observer +{ + using F = std::decay::type; + using TNode = REACT_IMPL::EventObserverNode; + + auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). + template Register(subject, std::forward(func)); + + return Observer(raw, subject.NodePtr()); +} + +template +< + typename D, + typename FIn +> +auto Observe(const Events& subject, FIn&& func) + -> Observer +{ + using F = std::decay::type; + + struct Wrapper_ + { + Wrapper_(FIn&& func) : MyFunc{ std::forward(func) } {} + Wrapper_(const Wrapper_& other) = default; + Wrapper_(Wrapper_&& other) : MyFunc{ std::move(other.MyFunc) } {} + + void operator()(EventToken) { MyFunc(); } + + F MyFunc; + }; + + Wrapper_ wrapper{ std::forward(func) }; + + using TNode = REACT_IMPL::EventObserverNode; + + auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). + template Register(subject, std::move(wrapper)); + + return Observer(raw, subject.NodePtr()); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// DetachAllObservers /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/Signal.h b/include/react/Signal.h index 6c404d75..3a456081 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -671,26 +671,58 @@ DECLARE_OP(||, LogicalOr); #undef DECLARE_OP /////////////////////////////////////////////////////////////////////////////////////////////////// -/// InputPack - Wraps several nodes in a tuple. Create with comma operator. +/// SignalList - Wraps several nodes in a tuple. Create with comma operator. /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename ... TValues > -struct InputPack +class SignalList { - std::tuple& ...> Data; - - template - InputPack(const Signal& first, const Signal& second) : - Data{ std::tie(first, second) } +public: + SignalList(const Signal& ... deps) : + data_{ std::tie(deps ...) } {} template - InputPack(const InputPack& curArgs, const Signal& newArg) : - Data{ std::tuple_cat(curArgs.Data, std::tie(newArg)) } + SignalList(const SignalList& curArgs, const Signal& newArg) : + data_{ std::tuple_cat(curArgs.data_, std::tie(newArg)) } {} + + template + Observer SyncedObserve(const TEvent& evn, F&& f) const + { + struct Wrapper_ + { + Wrapper_(const TEvent& evn, F&& func) : + MyEvent{ evn }, + MyFunc{ std::forward(func) } + {} + + Observer operator()(const Signal& ... deps) + { + return REACT::SyncedObserve(MyEvent, std::forward(MyFunc), deps ...); + } + + const TEvent& MyEvent; + + // Stored as universal ref + F MyFunc; + }; + + return REACT_IMPL::apply(Wrapper_{ evn, std::forward(f) }, data_); + } + +private: + std::tuple& ...> data_; + + template + friend class SignalList; + + template + friend auto operator->*(const SignalList&, F&&) + -> Signal::type>; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -703,9 +735,9 @@ template typename TRightVal > auto operator,(const Signal& a, const Signal& b) - -> InputPack + -> SignalList { - return InputPack(a, b); + return SignalList(a, b); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -717,10 +749,10 @@ template typename ... TCurValues, typename TAppendValue > -auto operator,(const InputPack& cur, const Signal& append) - -> InputPack +auto operator,(const SignalList& cur, const Signal& append) + -> SignalList { - return InputPack(cur, append); + return SignalList(cur, append); } /******************************************/ REACT_END /******************************************/ @@ -772,12 +804,12 @@ template typename F, typename ... TSignals > -auto operator->*(const InputPack& inputPack, F&& func) +auto operator->*(const SignalList& inputPack, F&& func) -> Signal::type> { return apply( REACT_IMPL::ApplyHelper::MakeSignal, - std::tuple_cat(std::forward_as_tuple(std::forward(func)), inputPack.Data)); + std::tuple_cat(std::forward_as_tuple(std::forward(func)), inputPack.data_)); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -796,25 +828,4 @@ auto Flatten(const Signal>& node) node.NodePtr(), node.Value().NodePtr())); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Observe -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename FIn, - typename S -> -auto Observe(const Signal& subject, FIn&& func) - -> Observer -{ - using F = std::decay::type; - using TNode = REACT_IMPL::SignalObserverNode; - - auto* obs = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). - template Register(subject, std::forward(func)); - - return Observer{ obs, subject.NodePtr() }; -} - /******************************************/ REACT_END /******************************************/ From 3fe9c2677bbbf9e65d550d1577bbd84d000bec89 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 14 May 2014 22:43:07 +0200 Subject: [PATCH 057/266] Added synced Observe overload. Same semantics as Observer(events), but additionally supports a variable number of signal dependencies, whose current values are passed to the obs func (synced for the current turn). Observer node has to take shared ownership of these dependencies to prevent them from getting destroyed. Changes of the dependencies do NOT trigger the observer, only the event does. --- include/react/Observer.h | 64 ++++++++++ include/react/Signal.h | 4 +- include/react/detail/graph/ObserverNodes.h | 129 ++++++++++++++++++++- src/test/ObserverTest.h | 53 ++++++++- 4 files changed, 241 insertions(+), 9 deletions(-) diff --git a/include/react/Observer.h b/include/react/Observer.h index 9e19a481..10edd612 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -167,6 +167,70 @@ auto Observe(const Events& subject, FIn&& func) return Observer(raw, subject.NodePtr()); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Observe - Synced +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename FIn, + typename E, + typename TDepValue1, + typename ... TDepValues, + class = std::enable_if< + ! std::is_same::value>::type +> +auto Observe(const Events& subject, FIn&& func, + const Signal& dep1, const Signal& ... deps) + -> Observer +{ + using F = std::decay::type; + using TNode = REACT_IMPL::SyncedObserverNode; + + auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). + template Register(subject, std::forward(func), dep1, deps ...); + + return Observer(raw, subject.NodePtr()); +} + +// Synced +template +< + typename D, + typename FIn, + typename TDepValue1, + typename ... TDepValues +> +auto Observe(const Events& subject, FIn&& func, + const Signal& dep1, const Signal& ... deps) + -> Observer +{ + using F = std::decay::type; + + struct Wrapper_ + { + Wrapper_(FIn&& func) : MyFunc{ std::forward(func) } {} + Wrapper_(const Wrapper_& other) = default; + Wrapper_(Wrapper_&& other) : MyFunc{ std::move(other.MyFunc) } {} + + void operator()(EventToken, const TDepValue1& arg1, const TDepValues& ... args) + { + MyFunc(arg1, args ...); + } + + F MyFunc; + }; + + using TNode = REACT_IMPL::SyncedObserverNode; + + Wrapper_ wrapper{ std::forward(func) }; + + auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). + template Register(subject, std::move(wrapper), dep1, deps ...); + + return Observer(raw, subject.NodePtr()); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// DetachAllObservers /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/Signal.h b/include/react/Signal.h index 3a456081..6bac38b2 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -691,7 +691,7 @@ class SignalList {} template - Observer SyncedObserve(const TEvent& evn, F&& f) const + Observer Observe(const TEvent& evn, F&& f) const { struct Wrapper_ { @@ -702,7 +702,7 @@ class SignalList Observer operator()(const Signal& ... deps) { - return REACT::SyncedObserve(MyEvent, std::forward(MyFunc), deps ...); + return REACT::Observe(MyEvent, std::forward(MyFunc), deps ...); } const TEvent& MyEvent; diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index b94aa869..13c20917 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -14,6 +14,15 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Forward declarations +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class SignalNode; + +template +class EventStreamNode; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// ObserverNode /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -31,9 +40,6 @@ class ObserverNode : /////////////////////////////////////////////////////////////////////////////////////////////////// /// SignalObserverNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class SignalNode; - template < typename D, @@ -105,9 +111,6 @@ class SignalObserverNode : public ObserverNode /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventObserverNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EventStreamNode; - template < typename D, @@ -179,4 +182,118 @@ class EventObserverNode : public ObserverNode } }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SyncedObserverNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename TValue, + typename TFunc, + typename ... TDepValues +> +class SyncedObserverNode : public ObserverNode +{ +public: + template + SyncedObserverNode(const SharedPtrT>& subject, F&& func, + const SharedPtrT>& ... depArgs) : + ObserverNode{ }, + subject_{ subject }, + func_{ std::forward(func) }, + deps_{ depArgs ... } + { + Engine::OnNodeCreate(*this); + subject->IncObsCount(); + Engine::OnNodeAttach(*this, *subject); + + REACT_EXPAND_PACK(D::Engine::OnNodeAttach(*this, *depArgs)); + } + + ~SyncedObserverNode() + { + Engine::OnNodeDestroy(*this); + } + + virtual const char* GetNodeType() const override { return "SyncedEventObserverNode"; } + virtual int DependencyCount() const override { return 1; } + + 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())); + + current_observer_state_::shouldDetach = false; + + ContinuationHolder::SetTurn(turn); + + if (auto p = subject_.lock()) + { + for (const auto& e : p->Events()) + apply(EvalFunctor{ e, func_ }, deps_); + } + + ContinuationHolder::Clear(); + + if (current_observer_state_::shouldDetach) + turn.QueueForDetach(*this); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + } + +private: + struct EvalFunctor + { + EvalFunctor(const TValue& e, TFunc& f) : + MyEvent{ e }, + MyFunc{ f } + {} + + void operator()(const SharedPtrT>& ... args) + { + MyFunc(MyEvent, args->ValueRef() ...); + } + + const TValue& MyEvent; + TFunc& MyFunc; + }; + + struct DetachFunctor + { + DetachFunctor(SyncedObserverNode& node) : MyNode{ node } + {} + + void operator()(const SharedPtrT>& ... deps) + { + REACT_EXPAND_PACK(D::Engine::OnNodeDetach(MyNode, *deps)); + } + + SyncedObserverNode& MyNode; + }; + + using DepHolderT = std::tuple>...>; + + WeakPtrT> subject_; + + DepHolderT deps_; + TFunc func_; + + virtual void detachObserver() + { + if (auto p = subject_.lock()) + { + p->DecObsCount(); + + Engine::OnNodeDetach(*this, *p); + apply(DetachFunctor{ *this }, deps_); + + subject_.reset(); + } + } +}; + /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/src/test/ObserverTest.h b/src/test/ObserverTest.h index 9a926917..bd470fa6 100644 --- a/src/test/ObserverTest.h +++ b/src/test/ObserverTest.h @@ -12,6 +12,7 @@ #include "react/Domain.h" #include "react/Signal.h" +#include "react/Event.h" #include "react/Observer.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -128,12 +129,62 @@ TYPED_TEST_P(ObserverTest, ScopedObserverTest) ASSERT_EQ(results[0], 2); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Detach test +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(ObserverTest, SyncedObserveTest) +{ + auto in1 = MyDomain::MakeVar(1); + auto in2 = MyDomain::MakeVar(1); + + auto sum = in1 + in2; + auto prod = in1 * in2; + auto diff = in1 - in2; + + auto src1 = MyDomain::MakeEventSource(); + auto src2 = MyDomain::MakeEventSource(); + + (sum,prod,diff).Observe(src1, [] (int sum, int prod, int diff) { + ASSERT_EQ(sum, 33); + ASSERT_EQ(prod, 242); + ASSERT_EQ(diff, 11); + }); + + (sum,prod,diff).Observe(src2, [] (int e, int sum, int prod, int diff) { + ASSERT_EQ(e, 42); + ASSERT_EQ(sum, 33); + ASSERT_EQ(prod, 242); + ASSERT_EQ(diff, 11); + }); + + Observe(src1, [] (int sum, int prod, int diff) { + ASSERT_EQ(sum, 33); + ASSERT_EQ(prod, 242); + ASSERT_EQ(diff, 11); + }, sum,prod,diff); + + Observe(src2, [] (int e, int sum, int prod, int diff) { + ASSERT_EQ(e, 42); + ASSERT_EQ(sum, 33); + ASSERT_EQ(prod, 242); + ASSERT_EQ(diff, 11); + }, sum,prod,diff); + + in1 <<= 22; + in2 <<= 11; + + src1.Emit(); + src2.Emit(42); + +} + /////////////////////////////////////////////////////////////////////////////////////////////////// REGISTER_TYPED_TEST_CASE_P ( ObserverTest, Detach, - ScopedObserverTest + ScopedObserverTest, + SyncedObserveTest ); } // ~namespace From 9c7017ef038cf345f27ca8a32b653923e792b485 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 14 May 2014 22:50:11 +0200 Subject: [PATCH 058/266] Wrong comments --- src/test/ObserverTest.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/ObserverTest.h b/src/test/ObserverTest.h index bd470fa6..a4c8ea36 100644 --- a/src/test/ObserverTest.h +++ b/src/test/ObserverTest.h @@ -106,7 +106,7 @@ TYPED_TEST_P(ObserverTest, Detach) } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Detach test +/// ScopedObserver test /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(ObserverTest, ScopedObserverTest) { @@ -130,7 +130,7 @@ TYPED_TEST_P(ObserverTest, ScopedObserverTest) } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Detach test +/// Synced Observe test /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(ObserverTest, SyncedObserveTest) { From db71209bf9e81a5e3fa79c9d13af1c83870ebb96 Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 16 May 2014 00:34:12 +0200 Subject: [PATCH 059/266] Too much magic. --- include/react/Signal.h | 24 ------------------------ src/test/ObserverTest.h | 13 ------------- 2 files changed, 37 deletions(-) diff --git a/include/react/Signal.h b/include/react/Signal.h index 6bac38b2..ad380da5 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -690,30 +690,6 @@ class SignalList data_{ std::tuple_cat(curArgs.data_, std::tie(newArg)) } {} - template - Observer Observe(const TEvent& evn, F&& f) const - { - struct Wrapper_ - { - Wrapper_(const TEvent& evn, F&& func) : - MyEvent{ evn }, - MyFunc{ std::forward(func) } - {} - - Observer operator()(const Signal& ... deps) - { - return REACT::Observe(MyEvent, std::forward(MyFunc), deps ...); - } - - const TEvent& MyEvent; - - // Stored as universal ref - F MyFunc; - }; - - return REACT_IMPL::apply(Wrapper_{ evn, std::forward(f) }, data_); - } - private: std::tuple& ...> data_; diff --git a/src/test/ObserverTest.h b/src/test/ObserverTest.h index a4c8ea36..88efbc47 100644 --- a/src/test/ObserverTest.h +++ b/src/test/ObserverTest.h @@ -144,19 +144,6 @@ TYPED_TEST_P(ObserverTest, SyncedObserveTest) auto src1 = MyDomain::MakeEventSource(); auto src2 = MyDomain::MakeEventSource(); - (sum,prod,diff).Observe(src1, [] (int sum, int prod, int diff) { - ASSERT_EQ(sum, 33); - ASSERT_EQ(prod, 242); - ASSERT_EQ(diff, 11); - }); - - (sum,prod,diff).Observe(src2, [] (int e, int sum, int prod, int diff) { - ASSERT_EQ(e, 42); - ASSERT_EQ(sum, 33); - ASSERT_EQ(prod, 242); - ASSERT_EQ(diff, 11); - }); - Observe(src1, [] (int sum, int prod, int diff) { ASSERT_EQ(sum, 33); ASSERT_EQ(prod, 242); From 08d8f30daa5bbbcfeff9d12e80f3781b658397dc Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 16 May 2014 01:36:06 +0200 Subject: [PATCH 060/266] Extended member variant of observe to support additional dependencies as well. --- include/react/Event.h | 12 ++++++------ src/test/ObserverTest.h | 13 +++++++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index 95e0cd0d..3da87552 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -91,10 +91,10 @@ class Events : public REACT_IMPL::EventStreamBase return REACT::Transform(*this, std::forward(f)); } - template - Observer Observe(F&& f) const + template + Observer Observe(F&& f, const TDeps& ... deps) const { - return REACT::Observe(*this, std::forward(f)); + return REACT::Observe(*this, std::forward(f), deps ...); } }; @@ -164,10 +164,10 @@ class Events : public REACT_IMPL::EventStreamBase(f)); } - template - Observer Observe(F&& f) const + template + Observer Observe(F&& f, const TDeps& ... deps) const { - return REACT::Observe(*this, std::forward(f)); + return REACT::Observe(*this, std::forward(f), deps ...); } }; diff --git a/src/test/ObserverTest.h b/src/test/ObserverTest.h index 88efbc47..fa954ba1 100644 --- a/src/test/ObserverTest.h +++ b/src/test/ObserverTest.h @@ -157,6 +157,19 @@ TYPED_TEST_P(ObserverTest, SyncedObserveTest) ASSERT_EQ(diff, 11); }, sum,prod,diff); + src1.Observe([] (int sum, int prod, int diff) { + ASSERT_EQ(sum, 33); + ASSERT_EQ(prod, 242); + ASSERT_EQ(diff, 11); + }, sum,prod,diff); + + src2.Observe([] (int e, int sum, int prod, int diff) { + ASSERT_EQ(e, 42); + ASSERT_EQ(sum, 33); + ASSERT_EQ(prod, 242); + ASSERT_EQ(diff, 11); + }, sum,prod,diff); + in1 <<= 22; in2 <<= 11; From a58fb5c62d3bc50303b4a9a1ccd63458bcf05203 Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 16 May 2014 02:49:17 +0200 Subject: [PATCH 061/266] Fixed type typo. --- include/react/Signal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/react/Signal.h b/include/react/Signal.h index ad380da5..94a871c4 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -784,7 +784,7 @@ auto operator->*(const SignalList& inputPack, F&& func) -> Signal::type> { return apply( - REACT_IMPL::ApplyHelper::MakeSignal, + REACT_IMPL::ApplyHelper::MakeSignal, std::tuple_cat(std::forward_as_tuple(std::forward(func)), inputPack.data_)); } From 154244a883010583f790493d1e1fc091441c085d Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 16 May 2014 03:09:27 +0200 Subject: [PATCH 062/266] Updated readme. --- README.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d5d7eda7..b0fdea84 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,19 @@ It provides abstractions to simplify the implementation of reactive behaviour. As an alternative to raw callbacks, it offers the following benefits: * Less boilerplate code; * consistent updating without redundant calculations or glitches; -* implicit parallelization. +* implicit parallelism. #### Compiling So far, the build has only been tested in Visual Studio 2013 as it's the development environment I'm using. The Intel C++ Compiler 14.0 with Visual Studio 2012/13 is theoretically supported as well, but last I checked, it did not compile anymore due to [some bugs]() with C++11 support. -You are encouraged to try compiling it with other C++11 compilers and tell me why it doesn't work :) +You are welcome to try compiling it with other C++11 compilers/on other platforms and report any issues you encounter. + +#### Status + +This library is still under development and should not be considered release quality yet. +That being said, I've been working on it for about 6 months and it's in a usable state. ###### Dependencies * [Intel TBB 4.2](https://www.threadingbuildingblocks.org/) (required) @@ -24,8 +29,8 @@ You are encouraged to try compiling it with other C++11 compilers and tell me wh #### Signals -Signals self-updating reactive variables. -They can be combined to expressions to create new signals, which are automatically recalculated whenever one of their data dependencies changes. +Signals are self-updating reactive variables. +They can be combined to expressions to create new signals, which are automatically rec-alculated whenever one of their data dependencies changes. ```C++ #include "react/Domain.h" @@ -41,12 +46,12 @@ auto height = D::MakeVar(2); auto area = width * height; -cout << "area: " << area() << endl; // => area: 2 +cout << "area: " << area.Value() << endl; // => area: 2 // Width changed, so area is re-calculated automatically width <<= 10; -cout << "area: " << area() << endl; // => area: 20 +cout << "area: " << area.Value() << endl; // => area: 20 ``` #### Events From 88bc567a52b1e50a4a31ada907da0f9822ea115a Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 16 May 2014 03:16:35 +0200 Subject: [PATCH 063/266] Readme tweaks. --- README.md | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index b0fdea84..0d91ea20 100644 --- a/README.md +++ b/README.md @@ -15,22 +15,27 @@ The Intel C++ Compiler 14.0 with Visual Studio 2012/13 is theoretically supporte You are welcome to try compiling it with other C++11 compilers/on other platforms and report any issues you encounter. -#### Status +#### Dependencies +* [Intel TBB 4.2](https://www.threadingbuildingblocks.org/) (required) +* [Google test framework](https://code.google.com/p/googletest/) (optional, to compile the tests) +* [Boost C++ Libraries](http://www.boost.org/) (optional, to use ReactiveLoop, which requires boost::coroutine) + +## Status This library is still under development and should not be considered release quality yet. That being said, I've been working on it for about 6 months and it's in a usable state. -###### Dependencies -* [Intel TBB 4.2](https://www.threadingbuildingblocks.org/) (required) -* [Google test framework](https://code.google.com/p/googletest/) (optional, to compile the tests) -* [Boost C++ Libraries](http://www.boost.org/) (optional, to use ReactiveLoop, which requires boost::coroutine) +## Documentation + +The documentation is still incomplete, but it already contains plenty of useful information and examples. +It can be found in the [wiki](/wiki). ## Features by example #### Signals Signals are self-updating reactive variables. -They can be combined to expressions to create new signals, which are automatically rec-alculated whenever one of their data dependencies changes. +They can be combined to expressions to create new signals, which are automatically re-calculated whenever one of their data dependencies changes. ```C++ #include "react/Domain.h" @@ -41,10 +46,12 @@ using namespace react; REACTIVE_DOMAIN(D); // Two variable that can be manipulated imperatively -auto width = D::MakeVar(1); -auto height = D::MakeVar(2); +D::VarSignalT width = D::MakeVar(1); +D::VarSignalT height = D::MakeVar(2); -auto area = width * height; +// Arithmetic operators are overloaded to lift expressions +// with signal operands and build new signals from them +D::SignalT area = width * height; cout << "area: " << area.Value() << endl; // => area: 2 @@ -70,8 +77,8 @@ using namespace react; REACTIVE_DOMAIN(D); // Two event sources -auto leftClicked = D::MakeEventSource(); -auto rightClicked = D::MakeEventSource(); +D::EventSource<> leftClicked = D::MakeEventSource(); +D::EventSource<> rightClicked = D::MakeEventSource(); // Merge both event streams and register an observer auto clickObserver = (leftClicked | rightClicked) @@ -110,6 +117,8 @@ using namespace react; auto in = D::MakeVar(0); + // The ->* operator is overloaded for a DSL + auto op1 = in ->* [] (int in) { int result = in /* Costly operation #1 */; From 52439d2b07e7e0976f2750a6a1cf7af115f45187 Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 16 May 2014 03:17:48 +0200 Subject: [PATCH 064/266] Fixed wiki link. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0d91ea20..9471f7d5 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ That being said, I've been working on it for about 6 months and it's in a usable ## Documentation The documentation is still incomplete, but it already contains plenty of useful information and examples. -It can be found in the [wiki](/wiki). +It can be found in the [wiki](https://github.com/schlangster/cpp.react/wiki). ## Features by example From e64230e3349bbc2601e2f2762b2d2539d6908892 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sat, 17 May 2014 12:57:36 +0200 Subject: [PATCH 065/266] Fixed wrong DependencyCount of SyncedObserverNode. --- include/react/detail/graph/ObserverNodes.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index 13c20917..54d6d42d 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -197,17 +197,17 @@ class SyncedObserverNode : public ObserverNode public: template SyncedObserverNode(const SharedPtrT>& subject, F&& func, - const SharedPtrT>& ... depArgs) : + const SharedPtrT>& ... deps) : ObserverNode{ }, subject_{ subject }, func_{ std::forward(func) }, - deps_{ depArgs ... } + deps_{ deps ... } { Engine::OnNodeCreate(*this); subject->IncObsCount(); Engine::OnNodeAttach(*this, *subject); - REACT_EXPAND_PACK(D::Engine::OnNodeAttach(*this, *depArgs)); + REACT_EXPAND_PACK(D::Engine::OnNodeAttach(*this, *deps)); } ~SyncedObserverNode() @@ -215,8 +215,8 @@ class SyncedObserverNode : public ObserverNode Engine::OnNodeDestroy(*this); } - virtual const char* GetNodeType() const override { return "SyncedEventObserverNode"; } - virtual int DependencyCount() const override { return 1; } + virtual const char* GetNodeType() const override { return "SyncedObserverNode"; } + virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } virtual void Tick(void* turnPtr) override { From e6edb4ebdaf15fd36b5dcc529c7da707852c593f Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 18 May 2014 01:58:03 +0200 Subject: [PATCH 066/266] Refactored observer registration while debugging a compiler crash after upgrading to VS2013.2. --- include/react/Observer.h | 79 ++++++++++---- include/react/detail/ObserverBase.h | 49 ++++----- include/react/detail/graph/GraphBase.h | 116 ++++++++++----------- include/react/detail/graph/ObserverNodes.h | 79 +++++++------- 4 files changed, 170 insertions(+), 153 deletions(-) diff --git a/include/react/Observer.h b/include/react/Observer.h index 10edd612..918f6f66 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -8,6 +8,9 @@ #include "react/detail/Defs.h" +#include +#include + #include "react/detail/IReactiveNode.h" #include "react/detail/ObserverBase.h" #include "react/detail/graph/ObserverNodes.h" @@ -104,13 +107,20 @@ template auto Observe(const Signal& subject, FIn&& func) -> Observer { + using REACT_IMPL::IObserver; + using REACT_IMPL::SignalObserverNode; + using REACT_IMPL::DomainSpecificObserverRegistry; + using F = std::decay::type; - using TNode = REACT_IMPL::SignalObserverNode; - auto* obs = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). - template Register(subject, std::forward(func)); + std::unique_ptr obsPtr{ + new SignalObserverNode{ + subject.NodePtr(), std::forward(func)}}; + + auto* rawObsPtr = DomainSpecificObserverRegistry::Instance() + .Register(std::move(obsPtr), subject.NodePtr().get()); - return Observer{ obs, subject.NodePtr() }; + return Observer{ rawObsPtr, subject.NodePtr() }; } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -127,13 +137,20 @@ template auto Observe(const Events& subject, FIn&& func) -> Observer { + using REACT_IMPL::IObserver; + using REACT_IMPL::EventObserverNode; + using REACT_IMPL::DomainSpecificObserverRegistry; + using F = std::decay::type; - using TNode = REACT_IMPL::EventObserverNode; - auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). - template Register(subject, std::forward(func)); + std::unique_ptr obsPtr{ + new EventObserverNode{ + subject.NodePtr(), std::forward(func)}}; + + auto* rawObsPtr = DomainSpecificObserverRegistry::Instance() + .Register(std::move(obsPtr), subject.NodePtr().get()); - return Observer(raw, subject.NodePtr()); + return Observer(rawObsPtr, subject.NodePtr()); } template @@ -144,6 +161,10 @@ template auto Observe(const Events& subject, FIn&& func) -> Observer { + using REACT_IMPL::IObserver; + using REACT_IMPL::EventObserverNode; + using REACT_IMPL::DomainSpecificObserverRegistry; + using F = std::decay::type; struct Wrapper_ @@ -159,12 +180,14 @@ auto Observe(const Events& subject, FIn&& func) Wrapper_ wrapper{ std::forward(func) }; - using TNode = REACT_IMPL::EventObserverNode; + std::unique_ptr obsPtr{ + new EventObserverNode{ + subject.NodePtr(), std::move(wrapper)}}; - auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). - template Register(subject, std::move(wrapper)); + auto* rawObsPtr = DomainSpecificObserverRegistry::Instance() + .Register(std::move(obsPtr), subject.NodePtr().get()); - return Observer(raw, subject.NodePtr()); + return Observer(rawObsPtr, subject.NodePtr()); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -184,16 +207,22 @@ auto Observe(const Events& subject, FIn&& func, const Signal& dep1, const Signal& ... deps) -> Observer { + using REACT_IMPL::IObserver; + using REACT_IMPL::SyncedObserverNode; + using REACT_IMPL::DomainSpecificObserverRegistry; + using F = std::decay::type; - using TNode = REACT_IMPL::SyncedObserverNode; - auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). - template Register(subject, std::forward(func), dep1, deps ...); + std::unique_ptr obsPtr{ + new SyncedObserverNode{ + subject.NodePtr(), std::forward(func), dep1.NodePtr(), deps.NodePtr() ...}}; + + auto* rawObsPtr = DomainSpecificObserverRegistry::Instance() + .Register(std::move(obsPtr), subject.NodePtr().get()); - return Observer(raw, subject.NodePtr()); + return Observer(rawObsPtr, subject.NodePtr()); } -// Synced template < typename D, @@ -205,6 +234,10 @@ auto Observe(const Events& subject, FIn&& func, const Signal& dep1, const Signal& ... deps) -> Observer { + using REACT_IMPL::IObserver; + using REACT_IMPL::SyncedObserverNode; + using REACT_IMPL::DomainSpecificObserverRegistry; + using F = std::decay::type; struct Wrapper_ @@ -221,14 +254,16 @@ auto Observe(const Events& subject, FIn&& func, F MyFunc; }; - using TNode = REACT_IMPL::SyncedObserverNode; - Wrapper_ wrapper{ std::forward(func) }; - auto* raw = REACT_IMPL::DomainSpecificObserverRegistry::Instance(). - template Register(subject, std::move(wrapper), dep1, deps ...); + std::unique_ptr obsPtr{ + new SyncedObserverNode{ + subject.NodePtr(), std::move(wrapper), dep1.NodePtr(), deps.NodePtr() ...}}; + + auto* rawObsPtr = DomainSpecificObserverRegistry::Instance() + .Register(std::move(obsPtr), subject.NodePtr().get()); - return Observer(raw, subject.NodePtr()); + return Observer(rawObsPtr, subject.NodePtr()); } /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/ObserverBase.h b/include/react/detail/ObserverBase.h index 5a6ea9de..405a926d 100644 --- a/include/react/detail/ObserverBase.h +++ b/include/react/detail/ObserverBase.h @@ -31,6 +31,7 @@ namespace current_observer_state_ /////////////////////////////////////////////////////////////////////////////////////////////////// /// ObserverRegistry /////////////////////////////////////////////////////////////////////////////////////////////////// +// Todo: still ugly, needs more refactoring template class ObserverRegistry { @@ -38,59 +39,49 @@ class ObserverRegistry class Entry { public: - Entry(std::unique_ptr&& obs, const Observable* subject) : - obs_{ std::move(obs) }, - Subject{ subject } + Entry(std::unique_ptr&& nodePtr, const Observable* subjectPtr) : + nodePtr_{ std::move(nodePtr) }, + SubjectPtr{ subjectPtr } {} Entry(const Entry&) = delete; Entry(Entry&& other) : - obs_{ std::move(other.obs_) }, - Subject{ other.Subject } + nodePtr_{ std::move(other.nodePtr_) }, + SubjectPtr{ other.SubjectPtr } {} - const Observable* Subject; + const Observable* SubjectPtr; private: // Manage lifetime - std::unique_ptr obs_; + std::unique_ptr nodePtr_; }; public: - template - < - typename TNode, - typename TSubject, - typename F, - typename ... TDeps - > - IObserver* Register(const TSubject& subject, F&& func, const TDeps& ... deps) + // Pass ownership of obs node to registry + IObserver* Register(std::unique_ptr&& obsPtr, const Observable* subjectPtr) { - std::unique_ptr ptr - { - new TNode(subject.NodePtr(), std::forward(func), deps.NodePtr() ...) - }; - - auto* obsPtr = ptr.get(); - - observerMap_.emplace(obsPtr, Entry(std::move(ptr), subject.NodePtr().get() )); + // Use raw ptr copy as index to find owned version of itself + auto* rawObsPtr = obsPtr.get(); + + observerMap_.emplace(rawObsPtr, Entry{ std::move(obsPtr), subjectPtr }); - return obsPtr; + return rawObsPtr; } - void Unregister(IObserver* obs) + void Unregister(IObserver* obsPtr) { - obs->detachObserver(); - observerMap_.erase(obs); + obsPtr->detachObserver(); + observerMap_.erase(obsPtr); } - void UnregisterFrom(const Observable* subject) + void UnregisterFrom(const Observable* subjectPtr) { auto it = observerMap_.begin(); while (it != observerMap_.end()) { - if (it->second.Subject == subject) + if (it->second.SubjectPtr == subjectPtr) { it->first->detachObserver(); it = observerMap_.erase(it); diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index 78dbb24a..c3f69d32 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -131,6 +131,59 @@ class ReactiveNode : public NodeBase ReactiveNode() = default; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Attach/detach helper functors +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct AttachFunctor +{ + AttachFunctor(TNode& node) : MyNode{ node } {} + + void operator()(const TDeps& ...deps) const + { + REACT_EXPAND_PACK(attach(deps)); + } + + template + void attach(const T& op) const + { + op.AttachRec(*this); + } + + template + void attach(const SharedPtrT& depPtr) const + { + D::Engine::OnNodeAttach(MyNode, *depPtr); + } + + TNode& MyNode; +}; + +template +struct DetachFunctor +{ + DetachFunctor(TNode& node) : MyNode{ node } {} + + void operator()(const TDeps& ... deps) const + { + REACT_EXPAND_PACK(detach(deps)); + } + + template + void detach(const T& op) const + { + op.DetachRec(*this); + } + + template + void detach(const SharedPtrT& depPtr) const + { + D::Engine::OnNodeDetach(MyNode, *depPtr); + } + + TNode& MyNode; +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// ReactiveOpBase /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -155,83 +208,28 @@ class ReactiveOpBase template void Attach(TNode& node) const { - apply(AttachFunctor{ node }, deps_); + apply(AttachFunctor{ node }, deps_); } template void Detach(TNode& node) const { - apply(DetachFunctor{ node }, deps_); + apply(DetachFunctor{ node }, deps_); } template void AttachRec(const TFunctor& functor) const { // Same memory layout, different func - apply(reinterpret_cast&>(functor), deps_); + apply(reinterpret_cast&>(functor), deps_); } template void DetachRec(const TFunctor& functor) const { - apply(reinterpret_cast&>(functor), deps_); + apply(reinterpret_cast&>(functor), deps_); } -protected: - // Attach - template - struct AttachFunctor - { - AttachFunctor(TNode& node) : MyNode{ node } - {} - - void operator()(const TDeps& ...deps) const - { - REACT_EXPAND_PACK(attach(deps)); - } - - template - void attach(const T& op) const - { - op.AttachRec(*this); - } - - template - void attach(const SharedPtrT& depPtr) const - { - D::Engine::OnNodeAttach(MyNode, *depPtr); - } - - TNode& MyNode; - }; - - // Detach - template - struct DetachFunctor - { - DetachFunctor(TNode& node) : MyNode{ node } - {} - - void operator()(const TDeps& ... deps) const - { - REACT_EXPAND_PACK(detach(deps)); - } - - template - void detach(const T& op) const - { - op.DetachRec(*this); - } - - template - void detach(const SharedPtrT& depPtr) const - { - D::Engine::OnNodeDetach(MyNode, *depPtr); - } - - TNode& MyNode; - }; - // Dependency counting template struct CountHelper { static const int value = T::dependency_count; }; diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index 54d6d42d..3640450f 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -8,6 +8,7 @@ #include "react/detail/Defs.h" +#include #include #include "GraphBase.h" @@ -43,14 +44,14 @@ class ObserverNode : template < typename D, - typename TArg, + typename S, typename TFunc > class SignalObserverNode : public ObserverNode { public: template - SignalObserverNode(const SharedPtrT>& subject, F&& func) : + SignalObserverNode(const SharedPtrT>& subject, F&& func) : ObserverNode{ }, subject_{ subject }, func_{ std::forward(func) } @@ -104,8 +105,8 @@ class SignalObserverNode : public ObserverNode } } - WeakPtrT> subject_; - TFunc func_; + WeakPtrT> subject_; + TFunc func_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -114,14 +115,14 @@ class SignalObserverNode : public ObserverNode template < typename D, - typename TArg, + typename E, typename TFunc > class EventObserverNode : public ObserverNode { public: template - EventObserverNode(const SharedPtrT>& subject, F&& func) : + EventObserverNode(const SharedPtrT>& subject, F&& func) : ObserverNode{ }, subject_{ subject }, func_{ std::forward(func) } @@ -167,8 +168,8 @@ class EventObserverNode : public ObserverNode } private: - WeakPtrT> subject_; - TFunc func_; + WeakPtrT> subject_; + TFunc func_; virtual void detachObserver() { @@ -188,16 +189,17 @@ class EventObserverNode : public ObserverNode template < typename D, - typename TValue, + typename E, typename TFunc, typename ... TDepValues > class SyncedObserverNode : public ObserverNode { public: + // NOTE: After upgrading to VS2013 Udpate2, using SharedPtrT here crashes the compiler template - SyncedObserverNode(const SharedPtrT>& subject, F&& func, - const SharedPtrT>& ... deps) : + SyncedObserverNode(const std::shared_ptr>& subject, F&& func, + const std::shared_ptr>& ... deps) : ObserverNode{ }, subject_{ subject }, func_{ std::forward(func) }, @@ -220,6 +222,22 @@ class SyncedObserverNode : public ObserverNode virtual void Tick(void* turnPtr) override { + struct EvalFunctor_ + { + EvalFunctor_(const E& e, TFunc& f) : + MyEvent{ e }, + MyFunc{ f } + {} + + void operator()(const SharedPtrT>& ... args) + { + MyFunc(MyEvent, args->ValueRef() ...); + } + + const E& MyEvent; + TFunc& MyFunc; + }; + using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -233,7 +251,7 @@ class SyncedObserverNode : public ObserverNode if (auto p = subject_.lock()) { for (const auto& e : p->Events()) - apply(EvalFunctor{ e, func_ }, deps_); + apply(EvalFunctor_{ e, func_ }, deps_); } ContinuationHolder::Clear(); @@ -246,38 +264,9 @@ class SyncedObserverNode : public ObserverNode } private: - struct EvalFunctor - { - EvalFunctor(const TValue& e, TFunc& f) : - MyEvent{ e }, - MyFunc{ f } - {} - - void operator()(const SharedPtrT>& ... args) - { - MyFunc(MyEvent, args->ValueRef() ...); - } - - const TValue& MyEvent; - TFunc& MyFunc; - }; - - struct DetachFunctor - { - DetachFunctor(SyncedObserverNode& node) : MyNode{ node } - {} - - void operator()(const SharedPtrT>& ... deps) - { - REACT_EXPAND_PACK(D::Engine::OnNodeDetach(MyNode, *deps)); - } - - SyncedObserverNode& MyNode; - }; - using DepHolderT = std::tuple>...>; - WeakPtrT> subject_; + WeakPtrT> subject_; DepHolderT deps_; TFunc func_; @@ -289,7 +278,11 @@ class SyncedObserverNode : public ObserverNode p->DecObsCount(); Engine::OnNodeDetach(*this, *p); - apply(DetachFunctor{ *this }, deps_); + + apply( + DetachFunctor>...>{ *this }, + deps_); subject_.reset(); } From 537b0a0447451148cb08eefa9dfbbd7114a9c494 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 18 May 2014 11:32:39 +0200 Subject: [PATCH 067/266] Added a few comments. --- include/react/detail/IReactiveNode.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/react/detail/IReactiveNode.h b/include/react/detail/IReactiveNode.h index f5f8264a..6ecceddd 100644 --- a/include/react/detail/IReactiveNode.h +++ b/include/react/detail/IReactiveNode.h @@ -17,7 +17,7 @@ struct IReactiveNode { virtual ~IReactiveNode() = default; - // Unique type identifier + /// Returns unique type identifier virtual const char* GetNodeType() const = 0; // Note: Could get rid of this ugly ptr by adding a template parameter to the interface @@ -30,9 +30,12 @@ struct IReactiveNode /// Output nodes can't have any successors. virtual bool IsOutputNode() const = 0; - /// May change in topology as a result of tick. + /// Dynamic nodes may change in topology as a result of tick. virtual bool IsDynamicNode() const = 0; + // Number of predecessors. + // This information is statically available at compile time on the graph layer, + // so the engine does not have to calculate it again. virtual int DependencyCount() const = 0; }; From 6258317a1210a103fcf11c4de3a986bbb5f74830 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 18 May 2014 11:33:25 +0200 Subject: [PATCH 068/266] Using vector again as the internal data structure of event streams. --- include/react/detail/graph/EventNodes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index f6389f07..35e2176f 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -44,7 +44,7 @@ class EventStreamNode : private EventStreamNodeAccessPolicy { public: - using DataT = std::deque; + using DataT = std::vector; using EngineT = typename D::Engine; using TurnT = typename EngineT::TurnT; From ed834f544825918057dbb913b84035eea58c89d3 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 18 May 2014 12:10:57 +0200 Subject: [PATCH 069/266] Added GenerateEvent. Works similar to the synced observer with additional signal parameters, except that it emits an event. --- include/react/Algorithm.h | 117 +++++++++++++++----- include/react/detail/graph/AlgorithmNodes.h | 95 +++++++++++++++- 2 files changed, 180 insertions(+), 32 deletions(-) diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index 432df60f..e8323f37 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -47,10 +47,12 @@ template auto Fold(V&& init, const Events& events, FIn&& func) -> Signal { + using REACT_IMPL::FoldNode; + using F = std::decay::type; return Signal( - std::make_shared>( + std::make_shared>( std::forward(init), events.NodePtr(), std::forward(func))); } @@ -68,10 +70,12 @@ template auto FoldByRef(V&& init, const Events& events, FIn&& func) -> Signal { + using REACT_IMPL::FoldByRefNode; + using F = std::decay::type; return Signal( - std::make_shared>( + std::make_shared>( std::forward(init), events.NodePtr(), std::forward(func))); } @@ -89,10 +93,12 @@ template auto Iterate(V&& init, const Events& events, FIn&& func) -> Signal { + using REACT_IMPL::IterateNode; + using F = std::decay::type; return Signal( - std::make_shared>( + std::make_shared>( std::forward(init), events.NodePtr(), std::forward(func))); } @@ -110,10 +116,12 @@ template auto IterateByRef(V&& init, const Events& events, FIn&& func) -> Signal { + using REACT_IMPL::IterateByRefNode; + using F = std::decay::type; return Signal( - std::make_shared>( + std::make_shared>( std::forward(init), events.NodePtr(), std::forward(func))); } @@ -129,8 +137,10 @@ template auto Hold(V&& init, const Events& events) -> Signal { + using REACT_IMPL::HoldNode; + return Signal( - std::make_shared>( + std::make_shared>( std::forward(init), events.NodePtr())); } @@ -143,11 +153,13 @@ template typename S, typename E > -auto Snapshot(const Signal& target, const Events& trigger) +auto Snapshot(const Events& trigger, const Signal& target) -> Signal { + using REACT_IMPL::SnapshotNode; + return Signal( - std::make_shared>( + std::make_shared>( target.NodePtr(), trigger.NodePtr())); } @@ -162,8 +174,10 @@ template auto Monitor(const Signal& target) -> Events { + using REACT_IMPL::MonitorNode; + return Events( - std::make_shared>( + std::make_shared>( target.NodePtr())); } @@ -176,14 +190,79 @@ template typename S, typename E > -auto Pulse(const Signal& target, const Events& trigger) +auto Pulse(const Events& trigger, const Signal& target) -> Events { + using REACT_IMPL::PulseNode; + return Events( - std::make_shared>( + std::make_shared>( target.NodePtr(), trigger.NodePtr())); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// GenerateEvents +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename E, + typename FIn, + typename ... TDepValues, + typename TOut = std::result_of::type, + class = std::enable_if< + ! std::is_same::value>::type +> +auto GenerateEvents(const Events& trigger, FIn&& func, + const Signal& ... deps) + -> Events +{ + using REACT_IMPL::EventGeneratorNode; + + using F = std::decay::type; + + return Events( + std::make_shared>( + trigger.NodePtr(), std::forward(func), deps.NodePtr() ...)); +} + +// Token stream version +template +< + typename D, + typename FIn, + typename ... TDepValues, + typename TOut = std::result_of::type +> +auto GenerateEvents(const Events& trigger, FIn&& func, + const Signal& ... deps) + -> Events +{ + using REACT_IMPL::EventGeneratorNode; + + using F = std::decay::type; + + struct Wrapper_ + { + Wrapper_(FIn&& func) : MyFunc{ std::forward(func) } {} + Wrapper_(const Wrapper_& other) = default; + Wrapper_(Wrapper_&& other) : MyFunc{ std::move(other.MyFunc) } {} + + void operator()(EventToken, const TDepValues& ... args) + { + MyFunc(args ...); + } + + F MyFunc; + }; + + Wrapper_ wrapper{ std::forward(func) }; + + return Events( + std::make_shared>( + trigger.NodePtr(), std::move(wrapper), deps.NodePtr() ...)); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Changed /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -217,22 +296,4 @@ auto ChangedTo(const Signal& target, V&& value) .Transform([=] (const S& v) { return EventToken::token; }) } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Incrementer -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct Incrementer -{ - T operator()(T v) const { return v+1; } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Decrementer -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct Decrementer -{ - T operator()(T v) const { return v-1; } -}; - /******************************************/ REACT_END /******************************************/ diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index 83da71d5..6837db44 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -510,13 +510,9 @@ class PulseNode : public EventStreamNode GetObjectId(*this), turn.Id())); if (events_.size() > 0) - { Engine::OnNodePulse(*this, turn); - } else - { Engine::OnNodeIdlePulse(*this, turn); - } } private: @@ -524,4 +520,95 @@ class PulseNode : public EventStreamNode const SharedPtrT> trigger_; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventGeneratorNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename E, + typename TOut, + typename TFunc, + typename ... TDepValues +> +class EventGeneratorNode : public EventStreamNode +{ +public: + template + EventGeneratorNode(const std::shared_ptr>& trigger, F&& func, + const std::shared_ptr>& ... deps) : + EventStreamNode{ }, + trigger_{ trigger }, + func_{ std::forward(func) }, + deps_{ deps ... } + { + Engine::OnNodeCreate(*this); + Engine::OnNodeAttach(*this, *trigger_); + REACT_EXPAND_PACK(Engine::OnNodeAttach(*this, *deps)); + } + + ~EventGeneratorNode() + { + + Engine::OnNodeDetach(*this, *trigger_); + + apply( + DetachFunctor>...>{ *this }, + deps_); + + Engine::OnNodeDestroy(*this); + } + + virtual const char* GetNodeType() const override { return "EventGeneratorNode"; } + virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } + + virtual void Tick(void* turnPtr) override + { + struct EvalFunctor_ + { + EvalFunctor_(const E& e, TFunc& f) : + MyEvent{ e }, + MyFunc{ f } + {} + + void operator()(const SharedPtrT>& ... args) + { + MyFunc(MyEvent, args->ValueRef() ...); + } + + const E& MyEvent; + TFunc& MyFunc; + }; + + typedef typename D::Engine::TurnT TurnT; + TurnT& turn = *reinterpret_cast(turnPtr); + + SetCurrentTurn(turn, true); + trigger_->SetCurrentTurn(turn); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + + for (const auto& e : trigger_->Events()) + apply(EvalFunctor_{ e, func_ }, deps_); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + + if (events_.size() > 0) + Engine::OnNodePulse(*this, turn); + else + Engine::OnNodeIdlePulse(*this, turn); + } + +private: + using DepHolderT = std::tuple>...>; + + const SharedPtrT> trigger_; + + TFunc func_; + DepHolderT deps_; +}; + /****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file From c6d36b078e39a7629f10e900d160213d52493a22 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 18 May 2014 12:12:04 +0200 Subject: [PATCH 070/266] Fixed lifesim benchmark after changes to Algorithm.h. --- src/benchmark/BenchmarkLifeSim.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/benchmark/BenchmarkLifeSim.h b/src/benchmark/BenchmarkLifeSim.h index 28858331..3d0df5df 100644 --- a/src/benchmark/BenchmarkLifeSim.h +++ b/src/benchmark/BenchmarkLifeSim.h @@ -39,6 +39,9 @@ using std::get; enum class Seasons { summer, winter }; enum class Migration { enter, leave }; +template +struct Incrementer { T operator()(T v) const { return v+1; } }; + typedef pair PositionT; template @@ -80,7 +83,7 @@ class Region : public ReactiveObject return count > 0 ? food/count : 0; }; - EventsT FoodOutput = Pulse(FoodOutputPerDay, theTime.NewDay); + EventsT FoodOutput = Pulse(theTime.NewDay, FoodOutputPerDay); Region(Time& time, int x, int y): theTime { time }, @@ -211,7 +214,7 @@ class Animal : public ReactiveObject //Event Migrating = EventSource(); SignalT ShouldMigrate = Hold(0, FoodReceived) < 10; - EventsT Moving = Pulse(ShouldMigrate, theTime.NewDay); + EventsT Moving = Pulse(theTime.NewDay, ShouldMigrate); }; /////////////////////////////////////////////////////////////////////////////////////////////////// From 2025fc36093d16c6b2afa3ed49a745b60d34422d Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 18 May 2014 12:12:56 +0200 Subject: [PATCH 071/266] Added testcase for GenerateEvents1. --- src/test/OperationsTest.h | 85 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 3 deletions(-) diff --git a/src/test/OperationsTest.h b/src/test/OperationsTest.h index 83ecadba..07e72acd 100644 --- a/src/test/OperationsTest.h +++ b/src/test/OperationsTest.h @@ -10,10 +10,12 @@ #include #include +#include #include "react/Domain.h" #include "react/Signal.h" #include "react/Event.h" +#include "react/Observer.h" #include "react/Algorithm.h" /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -22,6 +24,12 @@ namespace { using namespace react; using namespace std; +template +struct Incrementer { T operator()(T v) const { return v+1; } }; + +template +struct Decrementer { T operator()(T v) const { return v-1; } }; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventStreamTest fixture /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -173,7 +181,7 @@ TYPED_TEST_P(OperationsTest, Pulse1) vector results; - auto p = Pulse(target, trigger); + auto p = Pulse(trigger, target); Observe(p, [&] (int v) { results.push_back(v); @@ -198,7 +206,7 @@ TYPED_TEST_P(OperationsTest, Snapshot1) auto trigger = MyDomain::MakeEventSource(); auto target = MyDomain::MakeVar(10); - auto snap = Snapshot(target, trigger); + auto snap = Snapshot(trigger, target); target <<= 10; trigger.Emit(); @@ -261,6 +269,76 @@ TYPED_TEST_P(OperationsTest, IterateByRef1) ASSERT_EQ(x()[i], 123); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// GenerateEvents test +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(OperationsTest, GenerateEvents1) +{ + auto in1 = MyDomain::MakeVar(1); + auto in2 = MyDomain::MakeVar(1); + + auto sum = in1 + in2; + auto prod = in1 * in2; + auto diff = in1 - in2; + + auto src1 = MyDomain::MakeEventSource(); + auto src2 = MyDomain::MakeEventSource(); + + auto out1 = GenerateEvents(src1, [] (int sum, int prod, int diff) { + return make_tuple(sum, prod, diff); + }, sum, prod, diff); + + auto out2 = GenerateEvents(src2, [] (int e, int sum, int prod, int diff) { + return make_tuple(e, sum, prod, diff); + }, sum, prod, diff); + + Observe(out1, [] (const tuple& t) { + ASSERT_EQ(get<0>(t), 33); + ASSERT_EQ(get<1>(t), 242); + ASSERT_EQ(get<2>(t), 11); + + DetachThisObserver(); + }); + + Observe(out2, [] (const tuple& t) { + ASSERT_EQ(get<0>(t), 42); + ASSERT_EQ(get<1>(t), 33); + ASSERT_EQ(get<2>(t), 242); + ASSERT_EQ(get<3>(t), 11); + + DetachThisObserver(); + }); + + in1 <<= 22; + in2 <<= 11; + + src1.Emit(); + src2.Emit(42); + + Observe(out1, [] (const tuple& t) { + ASSERT_EQ(get<0>(t), 3300); + ASSERT_EQ(get<1>(t), 24200); + ASSERT_EQ(get<2>(t), 1100); + + DetachThisObserver(); + }); + + Observe(out2, [] (const tuple& t) { + ASSERT_EQ(get<0>(t), 420); + ASSERT_EQ(get<1>(t), 3300); + ASSERT_EQ(get<2>(t), 24200); + ASSERT_EQ(get<3>(t), 1100); + + DetachThisObserver(); + }); + + in1 <<= 220; + in2 <<= 110; + + src1.Emit(); + src2.Emit(420); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// REGISTER_TYPED_TEST_CASE_P ( @@ -273,7 +351,8 @@ REGISTER_TYPED_TEST_CASE_P Pulse1, Snapshot1, FoldByRef1, - IterateByRef1 + IterateByRef1, + GenerateEvents1 ); } // ~namespace From 99d71a7d424c0ba497a1e448b0f1607ab2b87d59 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 18 May 2014 18:57:56 +0200 Subject: [PATCH 072/266] Adapted new notation for passing additional signal dependencies to observers. Instead of putting them behind the function, which is hard to read with lambdas, they are wrapped by a signal pack and put before the function. The utility function With(x,y,z) can be used --- include/react/Event.h | 12 +++---- include/react/Observer.h | 69 +++++++++++++++++++++++++++++++--------- include/react/Signal.h | 56 +++++++++++++++++--------------- src/test/ObserverTest.h | 22 +++---------- 4 files changed, 95 insertions(+), 64 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index 3da87552..95e0cd0d 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -91,10 +91,10 @@ class Events : public REACT_IMPL::EventStreamBase return REACT::Transform(*this, std::forward(f)); } - template - Observer Observe(F&& f, const TDeps& ... deps) const + template + Observer Observe(F&& f) const { - return REACT::Observe(*this, std::forward(f), deps ...); + return REACT::Observe(*this, std::forward(f)); } }; @@ -164,10 +164,10 @@ class Events : public REACT_IMPL::EventStreamBase(f)); } - template - Observer Observe(F&& f, const TDeps& ... deps) const + template + Observer Observe(F&& f) const { - return REACT::Observe(*this, std::forward(f), deps ...); + return REACT::Observe(*this, std::forward(f)); } }; diff --git a/include/react/Observer.h b/include/react/Observer.h index 918f6f66..7bbb71ce 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -23,6 +23,9 @@ template class Signal; +template +class SignalPack; + template class Events; @@ -153,6 +156,7 @@ auto Observe(const Events& subject, FIn&& func) return Observer(rawObsPtr, subject.NodePtr()); } +// Token stream version template < typename D, @@ -198,13 +202,12 @@ template typename D, typename FIn, typename E, - typename TDepValue1, typename ... TDepValues, class = std::enable_if< ! std::is_same::value>::type > -auto Observe(const Events& subject, FIn&& func, - const Signal& dep1, const Signal& ... deps) +auto Observe(const Events& subject, + SignalPack depPack, FIn&& func) -> Observer { using REACT_IMPL::IObserver; @@ -213,9 +216,28 @@ auto Observe(const Events& subject, FIn&& func, using F = std::decay::type; - std::unique_ptr obsPtr{ - new SyncedObserverNode{ - subject.NodePtr(), std::forward(func), dep1.NodePtr(), deps.NodePtr() ...}}; + struct NodeBuilder_ + { + NodeBuilder_(const Events& subject, FIn&& func) : + MySubject{ subject }, + MyFunc{ std::forward(func) } + {} + + auto operator()(const Signal& ... deps) + -> std::unique_ptr + { + return std::unique_ptr { + new SyncedObserverNode { + MySubject.NodePtr(), std::forward(MyFunc), deps.NodePtr() ...}}; + } + + const Events& MySubject; + FIn MyFunc; + }; + + auto obsPtr = REACT_IMPL::apply( + NodeBuilder_{ subject, std::forward(func) }, + depPack.Data); auto* rawObsPtr = DomainSpecificObserverRegistry::Instance() .Register(std::move(obsPtr), subject.NodePtr().get()); @@ -223,15 +245,15 @@ auto Observe(const Events& subject, FIn&& func, return Observer(rawObsPtr, subject.NodePtr()); } +// Token stream version template < typename D, typename FIn, - typename TDepValue1, typename ... TDepValues > -auto Observe(const Events& subject, FIn&& func, - const Signal& dep1, const Signal& ... deps) +auto Observe(const Events& subject, + SignalPack depPack, FIn&& func) -> Observer { using REACT_IMPL::IObserver; @@ -246,19 +268,36 @@ auto Observe(const Events& subject, FIn&& func, Wrapper_(const Wrapper_& other) = default; Wrapper_(Wrapper_&& other) : MyFunc{ std::move(other.MyFunc) } {} - void operator()(EventToken, const TDepValue1& arg1, const TDepValues& ... args) + void operator()(EventToken, const TDepValues& ... args) { - MyFunc(arg1, args ...); + MyFunc(args ...); } F MyFunc; }; - Wrapper_ wrapper{ std::forward(func) }; + struct NodeBuilder_ + { + NodeBuilder_(const Events& subject, Wrapper_&& wrapper) : + MySubject{ subject }, + MyWrapper{ std::move(wrapper) } + {} - std::unique_ptr obsPtr{ - new SyncedObserverNode{ - subject.NodePtr(), std::move(wrapper), dep1.NodePtr(), deps.NodePtr() ...}}; + auto operator()(const Signal& ... deps) + -> std::unique_ptr + { + return std::unique_ptr { + new SyncedObserverNode { + MySubject.NodePtr(), std::move(MyWrapper), deps.NodePtr() ...}}; + } + + const Events& MySubject; + Wrapper_&& MyWrapper; + }; + + auto obsPtr = REACT_IMPL::apply( + NodeBuilder_{ subject, Wrapper_{ std::forward(func) } }, + depPack.Data); auto* rawObsPtr = DomainSpecificObserverRegistry::Instance() .Register(std::move(obsPtr), subject.NodePtr().get()); diff --git a/include/react/Signal.h b/include/react/Signal.h index 94a871c4..53f992f7 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -671,36 +671,42 @@ DECLARE_OP(||, LogicalOr); #undef DECLARE_OP /////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalList - Wraps several nodes in a tuple. Create with comma operator. +/// SignalPack - Wraps several nodes in a tuple. Create with comma operator. /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename ... TValues > -class SignalList +class SignalPack { public: - SignalList(const Signal& ... deps) : - data_{ std::tie(deps ...) } + SignalPack(const Signal& ... deps) : + Data{ std::tie(deps ...) } {} template - SignalList(const SignalList& curArgs, const Signal& newArg) : - data_{ std::tuple_cat(curArgs.data_, std::tie(newArg)) } + SignalPack(const SignalPack& curArgs, const Signal& newArg) : + Data{ std::tuple_cat(curArgs.Data, std::tie(newArg)) } {} -private: - std::tuple& ...> data_; - - template - friend class SignalList; - - template - friend auto operator->*(const SignalList&, F&&) - -> Signal::type>; + std::tuple& ...> Data; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// With - Utility function to create a SignalPack +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename ... TValues +> +auto With(const Signal& ... deps) + -> SignalPack +{ + return SignalPack(deps ...); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Comma operator overload to create input pack from 2 signals. /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -711,9 +717,9 @@ template typename TRightVal > auto operator,(const Signal& a, const Signal& b) - -> SignalList + -> SignalPack { - return SignalList(a, b); + return SignalPack(a, b); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -725,10 +731,10 @@ template typename ... TCurValues, typename TAppendValue > -auto operator,(const SignalList& cur, const Signal& append) - -> SignalList +auto operator,(const SignalPack& cur, const Signal& append) + -> SignalPack { - return SignalList(cur, append); + return SignalPack(cur, append); } /******************************************/ REACT_END /******************************************/ @@ -778,14 +784,14 @@ template < typename D, typename F, - typename ... TSignals + typename ... TValues > -auto operator->*(const SignalList& inputPack, F&& func) - -> Signal::type> +auto operator->*(const SignalPack& inputPack, F&& func) + -> Signal::type> { return apply( - REACT_IMPL::ApplyHelper::MakeSignal, - std::tuple_cat(std::forward_as_tuple(std::forward(func)), inputPack.data_)); + REACT_IMPL::ApplyHelper::MakeSignal, + std::tuple_cat(std::forward_as_tuple(std::forward(func)), inputPack.Data)); } /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/ObserverTest.h b/src/test/ObserverTest.h index fa954ba1..6b0293c1 100644 --- a/src/test/ObserverTest.h +++ b/src/test/ObserverTest.h @@ -144,38 +144,24 @@ TYPED_TEST_P(ObserverTest, SyncedObserveTest) auto src1 = MyDomain::MakeEventSource(); auto src2 = MyDomain::MakeEventSource(); - Observe(src1, [] (int sum, int prod, int diff) { + Observe(src1, With(sum,prod,diff), [] (int sum, int prod, int diff) { ASSERT_EQ(sum, 33); ASSERT_EQ(prod, 242); ASSERT_EQ(diff, 11); - }, sum,prod,diff); - - Observe(src2, [] (int e, int sum, int prod, int diff) { - ASSERT_EQ(e, 42); - ASSERT_EQ(sum, 33); - ASSERT_EQ(prod, 242); - ASSERT_EQ(diff, 11); - }, sum,prod,diff); - - src1.Observe([] (int sum, int prod, int diff) { - ASSERT_EQ(sum, 33); - ASSERT_EQ(prod, 242); - ASSERT_EQ(diff, 11); - }, sum,prod,diff); + }); - src2.Observe([] (int e, int sum, int prod, int diff) { + Observe(src2, With(sum,prod,diff), [] (int e, int sum, int prod, int diff) { ASSERT_EQ(e, 42); ASSERT_EQ(sum, 33); ASSERT_EQ(prod, 242); ASSERT_EQ(diff, 11); - }, sum,prod,diff); + }); in1 <<= 22; in2 <<= 11; src1.Emit(); src2.Emit(42); - } /////////////////////////////////////////////////////////////////////////////////////////////////// From 1c50d04f3b5de6ead49931469d1eed7b8f0e2044 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 18 May 2014 19:34:59 +0200 Subject: [PATCH 073/266] Huh? Who put those there? --- include/react/Event.h | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index 95e0cd0d..b215ccaa 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -511,19 +511,6 @@ auto Filter(TempEvents&& src, FIn&& filter) std::forward(filter), src.StealOp())); } -template -< - typename TEvents, - typename F, - class = std::enable_if< - IsEvent::value>::type -> -auto operator&(TEvents&& src, F&& filter) - -> decltype(Filter(std::forward(src), std::forward(filter))) -{ - return Filter(std::forward(src), std::forward(filter)); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Transform /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -563,19 +550,6 @@ auto Transform(TempEvents&& src, FIn&& func) std::forward(func), src.StealOp())); } -template -< - typename TEvents, - typename F, - class = std::enable_if< - IsEvent::value>::type -> -auto operator->*(TEvents&& src, F&& func) - -> decltype(Transform(std::forward(src), std::forward(func))) -{ - return Transform(std::forward(src), std::forward(func)); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Flatten /////////////////////////////////////////////////////////////////////////////////////////////////// From ad91d9f81afb8908e6f1fa09a75a329f19834cb6 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 19 May 2014 11:12:36 +0200 Subject: [PATCH 074/266] Enabled more bitwise operator overloads. << and >> still disabled because (a) MSVC crashes (b) ambiguity with event stream input. --- include/react/Signal.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/react/Signal.h b/include/react/Signal.h index 53f992f7..7e1c3b8f 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -662,9 +662,9 @@ DECLARE_OP(>=, GreaterEqual); DECLARE_OP(&&, LogicalAnd); DECLARE_OP(||, LogicalOr); -//DECLARE_OP(&, BitwiseAnd); -//DECLARE_OP(|, BitwiseOr); -//DECLARE_OP(^, BitwiseXor); +DECLARE_OP(&, BitwiseAnd); +DECLARE_OP(|, BitwiseOr); +DECLARE_OP(^, BitwiseXor); //DECLARE_OP(<<, BitwiseLeftShift); // MSVC: Internal compiler error //DECLARE_OP(>>, BitwiseRightShift); From 41cda7ce7de13a99f24fd13e1d3e643a827d899d Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 19 May 2014 11:13:59 +0200 Subject: [PATCH 075/266] Added wrapper util to add dummy type as first parameter of given func. Useful for EventTokens. --- include/react/common/Util.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/include/react/common/Util.h b/include/react/common/Util.h index e347553a..6af94566 100644 --- a/include/react/common/Util.h +++ b/include/react/common/Util.h @@ -152,6 +152,27 @@ MoveBindWrapper MoveIntoBind(T&& t) return MoveBindWrapper{std::forward(t)}; } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// AddDummyArgWrapper +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct AddDummyArgWrapper +{ + // Dummy int to make sure it calls the right ctor + template + AddDummyArgWrapper(int, FIn&& func) : MyFunc{ std::forward(func) } {} + + AddDummyArgWrapper(const AddDummyArgWrapper& other) = default; + AddDummyArgWrapper(AddDummyArgWrapper&& other) : MyFunc{ std::move(other.MyFunc) } {} + + TRet operator()(TArg, const TDepValues& ... args) + { + return MyFunc(args ...); + } + + F MyFunc; +}; + /****************************************/ REACT_IMPL_END /***************************************/ namespace std From 47ac2e790868c2b9156a4b47e2649a6c6c405791 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 19 May 2014 11:15:15 +0200 Subject: [PATCH 076/266] Added synced overloads for event Filter and Transform. --- include/react/Event.h | 252 +++++++++++++++++++++--- include/react/detail/graph/EventNodes.h | 204 +++++++++++++++++-- 2 files changed, 418 insertions(+), 38 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index b215ccaa..ecc10e47 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -14,10 +14,20 @@ #include "react/Observer.h" #include "react/TypeTraits.h" +#include "react/common/Util.h" #include "react/detail/EventBase.h" /*****************************************/ REACT_BEGIN /*****************************************/ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Forward declarations +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class Signal; + +template +class SignalPack; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventToken /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -337,20 +347,24 @@ template auto MakeEventSource() -> EventSource { + using REACT_IMPL::EventSourceNode; + return EventSource( - std::make_shared>()); + std::make_shared>()); } template auto MakeEventSource() -> EventSource { + using REACT_IMPL::EventSourceNode; + return EventSource( - std::make_shared>()); + std::make_shared>()); } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward +/// Forward - Why would anyone ever need this /////////////////////////////////////////////////////////////////////////////////////////////////// template < @@ -360,8 +374,10 @@ template auto Forward(const Events& other) -> Events { + using REACT_IMPL::EventForwardNode; + return Events( - std::make_shared>(other.NodePtr())); + std::make_shared>(other.NodePtr())); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -380,11 +396,13 @@ template auto Merge(const Events& arg1, const Events& ... args) -> TempEvents { + using REACT_IMPL::EventOpNode; + static_assert(sizeof...(TArgs) > 0, "react::Merge requires at least 2 arguments."); return TempEvents( - std::make_shared>( + std::make_shared>( arg1.NodePtr(), args.NodePtr() ...)); } @@ -407,8 +425,10 @@ template auto operator|(const TLeftEvents& lhs, const TRightEvents& rhs) -> TempEvents { + using REACT_IMPL::EventOpNode; + return TempEvents( - std::make_shared>( + std::make_shared>( lhs.NodePtr(), rhs.NodePtr())); } @@ -425,8 +445,10 @@ template auto operator|(TempEvents&& lhs, TempEvents&& rhs) -> TempEvents { + using REACT_IMPL::EventOpNode; + return TempEvents( - std::make_shared>( + std::make_shared>( lhs.StealOp(), rhs.StealOp())); } @@ -447,8 +469,10 @@ template auto operator|(TempEvents&& lhs, const TRightEvents& rhs) -> TempEvents { + using REACT_IMPL::EventOpNode; + return TempEvents( - std::make_shared>( + std::make_shared>( lhs.StealOp(), rhs.NodePtr())); } @@ -469,8 +493,10 @@ template auto operator|(const TLeftEvents& lhs, TempEvents&& rhs) -> TempEvents { + using REACT_IMPL::EventOpNode; + return TempEvents( - std::make_shared>( + std::make_shared>( lhs.NodePtr(), rhs.StealOp())); } @@ -489,8 +515,10 @@ template auto Filter(const Events& src, FIn&& filter) -> TempEvents { + using REACT_IMPL::EventOpNode; + return TempEvents( - std::make_shared>( + std::make_shared>( std::forward(filter), src.NodePtr())); } @@ -506,50 +534,222 @@ template auto Filter(TempEvents&& src, FIn&& filter) -> TempEvents { + using REACT_IMPL::EventOpNode; + return TempEvents( - std::make_shared>( + std::make_shared>( std::forward(filter), src.StealOp())); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Filter - Synced +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename E, + typename FIn, + typename ... TDepValues +> +auto Filter(const Events& source, SignalPack depPack, FIn&& func) + -> Events +{ + using REACT_IMPL::SyncedEventFilterNode; + + using F = std::decay::type; + + struct NodeBuilder_ + { + NodeBuilder_(const Events& source, FIn&& func) : + MySource{ source }, + MyFunc{ std::forward(func) } + {} + + auto operator()(const Signal& ... deps) + -> Events + { + return Events( + std::make_shared>( + MySource.NodePtr(), std::forward(MyFunc), deps.NodePtr() ...)); + } + + const Events& MySource; + FIn MyFunc; + }; + + return REACT_IMPL::apply( + NodeBuilder_{ source, std::forward(func) }, + depPack.Data); +} + +// Token stream version +template +< + typename D, + typename FIn, + typename ... TDepValues +> +auto Filter(const Events& source, SignalPack depPack, FIn&& func) + -> Events +{ + using REACT_IMPL::SyncedEventFilterNode; + using REACT_IMPL::AddDummyArgWrapper; + + using F = std::decay::type; + using WrapperT = AddDummyArgWrapper; + + struct NodeBuilder_ + { + NodeBuilder_(const Events& source, WrapperT&& wrapper) : + MySource{ source }, + MyWrapper{ std::move(wrapper) } + {} + + auto operator()(const Signal& ... deps) + -> Events + { + return Events( + std::make_shared>( + MySource.NodePtr(), std::move(MyWrapper), deps.NodePtr() ...)); + } + + const Events& MySource; + WrapperT&& MyWrapper; + }; + + return REACT_IMPL::apply( + NodeBuilder_{ source, WrapperT{ 0, std::forward(func) } }, + depPack.Data); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Transform /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, - typename EIn, + typename TIn, typename FIn, typename F = std::decay::type, - typename EOut = std::result_of::type, - typename TOp = REACT_IMPL::EventTransformOp> + typename TOut = std::result_of::type, + typename TOp = REACT_IMPL::EventTransformOp> > -auto Transform(const Events& src, FIn&& func) - -> TempEvents +auto Transform(const Events& src, FIn&& func) + -> TempEvents { - return TempEvents( - std::make_shared>( + using REACT_IMPL::EventOpNode; + + return TempEvents( + std::make_shared>( std::forward(func), src.NodePtr())); } template < typename D, - typename EIn, + typename TIn, typename TOpIn, typename FIn, typename F = std::decay::type, - typename EOut = std::result_of::type, - typename TOpOut = REACT_IMPL::EventTransformOp + typename TOut = std::result_of::type, + typename TOpOut = REACT_IMPL::EventTransformOp > -auto Transform(TempEvents&& src, FIn&& func) - -> TempEvents +auto Transform(TempEvents&& src, FIn&& func) + -> TempEvents { - return TempEvents( - std::make_shared>( + using REACT_IMPL::EventOpNode; + + return TempEvents( + std::make_shared>( std::forward(func), src.StealOp())); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Transform - Synced +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename TIn, + typename FIn, + typename ... TDepValues, + typename TOut = std::result_of::type +> +auto Transform(const Events& source, SignalPack depPack, FIn&& func) + -> Events +{ + using REACT_IMPL::SyncedEventTransformNode; + + using F = std::decay::type; + + struct NodeBuilder_ + { + NodeBuilder_(const Events& source, FIn&& func) : + MySource{ source }, + MyFunc{ std::forward(func) } + {} + + auto operator()(const Signal& ... deps) + -> Events + { + return Events( + std::make_shared>( + MySource.NodePtr(), std::forward(MyFunc), deps.NodePtr() ...)); + } + + const Events& MySource; + FIn MyFunc; + }; + + return REACT_IMPL::apply( + NodeBuilder_{ source, std::forward(func) }, + depPack.Data); +} + +// Token stream version +template +< + typename D, + typename FIn, + typename ... TDepValues, + typename TOut = std::result_of::type +> +auto Transform(const Events& source, SignalPack depPack, FIn&& func) + -> Events +{ + using REACT_IMPL::SyncedEventTransformNode; + using REACT_IMPL::AddDummyArgWrapper; + + using F = std::decay::type; + using WrapperT = AddDummyArgWrapper; + + struct NodeBuilder_ + { + NodeBuilder_(const Events& source, WrapperT&& wrapper) : + MySource{ source }, + MyWrapper{ std::move(wrapper) } + {} + + auto operator()(const Signal& ... deps) + -> Events + { + return Events( + std::make_shared< + SyncedEventTransformNode>( + MySource.NodePtr(), std::move(MyWrapper), deps.NodePtr() ...)); + } + + const Events& MySource; + WrapperT&& MyWrapper; + }; + + return REACT_IMPL::apply( + NodeBuilder_{ + source, WrapperT{ 0, std::forward(func) } }, + depPack.Data); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Flatten /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 35e2176f..d541a7ff 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -20,6 +20,15 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Forward declarations +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class SignalNode; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventStreamNodeAccessPolicy +/////////////////////////////////////////////////////////////////////////////////////////////////// // Empty base class optimization template struct EventStreamNodeAccessPolicy : @@ -298,23 +307,23 @@ class EventFilterOp : public ReactiveOpBase private: const TDep& getDep() const { return std::get<0>(deps_); } - template + template struct FilteredEventCollector { - FilteredEventCollector(const TFilter& filter, const TFiltered& filtered) : + FilteredEventCollector(const TFilter& filter, const TCollector& collector) : MyFilter{ filter }, - MyFiltered{ filtered } + MyCollector{ collector } {} void operator()(const E& e) const { // Accepted? if (MyFilter(e)) - MyFiltered(e); + MyCollector(e); } const TFilter& MyFilter; - const TFiltered& MyFiltered; + const TCollector& MyCollector; // The wrapped collector }; template @@ -491,13 +500,6 @@ class EventOpNode : public EventStreamNode /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventFlattenNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -class SignalNode; - template < typename D, @@ -576,4 +578,182 @@ class EventFlattenNode : public EventStreamNode SharedPtrT> inner_; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SycnedEventTransformNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename TIn, + typename TOut, + typename TFunc, + typename ... TDepValues +> +class SyncedEventTransformNode : public EventStreamNode +{ +public: + template + SyncedEventTransformNode(const std::shared_ptr>& source, F&& func, + const std::shared_ptr>& ... deps) : + EventStreamNode{ }, + source_{ source }, + func_{ std::forward(func) }, + deps_{ deps ... } + { + Engine::OnNodeCreate(*this); + Engine::OnNodeAttach(*this, *source); + REACT_EXPAND_PACK(D::Engine::OnNodeAttach(*this, *deps)); + } + + ~SyncedEventTransformNode() + { + Engine::OnNodeDetach(*this, *source_); + + apply( + DetachFunctor>...>{ *this }, + deps_); + + Engine::OnNodeDestroy(*this); + } + + virtual const char* GetNodeType() const override { return "SyncedEventTransformNode"; } + virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } + + virtual void Tick(void* turnPtr) override + { + struct EvalFunctor_ + { + EvalFunctor_(const TIn& e, TFunc& f) : + MyEvent{ e }, + MyFunc{ f } + {} + + TOut operator()(const SharedPtrT>& ... args) + { + return MyFunc(MyEvent, args->ValueRef() ...); + } + + const TIn& MyEvent; + TFunc& MyFunc; + }; + + using TurnT = typename D::Engine::TurnT; + TurnT& turn = *reinterpret_cast(turnPtr); + + SetCurrentTurn(turn, true); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + + for (const auto& e : source_->Events()) + events_.push_back(apply(EvalFunctor_{ e, func_ }, deps_)); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + + if (! events_.empty()) + Engine::OnNodePulse(*this, turn); + else + Engine::OnNodeIdlePulse(*this, turn); + } + +private: + using DepHolderT = std::tuple>...>; + + SharedPtrT> source_; + + DepHolderT deps_; + TFunc func_; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SycnedEventFilterNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename E, + typename TFunc, + typename ... TDepValues +> +class SyncedEventFilterNode : public EventStreamNode +{ +public: + template + SyncedEventFilterNode(const std::shared_ptr>& source, F&& filter, + const std::shared_ptr>& ... deps) : + EventStreamNode{ }, + source_{ source }, + filter_{ std::forward(filter) }, + deps_{ deps ... } + { + Engine::OnNodeCreate(*this); + Engine::OnNodeAttach(*this, *source); + REACT_EXPAND_PACK(D::Engine::OnNodeAttach(*this, *deps)); + } + + ~SyncedEventFilterNode() + { + Engine::OnNodeDetach(*this, *source_); + + apply( + DetachFunctor>...>{ *this }, + deps_); + + Engine::OnNodeDestroy(*this); + } + + virtual const char* GetNodeType() const override { return "SyncedEventFilterNode"; } + virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } + + virtual void Tick(void* turnPtr) override + { + struct EvalFunctor_ + { + EvalFunctor_(const E& e, TFunc& f) : + MyEvent{ e }, + MyFilter{ f } + {} + + bool operator()(const SharedPtrT>& ... args) + { + return MyFilter(MyEvent, args->ValueRef() ...); + } + + const E& MyEvent; + TFunc& MyFilter; + }; + + using TurnT = typename D::Engine::TurnT; + TurnT& turn = *reinterpret_cast(turnPtr); + + SetCurrentTurn(turn, true); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + + for (const auto& e : source_->Events()) + if (apply(EvalFunctor_{ e, filter_ }, deps_)) + events_.push_back(e); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + + if (! events_.empty()) + Engine::OnNodePulse(*this, turn); + else + Engine::OnNodeIdlePulse(*this, turn); + } + +private: + using DepHolderT = std::tuple>...>; + + SharedPtrT> source_; + + DepHolderT deps_; + TFunc filter_; +}; + /****************************************/ REACT_IMPL_END /***************************************/ From b155613ac128e25c6ff87c6deae9474b73e2aebf Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 19 May 2014 11:16:30 +0200 Subject: [PATCH 077/266] Observe refactoring. --- include/react/Observer.h | 42 +++++++++++----------------------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/include/react/Observer.h b/include/react/Observer.h index 7bbb71ce..b9f1c49c 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -11,6 +11,7 @@ #include #include +#include "react/common/Util.h" #include "react/detail/IReactiveNode.h" #include "react/detail/ObserverBase.h" #include "react/detail/graph/ObserverNodes.h" @@ -168,24 +169,15 @@ auto Observe(const Events& subject, FIn&& func) using REACT_IMPL::IObserver; using REACT_IMPL::EventObserverNode; using REACT_IMPL::DomainSpecificObserverRegistry; + using REACT_IMPL::AddDummyArgWrapper; using F = std::decay::type; + using WrapperT = AddDummyArgWrapper; - struct Wrapper_ - { - Wrapper_(FIn&& func) : MyFunc{ std::forward(func) } {} - Wrapper_(const Wrapper_& other) = default; - Wrapper_(Wrapper_&& other) : MyFunc{ std::move(other.MyFunc) } {} - - void operator()(EventToken) { MyFunc(); } - - F MyFunc; - }; - - Wrapper_ wrapper{ std::forward(func) }; + WrapperT wrapper{ 0, std::forward(func) }; std::unique_ptr obsPtr{ - new EventObserverNode{ + new EventObserverNode{ subject.NodePtr(), std::move(wrapper)}}; auto* rawObsPtr = DomainSpecificObserverRegistry::Instance() @@ -259,26 +251,14 @@ auto Observe(const Events& subject, using REACT_IMPL::IObserver; using REACT_IMPL::SyncedObserverNode; using REACT_IMPL::DomainSpecificObserverRegistry; + using REACT_IMPL::AddDummyArgWrapper; using F = std::decay::type; - - struct Wrapper_ - { - Wrapper_(FIn&& func) : MyFunc{ std::forward(func) } {} - Wrapper_(const Wrapper_& other) = default; - Wrapper_(Wrapper_&& other) : MyFunc{ std::move(other.MyFunc) } {} - - void operator()(EventToken, const TDepValues& ... args) - { - MyFunc(args ...); - } - - F MyFunc; - }; + using WrapperT = AddDummyArgWrapper; struct NodeBuilder_ { - NodeBuilder_(const Events& subject, Wrapper_&& wrapper) : + NodeBuilder_(const Events& subject, WrapperT&& wrapper) : MySubject{ subject }, MyWrapper{ std::move(wrapper) } {} @@ -287,16 +267,16 @@ auto Observe(const Events& subject, -> std::unique_ptr { return std::unique_ptr { - new SyncedObserverNode { + new SyncedObserverNode { MySubject.NodePtr(), std::move(MyWrapper), deps.NodePtr() ...}}; } const Events& MySubject; - Wrapper_&& MyWrapper; + WrapperT&& MyWrapper; }; auto obsPtr = REACT_IMPL::apply( - NodeBuilder_{ subject, Wrapper_{ std::forward(func) } }, + NodeBuilder_{ subject, WrapperT{ 0, std::forward(func) } }, depPack.Data); auto* rawObsPtr = DomainSpecificObserverRegistry::Instance() From d18fab40852c40cf107a1633e008d2154485960f Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 19 May 2014 11:45:05 +0200 Subject: [PATCH 078/266] Removed GenerateEvents. No longer required due to extended Transform. --- include/react/Algorithm.h | 63 -------------- include/react/detail/graph/AlgorithmNodes.h | 91 --------------------- 2 files changed, 154 deletions(-) diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index e8323f37..332516d8 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -200,69 +200,6 @@ auto Pulse(const Events& trigger, const Signal& target) target.NodePtr(), trigger.NodePtr())); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// GenerateEvents -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E, - typename FIn, - typename ... TDepValues, - typename TOut = std::result_of::type, - class = std::enable_if< - ! std::is_same::value>::type -> -auto GenerateEvents(const Events& trigger, FIn&& func, - const Signal& ... deps) - -> Events -{ - using REACT_IMPL::EventGeneratorNode; - - using F = std::decay::type; - - return Events( - std::make_shared>( - trigger.NodePtr(), std::forward(func), deps.NodePtr() ...)); -} - -// Token stream version -template -< - typename D, - typename FIn, - typename ... TDepValues, - typename TOut = std::result_of::type -> -auto GenerateEvents(const Events& trigger, FIn&& func, - const Signal& ... deps) - -> Events -{ - using REACT_IMPL::EventGeneratorNode; - - using F = std::decay::type; - - struct Wrapper_ - { - Wrapper_(FIn&& func) : MyFunc{ std::forward(func) } {} - Wrapper_(const Wrapper_& other) = default; - Wrapper_(Wrapper_&& other) : MyFunc{ std::move(other.MyFunc) } {} - - void operator()(EventToken, const TDepValues& ... args) - { - MyFunc(args ...); - } - - F MyFunc; - }; - - Wrapper_ wrapper{ std::forward(func) }; - - return Events( - std::make_shared>( - trigger.NodePtr(), std::move(wrapper), deps.NodePtr() ...)); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Changed /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index 6837db44..54470efc 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -520,95 +520,4 @@ class PulseNode : public EventStreamNode const SharedPtrT> trigger_; }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventGeneratorNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E, - typename TOut, - typename TFunc, - typename ... TDepValues -> -class EventGeneratorNode : public EventStreamNode -{ -public: - template - EventGeneratorNode(const std::shared_ptr>& trigger, F&& func, - const std::shared_ptr>& ... deps) : - EventStreamNode{ }, - trigger_{ trigger }, - func_{ std::forward(func) }, - deps_{ deps ... } - { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *trigger_); - REACT_EXPAND_PACK(Engine::OnNodeAttach(*this, *deps)); - } - - ~EventGeneratorNode() - { - - Engine::OnNodeDetach(*this, *trigger_); - - apply( - DetachFunctor>...>{ *this }, - deps_); - - Engine::OnNodeDestroy(*this); - } - - virtual const char* GetNodeType() const override { return "EventGeneratorNode"; } - virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } - - virtual void Tick(void* turnPtr) override - { - struct EvalFunctor_ - { - EvalFunctor_(const E& e, TFunc& f) : - MyEvent{ e }, - MyFunc{ f } - {} - - void operator()(const SharedPtrT>& ... args) - { - MyFunc(MyEvent, args->ValueRef() ...); - } - - const E& MyEvent; - TFunc& MyFunc; - }; - - typedef typename D::Engine::TurnT TurnT; - TurnT& turn = *reinterpret_cast(turnPtr); - - SetCurrentTurn(turn, true); - trigger_->SetCurrentTurn(turn); - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - for (const auto& e : trigger_->Events()) - apply(EvalFunctor_{ e, func_ }, deps_); - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - if (events_.size() > 0) - Engine::OnNodePulse(*this, turn); - else - Engine::OnNodeIdlePulse(*this, turn); - } - -private: - using DepHolderT = std::tuple>...>; - - const SharedPtrT> trigger_; - - TFunc func_; - DepHolderT deps_; -}; - /****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file From f8bb9d68395d111f95d91cfe4e68915ec933eedf Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 19 May 2014 11:46:09 +0200 Subject: [PATCH 079/266] Misc fixes. --- include/react/Event.h | 8 ++++++-- include/react/detail/graph/EventNodes.h | 6 ++++++ include/react/detail/graph/SignalNodes.h | 3 +++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index ecc10e47..0d295444 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -549,7 +549,9 @@ template typename D, typename E, typename FIn, - typename ... TDepValues + typename ... TDepValues, + class = std::enable_if< + ! std::is_same::value>::type > auto Filter(const Events& source, SignalPack depPack, FIn&& func) -> Events @@ -674,7 +676,9 @@ template typename TIn, typename FIn, typename ... TDepValues, - typename TOut = std::result_of::type + typename TOut = std::result_of::type, + class = std::enable_if< + ! std::is_same::value>::type > auto Transform(const Events& source, SignalPack depPack, FIn&& func) -> Events diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index d541a7ff..6ed76821 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -642,6 +642,9 @@ class SyncedEventTransformNode : public EventStreamNode TurnT& turn = *reinterpret_cast(turnPtr); SetCurrentTurn(turn, true); + // Update of this node could be triggered from deps, + // so make sure source doesnt contain events from last turn + source_->SetCurrentTurn(turn); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); @@ -730,6 +733,9 @@ class SyncedEventFilterNode : public EventStreamNode TurnT& turn = *reinterpret_cast(turnPtr); SetCurrentTurn(turn, true); + // Update of this node could be triggered from deps, + // so make sure source doesnt contain events from last turn + source_->SetCurrentTurn(turn); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index e78d6775..6d9aac4e 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -14,6 +14,9 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Forward declarations +/////////////////////////////////////////////////////////////////////////////////////////////////// template bool Equals(const L& lhs, const R& rhs); From ec43acd757192a9a5db98504c3a8053b6f4d94ac Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 19 May 2014 13:14:47 +0200 Subject: [PATCH 080/266] Fixed DetachThisObserver. --- include/react/Observer.h | 2 +- include/react/detail/ObserverBase.h | 10 ++-- include/react/detail/graph/ObserverNodes.h | 16 +++--- src/test/ObserverTest.h | 58 ++++++++++++++++++++-- 4 files changed, 73 insertions(+), 13 deletions(-) diff --git a/include/react/Observer.h b/include/react/Observer.h index b9f1c49c..fdd34661 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -302,7 +302,7 @@ void DetachAllObservers(const TSubject& subject) /////////////////////////////////////////////////////////////////////////////////////////////////// inline void DetachThisObserver() { - REACT_IMPL::current_observer_state_::shouldDetach = true; + REACT_IMPL::GlobalObserverState<>::ShouldDetach = true; } /******************************************/ REACT_END /******************************************/ \ No newline at end of file diff --git a/include/react/detail/ObserverBase.h b/include/react/detail/ObserverBase.h index 405a926d..b57b3b74 100644 --- a/include/react/detail/ObserverBase.h +++ b/include/react/detail/ObserverBase.h @@ -23,10 +23,14 @@ class Observable; class IObserver; // tbb tasks are non-preemptible, thread local flag for each worker -namespace current_observer_state_ +template +struct GlobalObserverState { - static REACT_TLS bool shouldDetach = false; -} + static REACT_TLS bool ShouldDetach; +}; + +template <> +bool GlobalObserverState::ShouldDetach = false; /////////////////////////////////////////////////////////////////////////////////////////////////// /// ObserverRegistry diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index 3640450f..00bee39d 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -77,7 +77,7 @@ class SignalObserverNode : public ObserverNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - current_observer_state_::shouldDetach = false; + GlobalObserverState<>::ShouldDetach = false; ContinuationHolder::SetTurn(turn); @@ -86,7 +86,7 @@ class SignalObserverNode : public ObserverNode ContinuationHolder::Clear(); - if (current_observer_state_::shouldDetach) + if (GlobalObserverState<>::ShouldDetach) turn.QueueForDetach(*this); REACT_LOG(D::Log().template Append( @@ -148,7 +148,7 @@ class EventObserverNode : public ObserverNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - current_observer_state_::shouldDetach = false; + GlobalObserverState<>::ShouldDetach = false; ContinuationHolder::SetTurn(turn); @@ -160,7 +160,7 @@ class EventObserverNode : public ObserverNode ContinuationHolder::Clear(); - if (current_observer_state_::shouldDetach) + if (GlobalObserverState<>::ShouldDetach) turn.QueueForDetach(*this); REACT_LOG(D::Log().template Append( @@ -244,19 +244,23 @@ class SyncedObserverNode : public ObserverNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - current_observer_state_::shouldDetach = false; + GlobalObserverState<>::ShouldDetach = false; ContinuationHolder::SetTurn(turn); if (auto p = subject_.lock()) { + // Update of this node could be triggered from deps, + // so make sure source doesnt contain events from last turn + p->SetCurrentTurn(turn); + for (const auto& e : p->Events()) apply(EvalFunctor_{ e, func_ }, deps_); } ContinuationHolder::Clear(); - if (current_observer_state_::shouldDetach) + if (GlobalObserverState<>::ShouldDetach) turn.QueueForDetach(*this); REACT_LOG(D::Log().template Append( diff --git a/src/test/ObserverTest.h b/src/test/ObserverTest.h index 6b0293c1..2448db25 100644 --- a/src/test/ObserverTest.h +++ b/src/test/ObserverTest.h @@ -115,9 +115,8 @@ TYPED_TEST_P(ObserverTest, ScopedObserverTest) auto in = MyDomain::MakeVar(1); { - MyDomain::ScopedObserverT obs = in.Observe([&] (int v) { + MyDomain::ScopedObserverT obs = Observe(in, [&] (int v) { results.push_back(v); - }); in <<=2; @@ -164,13 +163,66 @@ TYPED_TEST_P(ObserverTest, SyncedObserveTest) src2.Emit(42); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// DetachThisObserver1 test +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(ObserverTest, DetachThisObserver1) +{ + auto src = MyDomain::MakeEventSource(); + + int count = 0; + + Observe(src, [&] { + ++count; + DetachThisObserver(); + }); + + src.Emit(); + src.Emit(); + + printf("Count %d\n", count); + ASSERT_EQ(count, 1); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// DetachThisObserver2 test +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(ObserverTest, DetachThisObserver2) +{ + auto in1 = MyDomain::MakeVar(1); + auto in2 = MyDomain::MakeVar(1); + + auto sum = in1 + in2; + auto prod = in1 * in2; + auto diff = in1 - in2; + + auto src = MyDomain::MakeEventSource(); + + int count = 0; + + Observe(src, With(sum,prod,diff), [&] (int sum, int prod, int diff) { + ++count; + DetachThisObserver(); + }); + + in1 <<= 22; + in2 <<= 11; + + src.Emit(); + src.Emit(); + + ASSERT_EQ(count, 1); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// REGISTER_TYPED_TEST_CASE_P ( ObserverTest, Detach, ScopedObserverTest, - SyncedObserveTest + SyncedObserveTest, + DetachThisObserver1, + DetachThisObserver2 ); } // ~namespace From 223960a59f4859863267897e75175abc39a239d8 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 19 May 2014 13:15:43 +0200 Subject: [PATCH 081/266] Fixed GenerateEvents/Transform testcase. --- src/test/OperationsTest.h | 47 ++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/src/test/OperationsTest.h b/src/test/OperationsTest.h index 07e72acd..810efa8b 100644 --- a/src/test/OperationsTest.h +++ b/src/test/OperationsTest.h @@ -270,9 +270,9 @@ TYPED_TEST_P(OperationsTest, IterateByRef1) } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// GenerateEvents test +/// SyncedTransform1 test /////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, GenerateEvents1) +TYPED_TEST_P(OperationsTest, SyncedTransform1) { auto in1 = MyDomain::MakeVar(1); auto in2 = MyDomain::MakeVar(1); @@ -284,15 +284,20 @@ TYPED_TEST_P(OperationsTest, GenerateEvents1) auto src1 = MyDomain::MakeEventSource(); auto src2 = MyDomain::MakeEventSource(); - auto out1 = GenerateEvents(src1, [] (int sum, int prod, int diff) { + auto out1 = Transform(src1, With(sum,prod,diff), [] (int sum, int prod, int diff) { return make_tuple(sum, prod, diff); - }, sum, prod, diff); + }); - auto out2 = GenerateEvents(src2, [] (int e, int sum, int prod, int diff) { + auto out2 = Transform(src2, With(sum,prod,diff), [] (int e, int sum, int prod, int diff) { return make_tuple(e, sum, prod, diff); - }, sum, prod, diff); + }); + + int obsCount1 = 0; + int obsCount2 = 0; + + Observe(out1, [&] (const tuple& t) { + ++obsCount1; - Observe(out1, [] (const tuple& t) { ASSERT_EQ(get<0>(t), 33); ASSERT_EQ(get<1>(t), 242); ASSERT_EQ(get<2>(t), 11); @@ -300,7 +305,9 @@ TYPED_TEST_P(OperationsTest, GenerateEvents1) DetachThisObserver(); }); - Observe(out2, [] (const tuple& t) { + Observe(out2, [&] (const tuple& t) { + ++obsCount2; + ASSERT_EQ(get<0>(t), 42); ASSERT_EQ(get<1>(t), 33); ASSERT_EQ(get<2>(t), 242); @@ -315,19 +322,26 @@ TYPED_TEST_P(OperationsTest, GenerateEvents1) src1.Emit(); src2.Emit(42); - Observe(out1, [] (const tuple& t) { - ASSERT_EQ(get<0>(t), 3300); + ASSERT_EQ(obsCount1, 1); + ASSERT_EQ(obsCount2, 1); + + Observe(out1, [&] (const tuple& t) { + ++obsCount1; + + ASSERT_EQ(get<0>(t), 330); ASSERT_EQ(get<1>(t), 24200); - ASSERT_EQ(get<2>(t), 1100); + ASSERT_EQ(get<2>(t), 110); DetachThisObserver(); }); - Observe(out2, [] (const tuple& t) { + Observe(out2, [&] (const tuple& t) { + ++obsCount2; + ASSERT_EQ(get<0>(t), 420); - ASSERT_EQ(get<1>(t), 3300); + ASSERT_EQ(get<1>(t), 330); ASSERT_EQ(get<2>(t), 24200); - ASSERT_EQ(get<3>(t), 1100); + ASSERT_EQ(get<3>(t), 110); DetachThisObserver(); }); @@ -337,6 +351,9 @@ TYPED_TEST_P(OperationsTest, GenerateEvents1) src1.Emit(); src2.Emit(420); + + ASSERT_EQ(obsCount1, 2); + ASSERT_EQ(obsCount2, 2); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -352,7 +369,7 @@ REGISTER_TYPED_TEST_CASE_P Snapshot1, FoldByRef1, IterateByRef1, - GenerateEvents1 + SyncedTransform1 ); } // ~namespace From 73b1fbe62045c72ea6dcf0163359ebb64213cd2b Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 19 May 2014 13:20:56 +0200 Subject: [PATCH 082/266] Updated sandbox based on recent changes. --- src/sandbox/Main.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sandbox/Main.cpp b/src/sandbox/Main.cpp index daf74323..3c7f71ea 100644 --- a/src/sandbox/Main.cpp +++ b/src/sandbox/Main.cpp @@ -136,7 +136,7 @@ void EventExample2() // stream just indicates that it has fired, i.e. it behaves like a token stream. auto emitter = D::MakeEventSource(); - auto counter = Iterate(0, emitter, Incrementer()); + auto counter = Iterate(0, emitter, [] (int v) { return v+1; }); // In this case, the observer func must not declare a parameter for token streams. Observe(emitter, [] { @@ -223,7 +223,7 @@ class Manager : public ReactiveObject Manager(Company& c) : CurrentCompany{ MakeVar(ref(c)) } { - nameObs = REACTIVE_REF(CurrentCompany, Name).Observe([] (string name) { + nameObs = Observe(REACTIVE_REF(CurrentCompany, Name), [] (string name) { cout << "Manager: Now managing " << name << endl; }); } @@ -345,8 +345,6 @@ int main() LoopTest(); #endif - //Debug(); - #ifdef REACT_ENABLE_LOGGING std::ofstream logfile; logfile.open("log.txt"); From f2d981d798fa0b1b2966dd207796b47116ab8b50 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 19 May 2014 13:42:17 +0200 Subject: [PATCH 083/266] Removed Observe aliases from Signal and Events. The had no benefit as Observe cannot be chained further. --- include/react/Event.h | 12 ------------ include/react/Signal.h | 12 ------------ src/test/OperationsTest.h | 2 +- src/test/SignalTest.h | 31 +++---------------------------- 4 files changed, 4 insertions(+), 53 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index 0d295444..c622fe82 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -100,12 +100,6 @@ class Events : public REACT_IMPL::EventStreamBase { return REACT::Transform(*this, std::forward(f)); } - - template - Observer Observe(F&& f) const - { - return REACT::Observe(*this, std::forward(f)); - } }; // Specialize for references @@ -173,12 +167,6 @@ class Events : public REACT_IMPL::EventStreamBase(f)); } - - template - Observer Observe(F&& f) const - { - return REACT::Observe(*this, std::forward(f)); - } }; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/Signal.h b/include/react/Signal.h index 7e1c3b8f..7c14772c 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -68,12 +68,6 @@ class Signal : public REACT_IMPL::SignalBase { return REACT::Flatten(*this); } - - template - Observer Observe(F&& f) const - { - return REACT::Observe(*this, std::forward(f)); - } }; // Specialize for references @@ -117,12 +111,6 @@ class Signal : public REACT_IMPL::SignalBase> { return BaseT::IsValid(); } - - template - Observer Observe(F&& f) const - { - return REACT::Observe(*this, std::forward(f)); - } }; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/OperationsTest.h b/src/test/OperationsTest.h index 810efa8b..dfc10c32 100644 --- a/src/test/OperationsTest.h +++ b/src/test/OperationsTest.h @@ -132,7 +132,7 @@ TYPED_TEST_P(OperationsTest, Monitor1) return v > 10; }; - auto obs = Monitor(target).Filter(filterFunc).Observe([&] (int v) { + auto obs = Observe(Monitor(target).Filter(filterFunc), [&] (int v) { results.push_back(v); }); diff --git a/src/test/SignalTest.h b/src/test/SignalTest.h index 80cb4635..6668a5b4 100644 --- a/src/test/SignalTest.h +++ b/src/test/SignalTest.h @@ -514,38 +514,13 @@ TYPED_TEST_P(SignalTest, Flatten4) /// Member1 test /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(SignalTest, Member1) -{ - auto in1 = MyDomain::MakeVar(10); - auto in2 = MyDomain::MakeVar(std::string{ "a" }); - - auto x = in1 + 1; - - in1.Observe([] (int v) { - ASSERT_EQ(v, 20); - }); - - x.Observe([] (int v) { - ASSERT_EQ(v, 21); - }); - - in2.Observe([] (const std::string& v) { - ASSERT_EQ(v, "b"); - }); - - in1 <<= 20; - in2 <<= std::string{ "b"}; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Member2 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(SignalTest, Member2) { auto outer = MyDomain::MakeVar(10); auto inner = MyDomain::MakeVar(outer); + auto flattened = inner.Flatten(); - flattened.Observe([] (int v) { + Observe(flattened, [] (int v) { ASSERT_EQ(v, 30); }); @@ -560,7 +535,7 @@ REGISTER_TYPED_TEST_CASE_P Signals1, Signals2, Signals3, Signals4, FunctionBind1, FunctionBind2, Flatten1, Flatten2, Flatten3, Flatten4, - Member1, Member2 + Member1 ); } // ~namespace From 5fad4405fecab23796f2f0288dafb04fcc37e882 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 19 May 2014 15:56:40 +0200 Subject: [PATCH 084/266] Made AddDummyArgWrapper more flexible w.r.t. to const argument refs. --- include/react/Event.h | 4 ++-- include/react/Observer.h | 2 +- include/react/common/Util.h | 20 +++++++++++++++++++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index c622fe82..46fc1ecb 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -586,7 +586,7 @@ auto Filter(const Events& source, SignalPack depP using REACT_IMPL::AddDummyArgWrapper; using F = std::decay::type; - using WrapperT = AddDummyArgWrapper; + using WrapperT = AddDummyArgWrapper; struct NodeBuilder_ { @@ -714,7 +714,7 @@ auto Transform(const Events& source, SignalPack d using REACT_IMPL::AddDummyArgWrapper; using F = std::decay::type; - using WrapperT = AddDummyArgWrapper; + using WrapperT = AddDummyArgWrapper; struct NodeBuilder_ { diff --git a/include/react/Observer.h b/include/react/Observer.h index fdd34661..ad1fcfb4 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -254,7 +254,7 @@ auto Observe(const Events& subject, using REACT_IMPL::AddDummyArgWrapper; using F = std::decay::type; - using WrapperT = AddDummyArgWrapper; + using WrapperT = AddDummyArgWrapper; struct NodeBuilder_ { diff --git a/include/react/common/Util.h b/include/react/common/Util.h index 6af94566..8b5d739a 100644 --- a/include/react/common/Util.h +++ b/include/react/common/Util.h @@ -165,7 +165,7 @@ struct AddDummyArgWrapper AddDummyArgWrapper(const AddDummyArgWrapper& other) = default; AddDummyArgWrapper(AddDummyArgWrapper&& other) : MyFunc{ std::move(other.MyFunc) } {} - TRet operator()(TArg, const TDepValues& ... args) + TRet operator()(TArg, TDepValues& ... args) { return MyFunc(args ...); } @@ -173,6 +173,24 @@ struct AddDummyArgWrapper F MyFunc; }; +template +struct AddDummyArgWrapper +{ + // Dummy int to make sure it calls the right ctor + template + AddDummyArgWrapper(int, FIn&& func) : MyFunc{ std::forward(func) } {} + + AddDummyArgWrapper(const AddDummyArgWrapper& other) = default; + AddDummyArgWrapper(AddDummyArgWrapper&& other) : MyFunc{ std::move(other.MyFunc) } {} + + void operator()(TArg, TDepValues& ... args) + { + MyFunc(args ...); + } + + F MyFunc; +}; + /****************************************/ REACT_IMPL_END /***************************************/ namespace std From 18ac8b3fd94078a155dd3ad3f6e804442e55efc2 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 19 May 2014 16:02:07 +0200 Subject: [PATCH 085/266] Streamlined API. Fold is now Iterate, old Iterate has been removed. The case where the old Iterate would be needed is now covered by the overload for token streams. Shuffled some parameters around for consistency. --- include/react/Algorithm.h | 54 +++--- include/react/detail/graph/AlgorithmNodes.h | 173 +++----------------- src/benchmark/BenchmarkLifeSim.h | 16 +- src/sandbox/Main.cpp | 16 +- src/test/OperationsTest.h | 54 +++--- 5 files changed, 93 insertions(+), 220 deletions(-) diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index 332516d8..8bf235d3 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -34,95 +34,95 @@ class EventSource; enum class EventToken; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Fold +/// Iterate /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, - typename V, typename E, + typename V, typename FIn, - typename S = std::decay::type + typename S = std::decay::type, + class = std::enable_if< + ! std::is_same::value>::type > -auto Fold(V&& init, const Events& events, FIn&& func) +auto Iterate(const Events& events, V&& init, FIn&& func) -> Signal { - using REACT_IMPL::FoldNode; + using REACT_IMPL::IterateNode; using F = std::decay::type; return Signal( - std::make_shared>( + std::make_shared>( std::forward(init), events.NodePtr(), std::forward(func))); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// FoldByRef - Pass current value as reference -/////////////////////////////////////////////////////////////////////////////////////////////////// +// Token stream version template < typename D, typename V, - typename E, typename FIn, typename S = std::decay::type > -auto FoldByRef(V&& init, const Events& events, FIn&& func) +auto Iterate(const Events& events, V&& init, FIn&& func) -> Signal { - using REACT_IMPL::FoldByRefNode; + using REACT_IMPL::IterateNode; + using REACT_IMPL::AddDummyArgWrapper; using F = std::decay::type; + using WrapperT = AddDummyArgWrapper; return Signal( - std::make_shared>( - std::forward(init), events.NodePtr(), std::forward(func))); + std::make_shared>( + std::forward(init), events.NodePtr(), WrapperT{ 0, std::forward(func) })); } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Iterate +/// IterateByRef - Pass current value as reference /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, - typename V, typename E, + typename V, typename FIn, typename S = std::decay::type > -auto Iterate(V&& init, const Events& events, FIn&& func) +auto IterateByRef(const Events& events, V&& init, FIn&& func) -> Signal { - using REACT_IMPL::IterateNode; + using REACT_IMPL::IterateByRefNode; using F = std::decay::type; return Signal( - std::make_shared>( + std::make_shared>( std::forward(init), events.NodePtr(), std::forward(func))); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IterateByRef -/////////////////////////////////////////////////////////////////////////////////////////////////// +// Token stream version template < typename D, typename V, - typename E, typename FIn, typename S = std::decay::type > -auto IterateByRef(V&& init, const Events& events, FIn&& func) +auto IterateByRef(const Events& events, V&& init, FIn&& func) -> Signal { using REACT_IMPL::IterateByRefNode; + using REACT_IMPL::AddDummyArgWrapper; using F = std::decay::type; + using WrapperT = AddDummyArgWrapper; return Signal( - std::make_shared>( - std::forward(init), events.NodePtr(), std::forward(func))); + std::make_shared>( + std::forward(init), events.NodePtr(), WrapperT{ 0, std::forward(func) })); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -134,7 +134,7 @@ template typename V, typename T = std::decay::type > -auto Hold(V&& init, const Events& events) +auto Hold(const Events& events, V&& init) -> Signal { using REACT_IMPL::HoldNode; diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index 54470efc..e665eeaa 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -16,57 +16,7 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// -/// FoldBaseNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename E -> -class FoldBaseNode : public SignalNode -{ -public: - template - FoldBaseNode(T&& init, const SharedPtrT>& events) : - SignalNode(std::forward(init)), - events_{ events } - {} - - 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())); - - S newValue = calcNewValue(); - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - if (! impl::Equals(newValue, value_)) - { - value_ = newValue; - Engine::OnNodePulse(*this, turn); - } - else - { - Engine::OnNodeIdlePulse(*this, turn); - } - } - - virtual int DependencyCount() const override { return 1; } - -protected: - SharedPtrT> events_; - - virtual S calcNewValue() const = 0; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// FoldNode +/// IterateNode /////////////////////////////////////////////////////////////////////////////////////////////////// template < @@ -75,63 +25,20 @@ template typename E, typename TFunc > -class FoldNode : public FoldBaseNode +class IterateNode : public SignalNode { public: template - FoldNode(T&& init, const SharedPtrT>& events, F&& func) : - FoldBaseNode(std::forward(init), events), + IterateNode(T&& init, const std::shared_ptr>& events, F&& func) : + SignalNode{ std::forward(init) }, + events_{ events }, func_{ std::forward(func) } { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *events); } - ~FoldNode() - { - Engine::OnNodeDetach(*this, *events_); - Engine::OnNodeDestroy(*this); - } - - virtual const char* GetNodeType() const override { return "FoldNode"; } - -protected: - TFunc func_; - - virtual S calcNewValue() const override - { - S newValue = value_; - for (const auto& e : events_->Events()) - newValue = func_(newValue,e); - - return newValue; - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// FoldByRefNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename E, - typename TFunc -> -class FoldByRefNode : public SignalNode -{ -public: - template - FoldByRefNode(T&& init, const SharedPtrT>& events, F&& func) : - SignalNode(std::forward(init)), - func_{ std::forward(func) }, - events_{ events } - { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *events); - } - - ~FoldByRefNode() + ~IterateNode() { Engine::OnNodeDetach(*this, *events_); Engine::OnNodeDestroy(*this); @@ -145,65 +52,31 @@ class FoldByRefNode : public SignalNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); + S newValue = value_; for (const auto& e : events_->Events()) - func_(value_,e); + newValue = func_(e, newValue); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - // Always assume change - Engine::OnNodePulse(*this, turn); - } - - virtual const char* GetNodeType() const override { return "FoldByRefNode"; } - virtual int DependencyCount() const override { return 1; } - -protected: - TFunc func_; - - SharedPtrT> events_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IterateNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename E, - typename TFunc -> -class IterateNode : public FoldBaseNode -{ -public: - template - IterateNode(T&& init, const SharedPtrT>& events, F&& func) : - FoldBaseNode(std::forward(init), events), - func_{ std::forward(func) } - { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *events); - } - - ~IterateNode() - { - Engine::OnNodeDetach(*this, *events_); - Engine::OnNodeDestroy(*this); + if (! impl::Equals(newValue, value_)) + { + value_ = std::move(newValue); + Engine::OnNodePulse(*this, turn); + } + else + { + Engine::OnNodeIdlePulse(*this, turn); + } } virtual const char* GetNodeType() const override { return "IterateNode"; } + virtual int DependencyCount() const override { return 1; } -protected: +private: + SharedPtrT> events_; + TFunc func_; - - virtual S calcNewValue() const override - { - S newValue = value_; - for (const auto& e : events_->Events()) - newValue = func_(newValue); - return newValue; - } }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -221,7 +94,7 @@ class IterateByRefNode : public SignalNode public: template IterateByRefNode(T&& init, const SharedPtrT>& events, F&& func) : - SignalNode(std::forward(init)), + SignalNode{ std::forward(init) }, func_{ std::forward(func) }, events_{ events } { @@ -244,7 +117,7 @@ class IterateByRefNode : public SignalNode GetObjectId(*this), turn.Id())); for (const auto& e : events_->Events()) - func_(value_); + func_(e, value_); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); diff --git a/src/benchmark/BenchmarkLifeSim.h b/src/benchmark/BenchmarkLifeSim.h index 3d0df5df..4dcea7a2 100644 --- a/src/benchmark/BenchmarkLifeSim.h +++ b/src/benchmark/BenchmarkLifeSim.h @@ -48,9 +48,9 @@ template class Time : public ReactiveObject { public: - EventSourceT NewDay = MakeEventSource(); + EventSourceT<> NewDay = MakeEventSource(); - SignalT TotalDays = Iterate(0, NewDay, Incrementer()); + SignalT TotalDays = Iterate(NewDay, 0, Incrementer()); SignalT DayOfYear = TotalDays % 365; SignalT Season = DayOfYear ->* [] (int day) { @@ -70,7 +70,7 @@ class Region : public ReactiveObject EventSourceT EnterOrLeave = MakeEventSource(); - SignalT AnimalCount = Fold(0, EnterOrLeave, [] (int count, Migration m) { + SignalT AnimalCount = Iterate(EnterOrLeave, 0, [] (Migration m, int count) { return m == Migration::enter ? count + 1 : count - 1; }); @@ -167,7 +167,7 @@ class Animal : public ReactiveObject CurrentRegion( MakeVar(initRegion) ), generator( seed ) { - Position = Fold(initRegion->Center(), Moving, [this] (PositionT position, bool shouldMigrate) { + Position = Iterate(Moving, initRegion->Center(), [this] (bool shouldMigrate, PositionT position) { std::uniform_int_distribution dist(-1,1); // Wander randomly @@ -202,9 +202,9 @@ class Animal : public ReactiveObject EventsT FoodReceived = REACTIVE_PTR(CurrentRegion, FoodOutput); - SignalT Age = Iterate(0, theTime.NewDay, Incrementer()); + SignalT Age = Iterate(theTime.NewDay, 0, Incrementer()); - SignalT Health = Fold(100, FoodReceived, [] (int health, int food) { + SignalT Health = Iterate(FoodReceived, 100, [] (int food, int health) { auto newHealth = health + food - 10; return newHealth < 0 ? 0 : newHealth > 10000 ? 10000 : newHealth; }); @@ -213,7 +213,7 @@ class Animal : public ReactiveObject //Event Migrating = EventSource(); - SignalT ShouldMigrate = Hold(0, FoodReceived) < 10; + SignalT ShouldMigrate = Hold(FoodReceived, 0) < 10; EventsT Moving = Pulse(theTime.NewDay, ShouldMigrate); }; @@ -282,7 +282,7 @@ struct Benchmark_LifeSim : public BenchmarkBase auto t0 = tbb::tick_count::now(); for (int i=0; i(); - auto fold1 = Fold(0, src, [] (int v, int d) { + auto it = Iterate(src, 0, [] (int d, int v) { return v + d; }); for (auto i=1; i<=100; i++) src << i; - cout << fold1() << endl; + cout << it() << endl; auto charSrc = D::MakeEventSource(); - auto strFold = Fold(string(""), charSrc, [] (string s, char c) { + auto str = Iterate(charSrc, string(""), [] (char c, string s) { return s + c; }); charSrc << 'T' << 'e' << 's' << 't'; - cout << "Str: " << strFold() << endl; + cout << "Str: " << str() << endl; } #ifdef REACT_ENABLE_REACTORS @@ -339,7 +339,7 @@ int main() ObjectExample1(); ObjectExample2(); - FoldExample1(); + IterateExample1(); #ifdef REACT_ENABLE_REACTORS LoopTest(); diff --git a/src/test/OperationsTest.h b/src/test/OperationsTest.h index dfc10c32..446274a9 100644 --- a/src/test/OperationsTest.h +++ b/src/test/OperationsTest.h @@ -43,12 +43,12 @@ class OperationsTest : public testing::Test TYPED_TEST_CASE_P(OperationsTest); /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Fold1 test +/// Iterate1 test /////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, Fold1) +TYPED_TEST_P(OperationsTest, Iterate1) { auto numSrc = MyDomain::MakeEventSource(); - auto numFold = Fold(0, numSrc, [] (int v, int d) { + auto numFold = Iterate(numSrc, 0, [] (int d, int v) { return v + d; }); @@ -60,7 +60,7 @@ TYPED_TEST_P(OperationsTest, Fold1) ASSERT_EQ(numFold(), 5050); auto charSrc = MyDomain::MakeEventSource(); - auto strFold = Fold(string(""), charSrc, [] (string s, char c) { + auto strFold = Iterate(charSrc, string(""), [] (char c, string s) { return s + c; }); @@ -70,40 +70,40 @@ TYPED_TEST_P(OperationsTest, Fold1) } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Fold2 test +/// Iterate2 test /////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, Fold2) +TYPED_TEST_P(OperationsTest, Iterate2) { - auto src = MyDomain::MakeEventSource(); - auto f = Fold(0, src, [] (int v, int d) { + auto numSrc = MyDomain::MakeEventSource(); + auto numFold = Iterate(numSrc, 0, [] (int d, int v) { return v + d; }); int c = 0; - Observe(f, [&] (int v) { + Observe(numFold, [&] (int v) { c++; ASSERT_EQ(v, 5050); }); MyDomain::DoTransaction([&] { for (auto i=1; i<=100; i++) - src << i; + numSrc << i; }); - ASSERT_EQ(f(), 5050); + ASSERT_EQ(numFold(), 5050); ASSERT_EQ(c, 1); } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Iterate1 test +/// Iterate3 test /////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, Iterate1) +TYPED_TEST_P(OperationsTest, Iterate3) { auto trigger = MyDomain::MakeEventSource(); { - auto inc = Iterate(0, trigger, Incrementer{}); + auto inc = Iterate(trigger, 0, Incrementer{}); for (auto i=1; i<=100; i++) trigger.Emit(); @@ -111,7 +111,7 @@ TYPED_TEST_P(OperationsTest, Iterate1) } { - auto dec = Iterate(100, trigger, Decrementer{}); + auto dec = Iterate(trigger, 100, Decrementer{}); for (auto i=1; i<=100; i++) trigger.Emit(); @@ -158,7 +158,7 @@ TYPED_TEST_P(OperationsTest, Hold1) { auto src = MyDomain::MakeEventSource(); - auto h = Hold(0, src); + auto h = Hold(src, 0); ASSERT_EQ(h(), 0); @@ -222,15 +222,15 @@ TYPED_TEST_P(OperationsTest, Snapshot1) } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// FoldByRef1 test +/// IterateByRef1 test /////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, FoldByRef1) +TYPED_TEST_P(OperationsTest, IterateByRef1) { auto src = MyDomain::MakeEventSource(); - auto f = FoldByRef( - std::vector(), + auto f = IterateByRef( src, - [] (std::vector& v, int d) { + std::vector(), + [] (int d, std::vector& v) { v.push_back(d); }); @@ -246,14 +246,14 @@ TYPED_TEST_P(OperationsTest, FoldByRef1) } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// IterateByRef1 test +/// IterateByRef2 test /////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, IterateByRef1) +TYPED_TEST_P(OperationsTest, IterateByRef2) { auto src = MyDomain::MakeEventSource(); auto x = IterateByRef( - std::vector(), src, + std::vector(), [] (std::vector& v) { v.push_back(123); }); @@ -360,15 +360,15 @@ TYPED_TEST_P(OperationsTest, SyncedTransform1) REGISTER_TYPED_TEST_CASE_P ( OperationsTest, - Fold1, - Fold2, Iterate1, + Iterate2, + Iterate3, Monitor1, Hold1, Pulse1, Snapshot1, - FoldByRef1, IterateByRef1, + IterateByRef2, SyncedTransform1 ); From a5f3628b6a90cfb48abddc55e344fd482672cd7c Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 19 May 2014 17:07:06 +0200 Subject: [PATCH 086/266] Removed special handling of token streams that would allow their value being omitted from client function signatures. Instead, an unnamed parameter of type "Token" can be declared. This makes it easier to identify token streams, the extra boilerplate is minimal and the API is simplified. --- include/react/Algorithm.h | 58 ++-------------- include/react/Domain.h | 8 +-- include/react/Event.h | 109 ++++--------------------------- include/react/Observer.h | 87 +----------------------- include/react/ReactiveObject.h | 8 +-- src/benchmark/BenchmarkLifeSim.h | 2 +- src/sandbox/Main.cpp | 6 +- src/test/ObserverTest.h | 6 +- src/test/OperationsTest.h | 8 +-- 9 files changed, 38 insertions(+), 254 deletions(-) diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index 8bf235d3..8b7c43e4 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -31,7 +31,7 @@ class Events; template class EventSource; -enum class EventToken; +enum class Token; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Iterate @@ -42,9 +42,7 @@ template typename E, typename V, typename FIn, - typename S = std::decay::type, - class = std::enable_if< - ! std::is_same::value>::type + typename S = std::decay::type > auto Iterate(const Events& events, V&& init, FIn&& func) -> Signal @@ -58,28 +56,6 @@ auto Iterate(const Events& events, V&& init, FIn&& func) std::forward(init), events.NodePtr(), std::forward(func))); } -// Token stream version -template -< - typename D, - typename V, - typename FIn, - typename S = std::decay::type -> -auto Iterate(const Events& events, V&& init, FIn&& func) - -> Signal -{ - using REACT_IMPL::IterateNode; - using REACT_IMPL::AddDummyArgWrapper; - - using F = std::decay::type; - using WrapperT = AddDummyArgWrapper; - - return Signal( - std::make_shared>( - std::forward(init), events.NodePtr(), WrapperT{ 0, std::forward(func) })); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// IterateByRef - Pass current value as reference /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -103,28 +79,6 @@ auto IterateByRef(const Events& events, V&& init, FIn&& func) std::forward(init), events.NodePtr(), std::forward(func))); } -// Token stream version -template -< - typename D, - typename V, - typename FIn, - typename S = std::decay::type -> -auto IterateByRef(const Events& events, V&& init, FIn&& func) - -> Signal -{ - using REACT_IMPL::IterateByRefNode; - using REACT_IMPL::AddDummyArgWrapper; - - using F = std::decay::type; - using WrapperT = AddDummyArgWrapper; - - return Signal( - std::make_shared>( - std::forward(init), events.NodePtr(), WrapperT{ 0, std::forward(func) })); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Hold /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -209,10 +163,10 @@ template typename S > auto Changed(const Signal& target) - -> Events + -> Events { return Monitor(target) - .Transform([] (const S& v) { return EventToken::token; }); + .Transform([] (const S& v) { return Token::token; }); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -225,12 +179,12 @@ template typename S = std::decay::type > auto ChangedTo(const Signal& target, V&& value) - -> Events + -> Events { return Monitor(target) .Transform([=] (const S& v) { return v == value; }) .Filter([] (bool v) { return v == true; }) - .Transform([=] (const S& v) { return EventToken::token; }) + .Transform([=] (const S& v) { return Token::token; }) } /******************************************/ REACT_END /******************************************/ diff --git a/include/react/Domain.h b/include/react/Domain.h index 836541c9..1531438b 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -52,7 +52,7 @@ class EventSource; template class TempEvents; -enum class EventToken; +enum class Token; using REACT_IMPL::TurnFlagsT; @@ -79,10 +79,10 @@ class DomainBase template using VarSignalT = VarSignal; - template + template using EventsT = Events; - template + template using EventSourceT = EventSource; using ObserverT = Observer; @@ -152,7 +152,7 @@ class DomainBase } static auto MakeEventSource() - -> EventSourceT + -> EventSourceT { return REACT::MakeEventSource(); } diff --git a/include/react/Event.h b/include/react/Event.h index 46fc1ecb..9046260f 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -29,9 +29,9 @@ template class SignalPack; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventToken +/// Token /////////////////////////////////////////////////////////////////////////////////////////////////// -enum class EventToken { token }; +enum class Token { value }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Events @@ -39,7 +39,7 @@ enum class EventToken { token }; template < typename D, - typename E = EventToken + typename E = Token > class Events : public REACT_IMPL::EventStreamBase { @@ -175,7 +175,7 @@ class Events : public REACT_IMPL::EventStreamBase class EventSource : public Events { @@ -205,10 +205,10 @@ class EventSource : public Events BaseT::emit(std::move(e)); } - template ::value>::type> + template ::value>::type> void Emit() const { - BaseT::emit(EventToken::token); + BaseT::emit(Token::value); } const EventSource& operator<<(const E& e) const @@ -343,12 +343,12 @@ auto MakeEventSource() template auto MakeEventSource() - -> EventSource + -> EventSource { using REACT_IMPL::EventSourceNode; - return EventSource( - std::make_shared>()); + return EventSource( + std::make_shared>()); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -537,9 +537,7 @@ template typename D, typename E, typename FIn, - typename ... TDepValues, - class = std::enable_if< - ! std::is_same::value>::type + typename ... TDepValues > auto Filter(const Events& source, SignalPack depPack, FIn&& func) -> Events @@ -572,46 +570,6 @@ auto Filter(const Events& source, SignalPack depPack, FIn& depPack.Data); } -// Token stream version -template -< - typename D, - typename FIn, - typename ... TDepValues -> -auto Filter(const Events& source, SignalPack depPack, FIn&& func) - -> Events -{ - using REACT_IMPL::SyncedEventFilterNode; - using REACT_IMPL::AddDummyArgWrapper; - - using F = std::decay::type; - using WrapperT = AddDummyArgWrapper; - - struct NodeBuilder_ - { - NodeBuilder_(const Events& source, WrapperT&& wrapper) : - MySource{ source }, - MyWrapper{ std::move(wrapper) } - {} - - auto operator()(const Signal& ... deps) - -> Events - { - return Events( - std::make_shared>( - MySource.NodePtr(), std::move(MyWrapper), deps.NodePtr() ...)); - } - - const Events& MySource; - WrapperT&& MyWrapper; - }; - - return REACT_IMPL::apply( - NodeBuilder_{ source, WrapperT{ 0, std::forward(func) } }, - depPack.Data); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Transform /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -664,9 +622,7 @@ template typename TIn, typename FIn, typename ... TDepValues, - typename TOut = std::result_of::type, - class = std::enable_if< - ! std::is_same::value>::type + typename TOut = std::result_of::type > auto Transform(const Events& source, SignalPack depPack, FIn&& func) -> Events @@ -699,49 +655,6 @@ auto Transform(const Events& source, SignalPack depPack, depPack.Data); } -// Token stream version -template -< - typename D, - typename FIn, - typename ... TDepValues, - typename TOut = std::result_of::type -> -auto Transform(const Events& source, SignalPack depPack, FIn&& func) - -> Events -{ - using REACT_IMPL::SyncedEventTransformNode; - using REACT_IMPL::AddDummyArgWrapper; - - using F = std::decay::type; - using WrapperT = AddDummyArgWrapper; - - struct NodeBuilder_ - { - NodeBuilder_(const Events& source, WrapperT&& wrapper) : - MySource{ source }, - MyWrapper{ std::move(wrapper) } - {} - - auto operator()(const Signal& ... deps) - -> Events - { - return Events( - std::make_shared< - SyncedEventTransformNode>( - MySource.NodePtr(), std::move(MyWrapper), deps.NodePtr() ...)); - } - - const Events& MySource; - WrapperT&& MyWrapper; - }; - - return REACT_IMPL::apply( - NodeBuilder_{ - source, WrapperT{ 0, std::forward(func) } }, - depPack.Data); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Flatten /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/Observer.h b/include/react/Observer.h index ad1fcfb4..cfa82ac9 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -30,8 +30,6 @@ class SignalPack; template class Events; -enum class EventToken; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Observer /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -134,9 +132,7 @@ template < typename D, typename FIn, - typename E, - class = std::enable_if< - ! std::is_same::value>::type + typename E > auto Observe(const Events& subject, FIn&& func) -> Observer @@ -157,35 +153,6 @@ auto Observe(const Events& subject, FIn&& func) return Observer(rawObsPtr, subject.NodePtr()); } -// Token stream version -template -< - typename D, - typename FIn -> -auto Observe(const Events& subject, FIn&& func) - -> Observer -{ - using REACT_IMPL::IObserver; - using REACT_IMPL::EventObserverNode; - using REACT_IMPL::DomainSpecificObserverRegistry; - using REACT_IMPL::AddDummyArgWrapper; - - using F = std::decay::type; - using WrapperT = AddDummyArgWrapper; - - WrapperT wrapper{ 0, std::forward(func) }; - - std::unique_ptr obsPtr{ - new EventObserverNode{ - subject.NodePtr(), std::move(wrapper)}}; - - auto* rawObsPtr = DomainSpecificObserverRegistry::Instance() - .Register(std::move(obsPtr), subject.NodePtr().get()); - - return Observer(rawObsPtr, subject.NodePtr()); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Observe - Synced /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -194,9 +161,7 @@ template typename D, typename FIn, typename E, - typename ... TDepValues, - class = std::enable_if< - ! std::is_same::value>::type + typename ... TDepValues > auto Observe(const Events& subject, SignalPack depPack, FIn&& func) @@ -237,54 +202,6 @@ auto Observe(const Events& subject, return Observer(rawObsPtr, subject.NodePtr()); } -// Token stream version -template -< - typename D, - typename FIn, - typename ... TDepValues -> -auto Observe(const Events& subject, - SignalPack depPack, FIn&& func) - -> Observer -{ - using REACT_IMPL::IObserver; - using REACT_IMPL::SyncedObserverNode; - using REACT_IMPL::DomainSpecificObserverRegistry; - using REACT_IMPL::AddDummyArgWrapper; - - using F = std::decay::type; - using WrapperT = AddDummyArgWrapper; - - struct NodeBuilder_ - { - NodeBuilder_(const Events& subject, WrapperT&& wrapper) : - MySubject{ subject }, - MyWrapper{ std::move(wrapper) } - {} - - auto operator()(const Signal& ... deps) - -> std::unique_ptr - { - return std::unique_ptr { - new SyncedObserverNode { - MySubject.NodePtr(), std::move(MyWrapper), deps.NodePtr() ...}}; - } - - const Events& MySubject; - WrapperT&& MyWrapper; - }; - - auto obsPtr = REACT_IMPL::apply( - NodeBuilder_{ subject, WrapperT{ 0, std::forward(func) } }, - depPack.Data); - - auto* rawObsPtr = DomainSpecificObserverRegistry::Instance() - .Register(std::move(obsPtr), subject.NodePtr().get()); - - return Observer(rawObsPtr, subject.NodePtr()); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// DetachAllObservers /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/ReactiveObject.h b/include/react/ReactiveObject.h index 463d18e5..faac0f36 100644 --- a/include/react/ReactiveObject.h +++ b/include/react/ReactiveObject.h @@ -47,7 +47,7 @@ class Observer; template class ScopedObserver; -enum class EventToken; +enum class Token; /////////////////////////////////////////////////////////////////////////////////////////////////// /// ReactiveObject @@ -67,10 +67,10 @@ class ReactiveObject template using VarSignalT = VarSignal; - template + template using EventsT = Events; - template + template using EventSourceT = EventSource; using ObserverT = Observer; @@ -149,7 +149,7 @@ class ReactiveObject } static auto MakeEventSource() - -> EventSource + -> EventSource { return REACT::MakeEventSource(); } diff --git a/src/benchmark/BenchmarkLifeSim.h b/src/benchmark/BenchmarkLifeSim.h index 4dcea7a2..aadf50bc 100644 --- a/src/benchmark/BenchmarkLifeSim.h +++ b/src/benchmark/BenchmarkLifeSim.h @@ -40,7 +40,7 @@ enum class Seasons { summer, winter }; enum class Migration { enter, leave }; template -struct Incrementer { T operator()(T v) const { return v+1; } }; +struct Incrementer { T operator()(Token,T v) const { return v+1; } }; typedef pair PositionT; diff --git a/src/sandbox/Main.cpp b/src/sandbox/Main.cpp index c2fbdaeb..72e62d57 100644 --- a/src/sandbox/Main.cpp +++ b/src/sandbox/Main.cpp @@ -136,10 +136,10 @@ void EventExample2() // stream just indicates that it has fired, i.e. it behaves like a token stream. auto emitter = D::MakeEventSource(); - auto counter = Iterate(emitter, 0, [] (int v) { return v+1; }); + auto counter = Iterate(emitter, 0, [] (Token, int v) { return v+1; }); - // In this case, the observer func must not declare a parameter for token streams. - Observe(emitter, [] { + // The observer func declares a parameter of type Token + Observe(emitter, [] (Token) { cout << "Emitter fired!" << endl; }); diff --git a/src/test/ObserverTest.h b/src/test/ObserverTest.h index 2448db25..fad8f89f 100644 --- a/src/test/ObserverTest.h +++ b/src/test/ObserverTest.h @@ -143,7 +143,7 @@ TYPED_TEST_P(ObserverTest, SyncedObserveTest) auto src1 = MyDomain::MakeEventSource(); auto src2 = MyDomain::MakeEventSource(); - Observe(src1, With(sum,prod,diff), [] (int sum, int prod, int diff) { + Observe(src1, With(sum,prod,diff), [] (Token, int sum, int prod, int diff) { ASSERT_EQ(sum, 33); ASSERT_EQ(prod, 242); ASSERT_EQ(diff, 11); @@ -172,7 +172,7 @@ TYPED_TEST_P(ObserverTest, DetachThisObserver1) int count = 0; - Observe(src, [&] { + Observe(src, [&] (Token) { ++count; DetachThisObserver(); }); @@ -200,7 +200,7 @@ TYPED_TEST_P(ObserverTest, DetachThisObserver2) int count = 0; - Observe(src, With(sum,prod,diff), [&] (int sum, int prod, int diff) { + Observe(src, With(sum,prod,diff), [&] (Token, int sum, int prod, int diff) { ++count; DetachThisObserver(); }); diff --git a/src/test/OperationsTest.h b/src/test/OperationsTest.h index 446274a9..dc447a55 100644 --- a/src/test/OperationsTest.h +++ b/src/test/OperationsTest.h @@ -25,10 +25,10 @@ using namespace react; using namespace std; template -struct Incrementer { T operator()(T v) const { return v+1; } }; +struct Incrementer { T operator()(Token, T v) const { return v+1; } }; template -struct Decrementer { T operator()(T v) const { return v-1; } }; +struct Decrementer { T operator()(Token, T v) const { return v-1; } }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventStreamTest fixture @@ -254,7 +254,7 @@ TYPED_TEST_P(OperationsTest, IterateByRef2) auto x = IterateByRef( src, std::vector(), - [] (std::vector& v) { + [] (Token, std::vector& v) { v.push_back(123); }); @@ -284,7 +284,7 @@ TYPED_TEST_P(OperationsTest, SyncedTransform1) auto src1 = MyDomain::MakeEventSource(); auto src2 = MyDomain::MakeEventSource(); - auto out1 = Transform(src1, With(sum,prod,diff), [] (int sum, int prod, int diff) { + auto out1 = Transform(src1, With(sum,prod,diff), [] (Token, int sum, int prod, int diff) { return make_tuple(sum, prod, diff); }); From ed2a5cda720c9b04f03650978226e7e57b13693f Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 19 May 2014 18:49:17 +0200 Subject: [PATCH 087/266] Cleanup. --- include/react/detail/graph/EventNodes.h | 4 ++-- include/react/detail/graph/ObserverNodes.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 6ed76821..260d70fc 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -602,7 +602,7 @@ class SyncedEventTransformNode : public EventStreamNode { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *source); - REACT_EXPAND_PACK(D::Engine::OnNodeAttach(*this, *deps)); + REACT_EXPAND_PACK(Engine::OnNodeAttach(*this, *deps)); } ~SyncedEventTransformNode() @@ -693,7 +693,7 @@ class SyncedEventFilterNode : public EventStreamNode { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *source); - REACT_EXPAND_PACK(D::Engine::OnNodeAttach(*this, *deps)); + REACT_EXPAND_PACK(Engine::OnNodeAttach(*this, *deps)); } ~SyncedEventFilterNode() diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index 00bee39d..fde6106f 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -209,7 +209,7 @@ class SyncedObserverNode : public ObserverNode subject->IncObsCount(); Engine::OnNodeAttach(*this, *subject); - REACT_EXPAND_PACK(D::Engine::OnNodeAttach(*this, *deps)); + REACT_EXPAND_PACK(Engine::OnNodeAttach(*this, *deps)); } ~SyncedObserverNode() From c02e17949f7038692e812f9df79445556ff6418b Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 19 May 2014 18:50:38 +0200 Subject: [PATCH 088/266] Added synced Iterate. IterateByRef is now handled by Iterate based on the return type of the passed function (void = pass by ref). --- include/react/Algorithm.h | 58 +++++-- include/react/detail/graph/AlgorithmNodes.h | 181 ++++++++++++++++++++ src/test/OperationsTest.h | 4 +- 3 files changed, 231 insertions(+), 12 deletions(-) diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index 8b7c43e4..fa0ebe48 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -33,6 +33,9 @@ class EventSource; enum class Token; +template +class SignalPack; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Iterate /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -48,16 +51,22 @@ auto Iterate(const Events& events, V&& init, FIn&& func) -> Signal { using REACT_IMPL::IterateNode; + using REACT_IMPL::IterateByRefNode; using F = std::decay::type; + using TNode = std::conditional< + std::is_same::type>::value, + IterateByRefNode, + IterateNode + >::type; return Signal( - std::make_shared>( + std::make_shared( std::forward(init), events.NodePtr(), std::forward(func))); } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// IterateByRef - Pass current value as reference +/// Iterate - Synced /////////////////////////////////////////////////////////////////////////////////////////////////// template < @@ -65,18 +74,47 @@ template typename E, typename V, typename FIn, + typename ... TDepValues, typename S = std::decay::type > -auto IterateByRef(const Events& events, V&& init, FIn&& func) +auto Iterate(const Events& events, V&& init, SignalPack depPack, FIn&& func) -> Signal { - using REACT_IMPL::IterateByRefNode; + using REACT_IMPL::SyncedIterateNode; + using REACT_IMPL::SyncedIterateByRefNode; using F = std::decay::type; - - return Signal( - std::make_shared>( - std::forward(init), events.NodePtr(), std::forward(func))); + using TNode = std::conditional< + std::is_same::type>::value, + SyncedIterateByRefNode, + SyncedIterateNode + >::type; + + struct NodeBuilder_ + { + NodeBuilder_(const Events& source, V&& init, FIn&& func) : + MySource{ source }, + MyInit{ std::forward(init) }, + MyFunc{ std::forward(func) } + {} + + auto operator()(const Signal& ... deps) + -> Signal + { + return Signal( + std::make_shared( + std::forward(MyInit), MySource.NodePtr(), + std::forward(MyFunc), deps.NodePtr() ...)); + } + + const Events& MySource; + V MyInit; + FIn MyFunc; + }; + + return REACT_IMPL::apply( + NodeBuilder_{ events, std::forward(init), std::forward(func) }, + depPack.Data); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -166,7 +204,7 @@ auto Changed(const Signal& target) -> Events { return Monitor(target) - .Transform([] (const S& v) { return Token::token; }); + .Transform([] (const S& v) { return Token::value; }); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -184,7 +222,7 @@ auto ChangedTo(const Signal& target, V&& value) return Monitor(target) .Transform([=] (const S& v) { return v == value; }) .Filter([] (bool v) { return v == true; }) - .Transform([=] (const S& v) { return Token::token; }) + .Transform([=] (const S& v) { return Token::value; }) } /******************************************/ REACT_END /******************************************/ diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index e665eeaa..7d331a31 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -135,6 +135,187 @@ class IterateByRefNode : public SignalNode SharedPtrT> events_; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SyncedIterateNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename S, + typename E, + typename TFunc, + typename ... TDepValues +> +class SyncedIterateNode : public SignalNode +{ +public: + template + SyncedIterateNode(T&& init, const std::shared_ptr>& events, F&& func, + const std::shared_ptr>& ... deps) : + SignalNode{ std::forward(init) }, + events_{ events }, + func_{ std::forward(func) } + + { + 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 + { + struct EvalFunctor_ + { + EvalFunctor_(const E& e, const S& v, TFunc& f) : + MyEvent{ e }, + MyValue{ v }, + MyFunc{ f } + {} + + S operator()(const SharedPtrT>& ... args) + { + return MyFunc(MyEvent, MyValue, args->ValueRef() ...); + } + + const E& MyEvent; + const S& MyValue; + TFunc& MyFunc; + }; + + using TurnT = typename D::Engine::TurnT; + TurnT& turn = *reinterpret_cast(turnPtr); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + + S newValue = value_; + for (const auto& e : events_->Events()) + newValue = apply(EvalFunctor_{ e, newValue, func_ }, deps_); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + + if (! impl::Equals(newValue, value_)) + { + value_ = std::move(newValue); + 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>...>; + + SharedPtrT> events_; + + DepHolderT deps_; + TFunc func_; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SyncedIterateByRefNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename S, + typename E, + typename TFunc, + typename ... TDepValues +> +class SyncedIterateByRefNode : public SignalNode +{ +public: + template + SyncedIterateByRefNode(T&& init, const std::shared_ptr>& events, F&& func, + const std::shared_ptr>& ... deps) : + SignalNode{ std::forward(init) }, + events_{ events }, + func_{ std::forward(func) } + + { + 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 + { + struct EvalFunctor_ + { + EvalFunctor_(const E& e, const S& v, TFunc& f) : + MyEvent{ e }, + MyValue{ v }, + MyFunc{ f } + {} + + void operator()(const SharedPtrT>& ... args) + { + MyFunc(MyEvent, MyValue, args->ValueRef() ...); + } + + const E& MyEvent; + S& MyValue; + TFunc& MyFunc; + }; + + using TurnT = typename D::Engine::TurnT; + TurnT& turn = *reinterpret_cast(turnPtr); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + + for (const auto& e : events_->Events()) + apply(EvalFunctor_{ e, newValue, func_ }, deps_); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + + Engine::OnNodePulse(*this, turn); + } + + virtual const char* GetNodeType() const override { return "SyncedIterateByRefNode"; } + virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } + +private: + using DepHolderT = std::tuple>...>; + + SharedPtrT> events_; + + DepHolderT deps_; + TFunc func_; +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// HoldNode /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/OperationsTest.h b/src/test/OperationsTest.h index dc447a55..f25503aa 100644 --- a/src/test/OperationsTest.h +++ b/src/test/OperationsTest.h @@ -227,7 +227,7 @@ TYPED_TEST_P(OperationsTest, Snapshot1) TYPED_TEST_P(OperationsTest, IterateByRef1) { auto src = MyDomain::MakeEventSource(); - auto f = IterateByRef( + auto f = Iterate( src, std::vector(), [] (int d, std::vector& v) { @@ -251,7 +251,7 @@ TYPED_TEST_P(OperationsTest, IterateByRef1) TYPED_TEST_P(OperationsTest, IterateByRef2) { auto src = MyDomain::MakeEventSource(); - auto x = IterateByRef( + auto x = Iterate( src, std::vector(), [] (Token, std::vector& v) { From 2220475237d3eb01687600f1332614104abd1b61 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 19 May 2014 20:09:12 +0200 Subject: [PATCH 089/266] Added Tokenize(). Turns any event stream into a token stream. --- include/react/Algorithm.h | 16 +++++++--------- include/react/Event.h | 34 +++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index fa0ebe48..2f746056 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -193,22 +193,21 @@ auto Pulse(const Events& trigger, const Signal& target) } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Changed +/// OnChanged /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename S > -auto Changed(const Signal& target) +auto OnChanged(const Signal& target) -> Events { - return Monitor(target) - .Transform([] (const S& v) { return Token::value; }); + return Monitor(target).Tokenize(); } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// ChangedTo +/// OnChangedTo /////////////////////////////////////////////////////////////////////////////////////////////////// template < @@ -216,13 +215,12 @@ template typename V, typename S = std::decay::type > -auto ChangedTo(const Signal& target, V&& value) +auto OnChangedTo(const Signal& target, V&& value) -> Events { return Monitor(target) - .Transform([=] (const S& v) { return v == value; }) - .Filter([] (bool v) { return v == true; }) - .Transform([=] (const S& v) { return Token::value; }) + .Filter([=] (const S& v) { return v == value; }) + .Tokenize(); } /******************************************/ REACT_END /******************************************/ diff --git a/include/react/Event.h b/include/react/Event.h index 9046260f..ed239fcf 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -80,8 +80,14 @@ class Events : public REACT_IMPL::EventStreamBase return REACT::Forward(*this); } + auto Tokenize() const + -> decltype(REACT::Tokenize(std::declval())) + { + return REACT::Tokenize(*this); + } + template - auto Merge(const Events& ... args) const + auto Merge(TArgs&& ... args) const -> decltype(REACT::Merge(std::declval(), std::forward(args) ...)) { return REACT::Merge(*this, std::forward(args) ...); @@ -147,8 +153,14 @@ class Events : public REACT_IMPL::EventStreamBase decltype(REACT::Tokenize(std::declval())) + { + return REACT::Tokenize(*this); + } + template - auto Merge(const Events& ... args) + auto Merge(TArgs&& ... args) -> decltype(REACT::Merge(std::declval(), std::forward(args) ...)) { return REACT::Merge(*this, std::forward(args) ...); @@ -293,7 +305,7 @@ class TempEvents : public Events } template - auto Merge(const Events& ... args) + auto Merge(TArgs&& ... args) -> decltype(REACT::Merge(std::declval(), std::forward(args) ...)) { return REACT::Merge(*this, std::forward(args) ...); @@ -671,4 +683,20 @@ auto Flatten(const Signal>& node) node.NodePtr(), node().NodePtr())); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Tokenize +/////////////////////////////////////////////////////////////////////////////////////////////////// +struct Tokenizer +{ + template + Token operator()(const T&) const { return Token::value; } +}; + +template +auto Tokenize(TEvents&& source) + -> decltype(Transform(source, Tokenizer{})) +{ + return Transform(source, Tokenizer{}); +} + /******************************************/ REACT_END /******************************************/ From 94df1acbfdfefecf4585fcc2b66452bf173c9626 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 19 May 2014 21:14:19 +0200 Subject: [PATCH 090/266] Refactored MakeSignal to be consistent with rest of the new API, i.e. MakeSignal(x, func) or MakeSignal(With(x,y,z), func). --- include/react/Domain.h | 17 ---- include/react/ReactiveObject.h | 24 +---- include/react/Signal.h | 166 ++++++++++++++++++--------------- src/test/SignalTest.h | 24 ++--- 4 files changed, 100 insertions(+), 131 deletions(-) diff --git a/include/react/Domain.h b/include/react/Domain.h index 1531438b..8aacea8f 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -124,23 +124,6 @@ class DomainBase return REACT::MakeVar(std::forward(value)); } - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// MakeSignal - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - < - typename FIn, - typename ... TArgs, - typename F = std::decay::type, - typename S = std::result_of::type, - typename TOp = REACT_IMPL::FunctionOp ...> - > - static auto MakeSignal(FIn&& func, const SignalT& ... args) - -> TempSignal - { - return REACT::MakeSignal(std::forward(func), args ...); - } - /////////////////////////////////////////////////////////////////////////////////////////////////// /// MakeEventSource /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/ReactiveObject.h b/include/react/ReactiveObject.h index faac0f36..383a5d02 100644 --- a/include/react/ReactiveObject.h +++ b/include/react/ReactiveObject.h @@ -122,22 +122,6 @@ class ReactiveObject return REACT::MakeVar(std::forward(value)); } - /////////////////////////////////////////////////////////////////////////////////////////////// - /// MakeSignal - /////////////////////////////////////////////////////////////////////////////////////////////// - template - < - typename FIn, - typename ... TArgs, - typename F = std::decay::type, - typename S = std::result_of::type - > - static auto MakeSignal(FIn&& func, const SignalT& ... args) - -> SignalT - { - return REACT::MakeSignal(std::forward(func), args ...); - } - /////////////////////////////////////////////////////////////////////////////////////////////// /// MakeEventSource /////////////////////////////////////////////////////////////////////////////////////////////// @@ -165,20 +149,20 @@ class ReactiveObject #define REACTIVE_REF(obj, name) \ Flatten( \ MakeSignal( \ + obj, \ [] (const REACT_IMPL::Identity::Type::ValueT& r) \ { \ using T = decltype(r.name); \ return static_cast::Type>(r.name); \ - }, \ - obj)) + })) #define REACTIVE_PTR(obj, name) \ Flatten( \ MakeSignal( \ + obj, \ [] (REACT_IMPL::Identity::Type::ValueT r) \ { \ REACT_ASSERT(r != nullptr); \ using T = decltype(r->name); \ return static_cast::Type>(r->name); \ - }, \ - obj)) + })) diff --git a/include/react/Signal.h b/include/react/Signal.h index 7c14772c..bf280ccf 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -247,6 +247,43 @@ bool Equals(const Signal& lhs, const Signal& rhs) /*****************************************/ REACT_BEGIN /*****************************************/ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SignalPack - Wraps several nodes in a tuple. Create with comma operator. +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename ... TValues +> +class SignalPack +{ +public: + SignalPack(const Signal& ... deps) : + Data{ std::tie(deps ...) } + {} + + template + SignalPack(const SignalPack& curArgs, const Signal& newArg) : + Data{ std::tuple_cat(curArgs.Data, std::tie(newArg)) } + {} + + std::tuple& ...> Data; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// With - Utility function to create a SignalPack +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename ... TValues +> +auto With(const Signal& ... deps) + -> SignalPack +{ + return SignalPack(deps ...); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// MakeVar /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -318,24 +355,61 @@ auto MakeVar(V&& value) /////////////////////////////////////////////////////////////////////////////////////////////////// /// MakeSignal /////////////////////////////////////////////////////////////////////////////////////////////////// +// Single arg template < typename D, + typename TValue, typename FIn, + typename F = std::decay::type, + typename S = std::result_of::type, + typename TOp = REACT_IMPL::FunctionOp> +> +auto MakeSignal(const Signal& arg, FIn&& func) + -> TempSignal +{ + return TempSignal( + std::make_shared>( + std::forward(func), arg.NodePtr())); +} + +// Multiple args +template +< + typename D, typename ... TValues, + typename FIn, typename F = std::decay::type, typename S = std::result_of::type, typename TOp = REACT_IMPL::FunctionOp ...> > -auto MakeSignal(FIn&& func, const Signal& ... args) +auto MakeSignal(const SignalPack& argPack, FIn&& func) -> TempSignal { - static_assert(sizeof...(TValues) > 0, - "react::MakeSignal requires at least 1 signal dependency."); + using REACT_IMPL::SignalOpNode; - return TempSignal( - std::make_shared>( - std::forward(func), args.NodePtr() ... )); + using F = std::decay::type; + + struct NodeBuilder_ + { + NodeBuilder_(FIn&& func) : + MyFunc{ std::forward(func) } + {} + + auto operator()(const Signal& ... args) + -> TempSignal + { + return TempSignal( + std::make_shared>( + std::forward(MyFunc), args.NodePtr() ...)); + } + + FIn MyFunc; + }; + + return REACT_IMPL::apply( + NodeBuilder_{ std::forward(func) }, + argPack.Data); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -659,44 +733,7 @@ DECLARE_OP(^, BitwiseXor); #undef DECLARE_OP /////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalPack - Wraps several nodes in a tuple. Create with comma operator. -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename ... TValues -> -class SignalPack -{ -public: - SignalPack(const Signal& ... deps) : - Data{ std::tie(deps ...) } - {} - - template - SignalPack(const SignalPack& curArgs, const Signal& newArg) : - Data{ std::tuple_cat(curArgs.Data, std::tie(newArg)) } - {} - - std::tuple& ...> Data; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// With - Utility function to create a SignalPack -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename ... TValues -> -auto With(const Signal& ... deps) - -> SignalPack -{ - return SignalPack(deps ...); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Comma operator overload to create input pack from 2 signals. +/// Comma operator overload to create signal pack from 2 signals. /////////////////////////////////////////////////////////////////////////////////////////////////// template < @@ -711,7 +748,7 @@ auto operator,(const Signal& a, const Signal& b) } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Comma operator overload to append node to existing input pack. +/// Comma operator overload to append node to existing signal pack. /////////////////////////////////////////////////////////////////////////////////////////////////// template < @@ -725,33 +762,10 @@ auto operator,(const SignalPack& cur, const Signal(cur, append); } -/******************************************/ REACT_END /******************************************/ - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -template -< - typename D, - typename F, - typename ... TValues -> -struct ApplyHelper -{ - static inline auto MakeSignal(F&& func, const Signal& ... args) - -> Signal::type> - { - return D::MakeSignal(std::forward(func), args ...); - } -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -/*****************************************/ REACT_BEGIN /*****************************************/ - /////////////////////////////////////////////////////////////////////////////////////////////////// -/// operator->* overload to connect inputs to a function and return the resulting node. +/// operator->* overload to connect signals to a function and return the resulting signal. /////////////////////////////////////////////////////////////////////////////////////////////////// -// Single input +// Single arg template < typename D, @@ -761,25 +775,23 @@ template class = std::enable_if< IsSignal>::value>::type > -auto operator->*(const TSignal& inputNode, F&& func) +auto operator->*(const TSignal& arg, F&& func) -> Signal::type> { - return D::MakeSignal(std::forward(func), inputNode); + return REACT::MakeSignal(arg, std::forward(func)); } -// Multiple inputs +// Multiple args template < typename D, typename F, typename ... TValues > -auto operator->*(const SignalPack& inputPack, F&& func) +auto operator->*(const SignalPack& argPack, F&& func) -> Signal::type> { - return apply( - REACT_IMPL::ApplyHelper::MakeSignal, - std::tuple_cat(std::forward_as_tuple(std::forward(func)), inputPack.Data)); + return REACT::MakeSignal(argPack, std::forward(func)); } /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/SignalTest.h b/src/test/SignalTest.h index 6668a5b4..1015a8bb 100644 --- a/src/test/SignalTest.h +++ b/src/test/SignalTest.h @@ -66,23 +66,13 @@ TYPED_TEST_P(SignalTest, Signals1) auto v3 = MyDomain::MakeVar(3); auto v4 = MyDomain::MakeVar(4); - auto s1 = MyDomain::MakeSignal - ( - [] (int a, int b) - { - return a + b; - }, - v1, v2 - ); - - auto s2 = MyDomain::MakeSignal - ( - [] (int a, int b) - { - return a + b; - }, - v3, v4 - ); + auto s1 = MakeSignal(With(v1,v2), [] (int a, int b) { + return a + b; + }); + + auto s2 = MakeSignal(With(v3,v4), [] (int a, int b) { + return a + b; + }); auto s3 = s1 + s2; From 188a2a96eefc1251da824b403b3cbb68baca7499 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 19 May 2014 21:23:19 +0200 Subject: [PATCH 091/266] Removed operator magic from readme example. --- README.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 9471f7d5..3175255c 100644 --- a/README.md +++ b/README.md @@ -77,12 +77,16 @@ using namespace react; REACTIVE_DOMAIN(D); // Two event sources -D::EventSource<> leftClicked = D::MakeEventSource(); -D::EventSource<> rightClicked = D::MakeEventSource(); +D::EventSourceT leftClicked = D::MakeEventSource(); +D::EventSourceT rightClicked = D::MakeEventSource(); -// Merge both event streams and register an observer -auto clickObserver = (leftClicked | rightClicked) - .Observe([] { cout << "button clicked!" << endl; }); +// Merge both event streams +auto merged = leftClicked | rightClicked; + +// React to events +auto obs = Observe(merged, [] (Token) { + cout << "clicked!" << endl; +}); ``` #### Implicit parallelism @@ -117,15 +121,13 @@ using namespace react; auto in = D::MakeVar(0); - // The ->* operator is overloaded for a DSL - - auto op1 = in ->* [] (int in) + auto op1 = MakeSignal(in, [] (int in) { int result = in /* Costly operation #1 */; return result; }; - auto op2 = in ->* [] (int in) + auto op2 = MakeSignal(in, [] (int in) { int result = in /* Costly operation #2 */; return result; @@ -246,8 +248,8 @@ public: NameObserver { // Reactive reference to inner event stream of signal - REACTIVE_REF(CurrentCompany, Name) - .Observe([] (const string& name) { + Observe(REACTIVE_REF(CurrentCompany, Name), + [] (const string& name) { cout << "Manager: Now managing " << name << endl; }); } From bfc7d4a45efdaea910b389637083adc96e91fbcd Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 20 May 2014 23:41:49 +0200 Subject: [PATCH 092/266] Fixes + comments. --- include/react/Algorithm.h | 17 +++++++++-------- include/react/Observer.h | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index 2f746056..4620552b 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -37,7 +37,7 @@ template class SignalPack; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Iterate +/// Iterate - Iteratively combines signal value with values from event stream /////////////////////////////////////////////////////////////////////////////////////////////////// template < @@ -77,7 +77,8 @@ template typename ... TDepValues, typename S = std::decay::type > -auto Iterate(const Events& events, V&& init, SignalPack depPack, FIn&& func) +auto Iterate(const Events& events, V&& init, + const SignalPack& depPack, FIn&& func) -> Signal { using REACT_IMPL::SyncedIterateNode; @@ -118,7 +119,7 @@ auto Iterate(const Events& events, V&& init, SignalPack de } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Hold +/// Hold - Hold the most recent event in a signal /////////////////////////////////////////////////////////////////////////////////////////////////// template < @@ -137,7 +138,7 @@ auto Hold(const Events& events, V&& init) } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Snapshot +/// Snapshot - Sets signal value to value of other signal when event is received /////////////////////////////////////////////////////////////////////////////////////////////////// template < @@ -156,7 +157,7 @@ auto Snapshot(const Events& trigger, const Signal& target) } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Monitor +/// Monitor - Emits value changes of target signal /////////////////////////////////////////////////////////////////////////////////////////////////// template < @@ -174,7 +175,7 @@ auto Monitor(const Signal& target) } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Pulse +/// Pulse - Emits value of target signal when event is received /////////////////////////////////////////////////////////////////////////////////////////////////// template < @@ -193,7 +194,7 @@ auto Pulse(const Events& trigger, const Signal& target) } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// OnChanged +/// OnChanged - Emits token when target signal was changed /////////////////////////////////////////////////////////////////////////////////////////////////// template < @@ -207,7 +208,7 @@ auto OnChanged(const Signal& target) } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// OnChangedTo +/// OnChangedTo - Emits token when target signal was changed to value /////////////////////////////////////////////////////////////////////////////////////////////////// template < diff --git a/include/react/Observer.h b/include/react/Observer.h index cfa82ac9..42491e46 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -164,7 +164,7 @@ template typename ... TDepValues > auto Observe(const Events& subject, - SignalPack depPack, FIn&& func) + const SignalPack& depPack, FIn&& func) -> Observer { using REACT_IMPL::IObserver; From e724f60e4d738e6f946a3347099ffcf7b391a0cd Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 20 May 2014 23:42:42 +0200 Subject: [PATCH 093/266] Added operator() as an equivalent to Emit(). --- include/react/Event.h | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index ed239fcf..4a7226bc 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -207,22 +207,21 @@ class EventSource : public Events Events{ std::move(nodePtr) } {} - void Emit(const E& e) const - { - BaseT::emit(e); - } + // Explicit emit + void Emit(const E& e) const { BaseT::emit(e); } + void Emit(E&& e) const { BaseT::emit(std::move(e)); } - void Emit(E&& e) const - { - BaseT::emit(std::move(e)); - } + template ::value>::type> + void Emit() const { BaseT::emit(Token::value); } + + // Function object style + void operator()(const E& e) const { BaseT::emit(e); } + void operator()(E&& e) const { BaseT::emit(std::move(e)); } template ::value>::type> - void Emit() const - { - BaseT::emit(Token::value); - } + void operator()() const { BaseT::emit(Token::value); } + // Stream style const EventSource& operator<<(const E& e) const { BaseT::emit(e); @@ -260,11 +259,13 @@ class EventSource : public Events> Events{ std::move(nodePtr) } {} - void Emit(std::reference_wrapper e) const - { - BaseT::emit(e); - } + // Explicit emit + void Emit(std::reference_wrapper e) const { BaseT::emit(e); } + + // Function object style + void operator()(std::reference_wrapper e) const { BaseT::emit(e); } + // Stream style const EventSource& operator<<(std::reference_wrapper e) const { BaseT::emit(e); @@ -551,7 +552,7 @@ template typename FIn, typename ... TDepValues > -auto Filter(const Events& source, SignalPack depPack, FIn&& func) +auto Filter(const Events& source, const SignalPack& depPack, FIn&& func) -> Events { using REACT_IMPL::SyncedEventFilterNode; @@ -636,7 +637,7 @@ template typename ... TDepValues, typename TOut = std::result_of::type > -auto Transform(const Events& source, SignalPack depPack, FIn&& func) +auto Transform(const Events& source, const SignalPack& depPack, FIn&& func) -> Events { using REACT_IMPL::SyncedEventTransformNode; From 08c60249b98bbff5bba3cff334908c2d41a9cd0e Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 20 May 2014 23:44:37 +0200 Subject: [PATCH 094/266] Removed Forward(). Couldn't think of a use case. --- include/react/Event.h | 17 -------- include/react/detail/graph/EventNodes.h | 53 ------------------------- src/test/EventStreamTest.h | 39 +----------------- 3 files changed, 1 insertion(+), 108 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index 4a7226bc..75f3b386 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -364,23 +364,6 @@ auto MakeEventSource() std::make_shared>()); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward - Why would anyone ever need this -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E -> -auto Forward(const Events& other) - -> Events -{ - using REACT_IMPL::EventForwardNode; - - return Events( - std::make_shared>(other.NodePtr())); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Merge /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 260d70fc..dd0e18e9 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -149,59 +149,6 @@ class EventSourceNode : bool changedFlag_ = false; }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventForwardNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E -> -class EventForwardNode : public EventStreamNode -{ -public: - EventForwardNode(const SharedPtrT>& other) : - EventStreamNode{ }, - other_{ other } - { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *other_); - } - - ~EventForwardNode() - { - Engine::OnNodeDetach(*this, *other_); - Engine::OnNodeDestroy(*this); - } - - virtual const char* GetNodeType() const override { return "EventForwardNode"; } - virtual int DependencyCount() const override { return 1; } - - virtual void Tick(void* turnPtr) override - { - typedef typename D::Engine::TurnT TurnT; - TurnT& turn = *reinterpret_cast(turnPtr); - - SetCurrentTurn(turn, true); - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - events_.insert(events_.end(), other_->Events().begin(), other_->Events().end()); - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - if (events_.size() > 0) - Engine::OnNodePulse(*this, turn); - else - Engine::OnNodeIdlePulse(*this, turn); - } - -private: - SharedPtrT> other_; -}; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventMergeOp /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/EventStreamTest.h b/src/test/EventStreamTest.h index cb6a0a94..5e7ca7bd 100644 --- a/src/test/EventStreamTest.h +++ b/src/test/EventStreamTest.h @@ -255,42 +255,6 @@ TYPED_TEST_P(EventStreamTest, EventTransform) ASSERT_TRUE(std::find(results.begin(), results.end(), "HELLO VORLD") != results.end()); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventForward test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(EventStreamTest, EventForward) -{ - using std::string; - - std::queue results; - - auto in = MyDomain::MakeEventSource(); - auto phase1 = Forward(in); - auto phase2 = Forward(phase1); - auto phase3 = Forward(phase2); - - Observe(phase3, [&] (int v) - { - results.push(v); - }); - - in << 1 << 2 << 3; - - ASSERT_FALSE(results.empty()); - ASSERT_EQ(results.front(),1); - results.pop(); - - ASSERT_FALSE(results.empty()); - ASSERT_EQ(results.front(),2); - results.pop(); - - ASSERT_FALSE(results.empty()); - ASSERT_EQ(results.front(),3); - results.pop(); - - ASSERT_TRUE(results.empty()); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// REGISTER_TYPED_TEST_CASE_P ( @@ -300,8 +264,7 @@ REGISTER_TYPED_TEST_CASE_P EventMerge2, EventMerge3, EventFilter, - EventTransform, - EventForward + EventTransform ); } // ~namespace From 8f897af6a75a876a8cad69392cfcda2331ea2f83 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 21 May 2014 03:02:45 +0200 Subject: [PATCH 095/266] Removed remaining Forward. --- include/react/Event.h | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index 75f3b386..c236a5d1 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -74,12 +74,6 @@ class Events : public REACT_IMPL::EventStreamBase return BaseT::IsValid(); } - auto Forward() const - -> decltype(REACT::Forward(std::declval())) - { - return REACT::Forward(*this); - } - auto Tokenize() const -> decltype(REACT::Tokenize(std::declval())) { @@ -147,12 +141,6 @@ class Events : public REACT_IMPL::EventStreamBase decltype(REACT::Forward(std::declval())) - { - return REACT::Forward(*this); - } - auto Tokenize() const -> decltype(REACT::Tokenize(std::declval())) { From f3d5bd0fd0945be2cd8bc84645b5872e21c6df80 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 21 May 2014 03:28:41 +0200 Subject: [PATCH 096/266] Using explicit DontMove tag instead of dummy int. --- include/react/common/Util.h | 5 +++++ include/react/detail/graph/EventNodes.h | 6 +++--- include/react/detail/graph/GraphBase.h | 2 +- include/react/detail/graph/SignalNodes.h | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/include/react/common/Util.h b/include/react/common/Util.h index 8b5d739a..758b5b40 100644 --- a/include/react/common/Util.h +++ b/include/react/common/Util.h @@ -152,6 +152,11 @@ MoveBindWrapper MoveIntoBind(T&& t) return MoveBindWrapper{std::forward(t)}; } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// DontMove! +/////////////////////////////////////////////////////////////////////////////////////////////////// +struct DontMove {}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// AddDummyArgWrapper /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index dd0e18e9..416193ba 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -162,7 +162,7 @@ class EventMergeOp : public ReactiveOpBase public: template EventMergeOp(TDepsIn&& ... deps) : - ReactiveOpBase(0u, std::forward(deps) ...) + ReactiveOpBase(DontMove{}, std::forward(deps) ...) {} EventMergeOp(EventMergeOp&& other) : @@ -229,7 +229,7 @@ class EventFilterOp : public ReactiveOpBase public: template EventFilterOp(TFilterIn&& filter, TDepIn&& dep) : - ReactiveOpBase{ 0u, std::forward(dep) }, + ReactiveOpBase{ DontMove{}, std::forward(dep) }, filter_{ std::forward(filter) } {} @@ -306,7 +306,7 @@ class EventTransformOp : public ReactiveOpBase public: template EventTransformOp(TFuncIn&& func, TDepIn&& dep) : - ReactiveOpBase{ 0u, std::forward(dep) }, + ReactiveOpBase{ DontMove{}, std::forward(dep) }, func_{ std::forward(func) } {} diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index c3f69d32..7bf5da51 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -194,7 +194,7 @@ class ReactiveOpBase using DepHolderT = std::tuple; template - ReactiveOpBase(uint /*not move ctor*/, TDepsIn&& ... deps) : + ReactiveOpBase(DontMove, TDepsIn&& ... deps) : deps_{ std::forward(deps) ... } {} diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index 6d9aac4e..ce196847 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -127,7 +127,7 @@ class FunctionOp : public ReactiveOpBase public: template FunctionOp(FIn&& func, TDepsIn&& ... deps) : - ReactiveOpBase{ 0u, std::forward(deps) ... }, + ReactiveOpBase{ DontMove{}, std::forward(deps) ... }, func_{ std::forward(func) } {} From 53acab3e62978c39fc30ed72e48b4ad3c309d256 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 21 May 2014 03:32:16 +0200 Subject: [PATCH 097/266] Fixed operand mixup for (const Signal& op TempSignal&&) case. Plus some cleanup. --- include/react/Signal.h | 84 +++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/include/react/Signal.h b/include/react/Signal.h index bf280ccf..3d669434 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -415,7 +415,7 @@ auto MakeSignal(const SignalPack& argPack, FIn&& func) /////////////////////////////////////////////////////////////////////////////////////////////////// /// Unary operators /////////////////////////////////////////////////////////////////////////////////////////////////// -#define DECLARE_OP(op,name) \ +#define REACT_DECLARE_OP(op,name) \ template \ struct name ## OpFunctor \ { \ @@ -458,19 +458,19 @@ auto operator ## op(TempSignal&& arg) F(), arg.StealOp())); \ } -DECLARE_OP(+, UnaryPlus); -DECLARE_OP(-, UnaryMinus); -DECLARE_OP(!, LogicalNegation); -DECLARE_OP(~, BitwiseComplement); -DECLARE_OP(++, Increment); -DECLARE_OP(--, Decrement); +REACT_DECLARE_OP(+, UnaryPlus); +REACT_DECLARE_OP(-, UnaryMinus); +REACT_DECLARE_OP(!, LogicalNegation); +REACT_DECLARE_OP(~, BitwiseComplement); +REACT_DECLARE_OP(++, Increment); +REACT_DECLARE_OP(--, Decrement); -#undef DECLARE_OP +#undef REACT_DECLARE_OP /////////////////////////////////////////////////////////////////////////////////////////////////// /// Binary operators /////////////////////////////////////////////////////////////////////////////////////////////////// -#define DECLARE_OP(op,name) \ +#define REACT_DECLARE_OP(op,name) \ template \ struct name ## OpFunctor \ { \ @@ -549,7 +549,7 @@ auto operator ## op(const TLeftSignal& lhs, const TRightSignal& rhs) { \ return TempSignal( \ std::make_shared>( \ - F(), lhs.NodePtr(), rhs.NodePtr())); \ + F{ }, lhs.NodePtr(), rhs.NodePtr())); \ } \ \ template \ @@ -573,7 +573,7 @@ auto operator ## op(const TLeftSignal& lhs, TRightValIn&& rhs) { \ return TempSignal( \ std::make_shared>( \ - F(std::forward(rhs)), lhs.NodePtr())); \ + F{ std::forward(rhs) }, lhs.NodePtr())); \ } \ \ template \ @@ -597,7 +597,7 @@ auto operator ## op(TLeftValIn&& lhs, const TRightSignal& rhs) { \ return TempSignal( \ std::make_shared>( \ - F(std::forward(lhs)), rhs.NodePtr())); \ + F{ std::forward(lhs) }, rhs.NodePtr())); \ } \ template \ < \ @@ -616,7 +616,7 @@ auto operator ## op(TempSignal&& lhs, { \ return TempSignal( \ std::make_shared>( \ - F(), lhs.StealOp(), rhs.StealOp())); \ + F{ }, lhs.StealOp(), rhs.StealOp())); \ } \ \ template \ @@ -640,7 +640,7 @@ template { \ return TempSignal( \ std::make_shared>( \ - F(), lhs.StealOp(), rhs.NodePtr())); \ + F{ }, lhs.StealOp(), rhs.NodePtr())); \ } \ \ template \ @@ -653,8 +653,8 @@ template typename F = name ## OpFunctor, \ typename S = std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp>, \ + REACT_IMPL::SignalNodePtrT, \ + TRightOp>, \ class = std::enable_if< \ IsSignal::value>::type \ > \ @@ -663,7 +663,7 @@ auto operator ## op(const TLeftSignal& lhs, TempSignal&& r { \ return TempSignal( \ std::make_shared>( \ - F(), lhs.NodePtr(), rhs.StealOp())); \ + F{ }, lhs.NodePtr(), rhs.StealOp())); \ } \ \ template \ @@ -684,7 +684,7 @@ auto operator ## op(TempSignal&& lhs, TRightValIn&& rhs) { \ return TempSignal( \ std::make_shared>( \ - F(std::forward(rhs)), lhs.StealOp())); \ + F{ std::forward(rhs) }, lhs.StealOp())); \ } \ \ template \ @@ -705,32 +705,32 @@ auto operator ## op(TLeftValIn&& lhs, TempSignal&& rhs) { \ return TempSignal( \ std::make_shared>( \ - F(std::forward(lhs)), rhs.StealOp())); \ + F{ std::forward(lhs) }, rhs.StealOp())); \ } -DECLARE_OP(+, Addition); -DECLARE_OP(-, Subtraction); -DECLARE_OP(*, Multiplication); -DECLARE_OP(/, Division); -DECLARE_OP(%, Modulo); - -DECLARE_OP(==, Equal); -DECLARE_OP(!=, NotEqual); -DECLARE_OP(<, Less); -DECLARE_OP(<=, LessEqual); -DECLARE_OP(>, Greater); -DECLARE_OP(>=, GreaterEqual); - -DECLARE_OP(&&, LogicalAnd); -DECLARE_OP(||, LogicalOr); - -DECLARE_OP(&, BitwiseAnd); -DECLARE_OP(|, BitwiseOr); -DECLARE_OP(^, BitwiseXor); -//DECLARE_OP(<<, BitwiseLeftShift); // MSVC: Internal compiler error -//DECLARE_OP(>>, BitwiseRightShift); - -#undef DECLARE_OP +REACT_DECLARE_OP(+, Addition); +REACT_DECLARE_OP(-, Subtraction); +REACT_DECLARE_OP(*, Multiplication); +REACT_DECLARE_OP(/, Division); +REACT_DECLARE_OP(%, Modulo); + +REACT_DECLARE_OP(==, Equal); +REACT_DECLARE_OP(!=, NotEqual); +REACT_DECLARE_OP(<, Less); +REACT_DECLARE_OP(<=, LessEqual); +REACT_DECLARE_OP(>, Greater); +REACT_DECLARE_OP(>=, GreaterEqual); + +REACT_DECLARE_OP(&&, LogicalAnd); +REACT_DECLARE_OP(||, LogicalOr); + +REACT_DECLARE_OP(&, BitwiseAnd); +REACT_DECLARE_OP(|, BitwiseOr); +REACT_DECLARE_OP(^, BitwiseXor); +//REACT_DECLARE_OP(<<, BitwiseLeftShift); // MSVC: Internal compiler error +//REACT_DECLARE_OP(>>, BitwiseRightShift); + +#undef REACT_DECLARE_OP /////////////////////////////////////////////////////////////////////////////////////////////////// /// Comma operator overload to create signal pack from 2 signals. From db32667221a20e9c2a0457f9a4f6511daa23d386 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 21 May 2014 03:36:11 +0200 Subject: [PATCH 098/266] Oops, fixed broken SyncedIterateNode. --- include/react/detail/graph/AlgorithmNodes.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index 7d331a31..137ff838 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -154,8 +154,8 @@ class SyncedIterateNode : public SignalNode const std::shared_ptr>& ... deps) : SignalNode{ std::forward(init) }, events_{ events }, - func_{ std::forward(func) } - + func_{ std::forward(func) }, + deps_{ deps ... } { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *events); @@ -201,8 +201,9 @@ class SyncedIterateNode : public SignalNode GetObjectId(*this), turn.Id())); S newValue = value_; + for (const auto& e : events_->Events()) - newValue = apply(EvalFunctor_{ e, newValue, func_ }, deps_); + newValue = apply(EvalFunctor_{ e, std::move(newValue), func_ }, deps_); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); From 4096eb01d201c20f044f21dfd12712c4a409da21 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 21 May 2014 03:38:41 +0200 Subject: [PATCH 099/266] Cleaned up LifeSim benchmark. --- src/benchmark/BenchmarkLifeSim.h | 260 +++++++++++++++---------------- 1 file changed, 127 insertions(+), 133 deletions(-) diff --git a/src/benchmark/BenchmarkLifeSim.h b/src/benchmark/BenchmarkLifeSim.h index aadf50bc..15b0722f 100644 --- a/src/benchmark/BenchmarkLifeSim.h +++ b/src/benchmark/BenchmarkLifeSim.h @@ -6,13 +6,15 @@ #pragma once -#include #include #include #include #include #include +#include #include +#include +#include #include "BenchmarkBase.h" @@ -23,18 +25,10 @@ using namespace react; -using std::atomic; -using std::vector; - -using std::tuple; -using std::unique_ptr; -using std::move; -using std::make_tuple; using std::cout; using std::string; -using std::pair; -using std::make_pair; -using std::get; +using std::unique_ptr; +using std::vector; enum class Seasons { summer, winter }; enum class Migration { enter, leave }; @@ -42,75 +36,96 @@ enum class Migration { enter, leave }; template struct Incrementer { T operator()(Token,T v) const { return v+1; } }; -typedef pair PositionT; +using PositionT = std::pair; +using BoundsT = std::tuple; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Time +/////////////////////////////////////////////////////////////////////////////////////////////////// template class Time : public ReactiveObject { public: - EventSourceT<> NewDay = MakeEventSource(); + EventSourceT<> NewDay = MakeEventSource(); - SignalT TotalDays = Iterate(NewDay, 0, Incrementer()); - SignalT DayOfYear = TotalDays % 365; + SignalT TotalDays = Iterate(NewDay, 0, Incrementer()); + SignalT DayOfYear = TotalDays % 365; - SignalT Season = DayOfYear ->* [] (int day) { - return day < 180 ? Seasons::winter : Seasons::summer; - }; + SignalT Season = MakeSignal( + DayOfYear, + [] (int day) { + return day < 180 ? Seasons::winter : Seasons::summer; + }); }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Region +/////////////////////////////////////////////////////////////////////////////////////////////////// template class Region : public ReactiveObject { - Time& theTime; + Time& theTime; public: - using BoundsT = tuple; - BoundsT Bounds; EventSourceT EnterOrLeave = MakeEventSource(); - SignalT AnimalCount = Iterate(EnterOrLeave, 0, [] (Migration m, int count) { - return m == Migration::enter ? count + 1 : count - 1; - }); + SignalT AnimalCount = Iterate( + EnterOrLeave, + 0, + [] (Migration m, int count) { + return m == Migration::enter ? count + 1 : count - 1; + }); - SignalT FoodPerDay = theTime.Season ->* [] (Seasons season) { - return season == Seasons::summer ? 20 : 10; - }; + SignalT FoodPerDay = MakeSignal( + theTime.Season, + [] (Seasons season) { + return season == Seasons::summer ? 20 : 10; + }); - SignalT FoodOutputPerDay = - (FoodPerDay, AnimalCount) ->* [] (int food, int count) { + SignalT FoodOutputPerDay = MakeSignal( + With(FoodPerDay,AnimalCount), + [] (int food, int count) { return count > 0 ? food/count : 0; - }; + }); EventsT FoodOutput = Pulse(theTime.NewDay, FoodOutputPerDay); - Region(Time& time, int x, int y): - theTime { time }, - Bounds { make_tuple(x*10, x*10+9, y*10, y*10+9) } - { - } + Region(Time& time, int x, int y) : + theTime{ time }, + Bounds{ x*10, x*10+9, y*10, y*10+9 } + {} - PositionT Center() + PositionT Center() const { - return make_pair(get<0>(Bounds) + 5, get<2>(Bounds) + 5); + using std::get; + + return PositionT(get<0>(Bounds) + 5, get<2>(Bounds) + 5); } - PositionT Clamp(PositionT pos) + PositionT Clamp(PositionT pos) const { + using std::get; + pos.first = get<0>(Bounds) + (abs(pos.first) % 10); pos.second = get<2>(Bounds) + (abs(pos.second) % 10); return pos; } - bool IsInRegion(PositionT pos) + bool IsInRegion(PositionT pos) const { + using std::get; + return get<0>(Bounds) <= pos.first && pos.first <= get<1>(Bounds) && get<2>(Bounds) <= pos.second && pos.second <= get<3>(Bounds); } }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// World +/////////////////////////////////////////////////////////////////////////////////////////////////// template class World : public ReactiveObject { @@ -135,11 +150,12 @@ class World : public ReactiveObject return r.get(); } - printf("FATAL ERROR %d %d\n", pos.first, pos.second); + printf("Out of bounds %d %d\n", pos.first, pos.second); + assert(false); return nullptr; } - PositionT Clamp(PositionT pos) + PositionT Clamp(PositionT pos) const { pos.first = abs(pos.first) % 10*w_; pos.second = abs(pos.second) % 10*w_; @@ -148,73 +164,83 @@ class World : public ReactiveObject } }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Animal +/////////////////////////////////////////////////////////////////////////////////////////////////// template class Animal : public ReactiveObject { Time& theTime; - World& theWorld; + World& theWorld; std::mt19937 generator; public: - SignalT Position; VarSignalT*> CurrentRegion; - SignalT*> NewRegion; - Animal(Time& time, World& world, Region* initRegion, unsigned seed) : - theTime( time ), - theWorld( world ), - CurrentRegion( MakeVar(initRegion) ), - generator( seed ) - { - Position = Iterate(Moving, initRegion->Center(), [this] (bool shouldMigrate, PositionT position) { - std::uniform_int_distribution dist(-1,1); - - // Wander randomly - for (int i=0; i<100; i++) - { - position.first += dist(generator); - position.second += dist(generator); - } - - if (shouldMigrate) - return theWorld.Clamp(position); - else - return CurrentRegion.Value()->Clamp(position); - }); + EventsT FoodReceived = REACTIVE_PTR(CurrentRegion, FoodOutput); + SignalT ShouldMigrate = Hold(FoodReceived, 0) < 10; + EventsT Moving = Pulse(theTime.NewDay, ShouldMigrate); - NewRegion = (Position) ->* [this] (PositionT pos) - { + SignalT Position; + + SignalT*> NewRegion = MakeSignal( + Position, + [this] (PositionT pos) { return theWorld.GetRegion(pos); - }; - - initRegion->EnterOrLeave << Migration::enter; - - Observe(NewRegion, [this] (Region* newRegion) { - CurrentRegion.Value()->EnterOrLeave << Migration::leave; - newRegion->EnterOrLeave << Migration::enter; - - CurrentRegion <<= newRegion; - - //Migrating << true; }); - } - - EventsT FoodReceived = REACTIVE_PTR(CurrentRegion, FoodOutput); - SignalT Age = Iterate(theTime.NewDay, 0, Incrementer()); + EventsT*> RegionChanged = Monitor(NewRegion);; - SignalT Health = Iterate(FoodReceived, 100, [] (int food, int health) { - auto newHealth = health + food - 10; - return newHealth < 0 ? 0 : newHealth > 10000 ? 10000 : newHealth; - }); + SignalT Age = Iterate(theTime.NewDay, 0, Incrementer()); - //Signal YoungAndHealthy = Age < 1000 && Health > 0; - - //Event Migrating = EventSource(); + SignalT Health = Iterate( + FoodReceived, + 100, + [] (int food, int health) { + auto newHealth = health + food - 10; + return newHealth < 0 ? 0 : newHealth > 10000 ? 10000 : newHealth; + }); - SignalT ShouldMigrate = Hold(FoodReceived, 0) < 10; - EventsT Moving = Pulse(theTime.NewDay, ShouldMigrate); + Animal(Time& time, World& world, Region* initRegion, unsigned seed) : + theTime{ time }, + theWorld{ world }, + generator{ seed }, + CurrentRegion{ MakeVar(initRegion) }, + Position + { + Iterate(Moving, initRegion->Center(), With(CurrentRegion), + [this] (bool moving, PositionT position, Region* region) { + std::uniform_int_distribution dist(-1,1); + + // Wander randomly + for (int i=0; i<100; i++) + { + position.first += dist(generator); + position.second += dist(generator); + } + + // Should migrate? + if (moving) + return theWorld.Clamp(position); + else + return region->Clamp(position); + }) + } + { + initRegion->EnterOrLeave(Migration::enter); + + Observe( + RegionChanged, + With(CurrentRegion), + [this] (Region* newRegion, Region* oldRegion) { + oldRegion->EnterOrLeave(Migration::leave); + newRegion->EnterOrLeave(Migration::enter); + + // Change region in continuation + CurrentRegion <<= newRegion; + }); + } }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -223,11 +249,8 @@ class Animal : public ReactiveObject struct BenchmarkParams_LifeSim { BenchmarkParams_LifeSim(int n, int w, int k) : - N( n ), - W( w ), - K( k ) - { - } + N{ n }, W{ w }, K{ k } + {} const int N; const int W; @@ -246,13 +269,13 @@ struct Benchmark_LifeSim : public BenchmarkBase { double Run(const BenchmarkParams_LifeSim& params) { - auto theTime = Time(); - auto theWorld = World(theTime, params.W); + Time theTime; + World theWorld{ theTime, params.W }; - auto animals = vector>>(); + vector>> animals{}; - std::mt19937 gen(2015); - std::uniform_int_distribution dist(0,theWorld.Regions.size()-1); + std::mt19937 gen{ 2015 }; + std::uniform_int_distribution dist{ 0u, theWorld.Regions.size()-1 }; for (int i=0; i animals.push_back(unique_ptr>(new Animal(theTime, theWorld, r, i+1))); } - atomic c(0); - int s = 0; - - //for (const auto& e : animals) - //{ - // Observe(e->Migrating, [&c] (bool _) { - // c++; - // }); - //} - - //Observe(theEnvironment.SummerIsComing, [] (int v) { - // printf("=== SUMMER ===\n"); - //}); - - //Observe(theEnvironment.WinterIsComing, [] (int v) { - // printf("=== WINTER ===\n"); - //}); - // WHEEL IN THE SKY KEEPS ON TURNING auto t0 = tbb::tick_count::now(); + for (int i=0; i Date: Thu, 22 May 2014 02:53:42 +0200 Subject: [PATCH 100/266] Refactored NodeUpdateTimer. Uses QPC directly instead of std::chrono, since VS2013 uses system_clock for everything. Not portable yet. --- include/react/common/Timing.h | 113 +++++++++++++++++++++++ include/react/detail/IReactiveNode.h | 3 + include/react/detail/graph/GraphBase.h | 72 ++++----------- include/react/detail/graph/SignalNodes.h | 19 ++-- project/msvc/CppReact.vcxproj | 1 + project/msvc/CppReact.vcxproj.filters | 3 + 6 files changed, 146 insertions(+), 65 deletions(-) create mode 100644 include/react/common/Timing.h diff --git a/include/react/common/Timing.h b/include/react/common/Timing.h new file mode 100644 index 00000000..14095e47 --- /dev/null +++ b/include/react/common/Timing.h @@ -0,0 +1,113 @@ + +// 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) + +#pragma once + +#include "react/detail/Defs.h" + +#include + +// Todo: Make portable +#include + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// GetPerformanceFrequency +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Todo: Initialization not thread-safe +inline const LARGE_INTEGER& GetPerformanceFrequency() +{ + static bool init = false; + static LARGE_INTEGER frequency; + + if (init == false) + { + QueryPerformanceFrequency(&frequency); + init = true; + } + + return frequency; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ConditionalTimer +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + long long heavy_threshold, + bool is_enabled +> +class ConditionalTimer; + +// Disabled +template +< + long long threshold +> +class ConditionalTimer +{ +public: + // Defines scoped timer that does nothing + struct ScopedTimer + { + ScopedTimer(const ConditionalTimer&) {} + }; + + bool IsThresholdExceeded() const { return false; } +}; + +// Enabled +template +< + long long threshold +> +class ConditionalTimer +{ +public: + class ScopedTimer + { + public: + ScopedTimer(ConditionalTimer& parent) : + parent_{ parent } + { + if (parent_.shouldMeasure_) + QueryPerformanceCounter(&startTime_); + } + + ~ScopedTimer() + { + if (!parent_.shouldMeasure_) + return; + + parent_.shouldMeasure_ = false; + + LARGE_INTEGER endTime, durationMS; + + QueryPerformanceCounter(&endTime); + + durationMS.QuadPart = endTime.QuadPart - startTime_.QuadPart; + durationMS.QuadPart *= 1000000; + durationMS.QuadPart /= GetPerformanceFrequency().QuadPart; + + parent_.isThresholdExceeded_ = durationMS.QuadPart > threshold; + } + + private: + ConditionalTimer& parent_; + + LARGE_INTEGER startTime_; + }; + + bool IsThresholdExceeded() const { return isThresholdExceeded_; } + +private: + // Only measure once + bool shouldMeasure_ = true; + bool isThresholdExceeded_ = false; +}; + +/****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/IReactiveNode.h b/include/react/detail/IReactiveNode.h index 6ecceddd..d80c2f31 100644 --- a/include/react/detail/IReactiveNode.h +++ b/include/react/detail/IReactiveNode.h @@ -37,6 +37,9 @@ struct IReactiveNode // This information is statically available at compile time on the graph layer, // so the engine does not have to calculate it again. virtual int DependencyCount() const = 0; + + // Heavyweight nodes are worth parallelizing. + virtual bool IsHeavyweight() const = 0; }; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index 7bf5da51..f764b8b5 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -8,13 +8,10 @@ #include "react/detail/Defs.h" -#include -#include #include -#include "tbb/tick_count.h" // vc12 chrono clock too inaccurate - #include "react/common/Util.h" +#include "react/common/Timing.h" #include "react/common/Types.h" #include "react/detail/IReactiveEngine.h" #include "react/detail/ObserverBase.h" @@ -28,65 +25,29 @@ template using WeakPtrT = std::weak_ptr; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// NodeUpdateTimer +/// UpdateTimingPolicy /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class NodeUpdateTimerBase; - -// Defines guard that does nothing -template -class NodeUpdateTimerBase -{ -public: - template - struct ScopedUpdateTimer - { - ScopedUpdateTimer(const T& node) {} - }; -}; - -template -class NodeUpdateTimerBase +template +< + typename D, + long long threshold +> +struct UpdateTimingPolicy : + private ConditionalTimer + < + threshold, + EnableNodeUpdateTimer::value + > { -public: - template - class ScopedUpdateTimer + class ScopedUpdateTimer : public ScopedTimer { public: - ScopedUpdateTimer(T& node) : - node_{ node } - { - if (node.shouldMeasure_) - t0_ = tbb::tick_count::now(); - } - - ~ScopedUpdateTimer() - { - if (!node_.shouldMeasure_) - return; - - node_.shouldMeasure_ = false; - - auto d = std::chrono::duration( - (tbb::tick_count::now() - t0_).seconds()); - - D::Engine::HintUpdateDuration( - node_, std::chrono::duration_cast(d).count()); - } - - private: - T& node_; - tbb::tick_count t0_; + ScopedUpdateTimer(UpdateTimingPolicy& parent) : ScopedTimer{ parent } {} }; -private: - bool shouldMeasure_ = true; + bool IsUpdateThresholdExceeded() const { return IsThresholdExceeded(); } }; -template -class NodeUpdateTimer : - public NodeUpdateTimerBase::value> {}; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// NodeBase /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -106,6 +67,7 @@ class NodeBase : virtual bool IsInputNode() const override { return false; } virtual bool IsOutputNode() const override { return false; } virtual bool IsDynamicNode() const override { return false; } + virtual bool IsHeavyweight() const override { return false; } SharedPtrT GetSharedPtr() const { diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index ce196847..bee48a5e 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -183,7 +183,7 @@ template > class SignalOpNode : public SignalNode, - public NodeUpdateTimer + public UpdateTimingPolicy { public: template @@ -218,7 +218,7 @@ class SignalOpNode : bool changed = false; {// timer - ScopedUpdateTimer timer{ *this }; + ScopedUpdateTimer scopedTimer{ *this }; S newValue = op_.Evaluate(); @@ -233,13 +233,14 @@ class SignalOpNode : GetObjectId(*this), turn.Id())); if (changed) - { Engine::OnNodePulse(*this, turn); - } else - { Engine::OnNodeIdlePulse(*this, turn); - } + } + + virtual bool IsHeavyweight() const override + { + return IsUpdateThresholdExceeded(); } TOp StealOp() @@ -311,14 +312,12 @@ class FlattenNode : public SignalNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - TInner newValue = inner_->ValueRef(); - REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - if (! REACT_IMPL::Equals(value_, newValue)) + if (! REACT_IMPL::Equals(value_, inner_->ValueRef())) { - value_ = newValue; + value_ = inner_->ValueRef(); Engine::OnNodePulse(*this, turn); } else diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index 3b121a34..ea023341 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -77,6 +77,7 @@ + diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index 3605a740..48ac8438 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -144,6 +144,9 @@ Header Files + + Header Files\common + From 536ebb9e4b1397c175ef5db35166837bd805974f Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 22 May 2014 02:55:44 +0200 Subject: [PATCH 101/266] Refactored TopoQueue for new update weight. Did some optimizations in the process. --- include/react/common/TopoQueue.h | 203 ++++++++++++++++++++----------- 1 file changed, 133 insertions(+), 70 deletions(-) diff --git a/include/react/common/TopoQueue.h b/include/react/common/TopoQueue.h index a46bdfef..c522b8c8 100644 --- a/include/react/common/TopoQueue.h +++ b/include/react/common/TopoQueue.h @@ -19,13 +19,13 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ template -struct NodeLevelHelper +struct TopoLevelFunctor { int operator()(const T& x) const { return x.Level; } }; template -struct NodeLevelHelper +struct TopoLevelFunctor { int operator()(const T* x) const { return x->Level; } }; @@ -36,63 +36,91 @@ struct NodeLevelHelper template class TopoQueue { +private: + struct Entry; + public: - using DataT = std::vector; - using LevelFunctorT = NodeLevelHelper; + // Store the level as part of the entry for cheap comparisons + using QueueDataT = std::vector; + using NextDataT = std::vector; + using LevelFunctorT = TopoLevelFunctor; - void Push(const T& node) + void Push(const T& value) { - data_.push_back(node); + queueData_.emplace_back(value, LevelFunctorT{}(value)); } bool FetchNext() { - next_.clear(); + // Throw away previous values + nextData_.clear(); - minLevel_ = std::numeric_limits::max(); - for (const auto& e : data_) - { - auto l = LevelFunctorT{}(e); - if (minLevel_ > l) - minLevel_ = l; - } + // Find min level of nodes in queue data + minLevel_ = (std::numeric_limits::max)(); + for (const auto& e : queueData_) + if (minLevel_ > e.Level) + minLevel_ = e.Level; - auto p = std::partition(data_.begin(), data_.end(), CompFunctor{ minLevel_ }); + // Swap entries with min level to the end + auto p = std::partition( + queueData_.begin(), + queueData_.end(), + LevelCompFunctor{ minLevel_ }); - next_.insert(next_.end(), p, data_.end()); - data_.resize(std::distance(data_.begin(), p)); + // Reserve once to avoid multiple re-allocations + nextData_.reserve(std::distance(p, queueData_.end())); - return !next_.empty(); + // Move min level values to next data + for (auto it = p; it != queueData_.end(); ++it) + nextData_.push_back(std::move(it->Value)); + + // Truncate moved entries + queueData_.resize(std::distance(queueData_.begin(), p)); + + return !nextData_.empty(); } - const DataT& NextNodes() const { return next_; } + const NextDataT& NextValues() const { return nextData_; } private: - struct CompFunctor + struct Entry + { + Entry() = default; + Entry(const Entry&) = default; + + Entry(const T& value, int level) : Value{ value }, Level{ level } {} + + T Value; + int Level; + }; + + struct LevelCompFunctor { - CompFunctor(int level) : Level{ level } {} - bool operator()(const T& x) { return LevelFunctorT{}(x) != Level; } + LevelCompFunctor(int level) : Level{ level } {} + + bool operator()(const Entry& e) const { return e.Level != Level; } + const int Level; }; - DataT next_; - DataT data_; - int minLevel_ = std::numeric_limits::max(); + NextDataT nextData_; + QueueDataT queueData_; + int minLevel_ = (std::numeric_limits::max)(); }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// WeightedRange +/// WeightedRange - Implements tbb range concept /////////////////////////////////////////////////////////////////////////////////////////////////// template -struct NodeWeightHelper +struct RangeWeightFunctor { - int operator()(const T& x) const { return x.Weight; } + uint operator()(const T& x) const { return x.IsHeavyweight() ? grain_size : 1; } }; template -struct NodeWeightHelper +struct RangeWeightFunctor { - int operator()(const T* x) const { return x->Weight; } + uint operator()(const T* x) const { return x->IsHeavyweight(); } }; template @@ -105,7 +133,7 @@ class WeightedRange public: using const_iterator = TIt; using ValueT = typename TIt::value_type; - using WeightFunctorT = NodeWeightHelper; + using WeightFunctorT = RangeWeightFunctor; WeightedRange() = default; WeightedRange(const WeightedRange&) = default; @@ -122,7 +150,8 @@ class WeightedRange TIt p = source.begin_; while (p != source.end_) { - sum += WeightFunctorT{}(*p); + // Note: assuming a pair with weight as second until more flexibility is needed + sum += p->second; ++p; if (sum >= grain_size) break; @@ -150,9 +179,9 @@ class WeightedRange uint Weight() const { return weight_; } private: - TIt begin_; - TIt end_; - uint weight_ = 0; + TIt begin_; + TIt end_; + uint weight_ = 0; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -165,30 +194,39 @@ class WeightedRange template class ConcurrentTopoQueue { +private: + struct Entry; + public: - using DataT = std::vector; - using RangeT = WeightedRange; - using LevelFunctorT = NodeLevelHelper; - using WeightFunctorT = NodeWeightHelper; + using QueueDataT = std::vector; + using NextDataT = std::vector>; + using NextRangeT = WeightedRange; + + using LevelFunctorT = TopoLevelFunctor; + using WeightFunctorT = RangeWeightFunctor; - void Push(const T& node) + void Push(const T& value) { auto& t = collectBuffer_.local(); - - t.Data.push_back(node); - t.Weight += WeightFunctorT{}(node); - auto l = LevelFunctorT{}(node); - if (t.MinLevel > l) - t.MinLevel = l; + + auto level = LevelFunctorT{}(value); + auto weight = WeightFunctorT{}(value); + + t.Data.emplace_back(value,level,weight); + + t.Weight += weight; + + if (t.MinLevel > level) + t.MinLevel = level; } bool FetchNext() { - nodes_.clear(); + nextData_.clear(); uint totalWeight = 0; // Determine current min level - minLevel_ = std::numeric_limits::max(); + minLevel_ = (std::numeric_limits::max)(); for (const auto& buf : collectBuffer_) if (minLevel_ > buf.MinLevel) minLevel_ = buf.MinLevel; @@ -199,59 +237,84 @@ class ConcurrentTopoQueue auto& v = buf.Data; // Swap min level nodes to end of v - auto p = std::partition(v.begin(), v.end(), CompFunctor{ minLevel_ }); + auto p = std::partition( + v.begin(), + v.end(), + LevelCompFunctor{ minLevel_ }); + + // Reserve once to avoid multiple re-allocations + nextData_.reserve(std::distance(p, v.end())); - // Copy them to global nodes_ - std::copy(p, v.end(), std::back_inserter(nodes_)); + // Move min level values to global next data + for (auto it = p; it != v.end(); ++it) + nextData_.emplace_back(std::move(it->Value), it->Weight); // Truncate remaining v.resize(std::distance(v.begin(), p)); // Calc new min level and weight for this buffer - buf.MinLevel = std::numeric_limits::max(); + buf.MinLevel = (std::numeric_limits::max)(); int oldWeight = buf.Weight; buf.Weight = 0; - for (const T& x : v) + for (const auto& x : v) { - buf.Weight += WeightFunctorT{}(x); - auto l = LevelFunctorT{}(x); - if (buf.MinLevel > l) - buf.MinLevel = l; + buf.Weight += x.Weight; + + if (buf.MinLevel > x.Level) + buf.MinLevel = x.Level; } // Add diff to nodes_ weight totalWeight += oldWeight - buf.Weight; } - range_ = RangeT(nodes_.begin(), nodes_.end(), totalWeight); + nextRange_ = NextRangeT{ nextData_.begin(), nextData_.end(), totalWeight }; // Found more nodes? - return !nodes_.empty(); + return !nextData_.empty(); } - const RangeT& NextRange() const + const NextRangeT& NextRange() const { - return range_; + return nextRange_; } private: - struct CompFunctor + struct Entry + { + Entry() = default; + Entry(const Entry&) = default; + + Entry(const T& value, int level, uint weight) : + Value{ value }, + Level{ level }, + Weight{ weight } + {} + + T Value; + int Level; + uint Weight; + }; + + struct LevelCompFunctor { - CompFunctor(int level) : Level{ level } {} - bool operator()(const T& x) { return LevelFunctorT{}(x) != Level; } + LevelCompFunctor(int level) : Level{ level } {} + + bool operator()(const Entry& e) const { return e.Level != Level; } + const int Level; }; struct ThreadLocalBuffer { - DataT Data; - int MinLevel = std::numeric_limits::max(); - uint Weight = 0; + QueueDataT Data; + int MinLevel = (std::numeric_limits::max)(); + uint Weight = 0; }; - int minLevel_ = std::numeric_limits::max(); - DataT nodes_; - RangeT range_; + int minLevel_ = (std::numeric_limits::max)(); + NextDataT nextData_; + NextRangeT nextRange_; tbb::enumerable_thread_specific collectBuffer_; }; From a3b465a252cb3322ab113cbc869091ee94c651f3 Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 22 May 2014 02:56:47 +0200 Subject: [PATCH 102/266] Refactored engines to use new node weight interface. --- include/react/detail/IReactiveEngine.h | 9 +------- include/react/engine/PulseCountEngine.h | 3 --- include/react/engine/SubtreeEngine.h | 7 ------ include/react/engine/ToposortEngine.h | 10 +++++---- src/engine/PulsecountEngine.cpp | 8 +------ src/engine/SubtreeEngine.cpp | 15 +++---------- src/engine/ToposortEngine.cpp | 30 ++++++++++--------------- 7 files changed, 23 insertions(+), 59 deletions(-) diff --git a/include/react/detail/IReactiveEngine.h b/include/react/detail/IReactiveEngine.h index 752da259..0bf20348 100644 --- a/include/react/detail/IReactiveEngine.h +++ b/include/react/detail/IReactiveEngine.h @@ -47,8 +47,6 @@ struct IReactiveEngine template bool TryMerge(F&& f) { return false; } - - void HintUpdateDuration(NodeT& node, uint dur) {} }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -160,12 +158,6 @@ struct EngineInterface { return Engine().TryMerge(std::forward(f)); } - - static void HintUpdateDuration(NodeT& node, uint dur) - { - Engine().HintUpdateDuration(node, dur); - } - }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -173,5 +165,6 @@ struct EngineInterface /////////////////////////////////////////////////////////////////////////////////////////////////// template struct EnableNodeUpdateTimer : std::false_type {}; template struct EnableParallelUpdating : std::false_type {}; +template struct EnableConcurrentInput : std::false_type {}; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/engine/PulseCountEngine.h b/include/react/engine/PulseCountEngine.h index 733a7032..5034cdb2 100644 --- a/include/react/engine/PulseCountEngine.h +++ b/include/react/engine/PulseCountEngine.h @@ -85,7 +85,6 @@ class Node : public IReactiveNode NodeVector Successors; ENodeState State = ENodeState::unchanged; - uint Weight = 0; private: atomic counter_ = 0; @@ -114,8 +113,6 @@ class EngineBase : public IReactiveEngine void OnDynamicNodeAttach(Node& node, Node& parent, TTurn& turn); void OnDynamicNodeDetach(Node& node, Node& parent, TTurn& turn); - void HintUpdateDuration(Node& node, uint dur); - private: NodeVectT changedInputs_; empty_task* rootTask_ = new(task::allocate_root()) empty_task; diff --git a/include/react/engine/SubtreeEngine.h b/include/react/engine/SubtreeEngine.h index 291c7044..ccf378b1 100644 --- a/include/react/engine/SubtreeEngine.h +++ b/include/react/engine/SubtreeEngine.h @@ -52,10 +52,6 @@ class Node : public IReactiveNode inline void SetQueuedFlag() { flags_.Set(); } inline void ClearQueuedFlag() { flags_.Clear(); } - inline bool IsHeavy() const { return flags_.Test(); } - inline void SetHeavyFlag() { flags_.Set(); } - inline void ClearHeavyFlag() { flags_.Clear(); } - inline bool IsMarked() const { return flags_.Test(); } inline void SetMarkedFlag() { flags_.Set(); } inline void ClearMarkedFlag() { flags_.Clear(); } @@ -105,7 +101,6 @@ class Node : public IReactiveNode enum EFlags : uint16_t { flag_queued = 0, - flag_heavy, flag_marked, flag_changed, flag_deferred, @@ -142,8 +137,6 @@ class EngineBase : public IReactiveEngine void OnDynamicNodeAttach(Node& node, Node& parent, TTurn& turn); void OnDynamicNodeDetach(Node& node, Node& parent, TTurn& turn); - void HintUpdateDuration(Node& node, uint dur); - private: void applyAsyncDynamicAttach(Node& node, Node& parent, TTurn& turn); void applyAsyncDynamicDetach(Node& node, Node& parent, TTurn& turn); diff --git a/include/react/engine/ToposortEngine.h b/include/react/engine/ToposortEngine.h index ee9433cb..f43452ac 100644 --- a/include/react/engine/ToposortEngine.h +++ b/include/react/engine/ToposortEngine.h @@ -76,7 +76,6 @@ class ParNode : public IReactiveNode int Level = 0; int NewLevel = 0; atomic Collected = false; - uint Weight = 1; NodeVector Successors; InvalidateMutexT InvalidateMutex; @@ -156,8 +155,6 @@ class ParEngineBase : public EngineBase void OnDynamicNodeAttach(ParNode& node, ParNode& parent, TTurn& turn); void OnDynamicNodeDetach(ParNode& node, ParNode& parent, TTurn& turn); - void HintUpdateDuration(ParNode& node, uint dur); - private: void applyDynamicAttach(ParNode& node, ParNode& parent, TTurn& turn); void applyDynamicDetach(ParNode& node, ParNode& parent, TTurn& turn); @@ -247,7 +244,7 @@ class PipeliningTurn : public TurnBase PipeliningTurn* successor_ = nullptr; int currentLevel_ = -1; - int maxLevel_ = numeric_limits::max(); /// This turn may only advance up to maxLevel + int maxLevel_ = (numeric_limits::max)(); /// This turn may only advance up to maxLevel int minLevel_ = -1; /// successor.maxLevel = this.minLevel - 1 int curUpperBound_ = -1; @@ -363,4 +360,9 @@ template <> struct EnableParallelUpdating> : std::true_ template <> struct EnableParallelUpdating> : std::true_type {}; template <> struct EnableParallelUpdating> : std::true_type {}; +template struct EnableConcurrentInput; +template <> struct EnableConcurrentInput> : std::true_type {}; +template <> struct EnableConcurrentInput> : std::true_type {}; +template <> struct EnableConcurrentInput> : std::true_type {}; + /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/src/engine/PulsecountEngine.cpp b/src/engine/PulsecountEngine.cpp index 730759b0..955d040c 100644 --- a/src/engine/PulsecountEngine.cpp +++ b/src/engine/PulsecountEngine.cpp @@ -133,7 +133,7 @@ class UpdaterTask: public task continue; // Heavyweight - spawn new task - if (succ->Weight > heavy_weight) + if (succ->IsHeavyweight()) { auto& t = *new(task::allocate_additional_child_of(*parent())) UpdaterTask(turn_, succ); @@ -287,12 +287,6 @@ void EngineBase::OnDynamicNodeDetach(Node& node, Node& parent, TTurn& tur parent.Successors.Remove(node); }// ~parent.ShiftMutex (write) -template -void EngineBase::HintUpdateDuration(Node& node, uint dur) -{ - node.Weight = dur; -} - // Explicit instantiation template class EngineBase; template class EngineBase>; diff --git a/src/engine/SubtreeEngine.cpp b/src/engine/SubtreeEngine.cpp index 2edea3a1..98b6e9b4 100644 --- a/src/engine/SubtreeEngine.cpp +++ b/src/engine/SubtreeEngine.cpp @@ -112,7 +112,7 @@ class UpdaterTask: public task succ->SetReadyCount(0); // Heavyweight - spawn new task - if (succ->IsHeavy()) + if (succ->IsHeavyweight()) { auto& t = *new(task::allocate_additional_child_of(*parent())) UpdaterTask(turn_, succ); @@ -155,7 +155,7 @@ void EngineBase::OnTurnPropagate(TTurn& turn) // Phase 1 while (scheduledNodes_.FetchNext()) { - for (auto* curNode : scheduledNodes_.NextNodes()) + for (auto* curNode : scheduledNodes_.NextValues()) { if (curNode->Level < curNode->NewLevel) { @@ -244,15 +244,6 @@ void EngineBase::OnDynamicNodeDetach(Node& node, Node& parent, TTurn& tur OnNodeDetach(node, parent); } -template -void EngineBase::HintUpdateDuration(Node& node, uint dur) -{ - if (dur > heavy_weight) - node.SetHeavyFlag(); - else - node.ClearHeavyFlag(); -} - template void EngineBase::applyAsyncDynamicAttach(Node& node, Node& parent, TTurn& turn) { @@ -306,7 +297,7 @@ void EngineBase::processChildren(Node& node, TTurn& turn) continue; // Light nodes use sequential toposort in phase 1 - if (! succ->IsHeavy()) + if (! succ->IsHeavyweight()) { if (!succ->IsQueued()) { diff --git a/src/engine/ToposortEngine.cpp b/src/engine/ToposortEngine.cpp index 62930b0c..2e030678 100644 --- a/src/engine/ToposortEngine.cpp +++ b/src/engine/ToposortEngine.cpp @@ -62,7 +62,7 @@ void SeqEngineBase::OnTurnPropagate(TTurn& turn) { while (scheduledNodes_.FetchNext()) { - for (auto* curNode : scheduledNodes_.NextNodes()) + for (auto* curNode : scheduledNodes_.NextValues()) { if (curNode->Level < curNode->NewLevel) { @@ -133,15 +133,17 @@ void ParEngineBase::OnTurnPropagate(TTurn& turn) while (topoQueue_.FetchNext()) { //using RangeT = tbb::blocked_range::const_iterator>; - using RangeT = ParEngineBase::TopoQueueT::RangeT; + using RangeT = ParEngineBase::TopoQueueT::NextRangeT; // Iterate all nodes of current level and start processing them in parallel tbb::parallel_for( topoQueue_.NextRange(), [&] (const RangeT& range) { - for (auto* curNode : range) + for (const auto& e : range) { + auto* curNode = e.first; + if (curNode->Level < curNode->NewLevel) { curNode->Level = curNode->NewLevel; @@ -186,15 +188,6 @@ void ParEngineBase::OnDynamicNodeDetach(ParNode& node, ParNode& parent, T dynRequests_.push_back(data); } -template -void ParEngineBase::HintUpdateDuration(ParNode& node, uint dur) -{ - if (dur < min_weight) - dur = min_weight; - - node.Weight = dur; -} - template void ParEngineBase::applyDynamicAttach(ParNode& node, ParNode& parent, TTurn& turn) { @@ -331,7 +324,7 @@ void PipeliningTurn::Remove() } else if (successor_) { - successor_->SetMaxLevel(numeric_limits::max()); + successor_->SetMaxLevel((numeric_limits::max)()); successor_->predecessor_ = nullptr; } @@ -434,10 +427,10 @@ void PipeliningEngine::OnTurnPropagate(PipeliningTurn& turn) while (turn.TopoQueue.FetchNext()) { - using RangeT = PipeliningTurn::TopoQueueT::RangeT; + using RangeT = PipeliningTurn::TopoQueueT::NextRangeT; - for (const auto* node : turn.TopoQueue.NextRange()) - turn.AdjustUpperBound(node->Level); + for (const auto& e : turn.TopoQueue.NextRange()) + turn.AdjustUpperBound(e.first->Level); advanceTurn(turn); @@ -446,8 +439,9 @@ void PipeliningEngine::OnTurnPropagate(PipeliningTurn& turn) turn.TopoQueue.NextRange(), [&] (const RangeT& range) { - for (auto* curNode : range) + for (const auto& e : range) { + auto* curNode = e.first; if (curNode->Level < curNode->NewLevel) { curNode->Level = curNode->NewLevel; @@ -492,7 +486,7 @@ void PipeliningEngine::OnDynamicNodeDetach(ParNode& node, ParNode& parent, Pipel void PipeliningEngine::applyDynamicAttach(ParNode& node, ParNode& parent, PipeliningTurn& turn) { - turn.WaitForMaxLevel(numeric_limits::max()); + turn.WaitForMaxLevel((numeric_limits::max)()); OnNodeAttach(node, parent); From cdcd1370a15954abb055613b367cf552be086fb2 Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 22 May 2014 02:58:03 +0200 Subject: [PATCH 103/266] Better way to resolve the windows.h max macro conflicts. --- include/react/detail/Defs.h | 9 --------- include/react/detail/ReactiveInput.h | 4 ++-- include/react/detail/graph/EventNodes.h | 2 +- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/include/react/detail/Defs.h b/include/react/detail/Defs.h index f181481c..26fdf665 100644 --- a/include/react/detail/Defs.h +++ b/include/react/detail/Defs.h @@ -6,15 +6,6 @@ #pragma once -#define NOMINMAX - -#ifdef max - #undef max -#endif -#ifdef min - #undef min -#endif - #include #include diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index f9dee44e..07cde0a6 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -155,8 +155,8 @@ class InputManager { auto curId = nextTurnId_.fetch_add(1, std::memory_order_relaxed); - if (curId == std::numeric_limits::max()) - nextTurnId_.fetch_sub(std::numeric_limits::max()); + if (curId == (std::numeric_limits::max)()) + nextTurnId_.fetch_sub((std::numeric_limits::max)()); return curId; } diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 416193ba..7d0f597e 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -75,7 +75,7 @@ class EventStreamNode : protected: DataT events_; - uint curTurnId_ = std::numeric_limits::max(); + uint curTurnId_ = (std::numeric_limits::max)(); }; template From 39a9b6e479802cf5d11af9279f14cae9c14df0a4 Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 22 May 2014 03:11:30 +0200 Subject: [PATCH 104/266] Using correct trait to enable conditional critical section for event buffer clearing. --- include/react/detail/graph/EventNodes.h | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 7d0f597e..c2cacd19 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -27,17 +27,21 @@ template class SignalNode; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventStreamNodeAccessPolicy +/// BufferClearAccessPolicy +/// +/// Provides thread safe access to clear event buffer if parallel updating is enabled. /////////////////////////////////////////////////////////////////////////////////////////////////// -// Empty base class optimization +// Note: Weird design due to empty base class optimization template -struct EventStreamNodeAccessPolicy : - private ConditionalCriticalSection< +struct BufferClearAccessPolicy : + private ConditionalCriticalSection + < tbb::spin_mutex, - EnableNodeUpdateTimer::value> + EnableParallelUpdating::value + > { template - void AccessCriticalSection(const F& f) { Access(f); } + void AccessBufferForClearing(const F& f) { Access(f); } }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -50,7 +54,7 @@ template > class EventStreamNode : public ReactiveNode, - private EventStreamNodeAccessPolicy + private BufferClearAccessPolicy { public: using DataT = std::vector; @@ -61,7 +65,7 @@ class EventStreamNode : void SetCurrentTurn(const TurnT& turn, bool forceUpdate = false, bool noClear = false) { - AccessCriticalSection([&] { + AccessBufferForClearing([&] { if (curTurnId_ != turn.Id() || forceUpdate) { curTurnId_ = turn.Id(); From ac735cbf94a3e3d77389df21ea212823d0d84176 Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 22 May 2014 12:56:02 +0200 Subject: [PATCH 105/266] Removed ->* overload from example. --- src/sandbox/Main.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sandbox/Main.cpp b/src/sandbox/Main.cpp index 72e62d57..12f3637d 100644 --- a/src/sandbox/Main.cpp +++ b/src/sandbox/Main.cpp @@ -68,9 +68,11 @@ void SignalExample2() auto height = D::MakeVar(70); auto depth = D::MakeVar(8); - auto volume = (width,height,depth) ->* [] (int w, int h, int d) { - return w * h * d; - }; + auto volume = MakeSignal( + With(width,height,depth), + [] (int w, int h, int d) { + return w * h * d; + }); // Observe returns an observer handle, which can be used to detach the observer explicitly. // This observer handle holds a shared_ptr to the subject, so as long as it exists, @@ -80,6 +82,7 @@ void SignalExample2() cout << "Volume changed to: " << v << endl; }); + // Change multiple inputs at once D::DoTransaction([&] { width <<= 90; depth <<= 80; From 662fb3acc89b98327a003c028a65cdbc20c8f5ce Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 22 May 2014 14:21:14 +0200 Subject: [PATCH 106/266] Improved TopoQueue design. --- include/react/common/TopoQueue.h | 69 ++++++++++++++------------- include/react/engine/SubtreeEngine.h | 11 ++++- include/react/engine/ToposortEngine.h | 33 +++++++++++-- 3 files changed, 74 insertions(+), 39 deletions(-) diff --git a/include/react/common/TopoQueue.h b/include/react/common/TopoQueue.h index c522b8c8..9d07594d 100644 --- a/include/react/common/TopoQueue.h +++ b/include/react/common/TopoQueue.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "tbb/enumerable_thread_specific.h" @@ -18,22 +19,10 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -template -struct TopoLevelFunctor -{ - int operator()(const T& x) const { return x.Level; } -}; - -template -struct TopoLevelFunctor -{ - int operator()(const T* x) const { return x->Level; } -}; - /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Sequential TopoQueue +/// TopoQueue - Sequential /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class TopoQueue { private: @@ -43,11 +32,18 @@ class TopoQueue // Store the level as part of the entry for cheap comparisons using QueueDataT = std::vector; using NextDataT = std::vector; - using LevelFunctorT = TopoLevelFunctor; + + TopoQueue() = default; + TopoQueue(const TopoQueue&) = default; + + template + TopoQueue(FIn&& levelFunc) : + levelFunc_{ levelFunc } + {} void Push(const T& value) { - queueData_.emplace_back(value, LevelFunctorT{}(value)); + queueData_.emplace_back(value, levelFunc_(value)); } bool FetchNext() @@ -105,24 +101,15 @@ class TopoQueue NextDataT nextData_; QueueDataT queueData_; + + TLevelFunc levelFunc_; + int minLevel_ = (std::numeric_limits::max)(); }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// WeightedRange - Implements tbb range concept /////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct RangeWeightFunctor -{ - uint operator()(const T& x) const { return x.IsHeavyweight() ? grain_size : 1; } -}; - -template -struct RangeWeightFunctor -{ - uint operator()(const T* x) const { return x->IsHeavyweight(); } -}; - template < typename TIt, @@ -133,7 +120,6 @@ class WeightedRange public: using const_iterator = TIt; using ValueT = typename TIt::value_type; - using WeightFunctorT = RangeWeightFunctor; WeightedRange() = default; WeightedRange(const WeightedRange&) = default; @@ -191,7 +177,12 @@ class WeightedRange /// 2. FetchNext() prepares all nodes of the next level in NextNodes(). /// The previous contents of NextNodes() are automatically cleared. /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template +< typename T, + uint grain_size, + typename TLevelFunc, + typename TWeightFunc +> class ConcurrentTopoQueue { private: @@ -202,15 +193,21 @@ class ConcurrentTopoQueue using NextDataT = std::vector>; using NextRangeT = WeightedRange; - using LevelFunctorT = TopoLevelFunctor; - using WeightFunctorT = RangeWeightFunctor; + ConcurrentTopoQueue() = default; + ConcurrentTopoQueue(const ConcurrentTopoQueue&) = default; + + template + ConcurrentTopoQueue(FIn1&& levelFunc, FIn2&& weightFunc) : + levelFunc_{std::forward(levelFunc) }, + weightFunc_{ std::forward(weightFunc) } + {} void Push(const T& value) { auto& t = collectBuffer_.local(); - auto level = LevelFunctorT{}(value); - auto weight = WeightFunctorT{}(value); + auto level = levelFunc_(value); + auto weight = weightFunc_(value); t.Data.emplace_back(value,level,weight); @@ -313,9 +310,13 @@ class ConcurrentTopoQueue }; int minLevel_ = (std::numeric_limits::max)(); + NextDataT nextData_; NextRangeT nextRange_; + TLevelFunc levelFunc_; + TWeightFunc weightFunc_; + tbb::enumerable_thread_specific collectBuffer_; }; diff --git a/include/react/engine/SubtreeEngine.h b/include/react/engine/SubtreeEngine.h index ccf378b1..7b9aa61d 100644 --- a/include/react/engine/SubtreeEngine.h +++ b/include/react/engine/SubtreeEngine.h @@ -113,7 +113,14 @@ class Node : public IReactiveNode atomic shouldUpdate_ = false; }; -//enum { x = sizeof(Node) }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Functors +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct GetLevelFunctor +{ + int operator()(const T* x) const { return x->Level; } +}; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EngineBase @@ -122,7 +129,7 @@ template class EngineBase : public IReactiveEngine { public: - using TopoQueueT = TopoQueue; + using TopoQueueT = TopoQueue>; using NodeShiftMutexT = Node::ShiftMutexT; void OnNodeAttach(Node& node, Node& parent); diff --git a/include/react/engine/ToposortEngine.h b/include/react/engine/ToposortEngine.h index f43452ac..ca3b561c 100644 --- a/include/react/engine/ToposortEngine.h +++ b/include/react/engine/ToposortEngine.h @@ -101,6 +101,21 @@ class ExclusiveTurn : public REACT_IMPL::TurnBase ExclusiveTurn(TurnIdT id, TurnFlagsT flags); }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Functors +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct GetLevelFunctor +{ + int operator()(const T* x) const { return x->Level; } +}; + +template +struct GetWeightFunctor +{ + uint operator()(T* x) const { return x->IsHeavyweight() ? grain_size : 1; } +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// EngineBase /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -125,7 +140,7 @@ template class SeqEngineBase : public EngineBase { public: - using TopoQueueT = TopoQueue; + using TopoQueueT = TopoQueue>; void OnTurnPropagate(TTurn& turn); @@ -148,7 +163,13 @@ class ParEngineBase : public EngineBase { public: using DynRequestVectT = concurrent_vector; - using TopoQueueT = ConcurrentTopoQueue; + using TopoQueueT = ConcurrentTopoQueue + < + ParNode*, + grain_size, + GetLevelFunctor, + GetWeightFunctor + >; void OnTurnPropagate(TTurn& turn); @@ -186,7 +207,13 @@ class PipeliningTurn : public TurnBase using NodeVectT = vector; using IntervalSetT = set>; using DynRequestVectT = concurrent_vector; - using TopoQueueT = ConcurrentTopoQueue; + using TopoQueueT = ConcurrentTopoQueue + < + ParNode*, + grain_size, + GetLevelFunctor, + GetWeightFunctor + >; PipeliningTurn(TurnIdT id, TurnFlagsT flags); From b27526923154b8a963916cf1ad842dca3d318a86 Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 22 May 2014 14:25:20 +0200 Subject: [PATCH 107/266] detachObservers() refactoring. --- include/react/detail/EngineBase.h | 9 +++++++-- include/react/detail/ReactiveInput.h | 6 +++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/include/react/detail/EngineBase.h b/include/react/detail/EngineBase.h index 7219764d..dbfa61ad 100644 --- a/include/react/detail/EngineBase.h +++ b/include/react/detail/EngineBase.h @@ -21,6 +21,7 @@ #include "react/detail/ReactiveInput.h" #include "react/detail/IReactiveNode.h" #include "react/detail/IReactiveEngine.h" +#include "react/detail/ObserverBase.h" #include "react/detail/Options.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -58,12 +59,16 @@ class TurnBase TurnIdT id_; - template - void detachObservers(TRegistry& registry) + template + void detachObservers() { if (detachedObserversPtr_ != nullptr) + { + auto& registry = DomainSpecificObserverRegistry::Instance(); + for (auto* o : *detachedObserversPtr_) registry.Unregister(o); + } } diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index 07cde0a6..35ce6a56 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -13,6 +13,7 @@ #include #include #include +#include #include "tbb/concurrent_vector.h" @@ -32,7 +33,6 @@ class ContinuationInput using InputClosureT = std::function; using InputVectT = tbb::concurrent_vector; - // Note: Shouldn't this be generated by default? Apparently it isn't. inline ContinuationInput& operator=(ContinuationInput&& other) { bufferedInputsPtr_ = std::move(other.bufferedInputsPtr_); @@ -212,7 +212,7 @@ class InputManager static void postProcessTurn(TurnT& turn) { - turn.detachObservers(DomainSpecificObserverRegistry::Instance()); + turn.detachObservers(); // Steal continuation from current turn if (! turn.continuation_.IsEmpty()) @@ -245,7 +245,7 @@ class InputManager Engine::OnTurnEnd(turn); - turn.detachObservers(DomainSpecificObserverRegistry::Instance()); + turn.detachObservers(); if (turn.continuation_.IsEmpty()) break; From bee9b615f034d46f94d8df26e50982603901528c Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 22 May 2014 22:08:48 +0200 Subject: [PATCH 108/266] Cleanup. --- include/react/detail/EngineBase.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/include/react/detail/EngineBase.h b/include/react/detail/EngineBase.h index dbfa61ad..33bfd45f 100644 --- a/include/react/detail/EngineBase.h +++ b/include/react/detail/EngineBase.h @@ -29,8 +29,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// TurnBase /////////////////////////////////////////////////////////////////////////////////////////////////// -class IObserver; - class TurnBase { public: @@ -71,7 +69,6 @@ class TurnBase } } - std::unique_ptr detachedObserversPtr_; ContinuationInput continuation_; }; @@ -131,7 +128,11 @@ class TurnQueueManager } private: - using MergedDataVectT = std::vector,BlockingCondition*>>; + using MergedDataVectT = + std::vector< + std::pair< + std::function, + BlockingCondition*>>; bool isMergeable_; QueueEntry* successor_ = nullptr; From 02741e6c3d8d1cc159805aef82524bd45e40d047 Mon Sep 17 00:00:00 2001 From: Sebastian Jeckel Date: Fri, 23 May 2014 16:11:20 +0200 Subject: [PATCH 109/266] Update README.md --- README.md | 240 +++++++++++++++--------------------------------------- 1 file changed, 67 insertions(+), 173 deletions(-) diff --git a/README.md b/README.md index 3175255c..a563f3f2 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,95 @@ -## Introduction +# Introduction Cpp.React is an experimental [Reactive Programming](http://en.wikipedia.org/wiki/Reactive_programming) library for C++11. It provides abstractions to simplify the implementation of reactive behaviour. -As an alternative to raw callbacks, it offers the following benefits: + +To react to changing data, variables can be declared in terms of functions that calcuate their values, rather than manipulating them directly. +A runtime engine will automatically handle the change propagation by re-calculating data if its dependencies have been modified. + +To react to events, event streams are provided as composable first class objects with value semantics. + +For seamless integration with existing code, _reactive domains_ encapsulate a reactive sub-system, with an input interface to trigger changes imperatively, and an output interface to apply side effects. +Inside of a reactive domain functional purity is maintained, which allows the easier reasoning about program behaviour and implicit parallelism. + +As an alternative to implementing change propagation or callback-based approaches manually, Cpp.React offers the following benefits: * Less boilerplate code; * consistent updating without redundant calculations or glitches; * implicit parallelism. -#### Compiling +# Documentation +The documentation is still incomplete, but it already contains plenty of useful information and examples. +It can be found in the [wiki](https://github.com/schlangster/cpp.react/wiki). + +# Development +This library is still work-in-progress and should not be considered release quality yet. +That being said, it's in a usable state, has been actively developed during the last 6 months and has seen a fair share of testing and tweaking during that time. -So far, the build has only been tested in Visual Studio 2013 as it's the development environment I'm using. -The Intel C++ Compiler 14.0 with Visual Studio 2012/13 is theoretically supported as well, but last I checked, it did not compile anymore due to [some bugs]() with C++11 support. +### Compiling +So far, Cpp.React has only been tested in Visual Studio 2013 as that's the development environment used to create it. You are welcome to try compiling it with other C++11 compilers/on other platforms and report any issues you encounter. -#### Dependencies -* [Intel TBB 4.2](https://www.threadingbuildingblocks.org/) (required) -* [Google test framework](https://code.google.com/p/googletest/) (optional, to compile the tests) -* [Boost C++ Libraries](http://www.boost.org/) (optional, to use ReactiveLoop, which requires boost::coroutine) +### Projects +The VS solution currently contains four pojects: -## Status +* CppReact - The library itself. +* CppReactBenchmark - A number of benchmarks used to compare the different propagation strategies. +* CppReactTest - The unit tests. +* CppReactSandbox - A project containing several basic examples. You can use this to start experimenting with the library. -This library is still under development and should not be considered release quality yet. -That being said, I've been working on it for about 6 months and it's in a usable state. +### Dependencies +Cpp.React uses several external dependencies, but only one of them is mandatory: -## Documentation +* [Intel TBB 4.2](https://www.threadingbuildingblocks.org/) (required) +* [Google test framework](https://code.google.com/p/googletest/) (optional, to compile the unit tests) +* [Boost C++ Libraries](http://www.boost.org/) (optional, to use ReactiveLoop, which requires boost::coroutine) -The documentation is still incomplete, but it already contains plenty of useful information and examples. -It can be found in the [wiki](https://github.com/schlangster/cpp.react/wiki). +TBB is required, because it enables the parallel propagation strategies. +Future plans are to separate the multi-threaded and single-threaded propagation engines more cleanly to remove that dependency if parallelism is not used. -## Features by example +# Features by example -#### Signals +## Signals Signals are self-updating reactive variables. They can be combined to expressions to create new signals, which are automatically re-calculated whenever one of their data dependencies changes. ```C++ -#include "react/Domain.h" -#include "react/Signal.h" -///... +using namespace std; using namespace react; REACTIVE_DOMAIN(D); -// Two variable that can be manipulated imperatively +// Two reactive variables that can be manipulated imperatively D::VarSignalT width = D::MakeVar(1); D::VarSignalT height = D::MakeVar(2); -// Arithmetic operators are overloaded to lift expressions -// with signal operands and build new signals from them -D::SignalT area = width * height; +// A signal that depends on width and height and multiplies their values +D::SignalT area = MakeSignal(With(width, height), + [] (int w, int h) { + return w * h; + }); cout << "area: " << area.Value() << endl; // => area: 2 // Width changed, so area is re-calculated automatically -width <<= 10; +width.Set(10); cout << "area: " << area.Value() << endl; // => area: 20 ``` -#### Events +When used with built-in operators, this can be simplified further: +```C++ +D::SignalT area = width * height; +``` + +## Events and Observers Event streams represent flows of discrete values. -They are first-class objects and can be merged, filtered, transformed or composed to more complex types. +They are first-class objects and can be merged, filtered, transformed or composed to more complex types: ```C++ -#include "react/Domain.h" -#include "react/Event.h" -#include "react/Observer.h" -//... using namespace std; using namespace react; @@ -81,28 +100,28 @@ D::EventSourceT leftClicked = D::MakeEventSource(); D::EventSourceT rightClicked = D::MakeEventSource(); // Merge both event streams -auto merged = leftClicked | rightClicked; +D::EventsT merged = leftClicked | rightClicked; // React to events auto obs = Observe(merged, [] (Token) { cout << "clicked!" << endl; }); + +// ... + +rightClicked.Emit(); // => clicked! ``` -#### Implicit parallelism +## Implicit parallelism The change propagation is handled implicitly by a _propagation engine_. -Depending on the selected engine, independent propagation paths are automatically parallelized. +Depending on the selected engine, updates can be parallelized: ```C++ -#include "react/Domain.h" -#include "react/Signal.h" -#include "react/engine/ToposortEngine.h" -//... using namespace react; -// Sequential +// Sequential updating { REACTIVE_DOMAIN(D, ToposortEngine); @@ -115,7 +134,7 @@ using namespace react; b <<= 20; } -// Parallel +// Parallel updating { REACTIVE_DOMAIN(D, ToposortEngine); @@ -138,141 +157,16 @@ using namespace react; // op1 and op2 can be re-calculated in parallel in <<= 123456789; } - -// Queued input -{ - REACTIVE_DOMAIN(D, ToposortEngine); - - auto a = D::MakeVar(1); - auto b = D::MakeVar(2); - auto c = D::MakeVar(3); - - auto x = (a + b) * c; - - // Thread-safe input (Note: unspecified order) - std::thread t1{ [&] { a <<= 10; } }; - std::thread t2{ [&] { b <<= 100; } }; - std::thread t3{ [&] { a <<= 1000; } }; - std::thread t4{ [&] { b <<= 10000; } }; - - t1.join(); t2.join(); t3.join(); t4.join(); -} -``` - -#### Reactive loops -(Note: Experimental) - -```C++ -#include "react/Domain.h" -#include "react/Event.h" -#include "react/Reactor.h" -//... -using namespace std; -using namespace react; - -REACTIVE_DOMAIN(D); - -using PointT = pair; -using PathT = vector; - -vector paths; - -auto mouseDown = D::MakeEventSource(); -auto mouseUp = D::MakeEventSource(); -auto mouseMove = D::MakeEventSource(); - -D::ReactiveLoopT loop -{ - [&] (D::ReactiveLoopT::Context& ctx) - { - PathT points; - - points.emplace_back(ctx.Await(mouseDown)); - - ctx.RepeatUntil(mouseUp, [&] { - points.emplace_back(ctx.Await(mouseMove)); - }); - - points.emplace_back(ctx.Await(mouseUp)); - - paths.push_back(points); - } -}; - -mouseDown << PointT(1,1); -mouseMove << PointT(2,2) << PointT(3,3) << PointT(4,4); -mouseUp << PointT(5,5); - -mouseMove << PointT(999,999); - -mouseDown << PointT(10,10); -mouseMove << PointT(20,20); -mouseUp << PointT(30,30); - -// => paths[0]: (1,1) (2,2) (3,3) (4,4) (5,5) -// => paths[1]: (10,10) (20,20) (30,30) ``` -#### Reactive objects and dynamic reactives - -```C++ -#include "react/Domain.h" -#include "react/Signal.h" -#include "react/ReactiveObject.h" -//... -using namespace std; -using namespace react; - -REACTIVE_DOMAIN(D); - -class Company : public ReactiveObject -{ -public: - VarSignalT Name; - - Company(const char* name) : - Name{ MakeVar(string(name)) } - {} - - bool operator==(const Company&) const; -}; - -class Manager : public ReactiveObject -{ -public: - VarSignalT CurrentCompany; - ObserverT NameObserver; - - Manager(initialCompany& company) : - CurrentCompany{ MakeVar(ref(company)) }, - NameObserver - { - // Reactive reference to inner event stream of signal - Observe(REACTIVE_REF(CurrentCompany, Name), - [] (const string& name) { - cout << "Manager: Now managing " << name << endl; - }); - } - {} -}; - -Company company1{ "Cellnet" }; -Company company2{ "Borland" }; - -Manager manager{ company1 }; - -company1.Name <<= string("BT Cellnet"); -company2.Name <<= string("Inprise"); - -// Observer moves from company1.Name to company2.Name -manager.CurrentCompany <<= ref(company2); - -company1.Name <<= string("O2"); -company2.Name <<= string("Borland"); -``` - -### More examples +## More examples * [Examples](https://github.com/schlangster/cpp.react/blob/master/src/sandbox/Main.cpp) -* [Benchmark](https://github.com/schlangster/cpp.react/blob/master/src/benchmark/BenchmarkLifeSim.h) * [Test cases](https://github.com/schlangster/cpp.react/tree/master/src/test) +* [Benchmark](https://github.com/schlangster/cpp.react/blob/master/src/benchmark/BenchmarkLifeSim.h) + +# Acknowledgements + +The API of Cpp.React has been inspired by the following two research papers: +* [Deprecating the Observer Pattern with Scala.React](http://infoscience.epfl.ch/record/176887/files/DeprecatingObservers2012.pdf) +* [REScala: Bridging Between Object-oriented and Functional Style in Reactive Applications](http://www.stg.tu-darmstadt.de/media/st/research/rescala_folder/REScala-Bridging-The-Gap-Between-Object-Oriented-And-Functional-Style-In-Reactive-Applications.pdf) From a57c08c96664645d3faaceeb7e5493236e2bbadd Mon Sep 17 00:00:00 2001 From: Sebastian Jeckel Date: Fri, 23 May 2014 16:54:26 +0200 Subject: [PATCH 110/266] Update README.md --- README.md | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index a563f3f2..5cf20dd1 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,18 @@ # Introduction - Cpp.React is an experimental [Reactive Programming](http://en.wikipedia.org/wiki/Reactive_programming) library for C++11. It provides abstractions to simplify the implementation of reactive behaviour. -To react to changing data, variables can be declared in terms of functions that calcuate their values, rather than manipulating them directly. +To react to changing data, reactive variables are declared in terms of functions that calcuate their values, rather than manipulating them directly. A runtime engine will automatically handle the change propagation by re-calculating data if its dependencies have been modified. To react to events, event streams are provided as composable first class objects with value semantics. For seamless integration with existing code, _reactive domains_ encapsulate a reactive sub-system, with an input interface to trigger changes imperatively, and an output interface to apply side effects. -Inside of a reactive domain functional purity is maintained, which allows the easier reasoning about program behaviour and implicit parallelism. -As an alternative to implementing change propagation or callback-based approaches manually, Cpp.React offers the following benefits: -* Less boilerplate code; -* consistent updating without redundant calculations or glitches; -* implicit parallelism. +As an alternative to implementing change propagation or callback-based approaches manually, using Cpp.React results in less boilerplate code and better scalability due to the guarentee of avoiding unnecessary recalculations and glitches. +Furthermore, since functional purity is maintained in each reactive domain, the propagation engine can safely support implicit parallelism for the updating process. +Behind the scenes, task-based programming and dynamic chunking based on collected timing data are employed to optimize parallel utilization. # Documentation The documentation is still incomplete, but it already contains plenty of useful information and examples. @@ -23,7 +20,7 @@ It can be found in the [wiki](https://github.com/schlangster/cpp.react/wiki). # Development This library is still work-in-progress and should not be considered release quality yet. -That being said, it's in a usable state, has been actively developed during the last 6 months and has seen a fair share of testing and tweaking during that time. +That being said, it's in a usable state, has been actively developed during the last 6 months and has seen a fair share of testing and tuning during that time. ### Compiling So far, Cpp.React has only been tested in Visual Studio 2013 as that's the development environment used to create it. @@ -33,10 +30,10 @@ You are welcome to try compiling it with other C++11 compilers/on other platform ### Projects The VS solution currently contains four pojects: -* CppReact - The library itself. -* CppReactBenchmark - A number of benchmarks used to compare the different propagation strategies. -* CppReactTest - The unit tests. -* CppReactSandbox - A project containing several basic examples. You can use this to start experimenting with the library. +* `CppReact` - The library itself. +* `CppReactBenchmark` - A number of benchmarks used to compare the different propagation strategies. +* `CppReactTest` - The unit tests. +* `CppReactSandbox` - A project containing several basic examples. You can use this to start experimenting with the library. ### Dependencies Cpp.React uses several external dependencies, but only one of them is mandatory: @@ -79,8 +76,9 @@ width.Set(10); cout << "area: " << area.Value() << endl; // => area: 20 ``` -When used with built-in operators, this can be simplified further: +For expressions that use operators only, `MakeSignal` can be omitted completely: ```C++ +// Lift as reactive expression D::SignalT area = width * height; ``` @@ -168,5 +166,6 @@ using namespace react; # Acknowledgements The API of Cpp.React has been inspired by the following two research papers: + * [Deprecating the Observer Pattern with Scala.React](http://infoscience.epfl.ch/record/176887/files/DeprecatingObservers2012.pdf) * [REScala: Bridging Between Object-oriented and Functional Style in Reactive Applications](http://www.stg.tu-darmstadt.de/media/st/research/rescala_folder/REScala-Bridging-The-Gap-Between-Object-Oriented-And-Functional-Style-In-Reactive-Applications.pdf) From 13d080c5426fb18cc93bfddb33675699c353c0ed Mon Sep 17 00:00:00 2001 From: Sebastian Jeckel Date: Fri, 23 May 2014 18:15:13 +0200 Subject: [PATCH 111/266] Update README.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 5cf20dd1..62b9204d 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ To react to events, event streams are provided as composable first class objects For seamless integration with existing code, _reactive domains_ encapsulate a reactive sub-system, with an input interface to trigger changes imperatively, and an output interface to apply side effects. -As an alternative to implementing change propagation or callback-based approaches manually, using Cpp.React results in less boilerplate code and better scalability due to the guarentee of avoiding unnecessary recalculations and glitches. +As an alternative to implementing change propagation or callback-based approaches manually, using Cpp.React results in less boilerplate code and better scalability due to the guarentees of avoiding unnecessary recalculations and glitches. Furthermore, since functional purity is maintained in each reactive domain, the propagation engine can safely support implicit parallelism for the updating process. Behind the scenes, task-based programming and dynamic chunking based on collected timing data are employed to optimize parallel utilization. @@ -19,16 +19,16 @@ The documentation is still incomplete, but it already contains plenty of useful It can be found in the [wiki](https://github.com/schlangster/cpp.react/wiki). # Development -This library is still work-in-progress and should not be considered release quality yet. -That being said, it's in a usable state, has been actively developed during the last 6 months and has seen a fair share of testing and tuning during that time. +Cpp.React is a work-in-progress and should not be considered release quality yet. +The project has been actively developed for about 6 months and has seen a fair share of testing and tuning during that time, so it's in a usable state. ### Compiling -So far, Cpp.React has only been tested in Visual Studio 2013 as that's the development environment used to create it. +At the moment, the only support build environment is Visual Studio 2013. -You are welcome to try compiling it with other C++11 compilers/on other platforms and report any issues you encounter. +You are welcome to try other C++11 compilers/platforms and report any issues you encounter. ### Projects -The VS solution currently contains four pojects: +The VS solution contains the following pojects: * `CppReact` - The library itself. * `CppReactBenchmark` - A number of benchmarks used to compare the different propagation strategies. @@ -43,7 +43,7 @@ Cpp.React uses several external dependencies, but only one of them is mandatory: * [Boost C++ Libraries](http://www.boost.org/) (optional, to use ReactiveLoop, which requires boost::coroutine) TBB is required, because it enables the parallel propagation strategies. -Future plans are to separate the multi-threaded and single-threaded propagation engines more cleanly to remove that dependency if parallelism is not used. +Future plans are to separate the multi-threaded and single-threaded propagation engines more cleanly to remove the TBB dependency if parallelism is not used. # Features by example From 2f7afd3656210081d52d5a96bb3d9194dcb21373 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 25 May 2014 00:17:02 +0200 Subject: [PATCH 112/266] Fixed logging. --- include/react/Domain.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/include/react/Domain.h b/include/react/Domain.h index 8aacea8f..dd1db9d7 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -13,8 +13,8 @@ #include "react/detail/Options.h" #ifdef REACT_ENABLE_LOGGING - #include "react/detail/logging/EventLog.h" - #include "react/detail/logging/EventRecords.h" + #include "react/logging/EventLog.h" + #include "react/logging/EventRecords.h" #endif //REACT_ENABLE_LOGGING #include "react/detail/IReactiveEngine.h" @@ -56,6 +56,10 @@ enum class Token; using REACT_IMPL::TurnFlagsT; +#ifdef REACT_ENABLE_LOGGING + using REACT_IMPL::EventLog; +#endif //REACT_ENABLE_LOGGING + /////////////////////////////////////////////////////////////////////////////////////////////////// /// DomainBase /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -161,7 +165,7 @@ class DomainBase /////////////////////////////////////////////////////////////////////////////////////////////// static EventLog& Log() { - static ObserverRegistry instance; + static EventLog instance; return instance; } #endif //REACT_ENABLE_LOGGING @@ -193,7 +197,7 @@ class DomainInitializer DomainInitializer() { #ifdef REACT_ENABLE_LOGGING - DomainSpecificData::Log(); + D::Log(); #endif //REACT_ENABLE_LOGGING typename D::Engine::Engine(); From 013cf80f74b5188fe46c799eacbf1484ad7bd0cc Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 25 May 2014 13:16:59 +0200 Subject: [PATCH 113/266] Misc fixes. --- include/react/common/Containers.h | 2 +- include/react/common/Timing.h | 21 +++++++++++++++++---- include/react/common/Util.h | 1 - include/react/detail/graph/SignalNodes.h | 2 +- src/benchmark/BenchmarkBase.h | 2 ++ 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/include/react/common/Containers.h b/include/react/common/Containers.h index 3b3a1613..78dcb4db 100644 --- a/include/react/common/Containers.h +++ b/include/react/common/Containers.h @@ -16,7 +16,7 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// -/// BitField +/// EnumFlags /////////////////////////////////////////////////////////////////////////////////////////////////// template class EnumFlags diff --git a/include/react/common/Timing.h b/include/react/common/Timing.h index 14095e47..ecea4436 100644 --- a/include/react/common/Timing.h +++ b/include/react/common/Timing.h @@ -74,17 +74,31 @@ class ConditionalTimer ScopedTimer(ConditionalTimer& parent) : parent_{ parent } { - if (parent_.shouldMeasure_) - QueryPerformanceCounter(&startTime_); + if (!parent_.shouldMeasure_) + return; + + startMeasure(); } - ~ScopedTimer() + // Note: virtual for performance reasons + virtual ~ScopedTimer() { if (!parent_.shouldMeasure_) return; parent_.shouldMeasure_ = false; + + endMeasure(); + } + + private: + void startMeasure() + { + QueryPerformanceCounter(&startTime_); + } + void endMeasure() + { LARGE_INTEGER endTime, durationMS; QueryPerformanceCounter(&endTime); @@ -96,7 +110,6 @@ class ConditionalTimer parent_.isThresholdExceeded_ = durationMS.QuadPart > threshold; } - private: ConditionalTimer& parent_; LARGE_INTEGER startTime_; diff --git a/include/react/common/Util.h b/include/react/common/Util.h index 758b5b40..31dddf7e 100644 --- a/include/react/common/Util.h +++ b/include/react/common/Util.h @@ -8,7 +8,6 @@ #include "react/detail/Defs.h" -#include #include #include #include diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index bee48a5e..a0ff22d8 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -34,7 +34,7 @@ class SignalNode : public ReactiveNode SignalNode() = default; template - SignalNode(T&& value) : + explicit SignalNode(T&& value) : ReactiveNode{ }, value_{ std::forward(value) } {} diff --git a/src/benchmark/BenchmarkBase.h b/src/benchmark/BenchmarkBase.h index c8ed25f1..36a117ef 100644 --- a/src/benchmark/BenchmarkBase.h +++ b/src/benchmark/BenchmarkBase.h @@ -7,9 +7,11 @@ #pragma once #include +#include #include #include #include +#include #include "react/common/Util.h" From 4dbe8e2b9c7f041dfc14c2031d31bca701b0c1ae Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 25 May 2014 13:18:18 +0200 Subject: [PATCH 114/266] Added domain traits. --- include/react/Domain.h | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/include/react/Domain.h b/include/react/Domain.h index dd1db9d7..0c012835 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -74,9 +74,21 @@ class DomainBase using Policy = TPolicy; using Engine = REACT_IMPL::EngineInterface; - /////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////// + /// Domain traits + /////////////////////////////////////////////////////////////////////////////////////////////// + static const bool uses_node_update_timer = + REACT_IMPL::EnableNodeUpdateTimer::value; + + static const bool uses_concurrent_input = + REACT_IMPL::EnableConcurrentInput::value; + + static const bool uses_parallel_updating = + REACT_IMPL::EnableParallelUpdating::value; + + /////////////////////////////////////////////////////////////////////////////////////////////// /// Aliases for reactives of current domain - /////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////// template using SignalT = Signal; From 5b284ab977e4fc07e828f64c68ec001c172357bd Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 25 May 2014 13:21:16 +0200 Subject: [PATCH 115/266] Using specialized versions of TurnBase and ReactiveInput for sequential and parallel operation to avoid concurrent_vectors when they are not required. --- include/react/detail/EngineBase.h | 88 +++++++++++++++++++++++-- include/react/detail/ReactiveInput.h | 87 +++++++++++++++++++++--- include/react/engine/PulseCountEngine.h | 2 +- include/react/engine/SubtreeEngine.h | 2 +- include/react/engine/ToposortEngine.h | 25 ++++--- src/engine/SubtreeEngine.cpp | 6 +- src/engine/ToposortEngine.cpp | 30 +++++---- 7 files changed, 197 insertions(+), 43 deletions(-) diff --git a/include/react/detail/EngineBase.h b/include/react/detail/EngineBase.h index 33bfd45f..74432388 100644 --- a/include/react/detail/EngineBase.h +++ b/include/react/detail/EngineBase.h @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -29,19 +30,90 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// TurnBase /////////////////////////////////////////////////////////////////////////////////////////////////// +template class TurnBase { public: - inline TurnBase(TurnIdT id, TurnFlagsT flags) : + TurnBase(TurnIdT id, TurnFlagsT flags); + + TurnIdT Id() const; + + void QueueForDetach(IObserver& obs); + + template + friend class InputManager; + + template + friend class ContinuationHolder; + +private: + using ContinuationT = ContinuationInput; + + template + void detachObservers(); +}; + +// Not thread-safe +template <> +class TurnBase +{ +public: + TurnBase(TurnIdT id, TurnFlagsT flags) : + id_{ id } + {} + + TurnIdT Id() const { return id_; } + + void QueueForDetach(IObserver& obs) + { + detachedObservers_.push_back(&obs); + } + + template + friend class InputManager; + + template + friend class ContinuationHolder; + +private: + using ObsVectT = std::vector; + using ContinuationT = ContinuationInput; + + TurnIdT id_; + + template + void detachObservers() + { + + auto& registry = DomainSpecificObserverRegistry::Instance(); + + for (auto* o : detachedObservers_) + registry.Unregister(o); + + detachedObservers_.clear(); + } + + ObsVectT detachedObservers_; + ContinuationT continuation_; +}; + +// Thread-safe +template <> +class TurnBase +{ +public: + TurnBase(TurnIdT id, TurnFlagsT flags) : id_{ id } {} - inline TurnIdT Id() const { return id_; } + TurnIdT Id() const { return id_; } - inline void QueueForDetach(IObserver& obs) + void QueueForDetach(IObserver& obs) { - if (detachedObserversPtr_ == nullptr) - detachedObserversPtr_ = std::unique_ptr(new ObsVectT()); + // Allocation of concurrent vector is not cheap -> create on demand + std::call_once(detachedObserversInit_, [this] { + detachedObserversPtr_.reset(new ObsVectT()); + }); detachedObserversPtr_->push_back(&obs); } @@ -54,6 +126,7 @@ class TurnBase private: using ObsVectT = tbb::concurrent_vector; + using ContinuationT = ContinuationInput; TurnIdT id_; @@ -66,11 +139,14 @@ class TurnBase for (auto* o : *detachedObserversPtr_) registry.Unregister(o); + + detachedObserversPtr_->clear(); } } + std::once_flag detachedObserversInit_; std::unique_ptr detachedObserversPtr_; - ContinuationInput continuation_; + ContinuationT continuation_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index 35ce6a56..26206eb0 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -27,34 +27,100 @@ using TurnFlagsT = uint; /////////////////////////////////////////////////////////////////////////////////////////////////// /// ContinuationInput /////////////////////////////////////////////////////////////////////////////////////////////////// +template class ContinuationInput { +public: + ContinuationInput() = default; + ContinuationInput(const ContinuationInput&) = delete; + ContinuationInput(ContinuationInput&&); + + bool IsEmpty() const; + + template + void Add(F&& input); + + void Execute(); +}; + +// Not thread-safe +template <> +class ContinuationInput +{ +public: + using InputClosureT = std::function; + using InputVectT = std::vector; + + ContinuationInput() = default; + ContinuationInput(const ContinuationInput&) = delete; + + ContinuationInput(ContinuationInput&& other) : + bufferedInputs_{ std::move(other.bufferedInputs_) } + {} + + ContinuationInput& operator=(ContinuationInput&& other) + { + bufferedInputs_ = std::move(other.bufferedInputs_); + return *this; + } + + bool IsEmpty() const { return bufferedInputs_.empty(); } + + template + void Add(F&& input) + { + bufferedInputs_.push_back(std::forward(input)); + } + + void Execute() + { + for (auto& f : bufferedInputs_) + f(); + bufferedInputs_.clear(); + } + +private: + InputVectT bufferedInputs_; +}; + +// Thread-safe +template <> +class ContinuationInput +{ public: using InputClosureT = std::function; using InputVectT = tbb::concurrent_vector; + + ContinuationInput() = default; + ContinuationInput(const ContinuationInput&) = delete; + + ContinuationInput(ContinuationInput&& other) : + bufferedInputsPtr_{ std::move(other.bufferedInputsPtr_) } + {} - inline ContinuationInput& operator=(ContinuationInput&& other) + ContinuationInput& operator=(ContinuationInput&& other) { bufferedInputsPtr_ = std::move(other.bufferedInputsPtr_); return *this; } - inline bool IsEmpty() const { return bufferedInputsPtr_ == nullptr; } + bool IsEmpty() const { return bufferedInputsPtr_ == nullptr; } template void Add(F&& input) { + // Allocation of concurrent vector is not cheap -> create on demand std::call_once(bufferedInputsInit_, [this] { bufferedInputsPtr_.reset(new InputVectT()); }); bufferedInputsPtr_->push_back(std::forward(input)); } - inline void Execute() + void Execute() { if (bufferedInputsPtr_ != nullptr) { - for (auto f : *bufferedInputsPtr_) + for (auto& f : *bufferedInputsPtr_) f(); bufferedInputsPtr_->clear(); } @@ -73,19 +139,20 @@ class ContinuationHolder { public: using TurnT = typename D::TurnT; + using ContinuationT = ContinuationInput; ContinuationHolder() = delete; - static void SetTurn(TurnT& turn) { ptr_ = &turn.continuation_; } - static void Clear() { ptr_ = nullptr; } - static ContinuationInput* Get() { return ptr_; } + static void SetTurn(TurnT& turn) { ptr_ = &turn.continuation_; } + static void Clear() { ptr_ = nullptr; } + static ContinuationT* Get() { return ptr_; } private: - static REACT_TLS ContinuationInput* ptr_; + static REACT_TLS ContinuationT* ptr_; }; template -ContinuationInput* ContinuationHolder::ptr_(nullptr); +typename ContinuationHolder::ContinuationT* ContinuationHolder::ptr_(nullptr); /////////////////////////////////////////////////////////////////////////////////////////////////// /// InputManager @@ -219,7 +286,7 @@ class InputManager processContinuations(std::move(turn.continuation_), 0); } - static void processContinuations(ContinuationInput&& cont, TurnFlagsT flags) + static void processContinuations(typename TurnT::ContinuationT&& cont, TurnFlagsT flags) { // No merging for continuations flags &= ~enable_input_merging; diff --git a/include/react/engine/PulseCountEngine.h b/include/react/engine/PulseCountEngine.h index 5034cdb2..8d58b1da 100644 --- a/include/react/engine/PulseCountEngine.h +++ b/include/react/engine/PulseCountEngine.h @@ -34,7 +34,7 @@ using tbb::task_list; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Turn /////////////////////////////////////////////////////////////////////////////////////////////////// -struct Turn : public TurnBase +struct Turn : public TurnBase { public: Turn(TurnIdT id, TurnFlagsT flags); diff --git a/include/react/engine/SubtreeEngine.h b/include/react/engine/SubtreeEngine.h index 7b9aa61d..615feb56 100644 --- a/include/react/engine/SubtreeEngine.h +++ b/include/react/engine/SubtreeEngine.h @@ -34,7 +34,7 @@ using tbb::spin_rw_mutex; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Turn /////////////////////////////////////////////////////////////////////////////////////////////////// -class Turn : public TurnBase +class Turn : public TurnBase { public: Turn(TurnIdT id, TurnFlagsT flags); diff --git a/include/react/engine/ToposortEngine.h b/include/react/engine/ToposortEngine.h index ca3b561c..ca10193e 100644 --- a/include/react/engine/ToposortEngine.h +++ b/include/react/engine/ToposortEngine.h @@ -30,6 +30,7 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ +template class TurnBase; namespace toposort { @@ -92,13 +93,21 @@ struct DynRequestData }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// ExclusiveTurn +/// ExclusiveSeqTurn /////////////////////////////////////////////////////////////////////////////////////////////////// +class ExclusiveSeqTurn : public REACT_IMPL::TurnBase +{ +public: + ExclusiveSeqTurn(TurnIdT id, TurnFlagsT flags); +}; -class ExclusiveTurn : public REACT_IMPL::TurnBase +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ExclusiveParTurn +/////////////////////////////////////////////////////////////////////////////////////////////////// +class ExclusiveParTurn : public REACT_IMPL::TurnBase { public: - ExclusiveTurn(TurnIdT id, TurnFlagsT flags); + ExclusiveParTurn(TurnIdT id, TurnFlagsT flags); }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -191,16 +200,16 @@ class ParEngineBase : public EngineBase /////////////////////////////////////////////////////////////////////////////////////////////////// /// Concrete engines /////////////////////////////////////////////////////////////////////////////////////////////////// -class BasicSeqEngine : public SeqEngineBase {}; -class QueuingSeqEngine : public DefaultQueuingEngine {}; +class BasicSeqEngine : public SeqEngineBase {}; +class QueuingSeqEngine : public DefaultQueuingEngine {}; -class BasicParEngine : public ParEngineBase {}; -class QueuingParEngine : public DefaultQueuingEngine {}; +class BasicParEngine : public ParEngineBase {}; +class QueuingParEngine : public DefaultQueuingEngine {}; /////////////////////////////////////////////////////////////////////////////////////////////////// /// PipeliningTurn /////////////////////////////////////////////////////////////////////////////////////////////////// -class PipeliningTurn : public TurnBase +class PipeliningTurn : public TurnBase { public: using ConcNodeVectT = concurrent_vector; diff --git a/src/engine/SubtreeEngine.cpp b/src/engine/SubtreeEngine.cpp index 98b6e9b4..30885289 100644 --- a/src/engine/SubtreeEngine.cpp +++ b/src/engine/SubtreeEngine.cpp @@ -26,8 +26,7 @@ static const uint heavy_weight = 1000; /////////////////////////////////////////////////////////////////////////////////////////////////// Turn::Turn(TurnIdT id, TurnFlagsT flags) : TurnBase(id, flags) -{ -} +{} /////////////////////////////////////////////////////////////////////////////////////////////////// /// PulsecountEngine @@ -212,8 +211,6 @@ void EngineBase::OnNodeIdlePulse(Node& node, TTurn& turn) { if (isInPhase2_) node.ClearChangedFlag(); - else - processChildren(node, turn); } template @@ -270,7 +267,6 @@ void EngineBase::applyAsyncDynamicAttach(Node& node, Node& parent, TTurn& node.SetShouldUpdate(true); node.WaitCount = 1; - } }// ~parent.ShiftMutex (write) diff --git a/src/engine/ToposortEngine.cpp b/src/engine/ToposortEngine.cpp index 2e030678..3d9bcb98 100644 --- a/src/engine/ToposortEngine.cpp +++ b/src/engine/ToposortEngine.cpp @@ -11,12 +11,18 @@ namespace toposort { /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Turn +/// ExclusiveSeqTurn /////////////////////////////////////////////////////////////////////////////////////////////////// -ExclusiveTurn::ExclusiveTurn(TurnIdT id, TurnFlagsT flags) : +ExclusiveSeqTurn::ExclusiveSeqTurn(TurnIdT id, TurnFlagsT flags) : TurnBase(id, flags) -{ -} +{} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ExclusiveParTurn +/////////////////////////////////////////////////////////////////////////////////////////////////// +ExclusiveParTurn::ExclusiveParTurn(TurnIdT id, TurnFlagsT flags) : + TurnBase(id, flags) +{} /////////////////////////////////////////////////////////////////////////////////////////////////// /// EngineBase @@ -49,10 +55,10 @@ void EngineBase::OnNodePulse(TNode& node, TTurn& turn) } // Explicit instantiation -template class EngineBase; -template class EngineBase; -template class EngineBase>; -template class EngineBase>; +template class EngineBase; +template class EngineBase; +template class EngineBase>; +template class EngineBase>; /////////////////////////////////////////////////////////////////////////////////////////////////// /// SeqEngineBase @@ -121,8 +127,8 @@ void SeqEngineBase::invalidateSuccessors(SeqNode& node) } // Explicit instantiation -template class SeqEngineBase; -template class SeqEngineBase>; +template class SeqEngineBase; +template class SeqEngineBase>; /////////////////////////////////////////////////////////////////////////////////////////////////// /// ParEngineBase @@ -228,8 +234,8 @@ void ParEngineBase::invalidateSuccessors(ParNode& node) } // Explicit instantiation -template class ParEngineBase; -template class ParEngineBase>; +template class ParEngineBase; +template class ParEngineBase>; /////////////////////////////////////////////////////////////////////////////////////////////////// /// PipeliningTurn From 3efb320d59f4b162499c47c9e48be768e3785928 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 25 May 2014 13:22:44 +0200 Subject: [PATCH 116/266] Fixed synced Iterate nodes + some other graph tweaks. --- include/react/detail/graph/AlgorithmNodes.h | 74 ++++++++++++--------- include/react/detail/graph/GraphBase.h | 9 ++- 2 files changed, 46 insertions(+), 37 deletions(-) diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index 137ff838..ec4f72be 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -59,7 +59,7 @@ class IterateNode : public SignalNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - if (! impl::Equals(newValue, value_)) + if (! Equals(newValue, value_)) { value_ = std::move(newValue); Engine::OnNodePulse(*this, turn); @@ -197,26 +197,34 @@ class SyncedIterateNode : public SignalNode 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())); - S newValue = value_; + if (! events_->Events().empty()) + { + S newValue = value_; - for (const auto& e : events_->Events()) - newValue = apply(EvalFunctor_{ e, std::move(newValue), func_ }, deps_); + for (const auto& e : events_->Events()) + newValue = apply(EvalFunctor_{ e, std::move(newValue), func_ }, deps_); + + if (! Equals(newValue, value_)) + { + changed = true; + value_ = std::move(newValue); + } + } REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - if (! impl::Equals(newValue, value_)) - { - value_ = std::move(newValue); + if (changed) Engine::OnNodePulse(*this, turn); - } else - { Engine::OnNodeIdlePulse(*this, turn); - } } virtual const char* GetNodeType() const override { return "SyncedIterateNode"; } @@ -250,8 +258,8 @@ class SyncedIterateByRefNode : public SignalNode const std::shared_ptr>& ... deps) : SignalNode{ std::forward(init) }, events_{ events }, - func_{ std::forward(func) } - + func_{ std::forward(func) }, + deps_{ deps ... } { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *events); @@ -274,7 +282,7 @@ class SyncedIterateByRefNode : public SignalNode { struct EvalFunctor_ { - EvalFunctor_(const E& e, const S& v, TFunc& f) : + EvalFunctor_(const E& e, S& v, TFunc& f) : MyEvent{ e }, MyValue{ v }, MyFunc{ f } @@ -293,16 +301,28 @@ class SyncedIterateByRefNode : public SignalNode 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())); - for (const auto& e : events_->Events()) - apply(EvalFunctor_{ e, newValue, func_ }, deps_); + if (! events_->Events().empty()) + { + for (const auto& e : events_->Events()) + apply(EvalFunctor_{ e, value_, func_ }, deps_); + + changed = true; + } REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - Engine::OnNodePulse(*this, turn); + if (changed) + Engine::OnNodePulse(*this, turn); + else + Engine::OnNodeIdlePulse(*this, turn); } virtual const char* GetNodeType() const override { return "SyncedIterateByRefNode"; } @@ -358,7 +378,8 @@ class HoldNode : public SignalNode if (! events_->Events().empty()) { const S& newValue = events_->Events().back(); - if (newValue != value_) + + if (! Equals(newValue, value_)) { changed = true; value_ = newValue; @@ -369,13 +390,9 @@ class HoldNode : public SignalNode GetObjectId(*this), turn.Id())); if (changed) - { Engine::OnNodePulse(*this, turn); - } else - { Engine::OnNodeIdlePulse(*this, turn); - } } virtual int DependencyCount() const override { return 1; } @@ -432,7 +449,8 @@ class SnapshotNode : public SignalNode if (! trigger_->Events().empty()) { const S& newValue = target_->ValueRef(); - if (newValue != value_) + + if (! Equals(newValue, value_)) { changed = true; value_ = newValue; @@ -443,13 +461,9 @@ class SnapshotNode : public SignalNode GetObjectId(*this), turn.Id(), std::this_thread::get_id().hash())); if (changed) - { Engine::OnNodePulse(*this, turn); - } else - { Engine::OnNodeIdlePulse(*this, turn); - } } private: @@ -500,14 +514,10 @@ class MonitorNode : public EventStreamNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - if (events_.size() > 0) - { + if (! events_.empty()) Engine::OnNodePulse(*this, turn); - } else - { Engine::OnNodeIdlePulse(*this, turn); - } } private: @@ -564,7 +574,7 @@ class PulseNode : public EventStreamNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - if (events_.size() > 0) + if (! events_.empty()) Engine::OnNodePulse(*this, turn); else Engine::OnNodeIdlePulse(*this, turn); diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index f764b8b5..64a1d47f 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -33,11 +33,7 @@ template long long threshold > struct UpdateTimingPolicy : - private ConditionalTimer - < - threshold, - EnableNodeUpdateTimer::value - > + private ConditionalTimer { class ScopedUpdateTimer : public ScopedTimer { @@ -91,6 +87,9 @@ class ReactiveNode : public NodeBase { public: ReactiveNode() = default; + + // Reactive nodes can't be copied + ReactiveNode(const ReactiveNode&) = delete; }; /////////////////////////////////////////////////////////////////////////////////////////////////// From b7b227d16234a2035f25f5076acfa1d6ddd6791e Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 25 May 2014 13:23:18 +0200 Subject: [PATCH 117/266] Added tests for synced Iterate and IterateByRef. --- src/test/OperationsTest.h | 201 +++++++++++++++++++++++++++++++++++++- 1 file changed, 200 insertions(+), 1 deletion(-) diff --git a/src/test/OperationsTest.h b/src/test/OperationsTest.h index f25503aa..cc79aff4 100644 --- a/src/test/OperationsTest.h +++ b/src/test/OperationsTest.h @@ -356,6 +356,203 @@ TYPED_TEST_P(OperationsTest, SyncedTransform1) ASSERT_EQ(obsCount2, 2); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SyncedIterate1 test +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(OperationsTest, SyncedIterate1) +{ + auto in1 = MyDomain::MakeVar(1); + auto in2 = MyDomain::MakeVar(1); + + auto op1 = in1 + in2; + auto op2 = (in1 + in2) * 10; + + auto src1 = MyDomain::MakeEventSource(); + auto src2 = MyDomain::MakeEventSource(); + + auto out1 = Iterate( + src1, + make_tuple(0,0), + With(op1,op2), + [] (Token, const tuple& t, int op1, int op2) { + return make_tuple(get<0>(t) + op1, get<1>(t) + op2); + }); + + auto out2 = Iterate( + src2, + make_tuple(0,0,0), + With(op1,op2), + [] (int e, const tuple& t, int op1, int op2) { + return make_tuple(get<0>(t) + e, get<1>(t) + op1, get<2>(t) + op2); + }); + + int obsCount1 = 0; + int obsCount2 = 0; + + Observe(out1, [&] (const tuple& t) { + ++obsCount1; + + ASSERT_EQ(get<0>(t), 33); + ASSERT_EQ(get<1>(t), 330); + + DetachThisObserver(); + }); + + Observe(out2, [&] (const tuple& t) { + ++obsCount2; + + ASSERT_EQ(get<0>(t), 42); + ASSERT_EQ(get<1>(t), 33); + ASSERT_EQ(get<2>(t), 330); + + DetachThisObserver(); + }); + + in1 <<= 22; + in2 <<= 11; + + src1.Emit(); + src2.Emit(42); + + ASSERT_EQ(obsCount1, 1); + ASSERT_EQ(obsCount2, 1); + + Observe(out1, [&] (const tuple& t) { + ++obsCount1; + + ASSERT_EQ(get<0>(t), 33 + 330); + ASSERT_EQ(get<1>(t), 330 + 3300); + + DetachThisObserver(); + }); + + Observe(out2, [&] (const tuple& t) { + ++obsCount2; + + ASSERT_EQ(get<0>(t), 42 + 420); + ASSERT_EQ(get<1>(t), 33 + 330); + ASSERT_EQ(get<2>(t), 330 + 3300); + + DetachThisObserver(); + }); + + in1 <<= 220; + in2 <<= 110; + + src1.Emit(); + src2.Emit(420); + + ASSERT_EQ(obsCount1, 2); + ASSERT_EQ(obsCount2, 2); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SyncedIterate2 test (by ref) +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(OperationsTest, SyncedIterate2) +{ + auto in1 = MyDomain::MakeVar(1); + auto in2 = MyDomain::MakeVar(1); + + auto op1 = in1 + in2; + auto op2 = (in1 + in2) * 10; + + auto src1 = MyDomain::MakeEventSource(); + auto src2 = MyDomain::MakeEventSource(); + + auto out1 = Iterate( + src1, + vector{}, + With(op1,op2), + [] (Token, vector& v, int op1, int op2) { + v.push_back(op1); + v.push_back(op2); + }); + + auto out2 = Iterate( + src2, + vector{}, + With(op1,op2), + [] (int e, vector& v, int op1, int op2) { + v.push_back(e); + v.push_back(op1); + v.push_back(op2); + }); + + int obsCount1 = 0; + int obsCount2 = 0; + + Observe(out1, [&] (const vector& v) { + ++obsCount1; + + ASSERT_EQ(v.size(), 2); + + ASSERT_EQ(v[0], 33); + ASSERT_EQ(v[1], 330); + + DetachThisObserver(); + }); + + Observe(out2, [&] (const vector& v) { + ++obsCount2; + + ASSERT_EQ(v.size(), 3); + + ASSERT_EQ(v[0], 42); + ASSERT_EQ(v[1], 33); + ASSERT_EQ(v[2], 330); + + DetachThisObserver(); + }); + + in1 <<= 22; + in2 <<= 11; + + src1.Emit(); + src2.Emit(42); + + ASSERT_EQ(obsCount1, 1); + ASSERT_EQ(obsCount2, 1); + + Observe(out1, [&] (const vector& v) { + ++obsCount1; + + ASSERT_EQ(v.size(), 4); + + ASSERT_EQ(v[0], 33); + ASSERT_EQ(v[1], 330); + ASSERT_EQ(v[2], 330); + ASSERT_EQ(v[3], 3300); + + DetachThisObserver(); + }); + + Observe(out2, [&] (const vector& v) { + ++obsCount2; + + ASSERT_EQ(v.size(), 6); + + ASSERT_EQ(v[0], 42); + ASSERT_EQ(v[1], 33); + ASSERT_EQ(v[2], 330); + + ASSERT_EQ(v[3], 420); + ASSERT_EQ(v[4], 330); + ASSERT_EQ(v[5], 3300); + + DetachThisObserver(); + }); + + in1 <<= 220; + in2 <<= 110; + + src1.Emit(); + src2.Emit(420); + + ASSERT_EQ(obsCount1, 2); + ASSERT_EQ(obsCount2, 2); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// REGISTER_TYPED_TEST_CASE_P ( @@ -369,7 +566,9 @@ REGISTER_TYPED_TEST_CASE_P Snapshot1, IterateByRef1, IterateByRef2, - SyncedTransform1 + SyncedTransform1, + SyncedIterate1, + SyncedIterate2 ); } // ~namespace From 77de3d6a2eabcb614b395bfa88e56325af250bf3 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 25 May 2014 15:14:20 +0200 Subject: [PATCH 118/266] Replaced SharedPtrT alias with actual std::shared_ptr. Switching to a different shared ptr impl was not possible anyway due to using make_shared. --- include/react/Event.h | 10 ++--- include/react/Signal.h | 10 ++--- include/react/detail/ReactiveBase.h | 6 +-- include/react/detail/graph/AlgorithmNodes.h | 49 +++++++++++---------- include/react/detail/graph/EventNodes.h | 40 ++++++++--------- include/react/detail/graph/GraphBase.h | 17 +++---- include/react/detail/graph/ObserverNodes.h | 23 +++++----- include/react/detail/graph/ReactorNodes.h | 8 ++-- include/react/detail/graph/SignalNodes.h | 13 +++--- 9 files changed, 86 insertions(+), 90 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index c236a5d1..b8f4e6b6 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -48,7 +48,7 @@ class Events : public REACT_IMPL::EventStreamBase private: using NodeT = REACT_IMPL::EventStreamNode; - using NodePtrT = REACT_IMPL::SharedPtrT; + using NodePtrT = std::shared_ptr; public: using ValueT = E; @@ -115,7 +115,7 @@ class Events : public REACT_IMPL::EventStreamBase>; - using NodePtrT = REACT_IMPL::SharedPtrT; + using NodePtrT = std::shared_ptr; public: using ValueT = E; @@ -181,7 +181,7 @@ class EventSource : public Events { private: using NodeT = REACT_IMPL::EventSourceNode; - using NodePtrT = REACT_IMPL::SharedPtrT; + using NodePtrT = std::shared_ptr; public: EventSource() = default; @@ -233,7 +233,7 @@ class EventSource : public Events> { private: using NodeT = REACT_IMPL::EventSourceNode>; - using NodePtrT = REACT_IMPL::SharedPtrT; + using NodePtrT = std::shared_ptr; public: EventSource() = default; @@ -274,7 +274,7 @@ class TempEvents : public Events { protected: using NodeT = REACT_IMPL::EventOpNode; - using NodePtrT = REACT_IMPL::SharedPtrT; + using NodePtrT = std::shared_ptr; public: TempEvents() = default; diff --git a/include/react/Signal.h b/include/react/Signal.h index 3d669434..1effa861 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -34,7 +34,7 @@ class Signal : public REACT_IMPL::SignalBase private: using NodeT = REACT_IMPL::SignalNode; - using NodePtrT = REACT_IMPL::SharedPtrT; + using NodePtrT = std::shared_ptr; public: using ValueT = S; @@ -83,7 +83,7 @@ class Signal : public REACT_IMPL::SignalBase> private: using NodeT = REACT_IMPL::SignalNode>; - using NodePtrT = REACT_IMPL::SharedPtrT; + using NodePtrT = std::shared_ptr; public: using ValueT = S; @@ -125,7 +125,7 @@ class VarSignal : public Signal { private: using NodeT = REACT_IMPL::VarNode; - using NodePtrT = REACT_IMPL::SharedPtrT; + using NodePtrT = std::shared_ptr; public: VarSignal() = default; @@ -172,7 +172,7 @@ class VarSignal : public Signal> { private: using NodeT = REACT_IMPL::VarNode>; - using NodePtrT = REACT_IMPL::SharedPtrT; + using NodePtrT = std::shared_ptr; public: using ValueT = S; @@ -213,7 +213,7 @@ class TempSignal : public Signal { private: using NodeT = REACT_IMPL::SignalOpNode; - using NodePtrT = REACT_IMPL::SharedPtrT; + using NodePtrT = std::shared_ptr; public: TempSignal() = default; diff --git a/include/react/detail/ReactiveBase.h b/include/react/detail/ReactiveBase.h index 09aaac7a..1a80e729 100644 --- a/include/react/detail/ReactiveBase.h +++ b/include/react/detail/ReactiveBase.h @@ -35,7 +35,7 @@ class ReactiveBase { public: using DomainT = typename TNode::DomainT; - using NodePtrT = SharedPtrT; + using NodePtrT = std::shared_ptr; ReactiveBase() = default; ReactiveBase(const ReactiveBase&) = default; @@ -52,7 +52,7 @@ class ReactiveBase ptr_{ std::move(ptr) } {} - const SharedPtrT& NodePtr() const + const std::shared_ptr& NodePtr() const { return ptr_; } @@ -68,7 +68,7 @@ class ReactiveBase } protected: - SharedPtrT ptr_; + std::shared_ptr ptr_; }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index ec4f72be..d9503de0 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -8,6 +8,7 @@ #include "react/detail/Defs.h" +#include #include #include "EventNodes.h" @@ -71,10 +72,10 @@ class IterateNode : public SignalNode } virtual const char* GetNodeType() const override { return "IterateNode"; } - virtual int DependencyCount() const override { return 1; } + virtual int DependencyCount() const override { return 1; } private: - SharedPtrT> events_; + std::shared_ptr> events_; TFunc func_; }; @@ -93,7 +94,7 @@ class IterateByRefNode : public SignalNode { public: template - IterateByRefNode(T&& init, const SharedPtrT>& events, F&& func) : + IterateByRefNode(T&& init, const std::shared_ptr>& events, F&& func) : SignalNode{ std::forward(init) }, func_{ std::forward(func) }, events_{ events } @@ -132,7 +133,7 @@ class IterateByRefNode : public SignalNode protected: TFunc func_; - SharedPtrT> events_; + std::shared_ptr> events_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -168,7 +169,7 @@ class SyncedIterateNode : public SignalNode apply( DetachFunctor>...>{ *this }, + std::shared_ptr>...>{ *this }, deps_); Engine::OnNodeDestroy(*this); @@ -184,7 +185,7 @@ class SyncedIterateNode : public SignalNode MyFunc{ f } {} - S operator()(const SharedPtrT>& ... args) + S operator()(const std::shared_ptr>& ... args) { return MyFunc(MyEvent, MyValue, args->ValueRef() ...); } @@ -231,9 +232,9 @@ class SyncedIterateNode : public SignalNode virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } private: - using DepHolderT = std::tuple>...>; + using DepHolderT = std::tuple>...>; - SharedPtrT> events_; + std::shared_ptr> events_; DepHolderT deps_; TFunc func_; @@ -272,7 +273,7 @@ class SyncedIterateByRefNode : public SignalNode apply( DetachFunctor>...>{ *this }, + std::shared_ptr>...>{ *this }, deps_); Engine::OnNodeDestroy(*this); @@ -288,7 +289,7 @@ class SyncedIterateByRefNode : public SignalNode MyFunc{ f } {} - void operator()(const SharedPtrT>& ... args) + void operator()(const std::shared_ptr>& ... args) { MyFunc(MyEvent, MyValue, args->ValueRef() ...); } @@ -329,9 +330,9 @@ class SyncedIterateByRefNode : public SignalNode virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } private: - using DepHolderT = std::tuple>...>; + using DepHolderT = std::tuple>...>; - SharedPtrT> events_; + std::shared_ptr> events_; DepHolderT deps_; TFunc func_; @@ -349,7 +350,7 @@ class HoldNode : public SignalNode { public: template - HoldNode(T&& init, const SharedPtrT>& events) : + HoldNode(T&& init, const std::shared_ptr>& events) : SignalNode(std::forward(init)), events_{ events } { @@ -398,7 +399,7 @@ class HoldNode : public SignalNode virtual int DependencyCount() const override { return 1; } private: - const SharedPtrT> events_; + const std::shared_ptr> events_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -413,8 +414,8 @@ template class SnapshotNode : public SignalNode { public: - SnapshotNode(const SharedPtrT>& target, - const SharedPtrT>& trigger) : + SnapshotNode(const std::shared_ptr>& target, + const std::shared_ptr>& trigger) : SignalNode{ target->ValueRef() }, target_{ target }, trigger_{ trigger } @@ -467,8 +468,8 @@ class SnapshotNode : public SignalNode } private: - const SharedPtrT> target_; - const SharedPtrT> trigger_; + const std::shared_ptr> target_; + const std::shared_ptr> trigger_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -482,7 +483,7 @@ template class MonitorNode : public EventStreamNode { public: - MonitorNode(const SharedPtrT>& target) : + MonitorNode(const std::shared_ptr>& target) : EventStreamNode{ }, target_{ target } { @@ -521,7 +522,7 @@ class MonitorNode : public EventStreamNode } private: - const SharedPtrT> target_; + const std::shared_ptr> target_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -536,8 +537,8 @@ template class PulseNode : public EventStreamNode { public: - PulseNode(const SharedPtrT>& target, - const SharedPtrT>& trigger) : + PulseNode(const std::shared_ptr>& target, + const std::shared_ptr>& trigger) : EventStreamNode{ }, target_{ target }, trigger_{ trigger } @@ -581,8 +582,8 @@ class PulseNode : public EventStreamNode } private: - const SharedPtrT> target_; - const SharedPtrT> trigger_; + const std::shared_ptr> target_; + const std::shared_ptr> trigger_; }; /****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index c2cacd19..43b3af3a 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -11,6 +11,7 @@ #include #include #include +#include #include "tbb/spin_mutex.h" @@ -34,11 +35,7 @@ class SignalNode; // Note: Weird design due to empty base class optimization template struct BufferClearAccessPolicy : - private ConditionalCriticalSection - < - tbb::spin_mutex, - EnableParallelUpdating::value - > + private ConditionalCriticalSection { template void AccessBufferForClearing(const F& f) { Access(f); } @@ -83,7 +80,7 @@ class EventStreamNode : }; template -using EventStreamNodePtrT = SharedPtrT>; +using EventStreamNodePtrT = std::shared_ptr>; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventSourceNode @@ -206,7 +203,7 @@ class EventMergeOp : public ReactiveOpBase } template - void collect(const SharedPtrT& depPtr) const + void collect(const std::shared_ptr& depPtr) const { depPtr->SetCurrentTurn(MyTurn); @@ -284,7 +281,8 @@ class EventFilterOp : public ReactiveOpBase } template - static void collectImpl(const TTurn& turn, const TCollector& collector, const SharedPtrT& depPtr) + static void collectImpl(const TTurn& turn, const TCollector& collector, + const std::shared_ptr& depPtr) { depPtr->SetCurrentTurn(turn); @@ -359,7 +357,7 @@ class EventTransformOp : public ReactiveOpBase } template - static void collectImpl(const TTurn& turn, const TCollector& collector, const SharedPtrT& depPtr) + static void collectImpl(const TTurn& turn, const TCollector& collector, const std::shared_ptr& depPtr) { depPtr->SetCurrentTurn(turn); @@ -460,8 +458,8 @@ template class EventFlattenNode : public EventStreamNode { public: - EventFlattenNode(const SharedPtrT>& outer, - const SharedPtrT>& inner) : + EventFlattenNode(const std::shared_ptr>& outer, + const std::shared_ptr>& inner) : EventStreamNode{ }, outer_{ outer }, inner_{ inner } @@ -525,8 +523,8 @@ class EventFlattenNode : public EventStreamNode } private: - SharedPtrT> outer_; - SharedPtrT> inner_; + std::shared_ptr> outer_; + std::shared_ptr> inner_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -562,7 +560,7 @@ class SyncedEventTransformNode : public EventStreamNode apply( DetachFunctor>...>{ *this }, + std::shared_ptr>...>{ *this }, deps_); Engine::OnNodeDestroy(*this); @@ -580,7 +578,7 @@ class SyncedEventTransformNode : public EventStreamNode MyFunc{ f } {} - TOut operator()(const SharedPtrT>& ... args) + TOut operator()(const std::shared_ptr>& ... args) { return MyFunc(MyEvent, args->ValueRef() ...); } @@ -613,9 +611,9 @@ class SyncedEventTransformNode : public EventStreamNode } private: - using DepHolderT = std::tuple>...>; + using DepHolderT = std::tuple>...>; - SharedPtrT> source_; + std::shared_ptr> source_; DepHolderT deps_; TFunc func_; @@ -653,7 +651,7 @@ class SyncedEventFilterNode : public EventStreamNode apply( DetachFunctor>...>{ *this }, + std::shared_ptr>...>{ *this }, deps_); Engine::OnNodeDestroy(*this); @@ -671,7 +669,7 @@ class SyncedEventFilterNode : public EventStreamNode MyFilter{ f } {} - bool operator()(const SharedPtrT>& ... args) + bool operator()(const std::shared_ptr>& ... args) { return MyFilter(MyEvent, args->ValueRef() ...); } @@ -705,9 +703,9 @@ class SyncedEventFilterNode : public EventStreamNode } private: - using DepHolderT = std::tuple>...>; + using DepHolderT = std::tuple>...>; - SharedPtrT> source_; + std::shared_ptr> source_; DepHolderT deps_; TFunc filter_; diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index 64a1d47f..7df5be9a 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -9,6 +9,7 @@ #include "react/detail/Defs.h" #include +#include #include "react/common/Util.h" #include "react/common/Timing.h" @@ -18,12 +19,6 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -template -using SharedPtrT = std::shared_ptr; - -template -using WeakPtrT = std::weak_ptr; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// UpdateTimingPolicy /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -65,14 +60,14 @@ class NodeBase : virtual bool IsDynamicNode() const override { return false; } virtual bool IsHeavyweight() const override { return false; } - SharedPtrT GetSharedPtr() const + std::shared_ptr GetSharedPtr() const { return shared_from_this(); } }; template -using NodeBasePtrT = SharedPtrT>; +using NodeBasePtrT = std::shared_ptr>; /////////////////////////////////////////////////////////////////////////////////////////////////// /// ReactiveNode @@ -112,7 +107,7 @@ struct AttachFunctor } template - void attach(const SharedPtrT& depPtr) const + void attach(const std::shared_ptr& depPtr) const { D::Engine::OnNodeAttach(MyNode, *depPtr); } @@ -137,7 +132,7 @@ struct DetachFunctor } template - void detach(const SharedPtrT& depPtr) const + void detach(const std::shared_ptr& depPtr) const { D::Engine::OnNodeDetach(MyNode, *depPtr); } @@ -196,7 +191,7 @@ class ReactiveOpBase struct CountHelper { static const int value = T::dependency_count; }; template - struct CountHelper> { static const int value = 1; }; + struct CountHelper> { static const int value = 1; }; template struct DepCounter; diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index fde6106f..cfa788c2 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -51,7 +51,7 @@ class SignalObserverNode : public ObserverNode { public: template - SignalObserverNode(const SharedPtrT>& subject, F&& func) : + SignalObserverNode(const std::shared_ptr>& subject, F&& func) : ObserverNode{ }, subject_{ subject }, func_{ std::forward(func) } @@ -105,8 +105,8 @@ class SignalObserverNode : public ObserverNode } } - WeakPtrT> subject_; - TFunc func_; + std::weak_ptr> subject_; + TFunc func_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -122,7 +122,7 @@ class EventObserverNode : public ObserverNode { public: template - EventObserverNode(const SharedPtrT>& subject, F&& func) : + EventObserverNode(const std::shared_ptr>& subject, F&& func) : ObserverNode{ }, subject_{ subject }, func_{ std::forward(func) } @@ -168,8 +168,9 @@ class EventObserverNode : public ObserverNode } private: - WeakPtrT> subject_; - TFunc func_; + std::weak_ptr> subject_; + + TFunc func_; virtual void detachObserver() { @@ -196,7 +197,7 @@ template class SyncedObserverNode : public ObserverNode { public: - // NOTE: After upgrading to VS2013 Udpate2, using SharedPtrT here crashes the compiler + // NOTE: After upgrading to VS2013 Udpate2, using std::shared_ptr here crashes the compiler template SyncedObserverNode(const std::shared_ptr>& subject, F&& func, const std::shared_ptr>& ... deps) : @@ -229,7 +230,7 @@ class SyncedObserverNode : public ObserverNode MyFunc{ f } {} - void operator()(const SharedPtrT>& ... args) + void operator()(const std::shared_ptr>& ... args) { MyFunc(MyEvent, args->ValueRef() ...); } @@ -268,9 +269,9 @@ class SyncedObserverNode : public ObserverNode } private: - using DepHolderT = std::tuple>...>; + using DepHolderT = std::tuple>...>; - WeakPtrT> subject_; + std::weak_ptr> subject_; DepHolderT deps_; TFunc func_; @@ -285,7 +286,7 @@ class SyncedObserverNode : public ObserverNode apply( DetachFunctor>...>{ *this }, + std::shared_ptr>...>{ *this }, deps_); subject_.reset(); diff --git a/include/react/detail/graph/ReactorNodes.h b/include/react/detail/graph/ReactorNodes.h index c54d6e8d..9549c1b8 100644 --- a/include/react/detail/graph/ReactorNodes.h +++ b/include/react/detail/graph/ReactorNodes.h @@ -35,7 +35,7 @@ class ReactorNode : public ReactiveNode { public: - using NodeBasePtrT = SharedPtrT>; + using NodeBasePtrT = std::shared_ptr>; using CoroutineT = boost::coroutines::coroutine; using LoopT = typename CoroutineT::pull_type; @@ -115,7 +115,7 @@ class ReactorNode : } template - E& Await(const SharedPtrT>& events) + E& Await(const std::shared_ptr>& events) { // First attach to target event node (*curOutPtr_)(&std::static_pointer_cast>(events)); @@ -132,7 +132,7 @@ class ReactorNode : } template - void RepeatUntil(const SharedPtrT>& events, F func) + void RepeatUntil(const std::shared_ptr>& events, F func) { // First attach to target event node if (turnPtr_ != nullptr) @@ -189,7 +189,7 @@ class ReactorNode : private: template - bool checkEvent(const SharedPtrT>& events) + bool checkEvent(const std::shared_ptr>& events) { if (turnPtr_ == nullptr) return false; diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index a0ff22d8..2fcc83d0 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -8,6 +8,7 @@ #include "react/detail/Defs.h" +#include #include #include "GraphBase.h" @@ -49,7 +50,7 @@ class SignalNode : public ReactiveNode }; template -using SignalNodePtrT = SharedPtrT>; +using SignalNodePtrT = std::shared_ptr>; /////////////////////////////////////////////////////////////////////////////////////////////////// /// VarNode @@ -160,7 +161,7 @@ class FunctionOp : public ReactiveOpBase } template - static auto eval(const SharedPtrT& depPtr) -> decltype(depPtr->ValueRef()) + static auto eval(const std::shared_ptr& depPtr) -> decltype(depPtr->ValueRef()) { return depPtr->ValueRef(); } @@ -268,8 +269,8 @@ template class FlattenNode : public SignalNode { public: - FlattenNode(const SharedPtrT>& outer, - const SharedPtrT>& inner) : + FlattenNode(const std::shared_ptr>& outer, + const std::shared_ptr>& inner) : SignalNode{ inner->ValueRef() }, outer_{ outer }, inner_{ inner } @@ -327,8 +328,8 @@ class FlattenNode : public SignalNode } private: - SharedPtrT> outer_; - SharedPtrT> inner_; + std::shared_ptr> outer_; + std::shared_ptr> inner_; }; /****************************************/ REACT_IMPL_END /***************************************/ From ba8fe52169f167716561e40eaa369049be1569f1 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 26 May 2014 01:31:02 +0200 Subject: [PATCH 119/266] Added Signal.Modify(func). Allows to safely modify a signal value by reference. Use case: Avoid re-allocation of value types like vectors. Also removed pointless forwarding in InputManager (reactives are not moved anywhere). --- include/react/Signal.h | 6 +++ include/react/detail/ReactiveInput.h | 66 +++++++++++++++++++++--- include/react/detail/SignalBase.h | 7 +++ include/react/detail/graph/SignalNodes.h | 55 ++++++++++++++++++-- 4 files changed, 122 insertions(+), 12 deletions(-) diff --git a/include/react/Signal.h b/include/react/Signal.h index 1effa861..995877b1 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -160,6 +160,12 @@ class VarSignal : public Signal BaseT::setValue(std::move(newValue)); return *this; } + + template + void Modify(const F& func) const + { + BaseT::modifyValue(func); + } }; // Specialize for references diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index 26206eb0..16226104 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -199,19 +199,36 @@ class InputManager } template - static void AddInput(R&& r, V&& v) + static void AddInput(R& r, V&& v) { if (ContinuationHolder::Get() != nullptr) { - addContinuationInput(std::forward(r), std::forward(v)); + addContinuationInput(r, std::forward(v)); } else if (transactionState_.Active) { - addTransactionInput(std::forward(r), std::forward(v)); + addTransactionInput(r, std::forward(v)); } else { - addSimpleInput(std::forward(r), std::forward(v)); + addSimpleInput(r, std::forward(v)); + } + } + + template + static void ModifyInput(R& r, const F& func) + { + if (ContinuationHolder::Get() != nullptr) + { + modifyContinuationInput(r, func); + } + else if (transactionState_.Active) + { + modifyTransactionInput(r, func); + } + else + { + modifySimpleInput(r, func); } } @@ -243,7 +260,7 @@ class InputManager // Create a turn with a single input template - static void addSimpleInput(R&& r, V&& v) + static void addSimpleInput(R& r, V&& v) { auto turn = makeTurn(0); @@ -259,17 +276,43 @@ class InputManager postProcessTurn(turn); } + template + static void modifySimpleInput(R& r, const F& func) + { + auto turn = makeTurn(0); + + Engine::OnTurnAdmissionStart(turn); + r.ModifyInput(func); + Engine::OnTurnAdmissionEnd(turn); + + // Return value, will always be true + r.ApplyInput(&turn); + + Engine::OnTurnPropagate(turn); + + Engine::OnTurnEnd(turn); + + postProcessTurn(turn); + } + // This input is part of an active transaction template - static void addTransactionInput(R&& r, V&& v) + static void addTransactionInput(R& r, V&& v) { r.AddInput(std::forward(v)); transactionState_.Inputs.push_back(&r); } + template + static void modifyTransactionInput(R& r, const F& func) + { + r.ModifyInput(func); + transactionState_.Inputs.push_back(&r); + } + // Input happened during a turn - buffer in continuation template - static void addContinuationInput(R&& r, V&& v) + static void addContinuationInput(R& r, const V& v) { // Copy v ContinuationHolder::Get()->Add( @@ -277,6 +320,15 @@ class InputManager ); } + template + static void modifyContinuationInput(R& r, const F& func) + { + // Copy func + ContinuationHolder::Get()->Add( + [&r,func] { modifyTransactionInput(r, func); } + ); + } + static void postProcessTurn(TurnT& turn) { turn.detachObservers(); diff --git a/include/react/detail/SignalBase.h b/include/react/detail/SignalBase.h index 93a25a91..8bbfc5d1 100644 --- a/include/react/detail/SignalBase.h +++ b/include/react/detail/SignalBase.h @@ -48,6 +48,13 @@ class SignalBase : public ReactiveBase> *reinterpret_cast*>(ptr_.get()), std::forward(newValue)); } + + template + void modifyValue(const F& func) const + { + InputManager::ModifyInput( + *reinterpret_cast*>(ptr_.get()), func); + } }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index 2fcc83d0..4f1d6ca7 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -91,19 +91,62 @@ class VarNode : void AddInput(V&& newValue) { newValue_ = std::forward(newValue); + + isInputAdded_ = true; + + // isInputAdded_ takes precedences over isInputModified_ + // the only difference between the two is that isInputModified_ doesn't/can't compare + isInputModified_ = false; + } + + // This is signal-specific + template + void ModifyInput(F& func) + { + // There hasn't been any Set(...) input yet, modify. + if (! isInputAdded_) + { + func(value_); + + isInputModified_ = true; + } + // There's a newValue, modify newValue instead. + // The modified newValue will handled like before, i.e. it'll be compared to value_ + // in ApplyInput + else + { + func(newValue_); + } } virtual bool ApplyInput(void* turnPtr) override { - if (! impl::Equals(value_, newValue_)) + using TurnT = typename D::Engine::TurnT; + TurnT& turn = *reinterpret_cast(turnPtr); + + if (isInputAdded_) { - using TurnT = typename D::Engine::TurnT; - TurnT& turn = *reinterpret_cast(turnPtr); + isInputAdded_ = false; + + if (! Equals(value_, newValue_)) + { + value_ = std::move(newValue_); + Engine::OnTurnInputChange(*this, turn); + return true; + } + else + { + return false; + } + } + else if (isInputModified_) + { + isInputModified_ = false; - value_ = std::move(newValue_); Engine::OnTurnInputChange(*this, turn); return true; } + else { return false; @@ -111,7 +154,9 @@ class VarNode : } private: - S newValue_; + S newValue_; + bool isInputAdded_ = false; + bool isInputModified_ = false; }; /////////////////////////////////////////////////////////////////////////////////////////////////// From 0dcb2eb1cfb468ad833a9e80378bb4cf38192ab8 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 26 May 2014 01:31:27 +0200 Subject: [PATCH 120/266] Added tests for Signal.Modify --- src/test/SignalTest.h | 147 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 1 deletion(-) diff --git a/src/test/SignalTest.h b/src/test/SignalTest.h index 1015a8bb..36f52a75 100644 --- a/src/test/SignalTest.h +++ b/src/test/SignalTest.h @@ -9,6 +9,7 @@ #include "gtest/gtest.h" #include +#include #include "react/Domain.h" #include "react/Signal.h" @@ -517,6 +518,148 @@ TYPED_TEST_P(SignalTest, Member1) outer <<= 30; } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Modify1 test +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(SignalTest, Modify1) +{ + using std::vector; + + auto v = MyDomain::MakeVar(vector{}); + + int obsCount = 0; + + Observe(v, [&] (const vector& v) { + ASSERT_EQ(v[0], 30); + ASSERT_EQ(v[1], 50); + ASSERT_EQ(v[2], 70); + + obsCount++; + }); + + v.Modify([] (vector& v) { + v.push_back(30); + v.push_back(50); + v.push_back(70); + }); + + ASSERT_EQ(obsCount, 1); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Modify2 test +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(SignalTest, Modify2) +{ + using std::vector; + + auto v = MyDomain::MakeVar(vector{}); + + int obsCount = 0; + + Observe(v, [&] (const vector& v) { + ASSERT_EQ(v[0], 30); + ASSERT_EQ(v[1], 50); + ASSERT_EQ(v[2], 70); + + obsCount++; + }); + + MyDomain::DoTransaction([&] { + v.Modify([] (vector& v) { + v.push_back(30); + }); + + v.Modify([] (vector& v) { + v.push_back(50); + }); + + v.Modify([] (vector& v) { + v.push_back(70); + }); + }); + + + ASSERT_EQ(obsCount, 1); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Modify3 test +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(SignalTest, Modify3) +{ + using std::vector; + + auto vect = MyDomain::MakeVar(vector{}); + + int obsCount = 0; + + // Terrible, but lets test it + Observe(vect, [&] (const vector& v) + { + if (obsCount == 0) + { + ASSERT_EQ(v[0], 30); + + vect.Modify([] (vector& v) { + v.push_back(50); + }); + } + else if (obsCount == 1) + { + ASSERT_EQ(v[1], 50); + + vect.Modify([] (vector& v) { + v.push_back(70); + }); + } + else + { + ASSERT_EQ(v[2], 70); + } + + obsCount++; + }); + + vect.Modify([] (vector& v) { + v.push_back(30); + }); + + ASSERT_EQ(obsCount, 3); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Modify4 test +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(SignalTest, Modify4) +{ + using std::vector; + + auto vect = MyDomain::MakeVar(vector{}); + + int obsCount = 0; + + Observe(vect, [&] (const vector& v) { + ASSERT_EQ(v[0], 30); + ASSERT_EQ(v[1], 50); + ASSERT_EQ(v[2], 70); + + obsCount++; + }); + + // Also terrible + MyDomain::DoTransaction([&] { + + vect.Set(vector{ 30, 50 }); + + vect.Modify([] (vector& v) { + v.push_back(70); + }); + }); + + ASSERT_EQ(obsCount, 1); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// REGISTER_TYPED_TEST_CASE_P ( @@ -525,7 +668,9 @@ REGISTER_TYPED_TEST_CASE_P Signals1, Signals2, Signals3, Signals4, FunctionBind1, FunctionBind2, Flatten1, Flatten2, Flatten3, Flatten4, - Member1 + Member1, + Modify1, Modify2, Modify3, Modify4 + ); } // ~namespace From 5b96e23354ac7144239438f917db8a94b74ea316 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 28 May 2014 12:52:30 +0200 Subject: [PATCH 121/266] Fixed typo (why did this even compile...). --- include/react/detail/graph/GraphBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index 7df5be9a..42a94e69 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -28,7 +28,7 @@ template long long threshold > struct UpdateTimingPolicy : - private ConditionalTimer + private ConditionalTimer { class ScopedUpdateTimer : public ScopedTimer { From 61351b6178eebc84f70e8ba212d77e13851942ab Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 29 May 2014 23:24:45 +0200 Subject: [PATCH 122/266] Lots of refactoring to support gcc 4.8.2 and clang 3.4. sandbox compiles so far, tests and benchmarks still need more work. --- include/react/Algorithm.h | 27 +- include/react/Domain.h | 46 +- include/react/Event.h | 915 ++++++++++---------- include/react/Observer.h | 21 +- include/react/ReactiveObject.h | 40 +- include/react/Reactor.h | 7 +- include/react/Signal.h | 749 ++++++++-------- include/react/TypeTraits.h | 7 +- include/react/common/Concurrency.h | 5 + include/react/common/Containers.h | 37 +- include/react/common/SourceIdSet.h | 7 +- include/react/common/Timing.h | 73 +- include/react/common/TopoQueue.h | 29 +- include/react/common/Types.h | 7 +- include/react/common/Util.h | 17 +- include/react/detail/Defs.h | 20 +- include/react/detail/EngineBase.h | 15 +- include/react/detail/EventBase.h | 9 +- include/react/detail/EventFwd.h | 41 + include/react/detail/IReactiveEngine.h | 5 + include/react/detail/IReactiveNode.h | 5 + include/react/detail/ObserverBase.h | 57 +- include/react/detail/Options.h | 7 +- include/react/detail/ReactiveBase.h | 18 +- include/react/detail/ReactiveInput.h | 18 +- include/react/detail/SignalBase.h | 13 +- include/react/detail/SignalFwd.h | 53 ++ include/react/detail/graph/AlgorithmNodes.h | 125 +-- include/react/detail/graph/EventNodes.h | 162 ++-- include/react/detail/graph/GraphBase.h | 74 +- include/react/detail/graph/ObserverNodes.h | 37 +- include/react/detail/graph/ReactorNodes.h | 7 +- include/react/detail/graph/SignalNodes.h | 68 +- include/react/engine/PulseCountEngine.h | 15 +- include/react/engine/SubtreeEngine.h | 21 +- include/react/engine/ToposortEngine.h | 41 +- include/react/logging/EventLog.h | 5 + include/react/logging/EventRecords.h | 5 + include/react/logging/Logging.h | 5 + project/msvc/CppReact.vcxproj | 2 + project/msvc/CppReact.vcxproj.filters | 6 + src/benchmark/BenchmarkLifeSim.h | 2 +- src/engine/PulsecountEngine.cpp | 6 +- src/engine/SubtreeEngine.cpp | 4 +- src/engine/ToposortEngine.cpp | 8 +- src/sandbox/Main.cpp | 7 +- 46 files changed, 1629 insertions(+), 1219 deletions(-) create mode 100644 include/react/detail/EventFwd.h create mode 100644 include/react/detail/SignalFwd.h diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index 4620552b..ce49e019 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_ALGORITHM_H_INCLUDED +#define REACT_ALGORITHM_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -45,7 +48,7 @@ template typename E, typename V, typename FIn, - typename S = std::decay::type + typename S = typename std::decay::type > auto Iterate(const Events& events, V&& init, FIn&& func) -> Signal @@ -53,9 +56,10 @@ auto Iterate(const Events& events, V&& init, FIn&& func) using REACT_IMPL::IterateNode; using REACT_IMPL::IterateByRefNode; - using F = std::decay::type; - using TNode = std::conditional< - std::is_same::type>::value, + using F = typename std::decay::type; + using TNode = typename std::conditional< + std::is_same::type>::value, IterateByRefNode, IterateNode >::type; @@ -75,7 +79,7 @@ template typename V, typename FIn, typename ... TDepValues, - typename S = std::decay::type + typename S = typename std::decay::type > auto Iterate(const Events& events, V&& init, const SignalPack& depPack, FIn&& func) @@ -84,9 +88,10 @@ auto Iterate(const Events& events, V&& init, using REACT_IMPL::SyncedIterateNode; using REACT_IMPL::SyncedIterateByRefNode; - using F = std::decay::type; - using TNode = std::conditional< - std::is_same::type>::value, + using F = typename std::decay::type; + using TNode = typename std::conditional< + std::is_same::type>::value, SyncedIterateByRefNode, SyncedIterateNode >::type; @@ -125,7 +130,7 @@ template < typename D, typename V, - typename T = std::decay::type + typename T = typename std::decay::type > auto Hold(const Events& events, V&& init) -> Signal @@ -214,7 +219,7 @@ template < typename D, typename V, - typename S = std::decay::type + typename S = typename std::decay::type > auto OnChangedTo(const Signal& target, V&& value) -> Events @@ -225,3 +230,5 @@ auto OnChangedTo(const Signal& target, V&& value) } /******************************************/ REACT_END /******************************************/ + +#endif // REACT_ALGORITHM_H_INCLUDED \ No newline at end of file diff --git a/include/react/Domain.h b/include/react/Domain.h index 0c012835..1b1d408a 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -4,11 +4,21 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DOMAIN_H_INCLUDED +#define REACT_DOMAIN_H_INCLUDED + #pragma once #include "react/detail/Defs.h" +#include +#include + #include "react/TypeTraits.h" + +#include "react/detail/EventFwd.h" +#include "react/detail/SignalFwd.h" + #include "react/detail/ReactiveInput.h" #include "react/detail/Options.h" @@ -34,26 +44,6 @@ class Observer; template class ScopedObserver; -template -class Signal; - -template -class VarSignal; - -template -class TempSignal; - -template -class Events; - -template -class EventSource; - -template -class TempEvents; - -enum class Token; - using REACT_IMPL::TurnFlagsT; #ifdef REACT_ENABLE_LOGGING @@ -113,8 +103,8 @@ class DomainBase template < typename V, - typename S = std::decay::type, - class = std::enable_if< + typename S = typename std::decay::type, + class = typename std::enable_if< !IsSignal::value>::type > static auto MakeVar(V&& value) @@ -129,9 +119,9 @@ class DomainBase template < typename V, - typename S = std::decay::type, - typename TInner = S::ValueT, - class = std::enable_if< + typename S = typename std::decay::type, + typename TInner = typename S::ValueT, + class = typename std::enable_if< IsSignal::value>::type > static auto MakeVar(V&& value) @@ -212,7 +202,7 @@ class DomainInitializer D::Log(); #endif //REACT_ENABLE_LOGGING - typename D::Engine::Engine(); + D::Engine::Engine(); } }; @@ -223,4 +213,6 @@ class DomainInitializer /////////////////////////////////////////////////////////////////////////////////////////////////// #define REACTIVE_DOMAIN(name, ...) \ struct name : public REACT::DomainBase> {}; \ - REACT_IMPL::DomainInitializer< name > name ## _initializer_; \ No newline at end of file + REACT_IMPL::DomainInitializer< name > name ## _initializer_; + +#endif // REACT_DOMAIN_H_INCLUDED \ No newline at end of file diff --git a/include/react/Event.h b/include/react/Event.h index b8f4e6b6..5581dde9 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_EVENT_H_INCLUDED +#define REACT_EVENT_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -12,6 +15,8 @@ #include #include +#include "react/detail/EventFwd.h" + #include "react/Observer.h" #include "react/TypeTraits.h" #include "react/common/Util.h" @@ -29,130 +34,470 @@ template class SignalPack; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Token +/// MakeEventSource /////////////////////////////////////////////////////////////////////////////////////////////////// -enum class Token { value }; +template +auto MakeEventSource() + -> EventSource +{ + using REACT_IMPL::EventSourceNode; + + return EventSource( + std::make_shared>()); +} + +template +auto MakeEventSource() + -> EventSource +{ + using REACT_IMPL::EventSourceNode; + + return EventSource( + std::make_shared>()); +} /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Events +/// Merge /////////////////////////////////////////////////////////////////////////////////////////////////// +// Note: Default template arguments are in forward declaration template < typename D, - typename E = Token + typename TArg1, + typename ... TArgs, + typename E = TArg1, + typename TOp = REACT_IMPL::EventMergeOp, + REACT_IMPL::EventStreamNodePtrT ...> > -class Events : public REACT_IMPL::EventStreamBase +auto Merge(const Events& arg1, const Events& ... args) + -> TempEvents { -protected: - using BaseT = REACT_IMPL::EventStreamBase; - -private: - using NodeT = REACT_IMPL::EventStreamNode; - using NodePtrT = std::shared_ptr; + using REACT_IMPL::EventOpNode; -public: - using ValueT = E; + static_assert(sizeof...(TArgs) > 0, + "react::Merge requires at least 2 arguments."); - Events() = default; - Events(const Events&) = default; + return TempEvents( + std::make_shared>( + arg1.NodePtr(), args.NodePtr() ...)); +} - Events(Events&& other) : - EventStreamBase{ std::move(other) } - {} +template +< + typename TLeftEvents, + typename TRightEvents, + typename D = typename TLeftEvents::DomainT, + typename TLeftVal = typename TLeftEvents::ValueT, + typename TRightVal = typename TRightEvents::ValueT, + typename E = TLeftVal, + typename TOp = REACT_IMPL::EventMergeOp, + REACT_IMPL::EventStreamNodePtrT>, + class = typename std::enable_if< + IsEvent::value>::type, + class = typename std::enable_if< + IsEvent::value>::type +> +auto operator|(const TLeftEvents& lhs, const TRightEvents& rhs) + -> TempEvents +{ + using REACT_IMPL::EventOpNode; - explicit Events(NodePtrT&& nodePtr) : - EventStreamBase{ std::move(nodePtr) } - {} + return TempEvents( + std::make_shared>( + lhs.NodePtr(), rhs.NodePtr())); +} - bool Equals(const Events& other) const - { - return BaseT::Equals(other); - } +template +< + typename D, + typename TLeftVal, + typename TLeftOp, + typename TRightVal, + typename TRightOp, + typename E = TLeftVal, + typename TOp = REACT_IMPL::EventMergeOp +> +auto operator|(TempEvents&& lhs, TempEvents&& rhs) + -> TempEvents +{ + using REACT_IMPL::EventOpNode; - bool IsValid() const - { - return BaseT::IsValid(); - } + return TempEvents( + std::make_shared>( + lhs.StealOp(), rhs.StealOp())); +} - auto Tokenize() const - -> decltype(REACT::Tokenize(std::declval())) - { - return REACT::Tokenize(*this); - } +template +< + typename D, + typename TLeftVal, + typename TLeftOp, + typename TRightEvents, + typename TRightVal = typename TRightEvents::ValueT, + typename E = TLeftVal, + typename TOp = REACT_IMPL::EventMergeOp>, + class = typename std::enable_if< + IsEvent::value>::type +> +auto operator|(TempEvents&& lhs, const TRightEvents& rhs) + -> TempEvents +{ + using REACT_IMPL::EventOpNode; - template - auto Merge(TArgs&& ... args) const - -> decltype(REACT::Merge(std::declval(), std::forward(args) ...)) - { - return REACT::Merge(*this, std::forward(args) ...); - } + return TempEvents( + std::make_shared>( + lhs.StealOp(), rhs.NodePtr())); +} - template - auto Filter(F&& f) const - -> decltype(REACT::Filter(std::declval(), std::forward(f))) - { - return REACT::Filter(*this, std::forward(f)); - } +template +< + typename TLeftEvents, + typename D, + typename TRightVal, + typename TRightOp, + typename TLeftVal = typename TLeftEvents::ValueT, + typename E = TLeftVal, + typename TOp = REACT_IMPL::EventMergeOp, + TRightOp>, + class = typename std::enable_if< + IsEvent::value>::type +> +auto operator|(const TLeftEvents& lhs, TempEvents&& rhs) + -> TempEvents +{ + using REACT_IMPL::EventOpNode; - template - auto Transform(F&& f) const - -> decltype(REACT::Transform(std::declval(), std::forward(f))) - { - return REACT::Transform(*this, std::forward(f)); - } -}; + return TempEvents( + std::make_shared>( + lhs.NodePtr(), rhs.StealOp())); +} -// Specialize for references +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Filter +/////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, - typename E + typename E, + typename FIn, + typename F = typename std::decay::type, + typename TOp = REACT_IMPL::EventFilterOp> > -class Events : public REACT_IMPL::EventStreamBase> +auto Filter(const Events& src, FIn&& filter) + -> TempEvents { -protected: - using BaseT = REACT_IMPL::EventStreamBase>; + using REACT_IMPL::EventOpNode; -private: - using NodeT = REACT_IMPL::EventStreamNode>; - using NodePtrT = std::shared_ptr; + return TempEvents( + std::make_shared>( + std::forward(filter), src.NodePtr())); +} -public: - using ValueT = E; +template +< + typename D, + typename E, + typename TOpIn, + typename FIn, + typename F = typename std::decay::type, + typename TOpOut = REACT_IMPL::EventFilterOp +> +auto Filter(TempEvents&& src, FIn&& filter) + -> TempEvents +{ + using REACT_IMPL::EventOpNode; - Events() = default; - Events(const Events&) = default; + return TempEvents( + std::make_shared>( + std::forward(filter), src.StealOp())); +} - Events(Events&& other) : - EventStreamBase{ std::move(other) } - {} +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Filter - Synced +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename E, + typename FIn, + typename ... TDepValues +> +auto Filter(const Events& source, const SignalPack& depPack, FIn&& func) + -> Events +{ + using REACT_IMPL::SyncedEventFilterNode; - explicit Events(NodePtrT&& nodePtr) : - EventStreamBase{ std::move(nodePtr) } - {} + using F = typename std::decay::type; - bool Equals(const Events& other) const + struct NodeBuilder_ { - return BaseT::Equals(other); - } + NodeBuilder_(const Events& source, FIn&& func) : + MySource( source ), + MyFunc( std::forward(func) ) + {} - bool IsValid() const - { - return BaseT::IsValid(); - } + auto operator()(const Signal& ... deps) + -> Events + { + return Events( + std::make_shared>( + MySource.NodePtr(), std::forward(MyFunc), deps.NodePtr() ...)); + } - auto Tokenize() const - -> decltype(REACT::Tokenize(std::declval())) - { - return REACT::Tokenize(*this); - } + const Events& MySource; + FIn MyFunc; + }; - template - auto Merge(TArgs&& ... args) - -> decltype(REACT::Merge(std::declval(), std::forward(args) ...)) - { - return REACT::Merge(*this, std::forward(args) ...); - } + return REACT_IMPL::apply( + NodeBuilder_( source, std::forward(func) ), + depPack.Data); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Transform +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename TIn, + typename FIn, + typename F = typename std::decay::type, + typename TOut = typename std::result_of::type, + typename TOp = REACT_IMPL::EventTransformOp> +> +auto Transform(const Events& src, FIn&& func) + -> TempEvents +{ + using REACT_IMPL::EventOpNode; + + return TempEvents( + std::make_shared>( + std::forward(func), src.NodePtr())); +} + +template +< + typename D, + typename TIn, + typename TOpIn, + typename FIn, + typename F = typename std::decay::type, + typename TOut = typename std::result_of::type, + typename TOpOut = REACT_IMPL::EventTransformOp +> +auto Transform(TempEvents&& src, FIn&& func) + -> TempEvents +{ + using REACT_IMPL::EventOpNode; + + return TempEvents( + std::make_shared>( + std::forward(func), src.StealOp())); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Transform - Synced +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename TIn, + typename FIn, + typename ... TDepValues, + typename TOut = typename std::result_of::type +> +auto Transform(const Events& source, const SignalPack& depPack, FIn&& func) + -> Events +{ + using REACT_IMPL::SyncedEventTransformNode; + + using F = typename std::decay::type; + + struct NodeBuilder_ + { + NodeBuilder_(const Events& source, FIn&& func) : + MySource( source ), + MyFunc( std::forward(func) ) + {} + + auto operator()(const Signal& ... deps) + -> Events + { + return Events( + std::make_shared>( + MySource.NodePtr(), std::forward(MyFunc), deps.NodePtr() ...)); + } + + const Events& MySource; + FIn MyFunc; + }; + + return REACT_IMPL::apply( + NodeBuilder_( source, std::forward(func) ), + depPack.Data); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Flatten +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename TInnerValue +> +auto Flatten(const Signal>& node) + -> Events +{ + return Events( + std::make_shared, TInnerValue>>( + node.NodePtr(), node().NodePtr())); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Token +/////////////////////////////////////////////////////////////////////////////////////////////////// +enum class Token { value }; + +struct Tokenizer +{ + template + Token operator()(const T&) const { return Token::value; } +}; + +template +auto Tokenize(TEvents&& source) + -> decltype(Transform(source, Tokenizer{})) +{ + return Transform(source, Tokenizer{}); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Events +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename E = Token +> +class Events : public REACT_IMPL::EventStreamBase +{ +protected: + using BaseT = REACT_IMPL::EventStreamBase; + +private: + using NodeT = REACT_IMPL::EventStreamNode; + using NodePtrT = std::shared_ptr; + +public: + using ValueT = E; + + Events() = default; + Events(const Events&) = default; + + Events(Events&& other) : + Events::EventStreamBase( std::move(other) ) + {} + + explicit Events(NodePtrT&& nodePtr) : + Events::EventStreamBase( std::move(nodePtr) ) + {} + + bool Equals(const Events& other) const + { + return Events::BaseT::Equals(other); + } + + bool IsValid() const + { + return Events::BaseT::IsValid(); + } + + auto Tokenize() const + -> decltype(REACT::Tokenize(std::declval())) + { + return REACT::Tokenize(*this); + } + + template + auto Merge(TArgs&& ... args) const + -> decltype(REACT::Merge(std::declval(), std::forward(args) ...)) + { + return REACT::Merge(*this, std::forward(args) ...); + } + + template + auto Filter(F&& f) const + -> decltype(REACT::Filter(std::declval(), std::forward(f))) + { + return REACT::Filter(*this, std::forward(f)); + } + + template + auto Transform(F&& f) const + -> decltype(REACT::Transform(std::declval(), std::forward(f))) + { + return REACT::Transform(*this, std::forward(f)); + } +}; + +// Specialize for references +template +< + typename D, + typename E +> +class Events : public REACT_IMPL::EventStreamBase> +{ +protected: + using BaseT = REACT_IMPL::EventStreamBase>; + +private: + using NodeT = REACT_IMPL::EventStreamNode>; + using NodePtrT = std::shared_ptr; + +public: + using ValueT = E; + + Events() = default; + Events(const Events&) = default; + + Events(Events&& other) : + Events::EventStreamBase( std::move(other) ) + {} + + explicit Events(NodePtrT&& nodePtr) : + Events::EventStreamBase( std::move(nodePtr) ) + {} + + bool Equals(const Events& other) const + { + return Events::BaseT::Equals(other); + } + + bool IsValid() const + { + return Events::BaseT::IsValid(); + } + + auto Tokenize() const + -> decltype(REACT::Tokenize(std::declval())) + { + return REACT::Tokenize(*this); + } + + template + auto Merge(TArgs&& ... args) + -> decltype(REACT::Merge(std::declval(), std::forward(args) ...)) + { + return REACT::Merge(*this, std::forward(args) ...); + } template auto Filter(F&& f) const @@ -188,37 +533,43 @@ class EventSource : public Events EventSource(const EventSource&) = default; EventSource(EventSource&& other) : - Events{ std::move(other) } + EventSource::Events( std::move(other) ) {} explicit EventSource(NodePtrT&& nodePtr) : - Events{ std::move(nodePtr) } + EventSource::Events( std::move(nodePtr) ) {} // Explicit emit - void Emit(const E& e) const { BaseT::emit(e); } - void Emit(E&& e) const { BaseT::emit(std::move(e)); } + void Emit(const E& e) const { EventSource::BaseT::emit(e); } + void Emit(E&& e) const { EventSource::BaseT::emit(std::move(e)); } - template ::value>::type> - void Emit() const { BaseT::emit(Token::value); } + void Emit() const + { + static_assert(std::is_same::value, "Can't emit on non token stream."); + EventSource::BaseT::emit(Token::value); + } // Function object style - void operator()(const E& e) const { BaseT::emit(e); } - void operator()(E&& e) const { BaseT::emit(std::move(e)); } + void operator()(const E& e) const { EventSource::BaseT::emit(e); } + void operator()(E&& e) const { EventSource::BaseT::emit(std::move(e)); } - template ::value>::type> - void operator()() const { BaseT::emit(Token::value); } + void operator()() const + { + static_assert(std::is_same::value, "Can't emit on non token stream."); + EventSource::BaseT::emit(Token::value); + } // Stream style const EventSource& operator<<(const E& e) const { - BaseT::emit(e); + EventSource::BaseT::emit(e); return *this; } const EventSource& operator<<(E&& e) const { - BaseT::emit(std::move(e)); + EventSource::BaseT::emit(std::move(e)); return *this; } }; @@ -240,23 +591,23 @@ class EventSource : public Events> EventSource(const EventSource&) = default; EventSource(EventSource&& other) : - Events{ std::move(other) } + EventSource::Events( std::move(other) ) {} explicit EventSource(NodePtrT&& nodePtr) : - Events{ std::move(nodePtr) } + EventSource::Events( std::move(nodePtr) ) {} // Explicit emit - void Emit(std::reference_wrapper e) const { BaseT::emit(e); } + void Emit(std::reference_wrapper e) const { EventSource::BaseT::emit(e); } // Function object style - void operator()(std::reference_wrapper e) const { BaseT::emit(e); } + void operator()(std::reference_wrapper e) const { EventSource::BaseT::emit(e); } // Stream style const EventSource& operator<<(std::reference_wrapper e) const { - BaseT::emit(e); + EventSource::BaseT::emit(e); return *this; } }; @@ -281,16 +632,16 @@ class TempEvents : public Events TempEvents(const TempEvents&) = default; TempEvents(TempEvents&& other) : - Events{ std::move(other) } + TempEvents::Events( std::move(other) ) {} explicit TempEvents(NodePtrT&& nodePtr) : - Events{ std::move(nodePtr) } + TempEvents::Events( std::move(nodePtr) ) {} TOp StealOp() { - return std::move(reinterpret_cast(ptr_.get())->StealOp()); + return std::move(reinterpret_cast(this->ptr_.get())->StealOp()); } template @@ -327,348 +678,4 @@ bool Equals(const Events& lhs, const Events& rhs) /****************************************/ REACT_IMPL_END /***************************************/ -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeEventSource -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -auto MakeEventSource() - -> EventSource -{ - using REACT_IMPL::EventSourceNode; - - return EventSource( - std::make_shared>()); -} - -template -auto MakeEventSource() - -> EventSource -{ - using REACT_IMPL::EventSourceNode; - - return EventSource( - std::make_shared>()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Merge -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TArg1, - typename ... TArgs, - typename E = TArg1, - typename TOp = REACT_IMPL::EventMergeOp, - REACT_IMPL::EventStreamNodePtrT ...> -> -auto Merge(const Events& arg1, const Events& ... args) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - static_assert(sizeof...(TArgs) > 0, - "react::Merge requires at least 2 arguments."); - - return TempEvents( - std::make_shared>( - arg1.NodePtr(), args.NodePtr() ...)); -} - -template -< - typename TLeftEvents, - typename TRightEvents, - typename D = TLeftEvents::DomainT, - typename TLeftVal = TLeftEvents::ValueT, - typename TRightVal = TRightEvents::ValueT, - typename E = TLeftVal, - typename TOp = REACT_IMPL::EventMergeOp, - REACT_IMPL::EventStreamNodePtrT>, - class = std::enable_if< - IsEvent::value>::type, - class = std::enable_if< - IsEvent::value>::type -> -auto operator|(const TLeftEvents& lhs, const TRightEvents& rhs) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - lhs.NodePtr(), rhs.NodePtr())); -} - -template -< - typename D, - typename TLeftVal, - typename TLeftOp, - typename TRightVal, - typename TRightOp, - typename E = TLeftVal, - typename TOp = REACT_IMPL::EventMergeOp -> -auto operator|(TempEvents&& lhs, TempEvents&& rhs) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - lhs.StealOp(), rhs.StealOp())); -} - -template -< - typename D, - typename TLeftVal, - typename TLeftOp, - typename TRightEvents, - typename TRightVal = TRightEvents::ValueT, - typename E = TLeftVal, - typename TOp = REACT_IMPL::EventMergeOp>, - class = std::enable_if< - IsEvent::value>::type -> -auto operator|(TempEvents&& lhs, const TRightEvents& rhs) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - lhs.StealOp(), rhs.NodePtr())); -} - -template -< - typename TLeftEvents, - typename D, - typename TRightVal, - typename TRightOp, - typename TLeftVal = TLeftEvents::ValueT, - typename E = TLeftVal, - typename TOp = REACT_IMPL::EventMergeOp, - TRightOp>, - class = std::enable_if< - IsEvent::value>::type -> -auto operator|(const TLeftEvents& lhs, TempEvents&& rhs) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - lhs.NodePtr(), rhs.StealOp())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Filter -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E, - typename FIn, - typename F = std::decay::type, - typename TOp = REACT_IMPL::EventFilterOp> -> -auto Filter(const Events& src, FIn&& filter) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - std::forward(filter), src.NodePtr())); -} - -template -< - typename D, - typename E, - typename TOpIn, - typename FIn, - typename F = std::decay::type, - typename TOpOut = REACT_IMPL::EventFilterOp -> -auto Filter(TempEvents&& src, FIn&& filter) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - std::forward(filter), src.StealOp())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Filter - Synced -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E, - typename FIn, - typename ... TDepValues -> -auto Filter(const Events& source, const SignalPack& depPack, FIn&& func) - -> Events -{ - using REACT_IMPL::SyncedEventFilterNode; - - using F = std::decay::type; - - struct NodeBuilder_ - { - NodeBuilder_(const Events& source, FIn&& func) : - MySource{ source }, - MyFunc{ std::forward(func) } - {} - - auto operator()(const Signal& ... deps) - -> Events - { - return Events( - std::make_shared>( - MySource.NodePtr(), std::forward(MyFunc), deps.NodePtr() ...)); - } - - const Events& MySource; - FIn MyFunc; - }; - - return REACT_IMPL::apply( - NodeBuilder_{ source, std::forward(func) }, - depPack.Data); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Transform -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TIn, - typename FIn, - typename F = std::decay::type, - typename TOut = std::result_of::type, - typename TOp = REACT_IMPL::EventTransformOp> -> -auto Transform(const Events& src, FIn&& func) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - std::forward(func), src.NodePtr())); -} - -template -< - typename D, - typename TIn, - typename TOpIn, - typename FIn, - typename F = std::decay::type, - typename TOut = std::result_of::type, - typename TOpOut = REACT_IMPL::EventTransformOp -> -auto Transform(TempEvents&& src, FIn&& func) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - std::forward(func), src.StealOp())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Transform - Synced -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TIn, - typename FIn, - typename ... TDepValues, - typename TOut = std::result_of::type -> -auto Transform(const Events& source, const SignalPack& depPack, FIn&& func) - -> Events -{ - using REACT_IMPL::SyncedEventTransformNode; - - using F = std::decay::type; - - struct NodeBuilder_ - { - NodeBuilder_(const Events& source, FIn&& func) : - MySource{ source }, - MyFunc{ std::forward(func) } - {} - - auto operator()(const Signal& ... deps) - -> Events - { - return Events( - std::make_shared>( - MySource.NodePtr(), std::forward(MyFunc), deps.NodePtr() ...)); - } - - const Events& MySource; - FIn MyFunc; - }; - - return REACT_IMPL::apply( - NodeBuilder_{ source, std::forward(func) }, - depPack.Data); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Flatten -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TInnerValue -> -auto Flatten(const Signal>& node) - -> Events -{ - return Events( - std::make_shared, TInnerValue>>( - node.NodePtr(), node().NodePtr())); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Tokenize -/////////////////////////////////////////////////////////////////////////////////////////////////// -struct Tokenizer -{ - template - Token operator()(const T&) const { return Token::value; } -}; - -template -auto Tokenize(TEvents&& source) - -> decltype(Transform(source, Tokenizer{})) -{ - return Transform(source, Tokenizer{}); -} - -/******************************************/ REACT_END /******************************************/ +#endif // REACT_EVENT_H_INCLUDED \ No newline at end of file diff --git a/include/react/Observer.h b/include/react/Observer.h index 42491e46..2381e6f6 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_OBSERVER_H_INCLUDED +#define REACT_OBSERVER_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -46,12 +49,20 @@ class Observer {} Observer(const Observer&) = delete; + Observer& operator=(const Observer&) = delete; Observer(Observer&& other) : nodePtr_{ std::move(other.nodePtr_) }, subject_{ std::move(other.subject_) } {} + Observer& operator=(Observer&& other) + { + nodePtr_ = std::move(other.nodePtr_); + subject_ = std::move(other.subject_); + return *this; + } + Observer(NodeT* nodePtr, const SubjectT& subject) : nodePtr_{ nodePtr }, subject_{ subject } @@ -113,7 +124,7 @@ auto Observe(const Signal& subject, FIn&& func) using REACT_IMPL::SignalObserverNode; using REACT_IMPL::DomainSpecificObserverRegistry; - using F = std::decay::type; + using F = typename std::decay::type; std::unique_ptr obsPtr{ new SignalObserverNode{ @@ -141,7 +152,7 @@ auto Observe(const Events& subject, FIn&& func) using REACT_IMPL::EventObserverNode; using REACT_IMPL::DomainSpecificObserverRegistry; - using F = std::decay::type; + using F = typename std::decay::type; std::unique_ptr obsPtr{ new EventObserverNode{ @@ -171,7 +182,7 @@ auto Observe(const Events& subject, using REACT_IMPL::SyncedObserverNode; using REACT_IMPL::DomainSpecificObserverRegistry; - using F = std::decay::type; + using F = typename std::decay::type; struct NodeBuilder_ { @@ -222,4 +233,6 @@ inline void DetachThisObserver() REACT_IMPL::GlobalObserverState<>::ShouldDetach = true; } -/******************************************/ REACT_END /******************************************/ \ No newline at end of file +/******************************************/ REACT_END /******************************************/ + +#endif // REACT_OBSERVER_H_INCLUDED \ No newline at end of file diff --git a/include/react/ReactiveObject.h b/include/react/ReactiveObject.h index 383a5d02..ea6383f0 100644 --- a/include/react/ReactiveObject.h +++ b/include/react/ReactiveObject.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_REACTIVEOBJECT_H_INCLUDED +#define REACT_REACTIVEOBJECT_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -14,30 +17,15 @@ #include "react/TypeTraits.h" #include "react/common/Util.h" +#include "react/detail/EventFwd.h" +#include "react/detail/SignalFwd.h" + /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Signal; - -template -class VarSignal; - -template -class TempSignal; - -template -class Events; - -template -class EventSource; - -template -class TempEvents; - template class ReactiveLoop; @@ -47,8 +35,6 @@ class Observer; template class ScopedObserver; -enum class Token; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// ReactiveObject /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -85,8 +71,8 @@ class ReactiveObject template < typename V, - typename S = std::decay::type, - class = std::enable_if< + typename S = typename std::decay::type, + class = typename std::enable_if< !IsSignal::value>::type > static auto MakeVar(V&& value) @@ -111,9 +97,9 @@ class ReactiveObject template < typename V, - typename S = std::decay::type, - typename TInner = S::ValueT, - class = std::enable_if< + typename S = typename std::decay::type, + typename TInner = typename S::ValueT, + class = typename std::enable_if< IsSignal::value>::type > static auto MakeVar(V&& value) @@ -162,7 +148,9 @@ class ReactiveObject obj, \ [] (REACT_IMPL::Identity::Type::ValueT r) \ { \ - REACT_ASSERT(r != nullptr); \ + assert(r != nullptr); \ using T = decltype(r->name); \ return static_cast::Type>(r->name); \ })) + +#endif // REACT_REACTIVEOBJECT_H_INCLUDED \ No newline at end of file diff --git a/include/react/Reactor.h b/include/react/Reactor.h index ec5ae9b5..723b249c 100644 --- a/include/react/Reactor.h +++ b/include/react/Reactor.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_REACTOR_H_INCLUDED +#define REACT_REACTOR_H_INCLUDED + #pragma once #ifdef REACT_ENABLE_REACTORS @@ -65,4 +68,6 @@ class ReactiveLoop /******************************************/ REACT_END /******************************************/ -#endif //REACT_ENABLE_REACTORS \ No newline at end of file +#endif // REACT_ENABLE_REACTORS + +#endif // REACT_REACTOR_H_INCLUDED \ No newline at end of file diff --git a/include/react/Signal.h b/include/react/Signal.h index 995877b1..bd589789 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_SIGNAL_H_INCLUDED +#define REACT_SIGNAL_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -13,246 +16,14 @@ #include #include +#include "react/detail/SignalFwd.h" + #include "react/Observer.h" #include "react/TypeTraits.h" #include "react/detail/SignalBase.h" /*****************************************/ REACT_BEGIN /*****************************************/ -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Signal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -class Signal : public REACT_IMPL::SignalBase -{ -protected: - using BaseT = REACT_IMPL::SignalBase; - -private: - using NodeT = REACT_IMPL::SignalNode; - using NodePtrT = std::shared_ptr; - -public: - using ValueT = S; - - Signal() = default; - Signal(const Signal&) = default; - - Signal(Signal&& other) : - SignalBase{ std::move(other) } - {} - - explicit Signal(NodePtrT&& nodePtr) : - SignalBase{ std::move(nodePtr) } - {} - - const S& Value() const { return BaseT::getValue(); } - const S& operator()() const { return BaseT::getValue(); } - - bool Equals(const Signal& other) const - { - return BaseT::Equals(other); - } - - bool IsValid() const - { - return BaseT::IsValid(); - } - - template ::value>::type> - S Flatten() const - { - return REACT::Flatten(*this); - } -}; - -// Specialize for references -template -< - typename D, - typename S -> -class Signal : public REACT_IMPL::SignalBase> -{ -protected: - using BaseT = REACT_IMPL::SignalBase>; - -private: - using NodeT = REACT_IMPL::SignalNode>; - using NodePtrT = std::shared_ptr; - -public: - using ValueT = S; - - Signal() = default; - Signal(const Signal&) = default; - - Signal(Signal&& other) : - SignalBase{ std::move(other) } - {} - - explicit Signal(NodePtrT&& nodePtr) : - SignalBase{ std::move(nodePtr) } - {} - - const S& Value() const { return BaseT::getValue(); } - const S& operator()() const { return BaseT::getValue(); } - - bool Equals(const Signal& other) const - { - return BaseT::Equals(other); - } - - bool IsValid() const - { - return BaseT::IsValid(); - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// VarSignal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -class VarSignal : public Signal -{ -private: - using NodeT = REACT_IMPL::VarNode; - using NodePtrT = std::shared_ptr; - -public: - VarSignal() = default; - VarSignal(const VarSignal&) = default; - - VarSignal(VarSignal&& other) : - Signal{ std::move(other) } - {} - - explicit VarSignal(NodePtrT&& nodePtr) : - Signal{ std::move(nodePtr) } - {} - - void Set(const S& newValue) const - { - BaseT::setValue(newValue); - } - - void Set(S&& newValue) const - { - BaseT::setValue(std::move(newValue)); - } - - const VarSignal& operator<<=(const S& newValue) const - { - BaseT::setValue(newValue); - return *this; - } - - const VarSignal& operator<<=(S&& newValue) const - { - BaseT::setValue(std::move(newValue)); - return *this; - } - - template - void Modify(const F& func) const - { - BaseT::modifyValue(func); - } -}; - -// Specialize for references -template -< - typename D, - typename S -> -class VarSignal : public Signal> -{ -private: - using NodeT = REACT_IMPL::VarNode>; - using NodePtrT = std::shared_ptr; - -public: - using ValueT = S; - - VarSignal() = default; - VarSignal(const VarSignal&) = default; - - VarSignal(VarSignal&& other) : - Signal{ std::move(other) } - {} - - explicit VarSignal(NodePtrT&& nodePtr) : - Signal{ std::move(nodePtr) } - {} - - void Set(std::reference_wrapper newValue) const - { - BaseT::setValue(newValue); - } - - const VarSignal& operator<<=(std::reference_wrapper newValue) const - { - BaseT::setValue(newValue); - return *this; - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// TempSignal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename TOp -> -class TempSignal : public Signal -{ -private: - using NodeT = REACT_IMPL::SignalOpNode; - using NodePtrT = std::shared_ptr; - -public: - TempSignal() = default; - TempSignal(const TempSignal&) = default; - - TempSignal(TempSignal&& other) : - Signal{ std::move(other) } - {} - - explicit TempSignal(NodePtrT&& ptr) : - Signal{ std::move(ptr) } - {} - - TOp StealOp() - { - return std::move(reinterpret_cast(ptr_.get())->StealOp()); - } -}; - -/******************************************/ REACT_END /******************************************/ - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -template -bool Equals(const Signal& lhs, const Signal& rhs) -{ - return lhs.Equals(rhs); -} - -/****************************************/ REACT_IMPL_END /***************************************/ - -/*****************************************/ REACT_BEGIN /*****************************************/ - /////////////////////////////////////////////////////////////////////////////////////////////////// /// SignalPack - Wraps several nodes in a tuple. Create with comma operator. /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -265,12 +36,12 @@ class SignalPack { public: SignalPack(const Signal& ... deps) : - Data{ std::tie(deps ...) } + Data( std::tie(deps ...) ) {} template SignalPack(const SignalPack& curArgs, const Signal& newArg) : - Data{ std::tuple_cat(curArgs.Data, std::tie(newArg)) } + Data( std::tuple_cat(curArgs.Data, std::tie(newArg)) ) {} std::tuple& ...> Data; @@ -297,9 +68,8 @@ template < typename D, typename V, - typename S = std::decay::type, - class = std::enable_if< - ! IsReactive::value>::type + typename S, + class /*SFINAE*/ > auto MakeVar(V&& value) -> VarSignal @@ -328,9 +98,9 @@ template < typename D, typename V, - typename S = std::decay::type, - typename TInner = S::ValueT, - class = std::enable_if< + typename S = typename std::decay::type, + typename TInner = typename S::ValueT, + class = typename std::enable_if< IsSignal::value>::type > auto MakeVar(V&& value) @@ -345,9 +115,9 @@ template < typename D, typename V, - typename S = std::decay::type, - typename TInner = S::ValueT, - class = std::enable_if< + typename S = typename std::decay::type, + typename TInner = typename S::ValueT, + class = typename std::enable_if< IsEvent::value>::type > auto MakeVar(V&& value) @@ -367,8 +137,8 @@ template typename D, typename TValue, typename FIn, - typename F = std::decay::type, - typename S = std::result_of::type, + typename F = typename std::decay::type, + typename S = typename std::result_of::type, typename TOp = REACT_IMPL::FunctionOp> > auto MakeSignal(const Signal& arg, FIn&& func) @@ -385,8 +155,8 @@ template typename D, typename ... TValues, typename FIn, - typename F = std::decay::type, - typename S = std::result_of::type, + typename F = typename std::decay::type, + typename S = typename std::result_of::type, typename TOp = REACT_IMPL::FunctionOp ...> > auto MakeSignal(const SignalPack& argPack, FIn&& func) @@ -394,12 +164,10 @@ auto MakeSignal(const SignalPack& argPack, FIn&& func) { using REACT_IMPL::SignalOpNode; - using F = std::decay::type; - struct NodeBuilder_ { NodeBuilder_(FIn&& func) : - MyFunc{ std::forward(func) } + MyFunc( std::forward(func) ) {} auto operator()(const Signal& ... args) @@ -414,7 +182,7 @@ auto MakeSignal(const SignalPack& argPack, FIn&& func) }; return REACT_IMPL::apply( - NodeBuilder_{ std::forward(func) }, + NodeBuilder_( std::forward(func) ), argPack.Data); } @@ -431,15 +199,15 @@ struct name ## OpFunctor template \ < \ typename TSignal, \ - typename D = TSignal::DomainT, \ - typename TVal = TSignal::ValueT, \ + typename D = typename TSignal::DomainT, \ + typename TVal = typename TSignal::ValueT, \ + class = typename std::enable_if< \ + IsSignal::value>::type, \ typename F = name ## OpFunctor, \ - typename S = std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp>, \ - class = std::enable_if< \ - IsSignal::value>::type \ + typename S = typename std::result_of::type, \ + typename TOp = REACT_IMPL::FunctionOp> \ > \ -auto operator ## op(const TSignal& arg) \ +auto operator op(const TSignal& arg) \ -> TempSignal \ { \ return TempSignal( \ @@ -453,10 +221,10 @@ template typename TVal, \ typename TOpIn, \ typename F = name ## OpFunctor, \ - typename S = std::result_of::type, \ + typename S = typename std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp \ > \ -auto operator ## op(TempSignal&& arg) \ +auto operator op(TempSignal&& arg) \ -> TempSignal \ { \ return TempSignal( \ @@ -464,12 +232,12 @@ auto operator ## op(TempSignal&& arg) F(), arg.StealOp())); \ } -REACT_DECLARE_OP(+, UnaryPlus); -REACT_DECLARE_OP(-, UnaryMinus); -REACT_DECLARE_OP(!, LogicalNegation); -REACT_DECLARE_OP(~, BitwiseComplement); -REACT_DECLARE_OP(++, Increment); -REACT_DECLARE_OP(--, Decrement); +REACT_DECLARE_OP(+, UnaryPlus) +REACT_DECLARE_OP(-, UnaryMinus) +REACT_DECLARE_OP(!, LogicalNegation) +REACT_DECLARE_OP(~, BitwiseComplement) +REACT_DECLARE_OP(++, Increment) +REACT_DECLARE_OP(--, Decrement) #undef REACT_DECLARE_OP @@ -491,12 +259,12 @@ template struct name ## OpRFunctor \ { \ name ## OpRFunctor(name ## OpRFunctor&& other) : \ - LeftVal{ std::move(other.LeftVal) } \ + LeftVal( std::move(other.LeftVal) ) \ {} \ \ template \ name ## OpRFunctor(T&& val) : \ - LeftVal{ std::forward(val) } \ + LeftVal( std::forward(val) ) \ {} \ \ name ## OpRFunctor(const name ## OpRFunctor& other) = delete; \ @@ -514,12 +282,12 @@ template struct name ## OpLFunctor \ { \ name ## OpLFunctor(name ## OpLFunctor&& other) : \ - RightVal{ std::move(other.RightVal) } \ + RightVal( std::move(other.RightVal) ) \ {} \ \ template \ name ## OpLFunctor(T&& val) : \ - RightVal{ std::forward(val) } \ + RightVal( std::forward(val) ) \ {} \ \ name ## OpLFunctor(const name ## OpLFunctor& other) = delete; \ @@ -537,73 +305,73 @@ template < \ typename TLeftSignal, \ typename TRightSignal, \ - typename D = TLeftSignal::DomainT, \ - typename TLeftVal = TLeftSignal::ValueT, \ - typename TRightVal = TRightSignal::ValueT, \ + typename D = typename TLeftSignal::DomainT, \ + typename TLeftVal = typename TLeftSignal::ValueT, \ + typename TRightVal = typename TRightSignal::ValueT, \ + class = typename std::enable_if< \ + IsSignal::value>::type, \ + class = typename std::enable_if< \ + IsSignal::value>::type, \ typename F = name ## OpFunctor, \ - typename S = std::result_of::type, \ + typename S = typename std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp, \ - REACT_IMPL::SignalNodePtrT>, \ - class = std::enable_if< \ - IsSignal::value>::type, \ - class = std::enable_if< \ - IsSignal::value>::type \ + REACT_IMPL::SignalNodePtrT> \ > \ -auto operator ## op(const TLeftSignal& lhs, const TRightSignal& rhs) \ +auto operator op(const TLeftSignal& lhs, const TRightSignal& rhs) \ -> TempSignal \ { \ return TempSignal( \ std::make_shared>( \ - F{ }, lhs.NodePtr(), rhs.NodePtr())); \ + F(), lhs.NodePtr(), rhs.NodePtr())); \ } \ \ template \ < \ typename TLeftSignal, \ typename TRightValIn, \ - typename D = TLeftSignal::DomainT, \ - typename TLeftVal = TLeftSignal::ValueT, \ - typename TRightVal = std::decay::type, \ + typename D = typename TLeftSignal::DomainT, \ + typename TLeftVal = typename TLeftSignal::ValueT, \ + typename TRightVal = typename std::decay::type, \ + class = typename std::enable_if< \ + IsSignal::value>::type, \ + class = typename std::enable_if< \ + ! IsSignal::value>::type, \ typename F = name ## OpLFunctor, \ - typename S = std::result_of::type, \ + typename S = typename std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp>, \ - class = std::enable_if< \ - IsSignal::value>::type, \ - class = std::enable_if< \ - ! IsSignal::value>::type \ + REACT_IMPL::SignalNodePtrT> \ > \ -auto operator ## op(const TLeftSignal& lhs, TRightValIn&& rhs) \ +auto operator op(const TLeftSignal& lhs, TRightValIn&& rhs) \ -> TempSignal \ { \ return TempSignal( \ std::make_shared>( \ - F{ std::forward(rhs) }, lhs.NodePtr())); \ + F( std::forward(rhs) ), lhs.NodePtr())); \ } \ \ template \ < \ typename TLeftValIn, \ typename TRightSignal, \ - typename D = TRightSignal::DomainT, \ - typename TLeftVal = std::decay::type, \ - typename TRightVal = TRightSignal::ValueT, \ + typename D = typename TRightSignal::DomainT, \ + typename TLeftVal = typename std::decay::type, \ + typename TRightVal = typename TRightSignal::ValueT, \ + class = typename std::enable_if< \ + ! IsSignal::value>::type, \ + class = typename std::enable_if< \ + IsSignal::value>::type, \ typename F = name ## OpRFunctor, \ - typename S = std::result_of::type, \ + typename S = typename std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp>, \ - class = std::enable_if< \ - ! IsSignal::value>::type, \ - class = std::enable_if< \ - IsSignal::value>::type \ + REACT_IMPL::SignalNodePtrT> \ > \ -auto operator ## op(TLeftValIn&& lhs, const TRightSignal& rhs) \ +auto operator op(TLeftValIn&& lhs, const TRightSignal& rhs) \ -> TempSignal \ { \ return TempSignal( \ std::make_shared>( \ - F{ std::forward(lhs) }, rhs.NodePtr())); \ + F( std::forward(lhs) ), rhs.NodePtr())); \ } \ template \ < \ @@ -613,16 +381,16 @@ template typename TRightVal, \ typename TRightOp, \ typename F = name ## OpFunctor, \ - typename S = std::result_of::type, \ + typename S = typename std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp \ > \ -auto operator ## op(TempSignal&& lhs, \ - TempSignal&& rhs) \ +auto operator op(TempSignal&& lhs, \ + TempSignal&& rhs) \ -> TempSignal \ { \ return TempSignal( \ std::make_shared>( \ - F{ }, lhs.StealOp(), rhs.StealOp())); \ + F(), lhs.StealOp(), rhs.StealOp())); \ } \ \ template \ @@ -631,22 +399,22 @@ template typename TLeftVal, \ typename TLeftOp, \ typename TRightSignal, \ - typename TRightVal = TRightSignal::ValueT, \ + typename TRightVal = typename TRightSignal::ValueT, \ + class = typename std::enable_if< \ + IsSignal::value>::type, \ typename F = name ## OpFunctor, \ - typename S = std::result_of::type, \ + typename S = typename std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp>, \ - class = std::enable_if< \ - IsSignal::value>::type \ + REACT_IMPL::SignalNodePtrT> \ > \ - auto operator ## op(TempSignal&& lhs, \ - const TRightSignal& rhs) \ +auto operator op(TempSignal&& lhs, \ + const TRightSignal& rhs) \ -> TempSignal \ { \ return TempSignal( \ std::make_shared>( \ - F{ }, lhs.StealOp(), rhs.NodePtr())); \ + F(), lhs.StealOp(), rhs.NodePtr())); \ } \ \ template \ @@ -655,21 +423,21 @@ template typename D, \ typename TRightVal, \ typename TRightOp, \ - typename TLeftVal = TLeftSignal::ValueT, \ + typename TLeftVal = typename TLeftSignal::ValueT, \ + class = typename std::enable_if< \ + IsSignal::value>::type, \ typename F = name ## OpFunctor, \ - typename S = std::result_of::type, \ + typename S = typename std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp, \ - TRightOp>, \ - class = std::enable_if< \ - IsSignal::value>::type \ + TRightOp> \ > \ -auto operator ## op(const TLeftSignal& lhs, TempSignal&& rhs) \ +auto operator op(const TLeftSignal& lhs, TempSignal&& rhs) \ -> TempSignal \ { \ return TempSignal( \ std::make_shared>( \ - F{ }, lhs.NodePtr(), rhs.StealOp())); \ + F(), lhs.NodePtr(), rhs.StealOp())); \ } \ \ template \ @@ -678,19 +446,19 @@ template typename TLeftVal, \ typename TLeftOp, \ typename TRightValIn, \ - typename TRightVal = std::decay::type, \ + typename TRightVal = typename std::decay::type, \ + class = typename std::enable_if< \ + ! IsSignal::value>::type, \ typename F = name ## OpLFunctor, \ - typename S = std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp, \ - class = std::enable_if< \ - ! IsSignal::value>::type \ + typename S = typename std::result_of::type, \ + typename TOp = REACT_IMPL::FunctionOp \ > \ -auto operator ## op(TempSignal&& lhs, TRightValIn&& rhs) \ +auto operator op(TempSignal&& lhs, TRightValIn&& rhs) \ -> TempSignal \ { \ return TempSignal( \ std::make_shared>( \ - F{ std::forward(rhs) }, lhs.StealOp())); \ + F( std::forward(rhs) ), lhs.StealOp())); \ } \ \ template \ @@ -699,44 +467,44 @@ template typename D, \ typename TRightVal, \ typename TRightOp, \ - typename TLeftVal = std::decay::type, \ + typename TLeftVal = typename std::decay::type, \ + class = typename std::enable_if< \ + ! IsSignal::value>::type, \ typename F = name ## OpRFunctor, \ - typename S = std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp, \ - class = std::enable_if< \ - ! IsSignal::value>::type \ + typename S = typename std::result_of::type, \ + typename TOp = REACT_IMPL::FunctionOp \ > \ -auto operator ## op(TLeftValIn&& lhs, TempSignal&& rhs) \ +auto operator op(TLeftValIn&& lhs, TempSignal&& rhs) \ -> TempSignal \ { \ return TempSignal( \ std::make_shared>( \ - F{ std::forward(lhs) }, rhs.StealOp())); \ + F( std::forward(lhs) ), rhs.StealOp())); \ } -REACT_DECLARE_OP(+, Addition); -REACT_DECLARE_OP(-, Subtraction); -REACT_DECLARE_OP(*, Multiplication); -REACT_DECLARE_OP(/, Division); -REACT_DECLARE_OP(%, Modulo); - -REACT_DECLARE_OP(==, Equal); -REACT_DECLARE_OP(!=, NotEqual); -REACT_DECLARE_OP(<, Less); -REACT_DECLARE_OP(<=, LessEqual); -REACT_DECLARE_OP(>, Greater); -REACT_DECLARE_OP(>=, GreaterEqual); - -REACT_DECLARE_OP(&&, LogicalAnd); -REACT_DECLARE_OP(||, LogicalOr); - -REACT_DECLARE_OP(&, BitwiseAnd); -REACT_DECLARE_OP(|, BitwiseOr); -REACT_DECLARE_OP(^, BitwiseXor); +REACT_DECLARE_OP(+, Addition) +REACT_DECLARE_OP(-, Subtraction) +REACT_DECLARE_OP(*, Multiplication) +REACT_DECLARE_OP(/, Division) +REACT_DECLARE_OP(%, Modulo) + +REACT_DECLARE_OP(==, Equal) +REACT_DECLARE_OP(!=, NotEqual) +REACT_DECLARE_OP(<, Less) +REACT_DECLARE_OP(<=, LessEqual) +REACT_DECLARE_OP(>, Greater) +REACT_DECLARE_OP(>=, GreaterEqual) + +REACT_DECLARE_OP(&&, LogicalAnd) +REACT_DECLARE_OP(||, LogicalOr) + +REACT_DECLARE_OP(&, BitwiseAnd) +REACT_DECLARE_OP(|, BitwiseOr) +REACT_DECLARE_OP(^, BitwiseXor) //REACT_DECLARE_OP(<<, BitwiseLeftShift); // MSVC: Internal compiler error //REACT_DECLARE_OP(>>, BitwiseRightShift); -#undef REACT_DECLARE_OP +#undef REACT_DECLARE_OP /////////////////////////////////////////////////////////////////////////////////////////////////// /// Comma operator overload to create signal pack from 2 signals. @@ -778,7 +546,7 @@ template typename F, template class TSignal, typename TValue, - class = std::enable_if< + class = typename std::enable_if< IsSignal>::value>::type > auto operator->*(const TSignal& arg, F&& func) @@ -816,4 +584,267 @@ auto Flatten(const Signal>& node) node.NodePtr(), node.Value().NodePtr())); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Signal +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename S +> +class Signal : public REACT_IMPL::SignalBase +{ +protected: + using BaseT = REACT_IMPL::SignalBase; + +private: + using NodeT = REACT_IMPL::SignalNode; + using NodePtrT = std::shared_ptr; + +public: + using ValueT = S; + + Signal() = default; + Signal(const Signal&) = default; + Signal& operator=(const Signal&) = default; + + Signal(Signal&& other) : + Signal::SignalBase( std::move(other) ) + {} + + Signal& operator=(Signal&& other) + { + Signal::SignalBase::operator=( std::move(other) ); + return *this; + } + + explicit Signal(NodePtrT&& nodePtr) : + Signal::SignalBase( std::move(nodePtr) ) + {} + + const S& Value() const { return Signal::BaseT::getValue(); } + const S& operator()() const { return Signal::BaseT::getValue(); } + + bool Equals(const Signal& other) const + { + return Signal::BaseT::Equals(other); + } + + bool IsValid() const + { + return Signal::BaseT::IsValid(); + } + + S Flatten() const + { + static_assert(IsReactive::value, "Flatten requires a Signal value type."); + return REACT::Flatten(*this); + } +}; + +// Specialize for references +template +< + typename D, + typename S +> +class Signal : public REACT_IMPL::SignalBase> +{ +protected: + using BaseT = REACT_IMPL::SignalBase>; + +private: + using NodeT = REACT_IMPL::SignalNode>; + using NodePtrT = std::shared_ptr; + +public: + using ValueT = S; + + Signal() = default; + Signal(const Signal&) = default; + Signal& operator=(const Signal&) = default; + + Signal(Signal&& other) : + Signal::SignalBase( std::move(other) ) + {} + + Signal& operator=(Signal&& other) + { + return Signal::SignalBase::operator=( std::move(other) ); + } + + explicit Signal(NodePtrT&& nodePtr) : + Signal::SignalBase( std::move(nodePtr) ) + {} + + const S& Value() const { return Signal::BaseT::getValue(); } + const S& operator()() const { return Signal::BaseT::getValue(); } + + bool Equals(const Signal& other) const + { + return Signal::BaseT::Equals(other); + } + + bool IsValid() const + { + return Signal::BaseT::IsValid(); + } +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// VarSignal +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename S +> +class VarSignal : public Signal +{ +private: + using NodeT = REACT_IMPL::VarNode; + using NodePtrT = std::shared_ptr; + +public: + VarSignal() = default; + VarSignal(const VarSignal&) = default; + VarSignal& operator=(const VarSignal&) = default; + + VarSignal(VarSignal&& other) : + VarSignal::Signal( std::move(other) ) + {} + + VarSignal& operator=(VarSignal&& other) + { + return VarSignal::Signal::operator=( std::move(other) ); + } + + explicit VarSignal(NodePtrT&& nodePtr) : + VarSignal::Signal( std::move(nodePtr) ) + {} + + void Set(const S& newValue) const + { + VarSignal::BaseT::setValue(newValue); + } + + void Set(S&& newValue) const + { + VarSignal::BaseT::setValue(std::move(newValue)); + } + + const VarSignal& operator<<=(const S& newValue) const + { + VarSignal::BaseT::setValue(newValue); + return *this; + } + + const VarSignal& operator<<=(S&& newValue) const + { + VarSignal::BaseT::setValue(std::move(newValue)); + return *this; + } + + template + void Modify(const F& func) const + { + VarSignal::BaseT::modifyValue(func); + } +}; + +// Specialize for references +template +< + typename D, + typename S +> +class VarSignal : public Signal> +{ +private: + using NodeT = REACT_IMPL::VarNode>; + using NodePtrT = std::shared_ptr; + +public: + using ValueT = S; + + VarSignal() = default; + VarSignal(const VarSignal&) = default; + VarSignal& operator=(const VarSignal&) = default; + + VarSignal(VarSignal&& other) : + VarSignal::Signal( std::move(other) ) + {} + + VarSignal& operator=(VarSignal&& other) + { + return VarSignal::Signal::operator=( std::move(other) ); + } + + explicit VarSignal(NodePtrT&& nodePtr) : + VarSignal::Signal( std::move(nodePtr) ) + {} + + void Set(std::reference_wrapper newValue) const + { + VarSignal::BaseT::setValue(newValue); + } + + const VarSignal& operator<<=(std::reference_wrapper newValue) const + { + VarSignal::BaseT::setValue(newValue); + return *this; + } +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// TempSignal +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename S, + typename TOp +> +class TempSignal : public Signal +{ +private: + using NodeT = REACT_IMPL::SignalOpNode; + using NodePtrT = std::shared_ptr; + +public: + TempSignal() = default; + TempSignal(const TempSignal&) = default; + TempSignal& operator=(const TempSignal&) = default; + + TempSignal(TempSignal&& other) : + TempSignal::Signal( std::move(other) ) + {} + + TempSignal& operator=(TempSignal&& other) + { + return TempSignal::Signal::operator=( std::move(other) ); + } + + explicit TempSignal(NodePtrT&& ptr) : + TempSignal::Signal( std::move(ptr) ) + {} + + TOp StealOp() + { + return std::move(reinterpret_cast(this->ptr_.get())->StealOp()); + } +}; + /******************************************/ REACT_END /******************************************/ + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +template +bool Equals(const Signal& lhs, const Signal& rhs) +{ + return lhs.Equals(rhs); +} + +/****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_SIGNAL_H_INCLUDED \ No newline at end of file diff --git a/include/react/TypeTraits.h b/include/react/TypeTraits.h index 09f84092..e0b7d1e1 100644 --- a/include/react/TypeTraits.h +++ b/include/react/TypeTraits.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_TYPETRAITS_H_INCLUDED +#define REACT_TYPETRAITS_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -97,4 +100,6 @@ struct RemoveInput> { using Type = Signal; }; template struct RemoveInput> { using Type = Events; }; -/******************************************/ REACT_END /******************************************/ \ No newline at end of file +/******************************************/ REACT_END /******************************************/ + +#endif // REACT_TYPETRAITS_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/Concurrency.h b/include/react/common/Concurrency.h index c3d9bb43..cd9efc73 100644 --- a/include/react/common/Concurrency.h +++ b/include/react/common/Concurrency.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_COMMON_CONCURRENCY_H_INCLUDED +#define REACT_COMMON_CONCURRENCY_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -109,3 +112,5 @@ class ConditionalCriticalSection }; /****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_COMMON_CONCURRENCY_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/Containers.h b/include/react/common/Containers.h index 78dcb4db..9b77de06 100644 --- a/include/react/common/Containers.h +++ b/include/react/common/Containers.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_COMMON_CONTAINERS_H_INCLUDED +#define REACT_COMMON_CONTAINERS_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -75,7 +78,7 @@ class NodeVector /////////////////////////////////////////////////////////////////////////////////////////////////// struct SplitTag {}; -template +template class NodeBuffer { public: @@ -83,36 +86,36 @@ class NodeBuffer using iterator = typename DataT::iterator; using const_iterator = typename DataT::const_iterator; - static const uint split_size = N / 2; + static const size_t split_size = N / 2; NodeBuffer() : - size_{ 0 }, - front_{ nodes_.begin() }, - back_{ nodes_.begin() } + size_( 0 ), + front_( nodes_.begin() ), + back_( nodes_.begin() ) {} NodeBuffer(T* node) : - size_{ 1 }, - front_{ nodes_.begin() }, - back_{ nodes_.begin() + 1 } + size_( 1 ), + front_( nodes_.begin() ), + back_( nodes_.begin() + 1 ) { nodes_[0] = node; } template NodeBuffer(TInput srcBegin, TInput srcEnd) : - size_{ std::distance(srcBegin, srcEnd) }, - front_{ nodes_.begin() }, - back_{ size_ != N ? nodes_.begin() + size_ : nodes_.begin() } + size_( std::distance(srcBegin, srcEnd) ), // parentheses to allow narrowing conversion + front_( nodes_.begin() ), + back_( size_ != N ? nodes_.begin() + size_ : nodes_.begin() ) { std::copy(srcBegin, srcEnd, front_); } // Other must be full NodeBuffer(NodeBuffer& other, SplitTag) : - size_{ split_size }, - front_{ nodes_.begin() }, - back_{ nodes_.begin() } + size_( split_size ), + front_( nodes_.begin() ), + back_( nodes_.begin() ) { for (auto i=0; i -// Todo: Make portable -#include +#if _WIN32 || _WIN64 + #include +#else + #include +#endif /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -19,6 +25,7 @@ /// GetPerformanceFrequency /////////////////////////////////////////////////////////////////////////////////////////////////// // Todo: Initialization not thread-safe +#if _WIN32 || _WIN64 inline const LARGE_INTEGER& GetPerformanceFrequency() { static bool init = false; @@ -32,16 +39,27 @@ inline const LARGE_INTEGER& GetPerformanceFrequency() return frequency; } +#endif /////////////////////////////////////////////////////////////////////////////////////////////////// /// ConditionalTimer /////////////////////////////////////////////////////////////////////////////////////////////////// template < - long long heavy_threshold, + long long threshold, bool is_enabled > -class ConditionalTimer; +class ConditionalTimer +{ +public: + class ScopedTimer + { + public: + ScopedTimer(const ConditionalTimer&); + }; + + bool IsThresholdExceeded() const; +}; // Disabled template @@ -52,8 +70,9 @@ class ConditionalTimer { public: // Defines scoped timer that does nothing - struct ScopedTimer + class ScopedTimer { + public: ScopedTimer(const ConditionalTimer&) {} }; @@ -68,11 +87,17 @@ template class ConditionalTimer { public: +#if _WIN32 || _WIN64 + using TimestampT = LARGE_INTEGER; +#elif __linux__ + using TimestampT = long long; +#endif + class ScopedTimer { public: ScopedTimer(ConditionalTimer& parent) : - parent_{ parent } + parent_( parent ) { if (!parent_.shouldMeasure_) return; @@ -80,8 +105,7 @@ class ConditionalTimer startMeasure(); } - // Note: virtual for performance reasons - virtual ~ScopedTimer() + ~ScopedTimer() { if (!parent_.shouldMeasure_) return; @@ -94,27 +118,46 @@ class ConditionalTimer private: void startMeasure() { - QueryPerformanceCounter(&startTime_); + startTime_ = now(); } void endMeasure() { - LARGE_INTEGER endTime, durationMS; + TimestampT endTime = now(); - QueryPerformanceCounter(&endTime); +#if _WIN32 || _WIN64 + LARGE_INTEGER durationMS; durationMS.QuadPart = endTime.QuadPart - startTime_.QuadPart; durationMS.QuadPart *= 1000000; durationMS.QuadPart /= GetPerformanceFrequency().QuadPart; parent_.isThresholdExceeded_ = durationMS.QuadPart > threshold; +#else + // TODO + parent_.isThresholdExceeded_ = false; +#endif } - ConditionalTimer& parent_; - - LARGE_INTEGER startTime_; + ConditionalTimer& parent_; + TimestampT startTime_; }; + static TimestampT now() + { +#if _WIN32 || _WIN64 + TimestampT result; + QueryPerformanceCounter(&result); + return result; +#else + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return static_cast(1000000000UL) + * static_cast(ts.tv_sec) + + static_cast(ts.tv_nsec); +#endif + } + bool IsThresholdExceeded() const { return isThresholdExceeded_; } private: @@ -124,3 +167,5 @@ class ConditionalTimer }; /****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_COMMON_TIMING_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/TopoQueue.h b/include/react/common/TopoQueue.h index 9d07594d..8084d419 100644 --- a/include/react/common/TopoQueue.h +++ b/include/react/common/TopoQueue.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_COMMON_TOPOQUEUE_H_INCLUDED +#define REACT_COMMON_TOPOQUEUE_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -38,7 +41,7 @@ class TopoQueue template TopoQueue(FIn&& levelFunc) : - levelFunc_{ levelFunc } + levelFunc_( std::forward(levelFunc) ) {} void Push(const T& value) @@ -84,7 +87,7 @@ class TopoQueue Entry() = default; Entry(const Entry&) = default; - Entry(const T& value, int level) : Value{ value }, Level{ level } {} + Entry(const T& value, int level) : Value( value ), Level( level ) {} T Value; int Level; @@ -92,7 +95,7 @@ class TopoQueue struct LevelCompFunctor { - LevelCompFunctor(int level) : Level{ level } {} + LevelCompFunctor(int level) : Level( level ) {} bool operator()(const Entry& e) const { return e.Level != Level; } @@ -125,9 +128,9 @@ class WeightedRange WeightedRange(const WeightedRange&) = default; WeightedRange(const TIt& a, const TIt& b, uint weight) : - begin_{ a }, - end_{ b }, - weight_{ weight } + begin_( a ), + end_( b ), + weight_( weight ) {} WeightedRange(WeightedRange& source, tbb::split) @@ -198,8 +201,8 @@ class ConcurrentTopoQueue template ConcurrentTopoQueue(FIn1&& levelFunc, FIn2&& weightFunc) : - levelFunc_{std::forward(levelFunc) }, - weightFunc_{ std::forward(weightFunc) } + levelFunc_( std::forward(levelFunc) ), + weightFunc_( std::forward(weightFunc) ) {} void Push(const T& value) @@ -283,9 +286,9 @@ class ConcurrentTopoQueue Entry(const Entry&) = default; Entry(const T& value, int level, uint weight) : - Value{ value }, - Level{ level }, - Weight{ weight } + Value( value ), + Level( level ), + Weight( weight ) {} T Value; @@ -320,4 +323,6 @@ class ConcurrentTopoQueue tbb::enumerable_thread_specific collectBuffer_; }; -/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file +/****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_COMMON_TOPOQUEUE_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/Types.h b/include/react/common/Types.h index 37b0f217..5d747eee 100644 --- a/include/react/common/Types.h +++ b/include/react/common/Types.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_COMMON_TYPES_H_INCLUDED +#define REACT_COMMON_TYPES_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -23,4 +26,6 @@ ObjectId GetObjectId(const O& obj) using UpdateDurationT = std::chrono::duration; -/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file +/****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_COMMON_TYPES_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/Util.h b/include/react/common/Util.h index 31dddf7e..14eee051 100644 --- a/include/react/common/Util.h +++ b/include/react/common/Util.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_COMMON_UTIL_H_INCLUDED +#define REACT_COMMON_UTIL_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -103,7 +106,7 @@ class ScopeGuard { public: explicit ScopeGuard(F func) : - func_{ std::move(func) } + func_( std::move(func) ) { } @@ -132,7 +135,7 @@ class MoveBindWrapper { public: explicit MoveBindWrapper(T&& v): - v_{ std::forward(v) } + v_( std::forward(v) ) {} template @@ -164,10 +167,10 @@ struct AddDummyArgWrapper { // Dummy int to make sure it calls the right ctor template - AddDummyArgWrapper(int, FIn&& func) : MyFunc{ std::forward(func) } {} + AddDummyArgWrapper(int, FIn&& func) : MyFunc( std::forward(func) ) {} AddDummyArgWrapper(const AddDummyArgWrapper& other) = default; - AddDummyArgWrapper(AddDummyArgWrapper&& other) : MyFunc{ std::move(other.MyFunc) } {} + AddDummyArgWrapper(AddDummyArgWrapper&& other) : MyFunc( std::move(other.MyFunc) ) {} TRet operator()(TArg, TDepValues& ... args) { @@ -182,10 +185,10 @@ struct AddDummyArgWrapper { // Dummy int to make sure it calls the right ctor template - AddDummyArgWrapper(int, FIn&& func) : MyFunc{ std::forward(func) } {} + AddDummyArgWrapper(int, FIn&& func) : MyFunc( std::forward(func) ) {} AddDummyArgWrapper(const AddDummyArgWrapper& other) = default; - AddDummyArgWrapper(AddDummyArgWrapper&& other) : MyFunc{ std::move(other.MyFunc) } {} + AddDummyArgWrapper(AddDummyArgWrapper&& other) : MyFunc( std::move(other.MyFunc) ) {} void operator()(TArg, TDepValues& ... args) { @@ -226,3 +229,5 @@ namespace std #define REACT_SCOPE_EXIT \ auto REACT_ANON_VAR(REACT_SCOPE_EXIT_PREFIX) \ = REACT_IMPL::ScopeGuardDummy_() + [&]() + +#endif // REACT_COMMON_UTIL_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/Defs.h b/include/react/detail/Defs.h index 26fdf665..477ad6b4 100644 --- a/include/react/detail/Defs.h +++ b/include/react/detail/Defs.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_DEFS_H_INCLUDED +#define REACT_DETAIL_DEFS_H_INCLUDED + #pragma once #include @@ -19,13 +22,13 @@ #define REACT_IMPL REACT ::impl #ifdef _DEBUG -#define REACT_MESSAGE(...) printf(__VA_ARGS__ ## "\n") +#define REACT_MESSAGE(...) printf(__VA_ARGS__) #else #define REACT_MESSAGE #endif // Assert with message -#define REACT_ASSERT(condition, ...) for (; !(condition); assert(condition)) printf(__VA_ARGS__ ## "\n") +#define REACT_ASSERT(condition, ...) for (; !(condition); assert(condition)) printf(__VA_ARGS__) #define REACT_ERROR(...) REACT_ASSERT(false, __VA_ARGS__) // Logging @@ -36,7 +39,16 @@ #endif // Thread local storage -#define REACT_TLS __declspec(thread) +#if _WIN32 || _WIN64 + // MSVC + #define REACT_TLS __declspec(thread) +#elif __GNUC__ + // GCC + #define REACT_TLS __thread +#else + // Standard C++11 + #define REACT_TLS thread_local +#endif /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -46,3 +58,5 @@ using uchar = unsigned char; using std::size_t; /****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_DEFS_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/EngineBase.h b/include/react/detail/EngineBase.h index 74432388..3ee59992 100644 --- a/include/react/detail/EngineBase.h +++ b/include/react/detail/EngineBase.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_ENGINEBASE_H_INCLUDED +#define REACT_DETAIL_ENGINEBASE_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -59,7 +62,7 @@ class TurnBase { public: TurnBase(TurnIdT id, TurnFlagsT flags) : - id_{ id } + id_( id ) {} TurnIdT Id() const { return id_; } @@ -103,7 +106,7 @@ class TurnBase { public: TurnBase(TurnIdT id, TurnFlagsT flags) : - id_{ id } + id_( id ) {} TurnIdT Id() const { return id_; } @@ -159,7 +162,7 @@ class TurnQueueManager { public: explicit QueueEntry(TurnFlagsT flags) : - isMergeable_{ (flags & enable_input_merging) != 0 } + isMergeable_( (flags & enable_input_merging) != 0 ) {} inline void Append(QueueEntry& tr) @@ -280,8 +283,8 @@ class DefaultQueueableTurn : { public: DefaultQueueableTurn(TurnIdT id, TurnFlagsT flags) : - TTurnBase{ id, flags }, - TurnQueueManager::QueueEntry{ flags } + TTurnBase( id, flags ), + TurnQueueManager::QueueEntry( flags ) {} }; @@ -324,3 +327,5 @@ class DefaultQueuingEngine : public TTEngineBase }; /****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_ENGINEBASE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/EventBase.h b/include/react/detail/EventBase.h index 0bdedd06..1af7d2bb 100644 --- a/include/react/detail/EventBase.h +++ b/include/react/detail/EventBase.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_EVENTBASE_H_INCLUDED +#define REACT_DETAIL_EVENTBASE_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -31,7 +34,7 @@ class EventStreamBase : public ReactiveBase> template explicit EventStreamBase(T&& ptr) : - ReactiveBase{ std::forward(ptr) } + EventStreamBase::ReactiveBase( std::forward(ptr) ) {} protected: @@ -39,9 +42,11 @@ class EventStreamBase : public ReactiveBase> void emit(T&& e) const { InputManager::AddInput( - *reinterpret_cast*>(ptr_.get()), + *reinterpret_cast*>(this->ptr_.get()), std::forward(e)); } }; /****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_EVENTBASE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/EventFwd.h b/include/react/detail/EventFwd.h new file mode 100644 index 00000000..8a80d9a7 --- /dev/null +++ b/include/react/detail/EventFwd.h @@ -0,0 +1,41 @@ + +// 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_EVENTFWD_H_INCLUDED +#define REACT_DETAIL_EVENTFWD_H_INCLUDED + +#pragma once + +#include "react/detail/Defs.h" + +#include "react/TypeTraits.h" + +/*****************************************/ REACT_BEGIN /*****************************************/ + +// Classes +template +class Events; + +template +class EventSource; + +template +class TempEvents; + +enum class Token; + +// Functions +template +auto MakeEventSource() + -> EventSource; + +template +auto MakeEventSource() + -> EventSource; + +/******************************************/ REACT_END /******************************************/ + +#endif // REACT_DETAIL_EVENTFWD_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/IReactiveEngine.h b/include/react/detail/IReactiveEngine.h index 0bf20348..12007096 100644 --- a/include/react/detail/IReactiveEngine.h +++ b/include/react/detail/IReactiveEngine.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_IREACTIVEENGINE_H_INCLUDED +#define REACT_DETAIL_IREACTIVEENGINE_H_INCLUDED + #pragma once #include @@ -168,3 +171,5 @@ template struct EnableParallelUpdating : std::false_type {}; template struct EnableConcurrentInput : std::false_type {}; /****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_IREACTIVEENGINE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/IReactiveNode.h b/include/react/detail/IReactiveNode.h index d80c2f31..b4d2e79c 100644 --- a/include/react/detail/IReactiveNode.h +++ b/include/react/detail/IReactiveNode.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_IREACTIVENODE_H_INCLUDED +#define REACT_DETAIL_IREACTIVENODE_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -53,3 +56,5 @@ struct IInputNode }; /****************************************/ REACT_IMPL_END /***************************************/ + +#endif //REACT_DETAIL_IREACTIVENODE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/ObserverBase.h b/include/react/detail/ObserverBase.h index b57b3b74..4c197579 100644 --- a/include/react/detail/ObserverBase.h +++ b/include/react/detail/ObserverBase.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_OBSERVERBASE_H_INCLUDED +#define REACT_DETAIL_OBSERVERBASE_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -20,7 +23,20 @@ template class Observable; -class IObserver; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// IObserver +/////////////////////////////////////////////////////////////////////////////////////////////////// +class IObserver +{ +public: + virtual ~IObserver() = default; + +private: + virtual void detachObserver() = 0; + + template + friend class ObserverRegistry; +}; // tbb tasks are non-preemptible, thread local flag for each worker template @@ -29,8 +45,8 @@ struct GlobalObserverState static REACT_TLS bool ShouldDetach; }; -template <> -bool GlobalObserverState::ShouldDetach = false; +template +REACT_TLS bool GlobalObserverState::ShouldDetach(false); /////////////////////////////////////////////////////////////////////////////////////////////////// /// ObserverRegistry @@ -44,15 +60,15 @@ class ObserverRegistry { public: Entry(std::unique_ptr&& nodePtr, const Observable* subjectPtr) : - nodePtr_{ std::move(nodePtr) }, - SubjectPtr{ subjectPtr } + nodePtr_( std::move(nodePtr) ), + SubjectPtr( subjectPtr ) {} Entry(const Entry&) = delete; Entry(Entry&& other) : - nodePtr_{ std::move(other.nodePtr_) }, - SubjectPtr{ other.SubjectPtr } + nodePtr_( std::move(other.nodePtr_) ), + SubjectPtr( other.SubjectPtr ) {} const Observable* SubjectPtr; @@ -121,6 +137,14 @@ template class Observable { public: + Observable() : + obsCount_( 0u ) + {}; + + Observable(const Observable& other) : + obsCount_( other.GetObsCount() ) + {} + void IncObsCount() { obsCount_.fetch_add(1, std::memory_order_relaxed); } void DecObsCount() { obsCount_.fetch_sub(1, std::memory_order_relaxed); } uint GetObsCount() const { return obsCount_.load(std::memory_order_relaxed); } @@ -132,22 +156,9 @@ class Observable } private: - std::atomic obsCount_ = 0; + std::atomic obsCount_; }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IObserver -/////////////////////////////////////////////////////////////////////////////////////////////////// -class IObserver -{ -public: - virtual ~IObserver() = default; - -private: - virtual void detachObserver() = 0; - - template - friend class ObserverRegistry; -}; +/****************************************/ REACT_IMPL_END /***************************************/ -/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file +#endif // REACT_DETAIL_OBSERVERBASE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/Options.h b/include/react/detail/Options.h index c591e537..3c93cd16 100644 --- a/include/react/detail/Options.h +++ b/include/react/detail/Options.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_OPTIONS_H_INCLUDED +#define REACT_DETAIL_OPTIONS_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -18,4 +21,6 @@ enum ETurnFlags enable_input_merging = 1 << 0 }; -/******************************************/ REACT_END /******************************************/ \ No newline at end of file +/******************************************/ REACT_END /******************************************/ + +#endif // REACT_DETAIL_OPTIONS_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/ReactiveBase.h b/include/react/detail/ReactiveBase.h index 1a80e729..da44e421 100644 --- a/include/react/detail/ReactiveBase.h +++ b/include/react/detail/ReactiveBase.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_REACTIVEBASE_H_INCLUDED +#define REACT_DETAIL_REACTIVEBASE_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -39,17 +42,24 @@ class ReactiveBase ReactiveBase() = default; ReactiveBase(const ReactiveBase&) = default; + ReactiveBase& operator=(const ReactiveBase&) = default; ReactiveBase(ReactiveBase&& other) : - ptr_{ std::move(other.ptr_) } + ptr_( std::move(other.ptr_) ) {} + ReactiveBase& operator=(ReactiveBase&& other) + { + ptr_.reset(std::move(other)); + return *this; + } + explicit ReactiveBase(const NodePtrT& ptr) : - ptr_{ ptr } + ptr_( ptr ) {} explicit ReactiveBase(NodePtrT&& ptr) : - ptr_{ std::move(ptr) } + ptr_( std::move(ptr) ) {} const std::shared_ptr& NodePtr() const @@ -72,3 +82,5 @@ class ReactiveBase }; /****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_REACTIVEBASE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index 16226104..128e9657 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_REACTIVEINPUT_H_INCLUDED +#define REACT_DETAIL_REACTIVEINPUT_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -18,6 +21,7 @@ #include "tbb/concurrent_vector.h" #include "react/detail/IReactiveNode.h." +#include "react/detail/Options.h." /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -55,7 +59,7 @@ class ContinuationInput ContinuationInput(const ContinuationInput&) = delete; ContinuationInput(ContinuationInput&& other) : - bufferedInputs_{ std::move(other.bufferedInputs_) } + bufferedInputs_( std::move(other.bufferedInputs_) ) {} ContinuationInput& operator=(ContinuationInput&& other) @@ -95,7 +99,7 @@ class ContinuationInput ContinuationInput(const ContinuationInput&) = delete; ContinuationInput(ContinuationInput&& other) : - bufferedInputsPtr_{ std::move(other.bufferedInputsPtr_) } + bufferedInputsPtr_( std::move(other.bufferedInputsPtr_) ) {} ContinuationInput& operator=(ContinuationInput&& other) @@ -152,7 +156,7 @@ class ContinuationHolder }; template -typename ContinuationHolder::ContinuationT* ContinuationHolder::ptr_(nullptr); +REACT_TLS typename ContinuationHolder::ContinuationT* ContinuationHolder::ptr_(nullptr); /////////////////////////////////////////////////////////////////////////////////////////////////// /// InputManager @@ -331,7 +335,7 @@ class InputManager static void postProcessTurn(TurnT& turn) { - turn.detachObservers(); + turn.template detachObservers(); // Steal continuation from current turn if (! turn.continuation_.IsEmpty()) @@ -364,7 +368,7 @@ class InputManager Engine::OnTurnEnd(turn); - turn.detachObservers(); + turn.template detachObservers(); if (turn.continuation_.IsEmpty()) break; @@ -380,4 +384,6 @@ std::atomic InputManager::nextTurnId_( 0 ); template typename InputManager::TransactionState InputManager::transactionState_; -/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file +/****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_REACTIVEINPUT_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/SignalBase.h b/include/react/detail/SignalBase.h index 8bbfc5d1..4e504294 100644 --- a/include/react/detail/SignalBase.h +++ b/include/react/detail/SignalBase.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_SIGNALBASE_H_INCLUDED +#define REACT_DETAIL_SIGNALBASE_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -32,20 +35,20 @@ class SignalBase : public ReactiveBase> template explicit SignalBase(T&& ptr) : - ReactiveBase{ std::forward(ptr) } + SignalBase::ReactiveBase( std::forward(ptr) ) {} protected: const S& getValue() const { - return ptr_->ValueRef(); + return this->ptr_->ValueRef(); } template void setValue(T&& newValue) const { InputManager::AddInput( - *reinterpret_cast*>(ptr_.get()), + *reinterpret_cast*>(this->ptr_.get()), std::forward(newValue)); } @@ -53,8 +56,10 @@ class SignalBase : public ReactiveBase> void modifyValue(const F& func) const { InputManager::ModifyInput( - *reinterpret_cast*>(ptr_.get()), func); + *reinterpret_cast*>(this->ptr_.get()), func); } }; /****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_SIGNALBASE_H_INCLUDED diff --git a/include/react/detail/SignalFwd.h b/include/react/detail/SignalFwd.h new file mode 100644 index 00000000..b46dcb86 --- /dev/null +++ b/include/react/detail/SignalFwd.h @@ -0,0 +1,53 @@ + +// 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_SIGNALFWD_H_INCLUDED +#define REACT_DETAIL_SIGNALFWD_H_INCLUDED + +#pragma once + +#include "react/detail/Defs.h" + +#include +#include + +#include "react/TypeTraits.h" + +/*****************************************/ REACT_BEGIN /*****************************************/ + +// Classes +template +class Signal; + +template +class VarSignal; + +template +class TempSignal; + +// Functions +template +< + typename D, + typename V, + typename S = typename std::decay::type, + class = typename std::enable_if< + ! IsReactive::value>::type +> +auto MakeVar(V&& value) + -> VarSignal; + +template +< + typename D, + typename S +> +auto MakeVar(std::reference_wrapper value) + -> VarSignal; + +/******************************************/ REACT_END /******************************************/ + +#endif // REACT_DETAIL_SIGNALFWD_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index d9503de0..8e8735ba 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -4,6 +4,9 @@ // (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" @@ -28,12 +31,14 @@ template > class IterateNode : public SignalNode { + using Engine = typename IterateNode::Engine; + public: template IterateNode(T&& init, const std::shared_ptr>& events, F&& func) : - SignalNode{ std::forward(init) }, - events_{ events }, - func_{ std::forward(func) } + IterateNode::SignalNode( std::forward(init) ), + events_( events ), + func_( std::forward(func) ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *events); @@ -53,16 +58,16 @@ class IterateNode : public SignalNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - S newValue = value_; + S newValue = this->value_; for (const auto& e : events_->Events()) newValue = func_(e, newValue); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - if (! Equals(newValue, value_)) + if (! Equals(newValue, this->value_)) { - value_ = std::move(newValue); + this->value_ = std::move(newValue); Engine::OnNodePulse(*this, turn); } else @@ -92,12 +97,14 @@ template > class IterateByRefNode : public SignalNode { + using Engine = typename IterateByRefNode::Engine; + public: template IterateByRefNode(T&& init, const std::shared_ptr>& events, F&& func) : - SignalNode{ std::forward(init) }, - func_{ std::forward(func) }, - events_{ events } + IterateByRefNode::SignalNode( std::forward(init) ), + func_( std::forward(func) ), + events_( events ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *events); @@ -118,7 +125,7 @@ class IterateByRefNode : public SignalNode GetObjectId(*this), turn.Id())); for (const auto& e : events_->Events()) - func_(e, value_); + func_(e, this->value_); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); @@ -149,14 +156,16 @@ template > 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) : - SignalNode{ std::forward(init) }, - events_{ events }, - func_{ std::forward(func) }, - deps_{ deps ... } + SyncedIterateNode::SignalNode( std::forward(init) ), + events_( events ), + func_( std::forward(func) ), + deps_( deps ... ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *events); @@ -169,7 +178,7 @@ class SyncedIterateNode : public SignalNode apply( DetachFunctor>...>{ *this }, + std::shared_ptr>...>( *this ), deps_); Engine::OnNodeDestroy(*this); @@ -180,9 +189,9 @@ class SyncedIterateNode : public SignalNode struct EvalFunctor_ { EvalFunctor_(const E& e, const S& v, TFunc& f) : - MyEvent{ e }, - MyValue{ v }, - MyFunc{ f } + MyEvent( e ), + MyValue( v ), + MyFunc( f ) {} S operator()(const std::shared_ptr>& ... args) @@ -207,15 +216,15 @@ class SyncedIterateNode : public SignalNode if (! events_->Events().empty()) { - S newValue = value_; + S newValue = this->value_; for (const auto& e : events_->Events()) - newValue = apply(EvalFunctor_{ e, std::move(newValue), func_ }, deps_); + newValue = apply(EvalFunctor_( e, std::move(newValue), func_ ), deps_); - if (! Equals(newValue, value_)) + if (! Equals(newValue, this->value_)) { changed = true; - value_ = std::move(newValue); + this->value_ = std::move(newValue); } } @@ -253,14 +262,16 @@ template > 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) : - SignalNode{ std::forward(init) }, - events_{ events }, - func_{ std::forward(func) }, - deps_{ deps ... } + SyncedIterateByRefNode::SignalNode( std::forward(init) ), + events_( events ), + func_( std::forward(func) ), + deps_( deps ... ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *events); @@ -273,7 +284,7 @@ class SyncedIterateByRefNode : public SignalNode apply( DetachFunctor>...>{ *this }, + std::shared_ptr>...>( *this ), deps_); Engine::OnNodeDestroy(*this); @@ -284,9 +295,9 @@ class SyncedIterateByRefNode : public SignalNode struct EvalFunctor_ { EvalFunctor_(const E& e, S& v, TFunc& f) : - MyEvent{ e }, - MyValue{ v }, - MyFunc{ f } + MyEvent( e ), + MyValue( v ), + MyFunc( f ) {} void operator()(const std::shared_ptr>& ... args) @@ -312,7 +323,7 @@ class SyncedIterateByRefNode : public SignalNode if (! events_->Events().empty()) { for (const auto& e : events_->Events()) - apply(EvalFunctor_{ e, value_, func_ }, deps_); + apply(EvalFunctor_( e, this->value_, func_ ), deps_); changed = true; } @@ -348,11 +359,13 @@ template > class HoldNode : public SignalNode { + using Engine = typename HoldNode::Engine; + public: template HoldNode(T&& init, const std::shared_ptr>& events) : - SignalNode(std::forward(init)), - events_{ events } + HoldNode::SignalNode( std::forward(init) ), + events_( events ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *events_); @@ -380,10 +393,10 @@ class HoldNode : public SignalNode { const S& newValue = events_->Events().back(); - if (! Equals(newValue, value_)) + if (! Equals(newValue, this->value_)) { changed = true; - value_ = newValue; + this->value_ = newValue; } } @@ -413,12 +426,14 @@ template > class SnapshotNode : public SignalNode { + using Engine = typename SnapshotNode::Engine; + public: SnapshotNode(const std::shared_ptr>& target, const std::shared_ptr>& trigger) : - SignalNode{ target->ValueRef() }, - target_{ target }, - trigger_{ trigger } + SnapshotNode::SignalNode( target->ValueRef() ), + target_( target ), + trigger_( trigger ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *target_); @@ -451,10 +466,10 @@ class SnapshotNode : public SignalNode { const S& newValue = target_->ValueRef(); - if (! Equals(newValue, value_)) + if (! Equals(newValue, this->value_)) { changed = true; - value_ = newValue; + this->value_ = newValue; } } @@ -482,10 +497,12 @@ template > class MonitorNode : public EventStreamNode { + using Engine = typename MonitorNode::Engine; + public: MonitorNode(const std::shared_ptr>& target) : - EventStreamNode{ }, - target_{ target } + MonitorNode::EventStreamNode( ), + target_( target ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *target_); @@ -505,17 +522,17 @@ class MonitorNode : public EventStreamNode using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); - SetCurrentTurn(turn, true); + this->SetCurrentTurn(turn, true); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - events_.push_back(target_->ValueRef()); + this->events_.push_back(target_->ValueRef()); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - if (! events_.empty()) + if (! this->events_.empty()) Engine::OnNodePulse(*this, turn); else Engine::OnNodeIdlePulse(*this, turn); @@ -536,12 +553,14 @@ template > class PulseNode : public EventStreamNode { + using Engine = typename PulseNode::Engine; + public: PulseNode(const std::shared_ptr>& target, const std::shared_ptr>& trigger) : - EventStreamNode{ }, - target_{ target }, - trigger_{ trigger } + PulseNode::EventStreamNode( ), + target_( target ), + trigger_( trigger ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *target_); @@ -563,19 +582,19 @@ class PulseNode : public EventStreamNode typedef typename D::Engine::TurnT TurnT; TurnT& turn = *reinterpret_cast(turnPtr); - SetCurrentTurn(turn, true); + this->SetCurrentTurn(turn, true); trigger_->SetCurrentTurn(turn); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); for (const auto& e : trigger_->Events()) - events_.push_back(target_->ValueRef()); + this->events_.push_back(target_->ValueRef()); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - if (! events_.empty()) + if (! this->events_.empty()) Engine::OnNodePulse(*this, turn); else Engine::OnNodeIdlePulse(*this, turn); @@ -586,4 +605,6 @@ class PulseNode : public EventStreamNode const std::shared_ptr> trigger_; }; -/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file +/****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_GRAPH_ALGORITHMNODES_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 43b3af3a..792185dc 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_GRAPH_EVENTNODES_H_INCLUDED +#define REACT_DETAIL_GRAPH_EVENTNODES_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -38,7 +41,7 @@ struct BufferClearAccessPolicy : private ConditionalCriticalSection { template - void AccessBufferForClearing(const F& f) { Access(f); } + void AccessBufferForClearing(const F& f) { this->Access(f); } }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -62,7 +65,7 @@ class EventStreamNode : void SetCurrentTurn(const TurnT& turn, bool forceUpdate = false, bool noClear = false) { - AccessBufferForClearing([&] { + this->AccessBufferForClearing([&] { if (curTurnId_ != turn.Id() || forceUpdate) { curTurnId_ = turn.Id(); @@ -76,7 +79,9 @@ class EventStreamNode : protected: DataT events_; - uint curTurnId_ = (std::numeric_limits::max)(); + +private: + uint curTurnId_ { (std::numeric_limits::max)() }; }; template @@ -94,9 +99,11 @@ class EventSourceNode : public EventStreamNode, public IInputNode { + using Engine = typename EventSourceNode::Engine; + public: EventSourceNode() : - EventStreamNode{ } + EventSourceNode::EventStreamNode{ } { Engine::OnNodeCreate(*this); } @@ -122,20 +129,20 @@ class EventSourceNode : if (changedFlag_) { changedFlag_ = false; - events_.clear(); + this->events_.clear(); } - events_.push_back(std::forward(v)); + this->events_.push_back(std::forward(v)); } virtual bool ApplyInput(void* turnPtr) override { - if (events_.size() > 0 && !changedFlag_) + if (this->events_.size() > 0 && !changedFlag_) { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); - SetCurrentTurn(turn, true, true); + this->SetCurrentTurn(turn, true, true); changedFlag_ = true; Engine::OnTurnInputChange(*this, turn); return true; @@ -163,23 +170,23 @@ class EventMergeOp : public ReactiveOpBase public: template EventMergeOp(TDepsIn&& ... deps) : - ReactiveOpBase(DontMove{}, std::forward(deps) ...) + EventMergeOp::ReactiveOpBase(DontMove(), std::forward(deps) ...) {} EventMergeOp(EventMergeOp&& other) : - ReactiveOpBase{ std::move(other) } + EventMergeOp::ReactiveOpBase( std::move(other) ) {} template void Collect(const TTurn& turn, const TCollector& collector) const { - apply(CollectFunctor{ turn, collector }, deps_); + apply(CollectFunctor( turn, collector ), this->deps_); } template void CollectRec(const TFunctor& functor) const { - apply(reinterpret_cast&>(functor), deps_); + apply(reinterpret_cast&>(functor), this->deps_); } private: @@ -187,8 +194,8 @@ class EventMergeOp : public ReactiveOpBase struct CollectFunctor { CollectFunctor(const TTurn& turn, const TCollector& collector) : - MyTurn{ turn }, - MyCollector{ collector } + MyTurn( turn ), + MyCollector( collector ) {} void operator()(const TDeps& ... deps) const @@ -199,7 +206,7 @@ class EventMergeOp : public ReactiveOpBase template void collect(const T& op) const { - op.CollectRec(*this); + op.template CollectRec(*this); } template @@ -230,13 +237,13 @@ class EventFilterOp : public ReactiveOpBase public: template EventFilterOp(TFilterIn&& filter, TDepIn&& dep) : - ReactiveOpBase{ DontMove{}, std::forward(dep) }, - filter_{ std::forward(filter) } + EventFilterOp::ReactiveOpBase{ DontMove(), std::forward(dep) }, + filter_( std::forward(filter) ) {} EventFilterOp(EventFilterOp&& other) : - ReactiveOpBase{ std::move(other) }, - filter_{ std::move(other.filter_) } + EventFilterOp::ReactiveOpBase{ std::move(other) }, + filter_( std::move(other.filter_) ) {} template @@ -253,14 +260,14 @@ class EventFilterOp : public ReactiveOpBase } private: - const TDep& getDep() const { return std::get<0>(deps_); } + const TDep& getDep() const { return std::get<0>(this->deps_); } template struct FilteredEventCollector { FilteredEventCollector(const TFilter& filter, const TCollector& collector) : - MyFilter{ filter }, - MyCollector{ collector } + MyFilter( filter ), + MyCollector( collector ) {} void operator()(const E& e) const @@ -308,19 +315,19 @@ class EventTransformOp : public ReactiveOpBase public: template EventTransformOp(TFuncIn&& func, TDepIn&& dep) : - ReactiveOpBase{ DontMove{}, std::forward(dep) }, - func_{ std::forward(func) } + EventTransformOp::ReactiveOpBase( DontMove(), std::forward(dep) ), + func_( std::forward(func) ) {} EventTransformOp(EventTransformOp&& other) : - ReactiveOpBase{ std::move(other) }, - func_{ std::move(other.func_) } + EventTransformOp::ReactiveOpBase( std::move(other) ), + func_( std::move(other.func_) ) {} template void Collect(const TTurn& turn, const TCollector& collector) const { - collectImpl(turn, TransformEventCollector{ func_, collector }, getDep()); + collectImpl(turn, TransformEventCollector( func_, collector ), getDep()); } template @@ -331,14 +338,14 @@ class EventTransformOp : public ReactiveOpBase } private: - const TDep& getDep() const { return std::get<0>(deps_); } + const TDep& getDep() const { return std::get<0>(this->deps_); } template struct TransformEventCollector { TransformEventCollector(const TFunc& func, const TTarget& target) : - MyFunc{ func }, - MyTarget{ target } + MyFunc( func ), + MyTarget( target ) {} void operator()(const E& e) const @@ -379,20 +386,22 @@ template > class EventOpNode : public EventStreamNode { + using Engine = typename EventOpNode::Engine; + public: template EventOpNode(TArgs&& ... args) : - EventStreamNode{ }, - op_{ std::forward(args) ... } + EventOpNode::EventStreamNode( ), + op_( std::forward(args) ... ) { Engine::OnNodeCreate(*this); - op_.Attach(*this); + op_.template Attach(*this); } ~EventOpNode() { if (!wasOpStolen_) - op_.Detach(*this); + op_.template Detach(*this); Engine::OnNodeDestroy(*this); } @@ -404,38 +413,36 @@ class EventOpNode : public EventStreamNode using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); - SetCurrentTurn(turn, true); + this->SetCurrentTurn(turn, true); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - op_.Collect(turn, EventCollector{ Events() }); + op_.Collect(turn, EventCollector{ this->events_ }); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - if (! events_.empty()) - { + if (! this->events_.empty()) Engine::OnNodePulse(*this, turn); - } else - { Engine::OnNodeIdlePulse(*this, turn); - } } TOp StealOp() { REACT_ASSERT(wasOpStolen_ == false, "Op was already stolen."); wasOpStolen_ = true; - op_.Detach(*this); + op_.template Detach(*this); return std::move(op_); } private: struct EventCollector { - EventCollector(DataT& events) : MyEvents{ events } {} + using DataT = typename EventOpNode::DataT; + + EventCollector(DataT& events) : MyEvents( events ) {} void operator()(const E& e) const { MyEvents.push_back(e); } @@ -457,12 +464,14 @@ template > class EventFlattenNode : public EventStreamNode { + using Engine = typename EventFlattenNode::Engine; + public: EventFlattenNode(const std::shared_ptr>& outer, const std::shared_ptr>& inner) : - EventStreamNode{ }, - outer_{ outer }, - inner_{ inner } + EventFlattenNode::EventStreamNode( ), + outer_( outer ), + inner_( inner ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *outer_); @@ -485,7 +494,7 @@ class EventFlattenNode : public EventStreamNode typedef typename D::Engine::TurnT TurnT; TurnT& turn = *reinterpret_cast(turnPtr); - SetCurrentTurn(turn, true); + this->SetCurrentTurn(turn, true); inner_->SetCurrentTurn(turn); auto newInner = outer_->ValueRef().NodePtr(); @@ -507,19 +516,18 @@ class EventFlattenNode : public EventStreamNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - events_.insert(events_.end(), inner_->Events().begin(), inner_->Events().end()); + this->events_.insert( + this->events_.end(), + inner_->Events().begin(), + inner_->Events().end()); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - if (events_.size() > 0) - { + if (this->events_.size() > 0) Engine::OnNodePulse(*this, turn); - } else - { Engine::OnNodeIdlePulse(*this, turn); - } } private: @@ -540,14 +548,16 @@ template > class SyncedEventTransformNode : public EventStreamNode { + using Engine = typename SyncedEventTransformNode::Engine; + public: template SyncedEventTransformNode(const std::shared_ptr>& source, F&& func, const std::shared_ptr>& ... deps) : - EventStreamNode{ }, - source_{ source }, - func_{ std::forward(func) }, - deps_{ deps ... } + SyncedEventTransformNode::EventStreamNode( ), + source_( source ), + func_( std::forward(func) ), + deps_( deps ... ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *source); @@ -560,7 +570,7 @@ class SyncedEventTransformNode : public EventStreamNode apply( DetachFunctor>...>{ *this }, + std::shared_ptr>...>( *this ), deps_); Engine::OnNodeDestroy(*this); @@ -574,8 +584,8 @@ class SyncedEventTransformNode : public EventStreamNode struct EvalFunctor_ { EvalFunctor_(const TIn& e, TFunc& f) : - MyEvent{ e }, - MyFunc{ f } + MyEvent( e ), + MyFunc( f ) {} TOut operator()(const std::shared_ptr>& ... args) @@ -590,7 +600,7 @@ class SyncedEventTransformNode : public EventStreamNode using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); - SetCurrentTurn(turn, true); + this->SetCurrentTurn(turn, true); // Update of this node could be triggered from deps, // so make sure source doesnt contain events from last turn source_->SetCurrentTurn(turn); @@ -599,12 +609,12 @@ class SyncedEventTransformNode : public EventStreamNode GetObjectId(*this), turn.Id())); for (const auto& e : source_->Events()) - events_.push_back(apply(EvalFunctor_{ e, func_ }, deps_)); + this->events_.push_back(apply(EvalFunctor_( e, func_ ), deps_)); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - if (! events_.empty()) + if (! this->events_.empty()) Engine::OnNodePulse(*this, turn); else Engine::OnNodeIdlePulse(*this, turn); @@ -631,14 +641,16 @@ template > class SyncedEventFilterNode : public EventStreamNode { + using Engine = typename SyncedEventFilterNode::Engine; + public: template SyncedEventFilterNode(const std::shared_ptr>& source, F&& filter, const std::shared_ptr>& ... deps) : - EventStreamNode{ }, - source_{ source }, - filter_{ std::forward(filter) }, - deps_{ deps ... } + SyncedEventFilterNode::EventStreamNode( ), + source_( source ), + filter_( std::forward(filter) ), + deps_(deps ... ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *source); @@ -651,7 +663,7 @@ class SyncedEventFilterNode : public EventStreamNode apply( DetachFunctor>...>{ *this }, + std::shared_ptr>...>( *this ), deps_); Engine::OnNodeDestroy(*this); @@ -665,8 +677,8 @@ class SyncedEventFilterNode : public EventStreamNode struct EvalFunctor_ { EvalFunctor_(const E& e, TFunc& f) : - MyEvent{ e }, - MyFilter{ f } + MyEvent( e ), + MyFilter( f ) {} bool operator()(const std::shared_ptr>& ... args) @@ -681,7 +693,7 @@ class SyncedEventFilterNode : public EventStreamNode using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); - SetCurrentTurn(turn, true); + this->SetCurrentTurn(turn, true); // Update of this node could be triggered from deps, // so make sure source doesnt contain events from last turn source_->SetCurrentTurn(turn); @@ -690,13 +702,13 @@ class SyncedEventFilterNode : public EventStreamNode GetObjectId(*this), turn.Id())); for (const auto& e : source_->Events()) - if (apply(EvalFunctor_{ e, filter_ }, deps_)) - events_.push_back(e); + if (apply(EvalFunctor_( e, filter_ ), deps_)) + this->events_.push_back(e); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - if (! events_.empty()) + if (! this->events_.empty()) Engine::OnNodePulse(*this, turn); else Engine::OnNodeIdlePulse(*this, turn); @@ -712,3 +724,5 @@ class SyncedEventFilterNode : public EventStreamNode }; /****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_GRAPH_EVENTNODES_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index 42a94e69..e182d2bc 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_GRAPH_GRAPHBASE_H_INCLUDED +#define REACT_DETAIL_GRAPH_GRAPHBASE_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -27,16 +30,42 @@ template typename D, long long threshold > -struct UpdateTimingPolicy : +class UpdateTimingPolicy : private ConditionalTimer { - class ScopedUpdateTimer : public ScopedTimer + using ScopedTimer = typename UpdateTimingPolicy::ScopedTimer; + +public: + class ScopedUpdateTimer : private ScopedTimer { public: - ScopedUpdateTimer(UpdateTimingPolicy& parent) : ScopedTimer{ parent } {} + ScopedUpdateTimer(UpdateTimingPolicy& parent) : + ScopedTimer( parent ) {} }; - bool IsUpdateThresholdExceeded() const { return IsThresholdExceeded(); } + bool IsUpdateThresholdExceeded() const { return this->IsThresholdExceeded(); } +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// DepCounter +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct CountHelper { static const int value = T::dependency_count; }; + +template +struct CountHelper> { static const int value = 1; }; + +template +struct DepCounter; + +template <> +struct DepCounter<0> { static int const value = 0; }; + +template +struct DepCounter +{ + static int const value = + CountHelper::type>::value + DepCounter::value; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -62,7 +91,7 @@ class NodeBase : std::shared_ptr GetSharedPtr() const { - return shared_from_this(); + return this->shared_from_this(); } }; @@ -93,7 +122,7 @@ class ReactiveNode : public NodeBase template struct AttachFunctor { - AttachFunctor(TNode& node) : MyNode{ node } {} + AttachFunctor(TNode& node) : MyNode( node ) {} void operator()(const TDeps& ...deps) const { @@ -103,7 +132,7 @@ struct AttachFunctor template void attach(const T& op) const { - op.AttachRec(*this); + op.template AttachRec(*this); } template @@ -118,7 +147,7 @@ struct AttachFunctor template struct DetachFunctor { - DetachFunctor(TNode& node) : MyNode{ node } {} + DetachFunctor(TNode& node) : MyNode( node ) {} void operator()(const TDeps& ... deps) const { @@ -128,7 +157,7 @@ struct DetachFunctor template void detach(const T& op) const { - op.DetachRec(*this); + op.template DetachRec(*this); } template @@ -151,11 +180,11 @@ class ReactiveOpBase template ReactiveOpBase(DontMove, TDepsIn&& ... deps) : - deps_{ std::forward(deps) ... } + deps_( std::forward(deps) ... ) {} ReactiveOpBase(ReactiveOpBase&& other) : - deps_{ std::move(other.deps_) } + deps_( std::move(other.deps_) ) {} // Can't be copied, only moved @@ -186,26 +215,6 @@ class ReactiveOpBase apply(reinterpret_cast&>(functor), deps_); } - // Dependency counting - template - struct CountHelper { static const int value = T::dependency_count; }; - - template - struct CountHelper> { static const int value = 1; }; - - template - struct DepCounter; - - template <> - struct DepCounter<0> { static int const value = 0; }; - - template - struct DepCounter - { - static int const value = - CountHelper::type>::value + DepCounter::value; - }; - public: static const int dependency_count = DepCounter::value; @@ -213,4 +222,7 @@ class ReactiveOpBase DepHolderT deps_; }; + /****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_GRAPH_GRAPHBASE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index cfa788c2..794a4f39 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_GRAPH_OBSERVERNODES_H_INCLUDED +#define REACT_DETAIL_GRAPH_OBSERVERNODES_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -49,12 +52,14 @@ template > class SignalObserverNode : public ObserverNode { + using Engine = typename SignalObserverNode::Engine; + public: template SignalObserverNode(const std::shared_ptr>& subject, F&& func) : - ObserverNode{ }, - subject_{ subject }, - func_{ std::forward(func) } + SignalObserverNode::ObserverNode( ), + subject_( subject ), + func_( std::forward(func) ) { Engine::OnNodeCreate(*this); subject->IncObsCount(); @@ -120,12 +125,14 @@ template > class EventObserverNode : public ObserverNode { + using Engine = typename EventObserverNode::Engine; + public: template EventObserverNode(const std::shared_ptr>& subject, F&& func) : - ObserverNode{ }, - subject_{ subject }, - func_{ std::forward(func) } + EventObserverNode::ObserverNode( ), + subject_( subject ), + func_( std::forward(func) ) { Engine::OnNodeCreate(*this); subject->IncObsCount(); @@ -196,15 +203,17 @@ template > class SyncedObserverNode : public ObserverNode { + using Engine = typename SyncedObserverNode::Engine; + public: // NOTE: After upgrading to VS2013 Udpate2, using std::shared_ptr here crashes the compiler template SyncedObserverNode(const std::shared_ptr>& subject, F&& func, const std::shared_ptr>& ... deps) : - ObserverNode{ }, - subject_{ subject }, - func_{ std::forward(func) }, - deps_{ deps ... } + SyncedObserverNode::ObserverNode( ), + subject_( subject ), + func_( std::forward(func) ), + deps_( deps ... ) { Engine::OnNodeCreate(*this); subject->IncObsCount(); @@ -226,8 +235,8 @@ class SyncedObserverNode : public ObserverNode struct EvalFunctor_ { EvalFunctor_(const E& e, TFunc& f) : - MyEvent{ e }, - MyFunc{ f } + MyEvent( e ), + MyFunc( f ) {} void operator()(const std::shared_ptr>& ... args) @@ -286,7 +295,7 @@ class SyncedObserverNode : public ObserverNode apply( DetachFunctor>...>{ *this }, + std::shared_ptr>...>( *this ), deps_); subject_.reset(); @@ -295,3 +304,5 @@ class SyncedObserverNode : public ObserverNode }; /****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_GRAPH_OBSERVERNODES_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/ReactorNodes.h b/include/react/detail/graph/ReactorNodes.h index 9549c1b8..0e737c58 100644 --- a/include/react/detail/graph/ReactorNodes.h +++ b/include/react/detail/graph/ReactorNodes.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_GRAPH_REACTORNODES_H_INCLUDED +#define REACT_DETAIL_GRAPH_REACTORNODES_H_INCLUDED + #pragma once #ifndef REACT_DISABLE_REACTORS @@ -212,4 +215,6 @@ class ReactorNode : /****************************************/ REACT_IMPL_END /***************************************/ -#endif //REACT_DISABLE_REACTORS \ No newline at end of file +#endif // REACT_DISABLE_REACTORS + +#endif // REACT_DETAIL_GRAPH_REACTORNODES_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index 4f1d6ca7..28bd80ed 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -6,6 +6,9 @@ #pragma once +#ifndef REACT_DETAIL_GRAPH_SIGNALNODES_H_INCLUDED +#define REACT_DETAIL_GRAPH_SIGNALNODES_H_INCLUDED + #include "react/detail/Defs.h" #include @@ -36,8 +39,8 @@ class SignalNode : public ReactiveNode template explicit SignalNode(T&& value) : - ReactiveNode{ }, - value_{ std::forward(value) } + SignalNode::ReactiveNode( ), + value_( std::forward(value) ) {} const S& ValueRef() const @@ -64,11 +67,13 @@ class VarNode : public SignalNode, public IInputNode { + using Engine = typename VarNode::Engine; + public: template VarNode(T&& value) : - SignalNode{ std::forward(value) }, - newValue_{ value_ } + VarNode::SignalNode( std::forward(value) ), + newValue_( value ) { Engine::OnNodeCreate(*this); } @@ -106,7 +111,7 @@ class VarNode : // There hasn't been any Set(...) input yet, modify. if (! isInputAdded_) { - func(value_); + func(this->value_); isInputModified_ = true; } @@ -128,9 +133,9 @@ class VarNode : { isInputAdded_ = false; - if (! Equals(value_, newValue_)) + if (! Equals(this->value_, newValue_)) { - value_ = std::move(newValue_); + this->value_ = std::move(newValue_); Engine::OnTurnInputChange(*this, turn); return true; } @@ -173,25 +178,25 @@ class FunctionOp : public ReactiveOpBase public: template FunctionOp(FIn&& func, TDepsIn&& ... deps) : - ReactiveOpBase{ DontMove{}, std::forward(deps) ... }, - func_{ std::forward(func) } + FunctionOp::ReactiveOpBase( DontMove(), std::forward(deps) ... ), + func_( std::forward(func) ) {} FunctionOp(FunctionOp&& other) : - ReactiveOpBase{ std::move(other) }, - func_{ std::move(other.func_) } + FunctionOp::ReactiveOpBase( std::move(other) ), + func_( std::move(other.func_) ) {} S Evaluate() { - return apply(EvalFunctor{ func_ }, deps_); + return apply(EvalFunctor( func_ ), this->deps_); } private: // Eval struct EvalFunctor { - EvalFunctor(F& f) : MyFunc{ f } {} + EvalFunctor(F& f) : MyFunc( f ) {} template S operator()(T&& ... args) @@ -231,22 +236,24 @@ class SignalOpNode : public SignalNode, public UpdateTimingPolicy { + using Engine = typename SignalOpNode::Engine; + public: template SignalOpNode(TArgs&& ... args) : - SignalNode{ }, - op_{ std::forward(args) ... } + SignalOpNode::SignalNode( ), + op_( std::forward(args) ... ) { - value_ = op_.Evaluate(); + this->value_ = op_.Evaluate(); Engine::OnNodeCreate(*this); - op_.Attach(*this); + op_.template Attach(*this); } ~SignalOpNode() { if (!wasOpStolen_) - op_.Detach(*this); + op_.template Detach(*this); Engine::OnNodeDestroy(*this); } @@ -264,13 +271,14 @@ class SignalOpNode : bool changed = false; {// timer - ScopedUpdateTimer scopedTimer{ *this }; + using TimerT = typename SignalOpNode::ScopedUpdateTimer; + TimerT scopedTimer( *this ); S newValue = op_.Evaluate(); - if (! REACT_IMPL::Equals(value_, newValue)) + if (! Equals(this->value_, newValue)) { - value_ = std::move(newValue); + this->value_ = std::move(newValue); changed = true; } }// ~timer @@ -286,14 +294,14 @@ class SignalOpNode : virtual bool IsHeavyweight() const override { - return IsUpdateThresholdExceeded(); + return this->IsUpdateThresholdExceeded(); } TOp StealOp() { REACT_ASSERT(wasOpStolen_ == false, "Op was already stolen."); wasOpStolen_ = true; - op_.Detach(*this); + op_.template Detach(*this); return std::move(op_); } @@ -313,12 +321,14 @@ template > class FlattenNode : public SignalNode { + using Engine = typename FlattenNode::Engine; + public: FlattenNode(const std::shared_ptr>& outer, const std::shared_ptr>& inner) : - SignalNode{ inner->ValueRef() }, - outer_{ outer }, - inner_{ inner } + FlattenNode::SignalNode( inner->ValueRef() ), + outer_( outer ), + inner_( inner ) { Engine::OnNodeCreate(*this); Engine::OnNodeAttach(*this, *outer_); @@ -361,9 +371,9 @@ class FlattenNode : public SignalNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - if (! REACT_IMPL::Equals(value_, inner_->ValueRef())) + if (! Equals(this->value_, inner_->ValueRef())) { - value_ = inner_->ValueRef(); + this->value_ = inner_->ValueRef(); Engine::OnNodePulse(*this, turn); } else @@ -378,3 +388,5 @@ class FlattenNode : public SignalNode }; /****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_GRAPH_SIGNALNODES_H_INCLUDED \ No newline at end of file diff --git a/include/react/engine/PulseCountEngine.h b/include/react/engine/PulseCountEngine.h index 8d58b1da..6c0ae9ae 100644 --- a/include/react/engine/PulseCountEngine.h +++ b/include/react/engine/PulseCountEngine.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_ENGINE_PULSECOUNTENGINE_H_INCLUDED +#define REACT_DETAIL_ENGINE_PULSECOUNTENGINE_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -84,11 +87,11 @@ class Node : public IReactiveNode ShiftMutexT ShiftMutex; NodeVector Successors; - ENodeState State = ENodeState::unchanged; + ENodeState State { ENodeState::unchanged }; private: - atomic counter_ = 0; - atomic mark_ = ENodeMark::unmarked; + atomic counter_ { 0 }; + atomic mark_ { ENodeMark::unmarked }; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -115,7 +118,7 @@ class EngineBase : public IReactiveEngine private: NodeVectT changedInputs_; - empty_task* rootTask_ = new(task::allocate_root()) empty_task; + empty_task* rootTask_ { new(task::allocate_root()) empty_task }; task_list spawnList_; }; @@ -151,4 +154,6 @@ template struct EnableParallelUpdating; template <> struct EnableParallelUpdating> : std::true_type {}; template <> struct EnableParallelUpdating> : std::true_type {}; -/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file +/****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_ENGINE_PULSECOUNTENGINE_H_INCLUDED \ No newline at end of file diff --git a/include/react/engine/SubtreeEngine.h b/include/react/engine/SubtreeEngine.h index 615feb56..fafd9bd5 100644 --- a/include/react/engine/SubtreeEngine.h +++ b/include/react/engine/SubtreeEngine.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_ENGINE_SUBTREEENGINE_H_INCLUDED +#define REACT_DETAIL_ENGINE_SUBTREEENGINE_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -93,9 +96,9 @@ class Node : public IReactiveNode NodeVector Successors; ShiftMutexT ShiftMutex; - uint16_t Level = 0; - uint16_t NewLevel = 0; - uint16_t WaitCount = 0; + uint16_t Level { 0 }; + uint16_t NewLevel { 0 }; + uint16_t WaitCount { 0 }; private: enum EFlags : uint16_t @@ -109,8 +112,8 @@ class Node : public IReactiveNode }; EnumFlags flags_; - atomic readyCount_ = 0; - atomic shouldUpdate_ = false; + atomic readyCount_ { 0 }; + atomic shouldUpdate_ { false }; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -156,10 +159,10 @@ class EngineBase : public IReactiveEngine TopoQueueT scheduledNodes_; vector subtreeRoots_; - empty_task* rootTask_ = new(task::allocate_root()) empty_task; + empty_task* rootTask_ { new(task::allocate_root()) empty_task }; task_list spawnList_; - bool isInPhase2_ = false; + bool isInPhase2_ { false }; }; class BasicEngine : public EngineBase {}; @@ -194,4 +197,6 @@ template struct EnableParallelUpdating; template <> struct EnableParallelUpdating> : std::true_type {}; template <> struct EnableParallelUpdating> : std::true_type {}; -/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file +/****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_ENGINE_SUBTREEENGINE_H_INCLUDED \ No newline at end of file diff --git a/include/react/engine/ToposortEngine.h b/include/react/engine/ToposortEngine.h index ca10193e..8de56945 100644 --- a/include/react/engine/ToposortEngine.h +++ b/include/react/engine/ToposortEngine.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_ENGINE_TOPOSORTENGINE_H_INCLUDED +#define REACT_DETAIL_ENGINE_TOPOSORTENGINE_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -59,11 +62,11 @@ static const uint grain_size = 100; class SeqNode : public IReactiveNode { public: - int Level = 0; - int NewLevel = 0; - bool Queued = false; + int Level { 0 }; + int NewLevel { 0 }; + bool Queued { false }; - NodeVector Successors; + NodeVector Successors; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -74,12 +77,12 @@ class ParNode : public IReactiveNode public: using InvalidateMutexT = spin_mutex; - int Level = 0; - int NewLevel = 0; - atomic Collected = false; + int Level { 0 }; + int NewLevel { 0 }; + atomic Collected { false }; - NodeVector Successors; - InvalidateMutexT InvalidateMutex; + NodeVector Successors; + InvalidateMutexT InvalidateMutex; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -276,14 +279,18 @@ class PipeliningTurn : public TurnBase IntervalSetT levelIntervals_; - PipeliningTurn* predecessor_ = nullptr; - PipeliningTurn* successor_ = nullptr; + PipeliningTurn* predecessor_ { nullptr }; + PipeliningTurn* successor_ { nullptr }; + + int currentLevel_ { -1 }; - int currentLevel_ = -1; - int maxLevel_ = (numeric_limits::max)(); /// This turn may only advance up to maxLevel - int minLevel_ = -1; /// successor.maxLevel = this.minLevel - 1 + /// This turn may only advance up to maxLevel + int maxLevel_ { (numeric_limits::max)() }; - int curUpperBound_ = -1; + /// successor.maxLevel = this.minLevel - 1 + int minLevel_ { -1 }; + + int curUpperBound_ { -1 }; mutex advMutex_; condition_variable advCondition_; @@ -344,7 +351,7 @@ class PipeliningEngine : public IReactiveEngine void advanceTurn(PipeliningTurn& turn); SeqMutexT seqMutex_; - PipeliningTurn* tail_ = nullptr; + PipeliningTurn* tail_ { nullptr }; NodeSetT dynamicNodes_; int maxDynamicLevel_; @@ -402,3 +409,5 @@ template <> struct EnableConcurrentInput> : std:: template <> struct EnableConcurrentInput> : std::true_type {}; /****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_ENGINE_TOPOSORTENGINE_H_INCLUDED \ No newline at end of file diff --git a/include/react/logging/EventLog.h b/include/react/logging/EventLog.h index ec0b70d4..e9622c7b 100644 --- a/include/react/logging/EventLog.h +++ b/include/react/logging/EventLog.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_LOGGING_EVENTLOG_H_INCLUDED +#define REACT_DETAIL_LOGGING_EVENTLOG_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -93,3 +96,5 @@ class EventLog }; /****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_LOGGING_EVENTLOG_H_INCLUDED \ No newline at end of file diff --git a/include/react/logging/EventRecords.h b/include/react/logging/EventRecords.h index 12b63cf1..ec064db9 100644 --- a/include/react/logging/EventRecords.h +++ b/include/react/logging/EventRecords.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_LOGGING_EVENTRECORDS_H_INCLUDED +#define REACT_DETAIL_LOGGING_EVENTRECORDS_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -255,3 +258,5 @@ class UserBreakpointEvent : public IEventRecord }; /****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_LOGGING_EVENTRECORDS_H_INCLUDED \ No newline at end of file diff --git a/include/react/logging/Logging.h b/include/react/logging/Logging.h index 1d67699d..c5546883 100644 --- a/include/react/logging/Logging.h +++ b/include/react/logging/Logging.h @@ -4,6 +4,9 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#ifndef REACT_DETAIL_LOGGING_LOGGING_H_INCLUDED +#define REACT_DETAIL_LOGGING_LOGGING_H_INCLUDED + #pragma once #include "react/detail/Defs.h" @@ -24,3 +27,5 @@ struct IEventRecord }; /****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_LOGGING_LOGGING_H_INCLUDED \ No newline at end of file diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index ea023341..0d9292cf 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -79,6 +79,7 @@ + @@ -94,6 +95,7 @@ + diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index 48ac8438..8b294c83 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -147,6 +147,12 @@ Header Files\common + + Header Files\detail + + + Header Files\detail + diff --git a/src/benchmark/BenchmarkLifeSim.h b/src/benchmark/BenchmarkLifeSim.h index 15b0722f..e6e3bbb6 100644 --- a/src/benchmark/BenchmarkLifeSim.h +++ b/src/benchmark/BenchmarkLifeSim.h @@ -150,7 +150,7 @@ class World : public ReactiveObject return r.get(); } - printf("Out of bounds %d %d\n", pos.first, pos.second); + //printf("Out of bounds %d %d\n", pos.first, pos.second); assert(false); return nullptr; } diff --git a/src/engine/PulsecountEngine.cpp b/src/engine/PulsecountEngine.cpp index 955d040c..d56b22eb 100644 --- a/src/engine/PulsecountEngine.cpp +++ b/src/engine/PulsecountEngine.cpp @@ -88,17 +88,17 @@ class UpdaterTask: public task template UpdaterTask(TTurn& turn, TInput srcBegin, TInput srcEnd) : - turn_{ turn }, + turn_( turn ), // For now, GCC requires still parenthesis here nodes_{ srcBegin, srcEnd } {} UpdaterTask(TTurn& turn, Node* node) : - turn_{ turn }, + turn_( turn ), nodes_{ node } {} UpdaterTask(UpdaterTask& other, SplitTag) : - turn_{ other.turn_ }, + turn_( other.turn_ ), nodes_{ other.nodes_, SplitTag{} } {} diff --git a/src/engine/SubtreeEngine.cpp b/src/engine/SubtreeEngine.cpp index 30885289..7debdb4a 100644 --- a/src/engine/SubtreeEngine.cpp +++ b/src/engine/SubtreeEngine.cpp @@ -63,12 +63,12 @@ class UpdaterTask: public task using BufferT = NodeBuffer; UpdaterTask(TTurn& turn, Node* node) : - turn_{ turn }, + turn_( turn ), // For now, GCC requires still parenthesis here nodes_{ node } {} UpdaterTask(UpdaterTask& other, SplitTag) : - turn_{ other.turn_ }, + turn_( other.turn_ ), nodes_{ other.nodes_, SplitTag{} } {} diff --git a/src/engine/ToposortEngine.cpp b/src/engine/ToposortEngine.cpp index 3d9bcb98..392f2e95 100644 --- a/src/engine/ToposortEngine.cpp +++ b/src/engine/ToposortEngine.cpp @@ -87,7 +87,7 @@ void SeqEngineBase::OnTurnPropagate(TTurn& turn) template void SeqEngineBase::OnDynamicNodeAttach(SeqNode& node, SeqNode& parent, TTurn& turn) { - OnNodeAttach(node, parent); + this->OnNodeAttach(node, parent); invalidateSuccessors(node); @@ -99,7 +99,7 @@ void SeqEngineBase::OnDynamicNodeAttach(SeqNode& node, SeqNode& parent, T template void SeqEngineBase::OnDynamicNodeDetach(SeqNode& node, SeqNode& parent, TTurn& turn) { - OnNodeDetach(node, parent); + this->OnNodeDetach(node, parent); } template @@ -197,7 +197,7 @@ void ParEngineBase::OnDynamicNodeDetach(ParNode& node, ParNode& parent, T template void ParEngineBase::applyDynamicAttach(ParNode& node, ParNode& parent, TTurn& turn) { - OnNodeAttach(node, parent); + this->OnNodeAttach(node, parent); invalidateSuccessors(node); @@ -209,7 +209,7 @@ void ParEngineBase::applyDynamicAttach(ParNode& node, ParNode& parent, TT template void ParEngineBase::applyDynamicDetach(ParNode& node, ParNode& parent, TTurn& turn) { - OnNodeDetach(node, parent); + this->OnNodeDetach(node, parent); } template diff --git a/src/sandbox/Main.cpp b/src/sandbox/Main.cpp index 12f3637d..3992e3b9 100644 --- a/src/sandbox/Main.cpp +++ b/src/sandbox/Main.cpp @@ -13,7 +13,7 @@ //#define REACT_ENABLE_LOGGING // Experimental. Requires boost::coroutine. -#define REACT_ENABLE_REACTORS +//#define REACT_ENABLE_REACTORS #include "react/Domain.h" #include "react/Signal.h" @@ -21,6 +21,10 @@ #include "react/Algorithm.h" #include "react/ReactiveObject.h" +#ifdef REACT_ENABLE_REACTORS + #include "react/Reactor.h" +#endif + using namespace std; using namespace react; @@ -277,7 +281,6 @@ void IterateExample1() } #ifdef REACT_ENABLE_REACTORS -#include "react/Reactor.h" void LoopTest() { From a87627f4961a7688215071857c2588d34c74fcf2 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sat, 31 May 2014 18:01:01 +0200 Subject: [PATCH 123/266] More refactoring. Tests and benchmarks now compile with GCC and clang. * The function aliases for MakeVar etc. in Domain have been removed. They were intended as syntactic sugar, but if the domain is a dependent type, D::template MakeEventSource defeats the purpose. Using MakeEventSource directly simpler in this case, and using it all the time is consistent. * ReactiveObject has been removed. Instead, a macro is used to define aliases. ReactiveObject aliases did not work, if the domain is a dependent type. * No longer using {} initialization, because it was giving me trouble. --- include/react/Algorithm.h | 20 +-- include/react/Domain.h | 99 ++++++------- include/react/Event.h | 54 +++++-- include/react/Observer.h | 40 +++-- include/react/ReactiveObject.h | 156 -------------------- include/react/Signal.h | 68 ++++++++- include/react/TypeTraits.h | 6 +- include/react/detail/EventBase.h | 1 + include/react/detail/EventFwd.h | 41 ----- include/react/detail/IReactiveEngine.h | 1 + include/react/detail/ReactiveInput.h | 13 +- include/react/detail/SignalFwd.h | 53 ------- include/react/detail/graph/AlgorithmNodes.h | 4 +- include/react/detail/graph/ObserverNodes.h | 2 + include/react/logging/EventRecords.h | 13 +- project/msvc/CppReact.vcxproj | 3 - project/msvc/CppReact.vcxproj.filters | 9 -- src/benchmark/BenchmarkBase.h | 7 +- src/benchmark/BenchmarkFanout.h | 25 ++-- src/benchmark/BenchmarkGrid.h | 49 +++--- src/benchmark/BenchmarkLifeSim.h | 80 +++++----- src/benchmark/BenchmarkRandom.h | 39 ++--- src/benchmark/BenchmarkSequence.h | 25 ++-- src/benchmark/Main.cpp | 25 ++-- src/logging/EventRecords.cpp | 90 +++++------ src/sandbox/Main.cpp | 52 ++++--- src/test/EventStreamTest.h | 45 +++--- src/test/MoveTest.h | 31 ++-- src/test/ObserverTest.h | 34 +++-- src/test/OperationsTest.h | 78 ++++++---- src/test/SignalTest.h | 130 ++++++++++------ src/test/TransactionTest.h | 26 ++-- 32 files changed, 620 insertions(+), 699 deletions(-) delete mode 100644 include/react/ReactiveObject.h delete mode 100644 include/react/detail/EventFwd.h delete mode 100644 include/react/detail/SignalFwd.h diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index ce49e019..3e62ffd8 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -57,12 +57,12 @@ auto Iterate(const Events& events, V&& init, FIn&& func) using REACT_IMPL::IterateByRefNode; using F = typename std::decay::type; + using R = typename std::result_of::type; using TNode = typename std::conditional< - std::is_same::type>::value, + std::is_same::value, IterateByRefNode, IterateNode - >::type; + >::type; return Signal( std::make_shared( @@ -89,19 +89,19 @@ auto Iterate(const Events& events, V&& init, using REACT_IMPL::SyncedIterateByRefNode; using F = typename std::decay::type; + using R = typename std::result_of::type; using TNode = typename std::conditional< - std::is_same::type>::value, + std::is_same::value, SyncedIterateByRefNode, SyncedIterateNode - >::type; + >::type; struct NodeBuilder_ { NodeBuilder_(const Events& source, V&& init, FIn&& func) : - MySource{ source }, - MyInit{ std::forward(init) }, - MyFunc{ std::forward(func) } + MySource( source ), + MyInit( std::forward(init) ), + MyFunc( std::forward(func) ) {} auto operator()(const Signal& ... deps) @@ -119,7 +119,7 @@ auto Iterate(const Events& events, V&& init, }; return REACT_IMPL::apply( - NodeBuilder_{ events, std::forward(init), std::forward(func) }, + NodeBuilder_( events, std::forward(init), std::forward(func) ), depPack.Data); } diff --git a/include/react/Domain.h b/include/react/Domain.h index 1b1d408a..9b170580 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -12,12 +12,6 @@ #include "react/detail/Defs.h" #include -#include - -#include "react/TypeTraits.h" - -#include "react/detail/EventFwd.h" -#include "react/detail/SignalFwd.h" #include "react/detail/ReactiveInput.h" #include "react/detail/Options.h" @@ -35,6 +29,26 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// +template +class Signal; + +template +class VarSignal; + +template +class TempSignal; + +template +class Events; + +template +class EventSource; + +template +class TempEvents; + +enum class Token; + template class ReactiveLoop; @@ -77,7 +91,7 @@ class DomainBase REACT_IMPL::EnableParallelUpdating::value; /////////////////////////////////////////////////////////////////////////////////////////////// - /// Aliases for reactives of current domain + /// Aliases for reactives of this domain /////////////////////////////////////////////////////////////////////////////////////////////// template using SignalT = Signal; @@ -97,55 +111,6 @@ class DomainBase using ReactiveLoopT = ReactiveLoop; - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// MakeVar - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - < - typename V, - typename S = typename std::decay::type, - class = typename std::enable_if< - !IsSignal::value>::type - > - static auto MakeVar(V&& value) - -> VarSignalT - { - return REACT::MakeVar(std::forward(value)); - } - - ////////////////////////////////////////////////////////////////////////////////////////////////////////// - /// MakeVar (higher order) - ////////////////////////////////////////////////////////////////////////////////////////////////////////// - template - < - typename V, - typename S = typename std::decay::type, - typename TInner = typename S::ValueT, - class = typename std::enable_if< - IsSignal::value>::type - > - static auto MakeVar(V&& value) - -> VarSignalT> - { - return REACT::MakeVar(std::forward(value)); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////// - /// MakeEventSource - /////////////////////////////////////////////////////////////////////////////////////////////////// - template - static auto MakeEventSource() - -> EventSourceT - { - return REACT::MakeEventSource(); - } - - static auto MakeEventSource() - -> EventSourceT - { - return REACT::MakeEventSource(); - } - /////////////////////////////////////////////////////////////////////////////////////////////////// /// DoTransaction /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -215,4 +180,26 @@ class DomainInitializer struct name : public REACT::DomainBase> {}; \ REACT_IMPL::DomainInitializer< name > name ## _initializer_; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Define type aliases for given domain +/////////////////////////////////////////////////////////////////////////////////////////////////// +#define USING_REACTIVE_DOMAIN(name) \ + template \ + using SignalT = Signal; \ + \ + template \ + using VarSignalT = VarSignal; \ + \ + template \ + using EventsT = Events; \ + \ + template \ + using EventSourceT = EventSource; \ + \ + using ObserverT = Observer; \ + \ + using ScopedObserverT = ScopedObserver; \ + \ + using ReactiveLoopT = ReactiveLoop; + #endif // REACT_DOMAIN_H_INCLUDED \ No newline at end of file diff --git a/include/react/Event.h b/include/react/Event.h index 5581dde9..fed4c33c 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -15,8 +15,6 @@ #include #include -#include "react/detail/EventFwd.h" - #include "react/Observer.h" #include "react/TypeTraits.h" #include "react/common/Util.h" @@ -27,6 +25,17 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// +template +class Events; + +template +class EventSource; + +template +class TempEvents; + +enum class Token; + template class Signal; @@ -36,7 +45,7 @@ class SignalPack; /////////////////////////////////////////////////////////////////////////////////////////////////// /// MakeEventSource /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template auto MakeEventSource() -> EventSource { @@ -46,16 +55,6 @@ auto MakeEventSource() std::make_shared>()); } -template -auto MakeEventSource() - -> EventSource -{ - using REACT_IMPL::EventSourceNode; - - return EventSource( - std::make_shared>()); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Merge /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -400,11 +399,18 @@ class Events : public REACT_IMPL::EventStreamBase Events() = default; Events(const Events&) = default; + Events& operator=(const Events&) = default; Events(Events&& other) : Events::EventStreamBase( std::move(other) ) {} + Events& operator=(Events&& other) + { + Events::EventStreamBase::operator=( std::move(other) ); + return *this; + } + explicit Events(NodePtrT&& nodePtr) : Events::EventStreamBase( std::move(nodePtr) ) {} @@ -467,11 +473,18 @@ class Events : public REACT_IMPL::EventStreamBase public: EventSource() = default; EventSource(const EventSource&) = default; + EventSource& operator=(const EventSource&) = default; EventSource(EventSource&& other) : EventSource::Events( std::move(other) ) {} + EventSource& operator=(EventSource&& other) + { + EventSource::Events::operator=( std::move(other) ); + return *this; + } + explicit EventSource(NodePtrT&& nodePtr) : EventSource::Events( std::move(nodePtr) ) {} @@ -589,11 +609,18 @@ class EventSource : public Events> public: EventSource() = default; EventSource(const EventSource&) = default; + EventSource& operator=(const EventSource&) = default; EventSource(EventSource&& other) : EventSource::Events( std::move(other) ) {} + EventSource& operator=(EventSource&& other) + { + EventSource::Events::operator=( std::move(other) ); + return *this; + } + explicit EventSource(NodePtrT&& nodePtr) : EventSource::Events( std::move(nodePtr) ) {} @@ -630,6 +657,7 @@ class TempEvents : public Events public: TempEvents() = default; TempEvents(const TempEvents&) = default; + TempEvents& operator=(const TempEvents&) = default; TempEvents(TempEvents&& other) : TempEvents::Events( std::move(other) ) diff --git a/include/react/Observer.h b/include/react/Observer.h index 2381e6f6..e808f8c8 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -45,27 +45,32 @@ class Observer public: Observer() : - nodePtr_{ nullptr } + nodePtr_( nullptr ) {} Observer(const Observer&) = delete; Observer& operator=(const Observer&) = delete; Observer(Observer&& other) : - nodePtr_{ std::move(other.nodePtr_) }, - subject_{ std::move(other.subject_) } - {} + nodePtr_( other.nodePtr_ ), + subject_( std::move(other.subject_) ) + { + other.nodePtr_ = nullptr; + } Observer& operator=(Observer&& other) { - nodePtr_ = std::move(other.nodePtr_); + nodePtr_ = other.nodePtr_; subject_ = std::move(other.subject_); + + other.nodePtr_ = nullptr; + return *this; } Observer(NodeT* nodePtr, const SubjectT& subject) : - nodePtr_{ nodePtr }, - subject_{ subject } + nodePtr_( nodePtr ), + subject_( subject ) {} bool IsValid() const @@ -94,8 +99,21 @@ template class ScopedObserver { public: + ScopedObserver() = delete; + ScopedObserver(const ScopedObserver&) = delete; + ScopedObserver& operator=(const ScopedObserver&) = delete; + + ScopedObserver(ScopedObserver&& other) : + obs_( std::move(other.obs_) ) + {} + + ScopedObserver& operator=(ScopedObserver&& other) + { + obs_ = std::move(other.obs_); + } + ScopedObserver(Observer&& obs) : - obs_{ std::move(obs) } + obs_( std::move(obs) ) {} ~ScopedObserver() @@ -187,8 +205,8 @@ auto Observe(const Events& subject, struct NodeBuilder_ { NodeBuilder_(const Events& subject, FIn&& func) : - MySubject{ subject }, - MyFunc{ std::forward(func) } + MySubject( subject ), + MyFunc( std::forward(func) ) {} auto operator()(const Signal& ... deps) @@ -204,7 +222,7 @@ auto Observe(const Events& subject, }; auto obsPtr = REACT_IMPL::apply( - NodeBuilder_{ subject, std::forward(func) }, + NodeBuilder_( subject, std::forward(func) ), depPack.Data); auto* rawObsPtr = DomainSpecificObserverRegistry::Instance() diff --git a/include/react/ReactiveObject.h b/include/react/ReactiveObject.h deleted file mode 100644 index ea6383f0..00000000 --- a/include/react/ReactiveObject.h +++ /dev/null @@ -1,156 +0,0 @@ - -// 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_REACTIVEOBJECT_H_INCLUDED -#define REACT_REACTIVEOBJECT_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include - -#include "react/TypeTraits.h" -#include "react/common/Util.h" - -#include "react/detail/EventFwd.h" -#include "react/detail/SignalFwd.h" - - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ReactiveLoop; - -template -class Observer; - -template -class ScopedObserver; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ReactiveObject -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ReactiveObject -{ -public: - /////////////////////////////////////////////////////////////////////////////////////////////// - /// Aliases - /////////////////////////////////////////////////////////////////////////////////////////////// - using DomainT = D; - - template - using SignalT = Signal; - - template - using VarSignalT = VarSignal; - - template - using EventsT = Events; - - template - using EventSourceT = EventSource; - - using ObserverT = Observer; - - using ScopedObserverT = ScopedObserver; - - using ReactiveLoopT = ReactiveLoop; - - /////////////////////////////////////////////////////////////////////////////////////////////// - /// MakeVar - /////////////////////////////////////////////////////////////////////////////////////////////// - template - < - typename V, - typename S = typename std::decay::type, - class = typename std::enable_if< - !IsSignal::value>::type - > - static auto MakeVar(V&& value) - -> VarSignalT - { - return REACT::MakeVar(std::forward(value)); - } - - template - < - typename S - > - static auto MakeVar(std::reference_wrapper value) - -> VarSignalT - { - return REACT::MakeVar(value); - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // MakeVar (higher order) - /////////////////////////////////////////////////////////////////////////////////////////////// - template - < - typename V, - typename S = typename std::decay::type, - typename TInner = typename S::ValueT, - class = typename std::enable_if< - IsSignal::value>::type - > - static auto MakeVar(V&& value) - -> VarSignalT> - { - return REACT::MakeVar(std::forward(value)); - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - /// MakeEventSource - /////////////////////////////////////////////////////////////////////////////////////////////// - template - static auto MakeEventSource() - -> EventSourceT - { - return REACT::MakeEventSource(); - } - - static auto MakeEventSource() - -> EventSource - { - return REACT::MakeEventSource(); - } -}; - -/******************************************/ REACT_END /******************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Flatten macros -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Note: Using static_cast rather than -> return type, because when using lambda for inline -// class initialization, decltype did not recognize the parameter r -#define REACTIVE_REF(obj, name) \ - Flatten( \ - MakeSignal( \ - obj, \ - [] (const REACT_IMPL::Identity::Type::ValueT& r) \ - { \ - using T = decltype(r.name); \ - return static_cast::Type>(r.name); \ - })) - -#define REACTIVE_PTR(obj, name) \ - Flatten( \ - MakeSignal( \ - obj, \ - [] (REACT_IMPL::Identity::Type::ValueT r) \ - { \ - assert(r != nullptr); \ - using T = decltype(r->name); \ - return static_cast::Type>(r->name); \ - })) - -#endif // REACT_REACTIVEOBJECT_H_INCLUDED \ No newline at end of file diff --git a/include/react/Signal.h b/include/react/Signal.h index bd589789..d031a844 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -16,14 +16,24 @@ #include #include -#include "react/detail/SignalFwd.h" - #include "react/Observer.h" #include "react/TypeTraits.h" #include "react/detail/SignalBase.h" /*****************************************/ REACT_BEGIN /*****************************************/ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Forward declarations +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class Signal; + +template +class VarSignal; + +template +class TempSignal; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// SignalPack - Wraps several nodes in a tuple. Create with comma operator. /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -68,8 +78,9 @@ template < typename D, typename V, - typename S, - class /*SFINAE*/ + typename S = typename std::decay::type, + class = typename std::enable_if< + ! IsReactive::value>::type > auto MakeVar(V&& value) -> VarSignal @@ -670,7 +681,8 @@ class Signal : public REACT_IMPL::SignalBase> Signal& operator=(Signal&& other) { - return Signal::SignalBase::operator=( std::move(other) ); + Signal::SignalBase::operator=( std::move(other) ); + return *this; } explicit Signal(NodePtrT&& nodePtr) : @@ -716,7 +728,8 @@ class VarSignal : public Signal VarSignal& operator=(VarSignal&& other) { - return VarSignal::Signal::operator=( std::move(other) ); + VarSignal::Signal::operator=( std::move(other) ); + return *this; } explicit VarSignal(NodePtrT&& nodePtr) : @@ -777,7 +790,8 @@ class VarSignal : public Signal> VarSignal& operator=(VarSignal&& other) { - return VarSignal::Signal::operator=( std::move(other) ); + VarSignal::Signal::operator=( std::move(other) ); + return *this; } explicit VarSignal(NodePtrT&& nodePtr) : @@ -822,7 +836,8 @@ class TempSignal : public Signal TempSignal& operator=(TempSignal&& other) { - return TempSignal::Signal::operator=( std::move(other) ); + TempSignal::Signal::operator=( std::move(other) ); + return *this; } explicit TempSignal(NodePtrT&& ptr) : @@ -847,4 +862,41 @@ bool Equals(const Signal& lhs, const Signal& rhs) /****************************************/ REACT_IMPL_END /***************************************/ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Flatten macros +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Note: Using static_cast rather than -> return type, because when using lambda for inline +// class initialization, decltype did not recognize the parameter r +// Note2: MSVC doesn't like typename in the lambda +#if _MSC_VER && !__INTEL_COMPILER + #define REACT_MSVC_NO_TYPENAME +#else + #define REACT_MSVC_NO_TYPENAME typename +#endif + +#define REACTIVE_REF(obj, name) \ + Flatten( \ + MakeSignal( \ + obj, \ + [] (const REACT_MSVC_NO_TYPENAME \ + REACT_IMPL::Identity::Type::ValueT& r) \ + { \ + using T = decltype(r.name); \ + using S = REACT_MSVC_NO_TYPENAME REACT::RemoveInput::Type; \ + return static_cast(r.name); \ + })) + +#define REACTIVE_PTR(obj, name) \ + Flatten( \ + MakeSignal( \ + obj, \ + [] (REACT_MSVC_NO_TYPENAME \ + REACT_IMPL::Identity::Type::ValueT r) \ + { \ + assert(r != nullptr); \ + using T = decltype(r->name); \ + using S = REACT_MSVC_NO_TYPENAME REACT::RemoveInput::Type; \ + return static_cast(r->name); \ + })) + #endif // REACT_SIGNAL_H_INCLUDED \ No newline at end of file diff --git a/include/react/TypeTraits.h b/include/react/TypeTraits.h index e0b7d1e1..3b14e7a7 100644 --- a/include/react/TypeTraits.h +++ b/include/react/TypeTraits.h @@ -91,14 +91,14 @@ struct IsReactive> { static const bool value = true; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// RemoveInput /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template struct RemoveInput { using Type = T; }; template -struct RemoveInput> { using Type = Signal; }; +struct RemoveInput> { using Type = Signal; }; template -struct RemoveInput> { using Type = Events; }; +struct RemoveInput> { using Type = Events; }; /******************************************/ REACT_END /******************************************/ diff --git a/include/react/detail/EventBase.h b/include/react/detail/EventBase.h index 1af7d2bb..ac2b6afb 100644 --- a/include/react/detail/EventBase.h +++ b/include/react/detail/EventBase.h @@ -31,6 +31,7 @@ class EventStreamBase : public ReactiveBase> { public: EventStreamBase() = default; + EventStreamBase(const EventStreamBase&) = default; template explicit EventStreamBase(T&& ptr) : diff --git a/include/react/detail/EventFwd.h b/include/react/detail/EventFwd.h deleted file mode 100644 index 8a80d9a7..00000000 --- a/include/react/detail/EventFwd.h +++ /dev/null @@ -1,41 +0,0 @@ - -// 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_EVENTFWD_H_INCLUDED -#define REACT_DETAIL_EVENTFWD_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include "react/TypeTraits.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -// Classes -template -class Events; - -template -class EventSource; - -template -class TempEvents; - -enum class Token; - -// Functions -template -auto MakeEventSource() - -> EventSource; - -template -auto MakeEventSource() - -> EventSource; - -/******************************************/ REACT_END /******************************************/ - -#endif // REACT_DETAIL_EVENTFWD_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/IReactiveEngine.h b/include/react/detail/IReactiveEngine.h index 12007096..b687b8d6 100644 --- a/include/react/detail/IReactiveEngine.h +++ b/include/react/detail/IReactiveEngine.h @@ -13,6 +13,7 @@ #include "react/detail/Defs.h" #include "react/common/Types.h" +#include "react/logging/EventRecords.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index 128e9657..cbafd9cf 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -178,7 +178,7 @@ class InputManager bool shouldPropagate = false; - auto turn = makeTurn(flags); + TurnT turn( nextTurnId(), flags ); // Phase 1 - Input admission transactionState_.Active = true; @@ -257,16 +257,11 @@ class InputManager static TransactionState transactionState_; - static TurnT makeTurn(TurnFlagsT flags) - { - return TurnT(nextTurnId(), flags); - } - // Create a turn with a single input template static void addSimpleInput(R& r, V&& v) { - auto turn = makeTurn(0); + TurnT turn( nextTurnId(), 0 ); Engine::OnTurnAdmissionStart(turn); r.AddInput(std::forward(v)); @@ -283,7 +278,7 @@ class InputManager template static void modifySimpleInput(R& r, const F& func) { - auto turn = makeTurn(0); + TurnT turn( nextTurnId(), 0 ); Engine::OnTurnAdmissionStart(turn); r.ModifyInput(func); @@ -350,7 +345,7 @@ class InputManager while (true) { bool shouldPropagate = false; - auto turn = makeTurn(flags); + TurnT turn( nextTurnId(), flags ); transactionState_.Active = true; Engine::OnTurnAdmissionStart(turn); diff --git a/include/react/detail/SignalFwd.h b/include/react/detail/SignalFwd.h deleted file mode 100644 index b46dcb86..00000000 --- a/include/react/detail/SignalFwd.h +++ /dev/null @@ -1,53 +0,0 @@ - -// 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_SIGNALFWD_H_INCLUDED -#define REACT_DETAIL_SIGNALFWD_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include - -#include "react/TypeTraits.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -// Classes -template -class Signal; - -template -class VarSignal; - -template -class TempSignal; - -// Functions -template -< - typename D, - typename V, - typename S = typename std::decay::type, - class = typename std::enable_if< - ! IsReactive::value>::type -> -auto MakeVar(V&& value) - -> VarSignal; - -template -< - typename D, - typename S -> -auto MakeVar(std::reference_wrapper value) - -> VarSignal; - -/******************************************/ REACT_END /******************************************/ - -#endif // REACT_DETAIL_SIGNALFWD_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index 8e8735ba..aad77a2b 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -458,7 +458,7 @@ class SnapshotNode : public SignalNode trigger_->SetCurrentTurn(turn); REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id(), std::this_thread::get_id().hash())); + GetObjectId(*this), turn.Id())); bool changed = false; @@ -474,7 +474,7 @@ class SnapshotNode : public SignalNode } REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id(), std::this_thread::get_id().hash())); + GetObjectId(*this), turn.Id())); if (changed) Engine::OnNodePulse(*this, turn); diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index 794a4f39..170cb0da 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -16,6 +16,8 @@ #include "GraphBase.h" +#include "react/detail/ReactiveInput.h" + /***************************************/ REACT_IMPL_BEGIN /**************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/logging/EventRecords.h b/include/react/logging/EventRecords.h index ec064db9..4ec91cbe 100644 --- a/include/react/logging/EventRecords.h +++ b/include/react/logging/EventRecords.h @@ -13,6 +13,7 @@ #include #include +#include #include "Logging.h" #include "react/common/Types.h" @@ -186,9 +187,9 @@ class NodeEvaluateBeginEvent : public IEventRecord virtual void Serialize(std::ostream& out) const; private: - ObjectId nodeId_; - int transactionId_; - size_t threadId_; + ObjectId nodeId_; + int transactionId_; + std::thread::id threadId_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -204,9 +205,9 @@ class NodeEvaluateEndEvent : public IEventRecord virtual void Serialize(std::ostream& out) const; private: - ObjectId nodeId_; - int transactionId_; - size_t threadId_; + ObjectId nodeId_; + int transactionId_; + std::thread::id threadId_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index 0d9292cf..12ea4a97 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -79,7 +79,6 @@ - @@ -95,7 +94,6 @@ - @@ -112,7 +110,6 @@ - diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index 8b294c83..52282a78 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -51,9 +51,6 @@ Header Files\common - - Header Files - Header Files @@ -147,12 +144,6 @@ Header Files\common - - Header Files\detail - - - Header Files\detail - diff --git a/src/benchmark/BenchmarkBase.h b/src/benchmark/BenchmarkBase.h index 36a117ef..deaaf931 100644 --- a/src/benchmark/BenchmarkBase.h +++ b/src/benchmark/BenchmarkBase.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -75,10 +76,10 @@ template typename TBenchmark, typename TParams > -void RunBenchmark(std::ostream& logfile, TBenchmark& b, TParams params) +void RunBenchmark(std::ostream& logfile, TBenchmark b, const TParams& params) { - std::cout << "Engine: " << typeid(TBenchmark::Domain::Policy::Engine).name() << std::endl << std::endl; - logfile << "Engine: " << typeid(TBenchmark::Domain::Policy::Engine).name() << std::endl << std::endl; + std::cout << "Engine: " << typeid(typename TBenchmark::Domain::Policy::Engine).name() << std::endl << std::endl; + logfile << "Engine: " << typeid(typename TBenchmark::Domain::Policy::Engine).name() << std::endl << std::endl; double sum = 0; double min = DBL_MAX; diff --git a/src/benchmark/BenchmarkFanout.h b/src/benchmark/BenchmarkFanout.h index 5f379ea9..bc2c8b3e 100644 --- a/src/benchmark/BenchmarkFanout.h +++ b/src/benchmark/BenchmarkFanout.h @@ -9,13 +9,16 @@ #ifndef CPP_REACT_BENCHMARK_FANOUT_H #define CPP_REACT_BENCHMARK_FANOUT_H -#include #include #include #include #include "BenchmarkBase.h" +#include "react/Signal.h" + +using namespace react; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Benchmark_Fanout /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -25,12 +28,7 @@ struct BenchmarkParams_Fanout N(n), K(k), Delay(delay) - { - } - - const int N; - const int K; - const int Delay; + {} void Print(std::ostream& out) const { @@ -38,6 +36,10 @@ struct BenchmarkParams_Fanout << ", K = " << K << ", Delay = " << Delay; } + + const int N; + const int K; + const int Delay; }; template @@ -45,14 +47,13 @@ struct Benchmark_Fanout : public BenchmarkBase { double Run(const BenchmarkParams_Fanout& params) { - using MyDomain = D; - using MyHandle = MyDomain::SignalT; + using MySignal = Signal; bool initializing = true; - auto in = MyDomain::MakeVar(1); + auto in = MakeVar(1); - std::vector nodes; + std::vector nodes; auto f = [&initializing,¶ms] (int a) { if (params.Delay > 0 && !initializing) @@ -65,7 +66,7 @@ struct Benchmark_Fanout : public BenchmarkBase for (int i=0; i* f; + auto t = MakeSignal(in, f); nodes.push_back(t); } diff --git a/src/benchmark/BenchmarkGrid.h b/src/benchmark/BenchmarkGrid.h index b64d6f27..f1e6af3c 100644 --- a/src/benchmark/BenchmarkGrid.h +++ b/src/benchmark/BenchmarkGrid.h @@ -14,6 +14,10 @@ #include "BenchmarkBase.h" +#include "react/Signal.h" + +using namespace react; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// GridGraphGenerator /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -25,32 +29,32 @@ template class GridGraphGenerator { public: - typedef typename D::template SignalT MyHandle; + using MySignal = Signal; - typedef std::function Func1T; - typedef std::function Func2T; + using Func1T = std::function; + using Func2T = std::function; - typedef std::vector HandleVect; - typedef std::vector WidthVect; + using SignalVectT = std::vector; + using WidthVectT = std::vector; - HandleVect InputSignals; - HandleVect OutputSignals; + SignalVectT InputSignals; + SignalVectT OutputSignals; - Func1T Function1; - Func2T Function2; + Func1T Function1; + Func2T Function2; - WidthVect Widths; + WidthVectT Widths; void Generate() { assert(InputSignals.size() >= 1); assert(Widths.size() >= 1); - HandleVect buf1 = InputSignals; - HandleVect buf2; + SignalVectT buf1 = InputSignals; + SignalVectT buf2; - HandleVect* curBuf = &buf1; - HandleVect* nextBuf = &buf2; + SignalVectT* curBuf = &buf1; + SignalVectT* nextBuf = &buf2; int curWidth = InputSignals.size(); @@ -93,7 +97,7 @@ class GridGraphGenerator curBuf->clear(); // Swap buffer pointers - HandleVect* t = curBuf; + SignalVectT* t = curBuf; curBuf = nextBuf; nextBuf = t; @@ -119,17 +123,16 @@ struct BenchmarkParams_Grid BenchmarkParams_Grid(int n, int k) : N(n), K(k) - { - } - - const int N; - const int K; + {} void Print(std::ostream& out) const { out << "N = " << N << ", K = " << K; } + + const int N; + const int K; }; template @@ -137,11 +140,9 @@ struct Benchmark_Grid : public BenchmarkBase { double Run(const BenchmarkParams_Grid& params) { - using MyDomain = D; - - auto in = MyDomain::MakeVar(1); + auto in = MakeVar(1); - GridGraphGenerator generator; + GridGraphGenerator generator; generator.InputSignals.push_back(in); diff --git a/src/benchmark/BenchmarkLifeSim.h b/src/benchmark/BenchmarkLifeSim.h index e6e3bbb6..168192f1 100644 --- a/src/benchmark/BenchmarkLifeSim.h +++ b/src/benchmark/BenchmarkLifeSim.h @@ -18,9 +18,9 @@ #include "BenchmarkBase.h" +#include "react/Domain.h" #include "react/Signal.h" #include "react/Event.h" -#include "react/ReactiveObject.h" #include "react/logging/EventLog.h" using namespace react; @@ -43,10 +43,12 @@ using BoundsT = std::tuple; /// Time /////////////////////////////////////////////////////////////////////////////////////////////////// template -class Time : public ReactiveObject +class Time { public: - EventSourceT<> NewDay = MakeEventSource(); + USING_REACTIVE_DOMAIN(D) + + EventSourceT<> NewDay = MakeEventSource(); SignalT TotalDays = Iterate(NewDay, 0, Incrementer()); SignalT DayOfYear = TotalDays % 365; @@ -62,14 +64,14 @@ class Time : public ReactiveObject /// Region /////////////////////////////////////////////////////////////////////////////////////////////////// template -class Region : public ReactiveObject +class Region { - Time& theTime; - public: + USING_REACTIVE_DOMAIN(D) + BoundsT Bounds; - EventSourceT EnterOrLeave = MakeEventSource(); + EventSourceT EnterOrLeave = MakeEventSource(); SignalT AnimalCount = Iterate( EnterOrLeave, @@ -93,8 +95,8 @@ class Region : public ReactiveObject EventsT FoodOutput = Pulse(theTime.NewDay, FoodOutputPerDay); Region(Time& time, int x, int y) : - theTime{ time }, - Bounds{ x*10, x*10+9, y*10, y*10+9 } + theTime( time ), + Bounds( x*10, x*10+9, y*10, y*10+9 ) {} PositionT Center() const @@ -121,17 +123,20 @@ class Region : public ReactiveObject return get<0>(Bounds) <= pos.first && pos.first <= get<1>(Bounds) && get<2>(Bounds) <= pos.second && pos.second <= get<3>(Bounds); } + +private: + Time& theTime; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// World /////////////////////////////////////////////////////////////////////////////////////////////////// template -class World : public ReactiveObject +class World { - int w_; - public: + USING_REACTIVE_DOMAIN(D) + vector>> Regions; World(Time& time, int w) : @@ -162,13 +167,16 @@ class World : public ReactiveObject return pos; } + +private: + int w_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Animal /////////////////////////////////////////////////////////////////////////////////////////////////// template -class Animal : public ReactiveObject +class Animal { Time& theTime; World& theWorld; @@ -176,6 +184,8 @@ class Animal : public ReactiveObject std::mt19937 generator; public: + USING_REACTIVE_DOMAIN(D) + VarSignalT*> CurrentRegion; EventsT FoodReceived = REACTIVE_PTR(CurrentRegion, FoodOutput); @@ -183,12 +193,7 @@ class Animal : public ReactiveObject EventsT Moving = Pulse(theTime.NewDay, ShouldMigrate); SignalT Position; - - SignalT*> NewRegion = MakeSignal( - Position, - [this] (PositionT pos) { - return theWorld.GetRegion(pos); - }); + SignalT*> NewRegion; EventsT*> RegionChanged = Monitor(NewRegion);; @@ -203,12 +208,12 @@ class Animal : public ReactiveObject }); Animal(Time& time, World& world, Region* initRegion, unsigned seed) : - theTime{ time }, - theWorld{ world }, - generator{ seed }, - CurrentRegion{ MakeVar(initRegion) }, + theTime( time ), + theWorld( world ), + generator( seed ), + CurrentRegion( MakeVar(initRegion) ), Position - { + ( Iterate(Moving, initRegion->Center(), With(CurrentRegion), [this] (bool moving, PositionT position, Region* region) { std::uniform_int_distribution dist(-1,1); @@ -226,7 +231,14 @@ class Animal : public ReactiveObject else return region->Clamp(position); }) - } + ), + NewRegion + ( + MakeSignal(Position, + [this] (PositionT pos) { + return theWorld.GetRegion(pos); + }) + ) { initRegion->EnterOrLeave(Migration::enter); @@ -249,19 +261,19 @@ class Animal : public ReactiveObject struct BenchmarkParams_LifeSim { BenchmarkParams_LifeSim(int n, int w, int k) : - N{ n }, W{ w }, K{ k } + N( n ), W( w ), K( k ) {} - const int N; - const int W; - const int K; - void Print(std::ostream& out) const { out << "N = " << N << ", K = " << K << ", W = " << W; } + + const int N; + const int W; + const int K; }; template @@ -270,12 +282,12 @@ struct Benchmark_LifeSim : public BenchmarkBase double Run(const BenchmarkParams_LifeSim& params) { Time theTime; - World theWorld{ theTime, params.W }; + World theWorld( theTime, params.W ); - vector>> animals{}; + vector>> animals; - std::mt19937 gen{ 2015 }; - std::uniform_int_distribution dist{ 0u, theWorld.Regions.size()-1 }; + std::mt19937 gen( 2015 ); + std::uniform_int_distribution dist( 0u, theWorld.Regions.size()-1 ); for (int i=0; i width = MakeVar(60); + // Note: Using auto from here on + auto height = MakeVar(70); + auto depth = MakeVar(8); auto area = width * height; auto volume = area * depth; @@ -68,9 +68,9 @@ void SignalExample2() { cout << "Signal Example 2" << endl; - auto width = D::MakeVar(60); - auto height = D::MakeVar(70); - auto depth = D::MakeVar(8); + auto width = MakeVar(60); + auto height = MakeVar(70); + auto depth = MakeVar(8); auto volume = MakeSignal( With(width,height,depth), @@ -99,7 +99,7 @@ void SignalExample3() { cout << "Signal Example 3" << endl; - auto src = D::MakeVar(0); + auto src = MakeVar(0); // Input values can be manipulated imperatively in observers. // Inputs are implicitly thread-safe, buffered and executed in a continuation turn. @@ -120,8 +120,8 @@ void EventExample1() { cout << "Event Example 1" << endl; - auto numbers1 = D::MakeEventSource(); - auto numbers2 = D::MakeEventSource(); + auto numbers1 = MakeEventSource(); + auto numbers2 = MakeEventSource(); auto anyNumber = numbers1 | numbers2; @@ -141,7 +141,7 @@ void EventExample2() // The event type can be omitted if not required, in which case the event // stream just indicates that it has fired, i.e. it behaves like a token stream. - auto emitter = D::MakeEventSource(); + auto emitter = MakeEventSource(); auto counter = Iterate(emitter, 0, [] (Token, int v) { return v+1; }); @@ -158,10 +158,12 @@ void EventExample2() cout << endl; } -class Person : public ReactiveObject +class Person { public: - VarSignalT Age = MakeVar(1); + USING_REACTIVE_DOMAIN(D) + + VarSignalT Age = MakeVar(1); SignalT Health = 100 - Age; SignalT Wisdom = Age * Age / 100; @@ -205,13 +207,15 @@ void ObjectExample1() cout << endl; } -class Company : public ReactiveObject +class Company { + USING_REACTIVE_DOMAIN(D) + public: VarSignalT Name; Company(const char* name) : - Name{ MakeVar(string(name)) } + Name( MakeVar(string(name)) ) {} inline bool operator==(const Company& other) const @@ -220,15 +224,17 @@ class Company : public ReactiveObject } }; -class Manager : public ReactiveObject +class Manager { + USING_REACTIVE_DOMAIN(D) + ObserverT nameObs; public: VarSignalT CurrentCompany; Manager(Company& c) : - CurrentCompany{ MakeVar(ref(c)) } + CurrentCompany( MakeVar(ref(c)) ) { nameObs = Observe(REACTIVE_REF(CurrentCompany, Name), [] (string name) { cout << "Manager: Now managing " << name << endl; @@ -260,7 +266,7 @@ void IterateExample1() { cout << "Iterate Example 1" << endl; - auto src = D::MakeEventSource(); + auto src = MakeEventSource(); auto it = Iterate(src, 0, [] (int d, int v) { return v + d; }); @@ -270,7 +276,7 @@ void IterateExample1() cout << it() << endl; - auto charSrc = D::MakeEventSource(); + auto charSrc = MakeEventSource(); auto str = Iterate(charSrc, string(""), [] (char c, string s) { return s + c; }); @@ -291,9 +297,9 @@ void LoopTest() vector paths; - auto mouseDown = D::MakeEventSource(); - auto mouseUp = D::MakeEventSource(); - auto mouseMove = D::MakeEventSource(); + auto mouseDown = MakeEventSource(); + auto mouseUp = MakeEventSource(); + auto mouseMove = MakeEventSource(); D::ReactiveLoopT loop { diff --git a/src/test/EventStreamTest.h b/src/test/EventStreamTest.h index 5e7ca7bd..71941328 100644 --- a/src/test/EventStreamTest.h +++ b/src/test/EventStreamTest.h @@ -26,7 +26,7 @@ template class EventStreamTest : public testing::Test { public: - REACTIVE_DOMAIN(MyDomain, TEngine); + REACTIVE_DOMAIN(MyDomain, TEngine) }; TYPED_TEST_CASE_P(EventStreamTest); @@ -36,8 +36,10 @@ TYPED_TEST_CASE_P(EventStreamTest); /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(EventStreamTest, EventSources) { - auto es1 = MyDomain::MakeEventSource(); - auto es2 = MyDomain::MakeEventSource(); + using D = typename EventSources::MyDomain; + + auto es1 = MakeEventSource(); + auto es2 = MakeEventSource(); std::queue results1; std::queue results2; @@ -91,9 +93,11 @@ TYPED_TEST_P(EventStreamTest, EventSources) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(EventStreamTest, EventMerge1) { - auto a1 = MyDomain::MakeEventSource(); - auto a2 = MyDomain::MakeEventSource(); - auto a3 = MyDomain::MakeEventSource(); + using D = typename EventMerge1::MyDomain; + + auto a1 = MakeEventSource(); + auto a2 = MakeEventSource(); + auto a3 = MakeEventSource(); auto merged = Merge(a1, a2, a3); @@ -104,7 +108,7 @@ TYPED_TEST_P(EventStreamTest, EventMerge1) results.push_back(v); }); - MyDomain::DoTransaction([&] { + D::DoTransaction([&] { a1 << 10; a2 << 20; a3 << 30; @@ -121,9 +125,11 @@ TYPED_TEST_P(EventStreamTest, EventMerge1) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(EventStreamTest, EventMerge2) { - auto a1 = MyDomain::MakeEventSource(); - auto a2 = MyDomain::MakeEventSource(); - auto a3 = MyDomain::MakeEventSource(); + using D = typename EventMerge2::MyDomain; + + auto a1 = MakeEventSource(); + auto a2 = MakeEventSource(); + auto a3 = MakeEventSource(); auto merged = Merge(a1, a2, a3); @@ -138,7 +144,7 @@ TYPED_TEST_P(EventStreamTest, EventMerge2) std::string s2("two"); std::string s3("three"); - MyDomain::DoTransaction([&] { + D::DoTransaction([&] { a1 << s1; a2 << s2; a3 << s3; @@ -155,8 +161,10 @@ TYPED_TEST_P(EventStreamTest, EventMerge2) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(EventStreamTest, EventMerge3) { - auto a1 = MyDomain::MakeEventSource(); - auto a2 = MyDomain::MakeEventSource(); + using D = typename EventMerge3::MyDomain; + + auto a1 = MakeEventSource(); + auto a2 = MakeEventSource(); auto f1 = Filter(a1, [] (int v) { return true; }); auto f2 = Filter(a2, [] (int v) { return true; }); @@ -194,11 +202,13 @@ TYPED_TEST_P(EventStreamTest, EventMerge3) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(EventStreamTest, EventFilter) { + using D = typename EventFilter::MyDomain; + using std::string; std::queue results; - auto in = MyDomain::MakeEventSource(); + auto in = MakeEventSource(); auto filtered = Filter(in, [] (const string& s) { @@ -225,12 +235,14 @@ TYPED_TEST_P(EventStreamTest, EventFilter) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(EventStreamTest, EventTransform) { + using D = typename EventTransform::MyDomain; + using std::string; std::vector results; - auto in1 = MyDomain::MakeEventSource(); - auto in2 = MyDomain::MakeEventSource(); + auto in1 = MakeEventSource(); + auto in2 = MakeEventSource(); auto merged = Merge(in1, in2); @@ -240,7 +252,6 @@ TYPED_TEST_P(EventStreamTest, EventTransform) return s; }); - Observe(transformed, [&] (const string& s) { results.push_back(s); diff --git a/src/test/MoveTest.h b/src/test/MoveTest.h index 7848126e..90717ea2 100644 --- a/src/test/MoveTest.h +++ b/src/test/MoveTest.h @@ -36,26 +36,23 @@ class MoveTest : public testing::Test int v = 0; Stats* stats = nullptr; - CopyCounter() - { - } + CopyCounter() = default; CopyCounter(int x, Stats* s) : - v{ x }, - stats{ s } - { - } + v( x ), + stats( s ) + {} CopyCounter(const CopyCounter& other) : - v{ other.v }, - stats{ other.stats } + v( other.v ), + stats( other.stats ) { stats->copyCount++; } CopyCounter(CopyCounter&& other) : - v{ other.v }, - stats{ other.stats } + v( other.v ), + stats( other.stats ) { stats->moveCount++; } @@ -95,12 +92,16 @@ TYPED_TEST_CASE_P(MoveTest); /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(MoveTest, Copy1) { + using D = typename Copy1::MyDomain; + using CopyCounter = typename Copy1::CopyCounter; + using Stats = typename Copy1::Stats; + Stats stats1; - auto a = MyDomain::MakeVar(CopyCounter{1,&stats1}); - auto b = MyDomain::MakeVar(CopyCounter{10,&stats1}); - auto c = MyDomain::MakeVar(CopyCounter{100,&stats1}); - auto d = MyDomain::MakeVar(CopyCounter{1000,&stats1}); + auto a = MakeVar(CopyCounter{1,&stats1}); + auto b = MakeVar(CopyCounter{10,&stats1}); + auto c = MakeVar(CopyCounter{100,&stats1}); + auto d = MakeVar(CopyCounter{1000,&stats1}); // 4x move to value_ // 4x copy to newValue_ (can't be unitialized for references) diff --git a/src/test/ObserverTest.h b/src/test/ObserverTest.h index fad8f89f..19dc8d04 100644 --- a/src/test/ObserverTest.h +++ b/src/test/ObserverTest.h @@ -37,8 +37,10 @@ TYPED_TEST_CASE_P(ObserverTest); /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(ObserverTest, Detach) { - auto a1 = MyDomain::MakeVar(1); - auto a2 = MyDomain::MakeVar(1); + using D = typename Detach::MyDomain; + + auto a1 = MakeVar(1); + auto a2 = MakeVar(1); auto result = a1 + a2; @@ -110,12 +112,14 @@ TYPED_TEST_P(ObserverTest, Detach) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(ObserverTest, ScopedObserverTest) { + using D = typename ScopedObserverTest::MyDomain; + std::vector results; - auto in = MyDomain::MakeVar(1); + auto in = MakeVar(1); { - MyDomain::ScopedObserverT obs = Observe(in, [&] (int v) { + ScopedObserver obs = Observe(in, [&] (int v) { results.push_back(v); }); @@ -133,15 +137,17 @@ TYPED_TEST_P(ObserverTest, ScopedObserverTest) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(ObserverTest, SyncedObserveTest) { - auto in1 = MyDomain::MakeVar(1); - auto in2 = MyDomain::MakeVar(1); + using D = typename SyncedObserveTest::MyDomain; + + auto in1 = MakeVar(1); + auto in2 = MakeVar(1); auto sum = in1 + in2; auto prod = in1 * in2; auto diff = in1 - in2; - auto src1 = MyDomain::MakeEventSource(); - auto src2 = MyDomain::MakeEventSource(); + auto src1 = MakeEventSource(); + auto src2 = MakeEventSource(); Observe(src1, With(sum,prod,diff), [] (Token, int sum, int prod, int diff) { ASSERT_EQ(sum, 33); @@ -168,7 +174,9 @@ TYPED_TEST_P(ObserverTest, SyncedObserveTest) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(ObserverTest, DetachThisObserver1) { - auto src = MyDomain::MakeEventSource(); + using D = typename DetachThisObserver1::MyDomain; + + auto src = MakeEventSource(); int count = 0; @@ -189,14 +197,16 @@ TYPED_TEST_P(ObserverTest, DetachThisObserver1) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(ObserverTest, DetachThisObserver2) { - auto in1 = MyDomain::MakeVar(1); - auto in2 = MyDomain::MakeVar(1); + using D = typename DetachThisObserver2::MyDomain; + + auto in1 = MakeVar(1); + auto in2 = MakeVar(1); auto sum = in1 + in2; auto prod = in1 * in2; auto diff = in1 - in2; - auto src = MyDomain::MakeEventSource(); + auto src = MakeEventSource(); int count = 0; diff --git a/src/test/OperationsTest.h b/src/test/OperationsTest.h index cc79aff4..5075674e 100644 --- a/src/test/OperationsTest.h +++ b/src/test/OperationsTest.h @@ -47,7 +47,9 @@ TYPED_TEST_CASE_P(OperationsTest); /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(OperationsTest, Iterate1) { - auto numSrc = MyDomain::MakeEventSource(); + using D = typename Iterate1::MyDomain; + + auto numSrc = MakeEventSource(); auto numFold = Iterate(numSrc, 0, [] (int d, int v) { return v + d; }); @@ -59,7 +61,7 @@ TYPED_TEST_P(OperationsTest, Iterate1) ASSERT_EQ(numFold(), 5050); - auto charSrc = MyDomain::MakeEventSource(); + auto charSrc = MakeEventSource(); auto strFold = Iterate(charSrc, string(""), [] (char c, string s) { return s + c; }); @@ -74,7 +76,9 @@ TYPED_TEST_P(OperationsTest, Iterate1) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(OperationsTest, Iterate2) { - auto numSrc = MyDomain::MakeEventSource(); + using D = typename Iterate2::MyDomain; + + auto numSrc = MakeEventSource(); auto numFold = Iterate(numSrc, 0, [] (int d, int v) { return v + d; }); @@ -86,7 +90,7 @@ TYPED_TEST_P(OperationsTest, Iterate2) ASSERT_EQ(v, 5050); }); - MyDomain::DoTransaction([&] { + D::DoTransaction([&] { for (auto i=1; i<=100; i++) numSrc << i; }); @@ -100,7 +104,9 @@ TYPED_TEST_P(OperationsTest, Iterate2) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(OperationsTest, Iterate3) { - auto trigger = MyDomain::MakeEventSource(); + using D = typename Iterate3::MyDomain; + + auto trigger = MakeEventSource(); { auto inc = Iterate(trigger, 0, Incrementer{}); @@ -124,7 +130,9 @@ TYPED_TEST_P(OperationsTest, Iterate3) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(OperationsTest, Monitor1) { - auto target = MyDomain::MakeVar(10); + using D = typename Monitor1::MyDomain; + + auto target = MakeVar(10); vector results; @@ -156,7 +164,9 @@ TYPED_TEST_P(OperationsTest, Monitor1) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(OperationsTest, Hold1) { - auto src = MyDomain::MakeEventSource(); + using D = typename Hold1::MyDomain; + + auto src = MakeEventSource(); auto h = Hold(src, 0); @@ -176,8 +186,10 @@ TYPED_TEST_P(OperationsTest, Hold1) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(OperationsTest, Pulse1) { - auto trigger = MyDomain::MakeEventSource(); - auto target = MyDomain::MakeVar(10); + using D = typename Pulse1::MyDomain; + + auto trigger = MakeEventSource(); + auto target = MakeVar(10); vector results; @@ -203,8 +215,10 @@ TYPED_TEST_P(OperationsTest, Pulse1) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(OperationsTest, Snapshot1) { - auto trigger = MyDomain::MakeEventSource(); - auto target = MyDomain::MakeVar(10); + using D = typename Snapshot1::MyDomain; + + auto trigger = MakeEventSource(); + auto target = MakeVar(10); auto snap = Snapshot(trigger, target); @@ -226,7 +240,9 @@ TYPED_TEST_P(OperationsTest, Snapshot1) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(OperationsTest, IterateByRef1) { - auto src = MyDomain::MakeEventSource(); + using D = typename IterateByRef1::MyDomain; + + auto src = MakeEventSource(); auto f = Iterate( src, std::vector(), @@ -250,7 +266,9 @@ TYPED_TEST_P(OperationsTest, IterateByRef1) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(OperationsTest, IterateByRef2) { - auto src = MyDomain::MakeEventSource(); + using D = typename IterateByRef2::MyDomain; + + auto src = MakeEventSource(); auto x = Iterate( src, std::vector(), @@ -274,15 +292,17 @@ TYPED_TEST_P(OperationsTest, IterateByRef2) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(OperationsTest, SyncedTransform1) { - auto in1 = MyDomain::MakeVar(1); - auto in2 = MyDomain::MakeVar(1); + using D = typename SyncedTransform1::MyDomain; + + auto in1 = MakeVar(1); + auto in2 = MakeVar(1); auto sum = in1 + in2; auto prod = in1 * in2; auto diff = in1 - in2; - auto src1 = MyDomain::MakeEventSource(); - auto src2 = MyDomain::MakeEventSource(); + auto src1 = MakeEventSource(); + auto src2 = MakeEventSource(); auto out1 = Transform(src1, With(sum,prod,diff), [] (Token, int sum, int prod, int diff) { return make_tuple(sum, prod, diff); @@ -361,14 +381,16 @@ TYPED_TEST_P(OperationsTest, SyncedTransform1) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(OperationsTest, SyncedIterate1) { - auto in1 = MyDomain::MakeVar(1); - auto in2 = MyDomain::MakeVar(1); + using D = typename SyncedIterate1::MyDomain; + + auto in1 = MakeVar(1); + auto in2 = MakeVar(1); auto op1 = in1 + in2; auto op2 = (in1 + in2) * 10; - auto src1 = MyDomain::MakeEventSource(); - auto src2 = MyDomain::MakeEventSource(); + auto src1 = MakeEventSource(); + auto src2 = MakeEventSource(); auto out1 = Iterate( src1, @@ -451,20 +473,22 @@ TYPED_TEST_P(OperationsTest, SyncedIterate1) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(OperationsTest, SyncedIterate2) { - auto in1 = MyDomain::MakeVar(1); - auto in2 = MyDomain::MakeVar(1); + using D = typename SyncedIterate2::MyDomain; + + auto in1 = MakeVar(1); + auto in2 = MakeVar(1); auto op1 = in1 + in2; auto op2 = (in1 + in2) * 10; - auto src1 = MyDomain::MakeEventSource(); - auto src2 = MyDomain::MakeEventSource(); + auto src1 = MakeEventSource(); + auto src2 = MakeEventSource(); auto out1 = Iterate( src1, vector{}, With(op1,op2), - [] (Token, vector& v, int op1, int op2) { + [] (Token, vector& v, int op1, int op2) -> void { v.push_back(op1); v.push_back(op2); }); @@ -473,7 +497,7 @@ TYPED_TEST_P(OperationsTest, SyncedIterate2) src2, vector{}, With(op1,op2), - [] (int e, vector& v, int op1, int op2) { + [] (int e, vector& v, int op1, int op2) -> void { v.push_back(e); v.push_back(op1); v.push_back(op2); diff --git a/src/test/SignalTest.h b/src/test/SignalTest.h index 36f52a75..5acfae2b 100644 --- a/src/test/SignalTest.h +++ b/src/test/SignalTest.h @@ -36,10 +36,12 @@ TYPED_TEST_CASE_P(SignalTest); /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(SignalTest, MakeVars) { - auto v1 = MyDomain::MakeVar(1); - auto v2 = MyDomain::MakeVar(2); - auto v3 = MyDomain::MakeVar(3); - auto v4 = MyDomain::MakeVar(4); + using D = typename MakeVars::MyDomain; + + auto v1 = MakeVar(1); + auto v2 = MakeVar(2); + auto v3 = MakeVar(3); + auto v4 = MakeVar(4); ASSERT_EQ(v1(),1); ASSERT_EQ(v2(),2); @@ -62,10 +64,12 @@ TYPED_TEST_P(SignalTest, MakeVars) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(SignalTest, Signals1) { - auto v1 = MyDomain::MakeVar(1); - auto v2 = MyDomain::MakeVar(2); - auto v3 = MyDomain::MakeVar(3); - auto v4 = MyDomain::MakeVar(4); + using D = typename Signals1::MyDomain; + + auto v1 = MakeVar(1); + auto v2 = MakeVar(2); + auto v3 = MakeVar(3); + auto v4 = MakeVar(4); auto s1 = MakeSignal(With(v1,v2), [] (int a, int b) { return a + b; @@ -110,8 +114,10 @@ TYPED_TEST_P(SignalTest, Signals1) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(SignalTest, Signals2) { - auto a1 = MyDomain::MakeVar(1); - auto a2 = MyDomain::MakeVar(1); + using D = typename Signals2::MyDomain; + + auto a1 = MakeVar(1); + auto a2 = MakeVar(1); auto b1 = a1 + 0; auto b2 = a1 + a2; @@ -183,8 +189,10 @@ TYPED_TEST_P(SignalTest, Signals2) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(SignalTest, Signals3) { - auto a1 = MyDomain::MakeVar(1); - auto a2 = MyDomain::MakeVar(1); + using D = typename Signals3::MyDomain; + + auto a1 = MakeVar(1); + auto a2 = MakeVar(1); auto b1 = a1 + 0; auto b2 = a1 + a2; @@ -215,7 +223,7 @@ TYPED_TEST_P(SignalTest, Signals3) ASSERT_EQ(result(),6); - MyDomain::DoTransaction([&] { + D::DoTransaction([&] { a1 <<= 2; a2 <<= 2; }); @@ -240,8 +248,10 @@ TYPED_TEST_P(SignalTest, Signals3) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(SignalTest, Signals4) { - auto a1 = MyDomain::MakeVar(1); - auto a2 = MyDomain::MakeVar(1); + using D = typename Signals4::MyDomain; + + auto a1 = MakeVar(1); + auto a2 = MakeVar(1); auto b1 = a1 + a2; auto b2 = b1 + a2; @@ -266,9 +276,11 @@ TYPED_TEST_P(SignalTest, Signals4) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(SignalTest, FunctionBind1) { - auto v1 = MyDomain::MakeVar(2); - auto v2 = MyDomain::MakeVar(30); - auto v3 = MyDomain::MakeVar(10); + using D = typename FunctionBind1::MyDomain; + + auto v1 = MakeVar(2); + auto v2 = MakeVar(30); + auto v3 = MakeVar(10); auto signal = (v1, v2, v3) ->* [=] (int a, int b, int c) -> int { @@ -289,8 +301,10 @@ float myfunc3(float a, float b) { return a * b; } /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(SignalTest, FunctionBind2) { - auto a = MyDomain::MakeVar(1); - auto b = MyDomain::MakeVar(1); + using D = typename FunctionBind2::MyDomain; + + auto a = MakeVar(1); + auto b = MakeVar(1); auto c = ((a+b), (a+100)) ->* &myfunc; auto d = c ->* &myfunc2; @@ -315,10 +329,12 @@ TYPED_TEST_P(SignalTest, FunctionBind2) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(SignalTest, Flatten1) { - auto inner1 = MyDomain::MakeVar(123); - auto inner2 = MyDomain::MakeVar(789); + using D = typename Flatten1::MyDomain; + + auto inner1 = MakeVar(123); + auto inner2 = MakeVar(789); - auto outer = MyDomain::MakeVar(inner1); + auto outer = MakeVar(inner1); auto flattened = Flatten(outer); @@ -354,12 +370,14 @@ TYPED_TEST_P(SignalTest, Flatten1) /// Flatten2 test /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(SignalTest, Flatten2) -{ - auto a0 = MyDomain::MakeVar(100); +{ + using D = typename Flatten2::MyDomain; + + auto a0 = MakeVar(100); - auto inner1 = MyDomain::MakeVar(200); + auto inner1 = MakeVar(200); - auto a1 = MyDomain::MakeVar(300); + auto a1 = MakeVar(300); auto a2 = a1 + 0; auto a3 = a2 + 0; auto a4 = a3 + 0; @@ -370,7 +388,7 @@ TYPED_TEST_P(SignalTest, Flatten2) ASSERT_EQ(inner1(),200); ASSERT_EQ(inner2(),300); - auto outer = MyDomain::MakeVar(inner1); + auto outer = MakeVar(inner1); auto flattened = Flatten(outer); @@ -400,7 +418,7 @@ TYPED_TEST_P(SignalTest, Flatten2) ASSERT_EQ(result(), 100 + 300); ASSERT_EQ(observeCount, 2); - MyDomain::DoTransaction([&] { + D::DoTransaction([&] { a0 <<= 5000; a1 <<= 6000; }); @@ -414,16 +432,18 @@ TYPED_TEST_P(SignalTest, Flatten2) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(SignalTest, Flatten3) { - auto inner1 = MyDomain::MakeVar(10); + using D = typename Flatten3::MyDomain; + + auto inner1 = MakeVar(10); - auto a1 = MyDomain::MakeVar(20); + auto a1 = MakeVar(20); auto a2 = a1 + 0; auto a3 = a2 + 0; auto inner2 = a3 + 0; - auto outer = MyDomain::MakeVar(inner1); + auto outer = MakeVar(inner1); - auto a0 = MyDomain::MakeVar(30); + auto a0 = MakeVar(30); auto flattened = Flatten(outer); @@ -439,7 +459,7 @@ TYPED_TEST_P(SignalTest, Flatten3) ASSERT_EQ(result(), 10 + 30); ASSERT_EQ(observeCount, 0); - MyDomain::DoTransaction([&] { + D::DoTransaction([&] { inner1 <<= 1000; a0 <<= 200000; a1 <<= 50000; @@ -449,7 +469,7 @@ TYPED_TEST_P(SignalTest, Flatten3) ASSERT_EQ(result(), 50000 + 200000); ASSERT_EQ(observeCount, 1); - MyDomain::DoTransaction([&] { + D::DoTransaction([&] { a0 <<= 667; a1 <<= 776; }); @@ -457,7 +477,7 @@ TYPED_TEST_P(SignalTest, Flatten3) ASSERT_EQ(result(), 776 + 667); ASSERT_EQ(observeCount, 2); - MyDomain::DoTransaction([&] { + D::DoTransaction([&] { inner1 <<= 999; a0 <<= 888; }); @@ -471,17 +491,19 @@ TYPED_TEST_P(SignalTest, Flatten3) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(SignalTest, Flatten4) { + using D = typename Flatten4::MyDomain; + std::vector results; - auto a1 = MyDomain::MakeVar(100); + auto a1 = MakeVar(100); auto inner1 = a1 + 0; - auto a2 = MyDomain::MakeVar(200); + auto a2 = MakeVar(200); auto inner2 = a2; - auto a3 = MyDomain::MakeVar(200); + auto a3 = MakeVar(200); - auto outer = MyDomain::MakeVar(inner1); + auto outer = MakeVar(inner1); auto flattened = Flatten(outer); @@ -491,7 +513,7 @@ TYPED_TEST_P(SignalTest, Flatten4) results.push_back(v); }); - MyDomain::DoTransaction([&] { + D::DoTransaction([&] { a3 <<= 400; outer <<= inner2; }); @@ -506,8 +528,10 @@ TYPED_TEST_P(SignalTest, Flatten4) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(SignalTest, Member1) { - auto outer = MyDomain::MakeVar(10); - auto inner = MyDomain::MakeVar(outer); + using D = typename Member1::MyDomain; + + auto outer = MakeVar(10); + auto inner = MakeVar(outer); auto flattened = inner.Flatten(); @@ -523,9 +547,11 @@ TYPED_TEST_P(SignalTest, Member1) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(SignalTest, Modify1) { + using D = typename Modify1::MyDomain; + using std::vector; - auto v = MyDomain::MakeVar(vector{}); + auto v = MakeVar(vector{}); int obsCount = 0; @@ -551,9 +577,11 @@ TYPED_TEST_P(SignalTest, Modify1) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(SignalTest, Modify2) { + using D = typename Modify2::MyDomain; + using std::vector; - auto v = MyDomain::MakeVar(vector{}); + auto v = MakeVar(vector{}); int obsCount = 0; @@ -565,7 +593,7 @@ TYPED_TEST_P(SignalTest, Modify2) obsCount++; }); - MyDomain::DoTransaction([&] { + D::DoTransaction([&] { v.Modify([] (vector& v) { v.push_back(30); }); @@ -588,9 +616,11 @@ TYPED_TEST_P(SignalTest, Modify2) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(SignalTest, Modify3) { + using D = typename Modify3::MyDomain; + using std::vector; - auto vect = MyDomain::MakeVar(vector{}); + auto vect = MakeVar(vector{}); int obsCount = 0; @@ -633,9 +663,11 @@ TYPED_TEST_P(SignalTest, Modify3) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(SignalTest, Modify4) { + using D = typename Modify4::MyDomain; + using std::vector; - auto vect = MyDomain::MakeVar(vector{}); + auto vect = MakeVar(vector{}); int obsCount = 0; @@ -648,7 +680,7 @@ TYPED_TEST_P(SignalTest, Modify4) }); // Also terrible - MyDomain::DoTransaction([&] { + D::DoTransaction([&] { vect.Set(vector{ 30, 50 }); diff --git a/src/test/TransactionTest.h b/src/test/TransactionTest.h index 484536ae..c6c7c866 100644 --- a/src/test/TransactionTest.h +++ b/src/test/TransactionTest.h @@ -37,9 +37,11 @@ TYPED_TEST_CASE_P(TransactionTest); /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(TransactionTest, Concurrent1) { + using D = typename Concurrent1::MyDomain; + std::vector results; - auto n1 = MyDomain::MakeVar(1); + auto n1 = MakeVar(1); auto n2 = n1 + 1; auto n3 = n2 + n1 + 1; auto n4 = n3 + 1; @@ -98,12 +100,14 @@ TYPED_TEST_P(TransactionTest, Concurrent1) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(TransactionTest, Concurrent2) { + using D = typename Concurrent2::MyDomain; + std::vector results; - auto in = MyDomain::MakeVar(-1); + auto in = MakeVar(-1); // 1. Generate graph - MyDomain::SignalT n0 = in; + Signal n0 = in; auto next = n0; @@ -163,6 +167,8 @@ TYPED_TEST_P(TransactionTest, Concurrent2) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(TransactionTest, Concurrent3) { + using D = typename Concurrent3::MyDomain; + std::vector results; std::function f_0 = [] (int a) -> int @@ -177,7 +183,7 @@ TYPED_TEST_P(TransactionTest, Concurrent3) return a + b; }; - auto n1 = MyDomain::MakeVar(1); + auto n1 = MakeVar(1); auto n2 = n1 ->* f_0; auto n3 = ((n2, n1) ->* f_n) ->* f_0; auto n4 = n3 ->* f_0; @@ -236,6 +242,8 @@ TYPED_TEST_P(TransactionTest, Concurrent3) /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(TransactionTest, Merging1) { + using D = typename Merging1::MyDomain; + std::vector results; std::atomic shouldSpin(false); @@ -247,7 +255,7 @@ TYPED_TEST_P(TransactionTest, Merging1) return a; }; - auto n1 = MyDomain::MakeVar(1); + auto n1 = MakeVar(1); auto n2 = n1 ->* f; Observe(n2, [&] (int v) @@ -258,25 +266,25 @@ TYPED_TEST_P(TransactionTest, Merging1) // Todo: improve this as it'll fail occasionally shouldSpin = true; std::thread t1([&] { - MyDomain::DoTransaction(enable_input_merging, [&] { + D::DoTransaction(enable_input_merging, [&] { n1 <<= 2; }); }); std::this_thread::sleep_for(std::chrono::milliseconds(2000)); std::thread t2([&] { - MyDomain::DoTransaction(enable_input_merging, [&] { + D::DoTransaction(enable_input_merging, [&] { n1 <<= 3; }); }); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::thread t3([&] { - MyDomain::DoTransaction(enable_input_merging, [&] { + D::DoTransaction(enable_input_merging, [&] { n1 <<= 4; }); }); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::thread t4([&] { - MyDomain::DoTransaction(enable_input_merging, [&] { + D::DoTransaction(enable_input_merging, [&] { n1 <<= 5; }); From ac23a8d4c2dc3151c7a0e646f7430cb7129db599 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 2 Jun 2014 00:59:15 +0200 Subject: [PATCH 124/266] Missing include. --- include/react/detail/ReactiveInput.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index cbafd9cf..9a4f60bb 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "tbb/concurrent_vector.h" From 945d8ed1566b2943fe780b54514188813ce3640b Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 2 Jun 2014 01:00:12 +0200 Subject: [PATCH 125/266] Workaround to ensure observer registry is destroyed after nodes in same compilation unit. --- include/react/Domain.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/react/Domain.h b/include/react/Domain.h index 9b170580..34fe75a3 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -168,6 +168,7 @@ class DomainInitializer #endif //REACT_ENABLE_LOGGING D::Engine::Engine(); + DomainSpecificObserverRegistry::Instance(); } }; From 137ccad79383ac4e0e2a54062e64bd98818262d7 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 2 Jun 2014 01:01:09 +0200 Subject: [PATCH 126/266] Define no longer necessary because examples will be split. --- include/react/Reactor.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/include/react/Reactor.h b/include/react/Reactor.h index 723b249c..a5890acf 100644 --- a/include/react/Reactor.h +++ b/include/react/Reactor.h @@ -9,8 +9,6 @@ #pragma once -#ifdef REACT_ENABLE_REACTORS - #include "react/detail/Defs.h" #include @@ -36,7 +34,7 @@ class ReactiveLoop { public: Context(NodeT& node) : - node_{ node } + node_( node ) {} template @@ -57,7 +55,7 @@ class ReactiveLoop template ReactiveLoop(F&& func) : - nodePtr_{ new REACT_IMPL::ReactorNode(std::forward(func)) } + nodePtr_( new REACT_IMPL::ReactorNode(std::forward(func)) ) { nodePtr_->StartLoop(); } @@ -68,6 +66,4 @@ class ReactiveLoop /******************************************/ REACT_END /******************************************/ -#endif // REACT_ENABLE_REACTORS - #endif // REACT_REACTOR_H_INCLUDED \ No newline at end of file From 5a397e1e66e36661afe4573361bc146e4de7dfb6 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 2 Jun 2014 01:10:45 +0200 Subject: [PATCH 127/266] Reorganized project structure in preparation for cross-platform build. --- .../src}/BenchmarkBase.h | 0 .../src}/BenchmarkFanout.h | 0 .../src}/BenchmarkGrid.h | 0 .../src}/BenchmarkLifeSim.h | 0 .../src}/BenchmarkRandom.h | 0 .../src}/BenchmarkSequence.h | 0 {src/benchmark => benchmarks/src}/Main.cpp | 0 examples/src/BasicAlgorithms.cpp | 310 +++++++++++++++ examples/src/BasicComposition.cpp | 190 +++++++++ examples/src/BasicEvents.cpp | 268 +++++++++++++ examples/src/BasicObservers.cpp | 242 ++++++++++++ examples/src/BasicReactors.cpp | 87 +++++ examples/src/BasicSignals.cpp | 356 +++++++++++++++++ examples/src/BasicSynchronization.cpp | 5 + examples/src/Main.cpp | 54 +++ project/msvc/CppReact.sln | 69 +++- project/msvc/CppReact.vcxproj | 14 +- project/msvc/CppReactBenchmark.vcxproj | 26 +- .../msvc/CppReactBenchmark.vcxproj.filters | 22 +- ...andbox.vcxproj => CppReactExample.vcxproj} | 15 +- ...ilters => CppReactExample.vcxproj.filters} | 2 +- project/msvc/CppReactTest.vcxproj | 46 +-- project/msvc/CppReactTest.vcxproj.filters | 60 +-- project/msvc/Example_BasicAlgorithms.vcxproj | 87 +++++ .../Example_BasicAlgorithms.vcxproj.filters | 22 ++ project/msvc/Example_BasicComposition.vcxproj | 87 +++++ .../Example_BasicComposition.vcxproj.filters | 22 ++ project/msvc/Example_BasicEvents.vcxproj | 87 +++++ .../msvc/Example_BasicEvents.vcxproj.filters | 22 ++ project/msvc/Example_BasicObservers.vcxproj | 87 +++++ .../Example_BasicObservers.vcxproj.filters | 22 ++ project/msvc/Example_BasicReactors.vcxproj | 87 +++++ .../Example_BasicReactors.vcxproj.filters | 22 ++ project/msvc/Example_BasicSignals.vcxproj | 87 +++++ .../msvc/Example_BasicSignals.vcxproj.filters | 22 ++ .../msvc/Example_BasicSynchronization.vcxproj | 87 +++++ ...ample_BasicSynchronization.vcxproj.filters | 22 ++ src/sandbox/Main.cpp | 369 ------------------ {src/test => tests/src}/EventStreamTest.cpp | 0 {src/test => tests/src}/EventStreamTest.h | 0 {src/test => tests/src}/EventStreamTestQ.cpp | 0 {src/test => tests/src}/MoveTest.cpp | 0 {src/test => tests/src}/MoveTest.h | 0 {src/test => tests/src}/ObserverTest.cpp | 0 {src/test => tests/src}/ObserverTest.h | 0 {src/test => tests/src}/ObserverTestQ.cpp | 0 {src/test => tests/src}/OperationsTest.cpp | 0 {src/test => tests/src}/OperationsTest.h | 0 {src/test => tests/src}/OperationsTestQ.cpp | 0 {src/test => tests/src}/SignalTest.cpp | 0 {src/test => tests/src}/SignalTest.h | 0 {src/test => tests/src}/SignalTestQ.cpp | 0 {src/test => tests/src}/TransactionTest.cpp | 0 {src/test => tests/src}/TransactionTest.h | 0 54 files changed, 2438 insertions(+), 460 deletions(-) rename {src/benchmark => benchmarks/src}/BenchmarkBase.h (100%) rename {src/benchmark => benchmarks/src}/BenchmarkFanout.h (100%) rename {src/benchmark => benchmarks/src}/BenchmarkGrid.h (100%) rename {src/benchmark => benchmarks/src}/BenchmarkLifeSim.h (100%) rename {src/benchmark => benchmarks/src}/BenchmarkRandom.h (100%) rename {src/benchmark => benchmarks/src}/BenchmarkSequence.h (100%) rename {src/benchmark => benchmarks/src}/Main.cpp (100%) create mode 100644 examples/src/BasicAlgorithms.cpp create mode 100644 examples/src/BasicComposition.cpp create mode 100644 examples/src/BasicEvents.cpp create mode 100644 examples/src/BasicObservers.cpp create mode 100644 examples/src/BasicReactors.cpp create mode 100644 examples/src/BasicSignals.cpp create mode 100644 examples/src/BasicSynchronization.cpp create mode 100644 examples/src/Main.cpp rename project/msvc/{CppReactSandbox.vcxproj => CppReactExample.vcxproj} (89%) rename project/msvc/{CppReactSandbox.vcxproj.filters => CppReactExample.vcxproj.filters} (94%) create mode 100644 project/msvc/Example_BasicAlgorithms.vcxproj create mode 100644 project/msvc/Example_BasicAlgorithms.vcxproj.filters create mode 100644 project/msvc/Example_BasicComposition.vcxproj create mode 100644 project/msvc/Example_BasicComposition.vcxproj.filters create mode 100644 project/msvc/Example_BasicEvents.vcxproj create mode 100644 project/msvc/Example_BasicEvents.vcxproj.filters create mode 100644 project/msvc/Example_BasicObservers.vcxproj create mode 100644 project/msvc/Example_BasicObservers.vcxproj.filters create mode 100644 project/msvc/Example_BasicReactors.vcxproj create mode 100644 project/msvc/Example_BasicReactors.vcxproj.filters create mode 100644 project/msvc/Example_BasicSignals.vcxproj create mode 100644 project/msvc/Example_BasicSignals.vcxproj.filters create mode 100644 project/msvc/Example_BasicSynchronization.vcxproj create mode 100644 project/msvc/Example_BasicSynchronization.vcxproj.filters delete mode 100644 src/sandbox/Main.cpp rename {src/test => tests/src}/EventStreamTest.cpp (100%) rename {src/test => tests/src}/EventStreamTest.h (100%) rename {src/test => tests/src}/EventStreamTestQ.cpp (100%) rename {src/test => tests/src}/MoveTest.cpp (100%) rename {src/test => tests/src}/MoveTest.h (100%) rename {src/test => tests/src}/ObserverTest.cpp (100%) rename {src/test => tests/src}/ObserverTest.h (100%) rename {src/test => tests/src}/ObserverTestQ.cpp (100%) rename {src/test => tests/src}/OperationsTest.cpp (100%) rename {src/test => tests/src}/OperationsTest.h (100%) rename {src/test => tests/src}/OperationsTestQ.cpp (100%) rename {src/test => tests/src}/SignalTest.cpp (100%) rename {src/test => tests/src}/SignalTest.h (100%) rename {src/test => tests/src}/SignalTestQ.cpp (100%) rename {src/test => tests/src}/TransactionTest.cpp (100%) rename {src/test => tests/src}/TransactionTest.h (100%) diff --git a/src/benchmark/BenchmarkBase.h b/benchmarks/src/BenchmarkBase.h similarity index 100% rename from src/benchmark/BenchmarkBase.h rename to benchmarks/src/BenchmarkBase.h diff --git a/src/benchmark/BenchmarkFanout.h b/benchmarks/src/BenchmarkFanout.h similarity index 100% rename from src/benchmark/BenchmarkFanout.h rename to benchmarks/src/BenchmarkFanout.h diff --git a/src/benchmark/BenchmarkGrid.h b/benchmarks/src/BenchmarkGrid.h similarity index 100% rename from src/benchmark/BenchmarkGrid.h rename to benchmarks/src/BenchmarkGrid.h diff --git a/src/benchmark/BenchmarkLifeSim.h b/benchmarks/src/BenchmarkLifeSim.h similarity index 100% rename from src/benchmark/BenchmarkLifeSim.h rename to benchmarks/src/BenchmarkLifeSim.h diff --git a/src/benchmark/BenchmarkRandom.h b/benchmarks/src/BenchmarkRandom.h similarity index 100% rename from src/benchmark/BenchmarkRandom.h rename to benchmarks/src/BenchmarkRandom.h diff --git a/src/benchmark/BenchmarkSequence.h b/benchmarks/src/BenchmarkSequence.h similarity index 100% rename from src/benchmark/BenchmarkSequence.h rename to benchmarks/src/BenchmarkSequence.h diff --git a/src/benchmark/Main.cpp b/benchmarks/src/Main.cpp similarity index 100% rename from src/benchmark/Main.cpp rename to benchmarks/src/Main.cpp diff --git a/examples/src/BasicAlgorithms.cpp b/examples/src/BasicAlgorithms.cpp new file mode 100644 index 00000000..d0f0f8b9 --- /dev/null +++ b/examples/src/BasicAlgorithms.cpp @@ -0,0 +1,310 @@ + +// 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) + +#include +#include +#include +#include + +#include "react/Domain.h" +#include "react/Signal.h" +#include "react/Event.h" +#include "react/Observer.h" +#include "react/Algorithm.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 1 - Converting events to signals +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example1 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + + class Sensor + { + public: + USING_REACTIVE_DOMAIN(D) + + EventSourceT Samples = MakeEventSource(); + SignalT LastSample = Hold(Samples, 0); + }; + + void Run() + { + cout << "Example 1 - Converting events to signals" << endl; + + Sensor mySensor; + + Observe(mySensor.LastSample, [] (int v) { + std::cout << v << std::endl; + }); + + mySensor.Samples << 20 << 21 << 21 << 22; // output: 20, 21, 22 + + D::DoTransaction([&] { + mySensor.Samples << 30 << 31 << 31 << 32; + }); // output: 32 + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 2 - Converting signals to events +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example2 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + + class Employee + { + public: + USING_REACTIVE_DOMAIN(D) + + VarSignalT Name = MakeVar(string( "Bob" )); + VarSignalT Salary = MakeVar(3000); + + EventsT SalaryChanged = Monitor(Salary); + }; + + void Run() + { + cout << "Example 2 - Converting signals to events" << endl; + + Employee bob; + + Observe( + bob.SalaryChanged, + With(bob.Name), + [] (int newSalary, const string& name) { + cout << name << " now earns " << newSalary << endl; + }); + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 3 - Creating stateful signals (1) +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example3 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + + class Counter + { + public: + USING_REACTIVE_DOMAIN(D) + + EventSourceT<> Increment = MakeEventSource(); + + SignalT Count = Iterate( + Increment, + 0, + [] (Token, int oldCount) { + return oldCount + 1; + }); + }; + + void Run() + { + cout << "Example 3 - Creating stateful signals (1)" << endl; + + Counter myCounter; + + // Note: Using function-style operator() instead of .Emit() and .Value() + myCounter.Increment(); + myCounter.Increment(); + myCounter.Increment(); + + cout << myCounter.Count() << endl; // output: 3 + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 4 - Creating stateful signals (2) +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example4 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + + class Sensor + { + public: + USING_REACTIVE_DOMAIN(D) + + EventSourceT Input = MakeEventSource(); + + SignalT Average = Iterate( + Input, + 0.0f, + [] (int sample, float oldAvg) { + return (oldAvg + sample) / 2.0f; + }); + }; + + void Run() + { + cout << "Example 4 - Creating stateful signals (2)" << endl; + + Sensor mySensor; + + mySensor.Input << 10 << 5 << 10 << 8; + + cout << "Average: " << mySensor.Average() << endl; // output: 3 + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 5 - Creating stateful signals (3) +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example5 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + + enum class ECmd { increment, decrement, reset }; + + class Counter + { + public: + USING_REACTIVE_DOMAIN(D) + + EventSourceT Update = MakeEventSource(); + VarSignalT Delta = MakeVar(1); + VarSignalT Start = MakeVar(0); + + SignalT Count = Iterate( + Update, + Start.Value(), + With(Delta, Start), + [] (ECmd cmd, int oldCount, int delta, int start) { + if (cmd == ECmd::increment) + return oldCount + delta; + else if (cmd == ECmd::decrement) + return oldCount - delta; + else + return start; + }); + }; + + void Run() + { + cout << "Example 5 - Creating stateful signals (3)" << endl; + + Counter myCounter; + + cout << "Start: " << myCounter.Count() << endl; // output: 0 + + myCounter.Update(ECmd::increment); + myCounter.Update(ECmd::increment); + myCounter.Update(ECmd::increment); + + cout << "3x increment by 1: " << myCounter.Count() << endl; // output: 3 + + myCounter.Delta <<= 5; + myCounter.Update(ECmd::decrement); + + cout << "1x decrement by 5: " << myCounter.Count() << endl; // output: -2 + + myCounter.Start <<= 100; + myCounter.Update(ECmd::reset); + + cout << "reset to 100: " << myCounter.Count() << endl; // output: 100 + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 6 - Avoiding expensive copies +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example6 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + + class Sensor + { + public: + USING_REACTIVE_DOMAIN(D) + + EventSourceT Input = MakeEventSource(); + + VarSignalT Threshold = MakeVar(42); + + SignalT> AllSamples = Iterate( + Input, + vector{}, + [] (int input, vector& all) { + all.push_back(input); + }); + + SignalT> CriticalSamples = Iterate( + Input, + vector{}, + With(Threshold), + [] (int input, vector& critical, int threshold) { + if (input > threshold) + critical.push_back(input); + }); + }; + + void Run() + { + cout << "Example 6 - Avoiding expensive copies" << endl; + + Sensor mySensor; + + mySensor.Input << 40 << 29 << 43 << 50; + + cout << "All samples: "; + for (auto const& v : mySensor.AllSamples()) + cout << v << " "; + cout << endl; + + cout << "Critical samples: "; + for (auto const& v : mySensor.CriticalSamples()) + cout << v << " "; + cout << endl; + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Run examples +/////////////////////////////////////////////////////////////////////////////////////////////////// +int main() +{ + example1::Run(); + example2::Run(); + example3::Run(); + example4::Run(); + example5::Run(); + example6::Run(); + + return 0; +} \ No newline at end of file diff --git a/examples/src/BasicComposition.cpp b/examples/src/BasicComposition.cpp new file mode 100644 index 00000000..66e2e61a --- /dev/null +++ b/examples/src/BasicComposition.cpp @@ -0,0 +1,190 @@ + +// 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) + +#include +#include +#include + +#include "react/Domain.h" +#include "react/Signal.h" +#include "react/Event.h" +#include "react/Observer.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 1 - Reactive class members +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example1 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + + class Shape + { + public: + USING_REACTIVE_DOMAIN(D) + + VarSignalT Width = MakeVar(0); + VarSignalT Height = MakeVar(0); + + SignalT Size = Width * Height; + + EventSourceT<> HasMoved = MakeEventSource(); + }; + + void Run() + { + cout << "Example 1 - Reactive class members" << endl; + + Shape myShape; + + Observe(myShape.Size, [] (int newValue) { + cout << "Size changed to " << newValue << endl; + }); + + D::DoTransaction([&] { + myShape.Width <<= 4; + myShape.Height <<= 4; + }); // output: Size changed to 16 + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 2 - Signals of references +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example2 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + + class Company + { + public: + const char* Name; + + Company(const char* name) : + Name( name ) + {} + + // Note: To be used as a signal value type, + // values of the type must be comparable + bool operator==(const Company& other) const + { + return this == &other; + } + }; + + class Employee + { + public: + USING_REACTIVE_DOMAIN(D) + + VarSignalT MyCompany; + + Employee(Company& company) : + MyCompany( MakeVar(ref(company)) ) + {} + }; + + void Run() + { + cout << "Example 2 - Signals of references" << endl; + + Company company1( "MetroTec" ); + Company company2( "ACME" ); + + Employee bob( company1 ); + + Observe(bob.MyCompany, [] (const Company& company) { + cout << "Bob works for " << company.Name << endl; + }); + + bob.MyCompany <<= ref(company2); // output: Bob now works for ACME + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 3 - Dynamic signal references +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example3 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + + class Company + { + public: + USING_REACTIVE_DOMAIN(D) + + VarSignalT Name; + + Company(const char* name) : + Name( MakeVar(string( name )) ) + {} + + bool operator==(const Company& other) const + { + return this == &other; + } + }; + + class Employee + { + public: + USING_REACTIVE_DOMAIN(D) + + VarSignalT MyCompany; + + Employee(Company& company) : + MyCompany( MakeVar(ref(company)) ) + {} + }; + + void Run() + { + cout << "Example 3 - Dynamic signal references" << endl; + + Company company1( "MetroTec" ); + Company company2( "ACME" ); + + Employee alice( company1 ); + + auto obs = Observe( + REACTIVE_REF(alice.MyCompany, Name), + [] (const string& name) { + cout << "Alice now works for " << name << endl; + }); + + company1.Name <<= string( "ModernTec" ); // output: Alice now works for ModernTec + alice.MyCompany <<= ref(company2); // output: Alice now works for ACME + company2.Name <<= string( "A.C.M.E." ); // output: Alice now works for A.C.M.E. + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Run examples +/////////////////////////////////////////////////////////////////////////////////////////////////// +int main() +{ + example1::Run(); + + example2::Run(); + + example3::Run(); + + return 0; +} \ No newline at end of file diff --git a/examples/src/BasicEvents.cpp b/examples/src/BasicEvents.cpp new file mode 100644 index 00000000..1de14039 --- /dev/null +++ b/examples/src/BasicEvents.cpp @@ -0,0 +1,268 @@ + +// 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) + +#include +#include +#include + +#include "react/Domain.h" +#include "react/Event.h" +#include "react/Observer.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 1 - Hello world +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example1 +{ + using namespace std; + using namespace react; + + // Defines a domain. + // Each domain represents a separate dependency graph, managed by a dedicated propagation engine. + // Reactives of different domains can not be combined. + REACTIVE_DOMAIN(D) + + // Define type aliases for the given domain in this namespace. + // Now we can use EventSourceT instead of D::EventSourceT. + USING_REACTIVE_DOMAIN(D) + + // An event source that emits values of type string + namespace v1 + { + EventSourceT mySource = MakeEventSource(); + + void Run() + { + cout << "Example 1 - Hello world (string source)" << endl; + + Observe(mySource, [] (const string& s) { + std::cout << s << std::endl; + }); + + mySource << string("Hello world #1"); + + // Or without the operator: + mySource.Emit(string("Hello world #2")); + + // Or as a function call: + mySource(string("Hello world #3")); + + cout << endl; + } + } + + // An event source without an explicit value type + namespace v2 + { + EventSourceT<> helloWorldTrigger = MakeEventSource(); + + void Run() + { + cout << "Example 1 - Hello world (token source)" << endl; + + int count = 0; + + Observe(helloWorldTrigger, [&] (Token) { + cout << "Hello world #" << ++count << endl; + }); + + helloWorldTrigger.Emit(); + + // Or without the stream operator: + helloWorldTrigger << Token::value; + + // Or as a function call: + helloWorldTrigger(); + + cout << endl; + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 2 - Merging event streams +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example2 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + USING_REACTIVE_DOMAIN(D) + + // An event stream that merges both sources + namespace v1 + { + EventSourceT<> leftClick = MakeEventSource(); + EventSourceT<> rightClick = MakeEventSource(); + + EventsT<> anyClick = Merge(leftClick, rightClick); + + void Run() + { + cout << "Example 2 - Merging event streams (Merge)" << endl; + + int count = 0; + + Observe(anyClick, [&] (Token) { + cout << "clicked #" << ++count << endl; + }); + + leftClick.Emit(); // output: clicked #1 + rightClick.Emit(); // output: clicked #2 + + cout << endl; + } + } + + // Using overloaded operator | instead of explicit Merge + namespace v2 + { + EventSourceT<> leftClick = MakeEventSource(); + EventSourceT<> rightClick = MakeEventSource(); + + EventsT<> anyClick = leftClick | rightClick; + + void Run() + { + cout << "Example 2 - Merging event streams (operator)" << endl; + + int count = 0; + + Observe(anyClick, [&] (Token) { + cout << "clicked #" << ++count << endl; + }); + + leftClick.Emit(); // output: clicked #1 + rightClick.Emit(); // output: clicked #2 + + cout << endl; + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 3 - Filtering events +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example3 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + USING_REACTIVE_DOMAIN(D) + + EventSourceT numbers = MakeEventSource(); + + EventsT greater10 = Filter(numbers, [] (int n) { + return n > 10; + }); + + void Run() + { + cout << "Example 3 - Filtering events" << endl; + + Observe(greater10, [] (int n) { + cout << n << endl; + }); + + numbers << 5 << 11 << 7 << 100; // output: 11, 100 + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 4 - Filtering events +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example4 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + USING_REACTIVE_DOMAIN(D) + + // Data types + enum class ETag { normal, critical }; + using TaggedNum = pair; + + EventSourceT numbers = MakeEventSource(); + + EventsT tagged = Transform(numbers, [] (int n) { + if (n > 10) + return TaggedNum( ETag::critical, n ); + else + return TaggedNum( ETag::normal, n ); + }); + + void Run() + { + cout << "Example 4 - Transforming events" << endl; + + Observe(tagged, [] (const TaggedNum& t) { + if (t.first == ETag::critical) + cout << "(critical) " << t.second << endl; + else + cout << "(normal) " << t.second << endl; + }); + + numbers << 5; // output: (normal) 5 + numbers << 20; // output: (critical) 20 + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 5 - Queuing multiple inputs +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example5 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + USING_REACTIVE_DOMAIN(D) + + EventSourceT src = MakeEventSource(); + + void Run() + { + cout << "Example 5 - Queuing multiple inputs" << endl; + + Observe(src, [] (int v) { + cout << v << endl; + }); // output: 1, 2, 3, 4 + + D::DoTransaction([] { + src << 1 << 2 << 3; + src << 4; + }); + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Run examples +/////////////////////////////////////////////////////////////////////////////////////////////////// +int main() +{ + example1::v1::Run(); + example1::v2::Run(); + + example2::v1::Run(); + example2::v2::Run(); + + example3::Run(); + + example4::Run(); + + example5::Run(); + + return 0; +} \ No newline at end of file diff --git a/examples/src/BasicObservers.cpp b/examples/src/BasicObservers.cpp new file mode 100644 index 00000000..4bd6bf61 --- /dev/null +++ b/examples/src/BasicObservers.cpp @@ -0,0 +1,242 @@ + +// 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) + +#include +#include +#include + +#include "react/Domain.h" +#include "react/Signal.h" +#include "react/Event.h" +#include "react/Observer.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 1 - Creating subject-bound observers +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example1 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + USING_REACTIVE_DOMAIN(D) + + auto x = MakeVar(1); + + namespace v1 + { + void Run() + { + cout << "Example 1 - Creating subject-bound observers (v1)" << endl; + + { + // Create a signal in the function scope + auto mySignal = MakeSignal(x, [] (int x) { return x; } ); + + // The lifetime of the observer is bound to mySignal. + // After Run() returns, mySignal is destroyed, and so is the observer + Observe(mySignal, [] (int mySignal) { + cout << mySignal << endl; + }); + + x <<= 2; // output: 2 + } + + x <<= 3; // no ouput + + cout << endl; + } + } + + namespace v2 + { + void Run() + { + cout << "Example 1 - Creating subject-bound observers (v2)" << endl; + + // Outer scope + { + // Unbound observer + ObserverT obs; + + // Inner scope + { + auto mySignal = MakeSignal(x, [] (int x) { return x; } ); + + // Move-assign to obs + obs = Observe(mySignal, [] (int mySignal) { + cout << mySignal << endl; + }); + + // The node linked to mySignal is now also owned by obs + + x <<= 2; // output: 2 + } + // ~Inner scope + + // mySignal was destroyed, but as long as obs exists and is still + // attached to the signal node, this signal node won't be destroyed + + x <<= 3; // output: 3 + } + // ~Outer scope + + // obs was destroyed + // -> the signal node is no longer owned by anything and is destroyed + // -> the observer node is destroyed as it was bound to the subject + + x <<= 4; // no ouput + + cout << endl; + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 2 - Detaching observers manually (1) +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example2 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + + class DelayedExecutor + { + public: + USING_REACTIVE_DOMAIN(D) + + template + void ScheduleForExecution(const F& func) + { + Observe(trigger_, [=] (Token) { func(); }); + } + + void Execute() + { + trigger_.Emit(); + DetachAllObservers(trigger_); + } + + private: + EventSourceT<> trigger_ = MakeEventSource(); + }; + + void Run() + { + cout << "Example 2 - Detaching observers manually (1)" << endl; + + DelayedExecutor exec; + + exec.ScheduleForExecution([] { + cout << "one" << endl; + }); + exec.ScheduleForExecution([] { + cout << "two" << endl; + }); + + exec.Execute(); // output: one, two + + exec.Execute(); // no output + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 3 - Detaching observers manually (2) +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example3 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + USING_REACTIVE_DOMAIN(D) + + EventSourceT<> trigger = MakeEventSource(); + + void Run() + { + cout << "Example 3 - Detaching observers manually (2)" << endl; + + ObserverT obs = Observe(trigger, [] (Token) { + cout << "Triggered!" << endl; + }); + + trigger.Emit(); // output: Triggered! + + obs.Detach(); // Remove the observer + + trigger.Emit(); // no output + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 4 - Using scoped observers +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example4 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + USING_REACTIVE_DOMAIN(D) + + EventSourceT<> trigger = MakeEventSource(); + + void Run() + { + cout << "Example 4 - Using scoped observers" << endl; + + // Inner scope + { + ScopedObserverT scopedObs + ( + Observe(trigger, [] (Token) { + cout << "Triggered!" << endl; + }) + ); + + trigger.Emit(); // output: Triggered! + } + // ~Inner scope + + trigger.Emit(); // no output + + // Note the semantic difference between ScopedObserverT and ObserverT. + // + // During its lifetime, the ObserverT handle of an observer guarantees that the + // observed subject will not be destroyed and allows explicit detach. + // But even after the ObserverT handle is destroyed, the subject may continue to exist + // and so will the observer. + // + // ScopedObserverT has similar semantics to a scoped lock. + // When it's destroyed, it detaches and destroys the observer. + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Run examples +/////////////////////////////////////////////////////////////////////////////////////////////////// +int main() +{ + example1::v1::Run(); + example1::v2::Run(); + + example2::Run(); + + example3::Run(); + + example4::Run(); + + return 0; +} \ No newline at end of file diff --git a/examples/src/BasicReactors.cpp b/examples/src/BasicReactors.cpp new file mode 100644 index 00000000..4d5cddfb --- /dev/null +++ b/examples/src/BasicReactors.cpp @@ -0,0 +1,87 @@ + +// 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) + +#include +#include +#include + +#include "react/Domain.h" +#include "react/Event.h" +#include "react/Reactor.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 1 - Creating reactive loops +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example1 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + USING_REACTIVE_DOMAIN(D) + + using PointT = pair; + using PathT = vector; + + vector paths; + + EventSourceT mouseDown = MakeEventSource(); + EventSourceT mouseUp = MakeEventSource(); + EventSourceT mouseMove = MakeEventSource(); + + ReactiveLoopT loop + { + [&] (ReactiveLoopT::Context& ctx) + { + PathT points; + + points.emplace_back(ctx.Await(mouseDown)); + + ctx.RepeatUntil(mouseUp, [&] { + points.emplace_back(ctx.Await(mouseMove)); + }); + + points.emplace_back(ctx.Await(mouseUp)); + + paths.push_back(points); + } + }; + + void Run() + { + cout << "Example 1 - Creating reactive loops" << endl; + + mouseDown << PointT(1,1); + mouseMove << PointT(2,2) << PointT(3,3) << PointT(4,4); + mouseUp << PointT(5,5); + + mouseMove << PointT(999,999); + + mouseDown << PointT(10,10); + mouseMove << PointT(20,20); + mouseUp << PointT(30,30); + + for (const auto& path : paths) + { + cout << "Path: "; + for (const auto& point : path) + cout << "(" << point.first << "," << point.second << ") "; + cout << endl; + } + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Run examples +/////////////////////////////////////////////////////////////////////////////////////////////////// +int main() +{ + example1::Run(); + + return 0; +} \ No newline at end of file diff --git a/examples/src/BasicSignals.cpp b/examples/src/BasicSignals.cpp new file mode 100644 index 00000000..b6e906e4 --- /dev/null +++ b/examples/src/BasicSignals.cpp @@ -0,0 +1,356 @@ + +// 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) + +#include +#include +#include +#include +#include +#include + +#include "react/Domain.h" +#include "react/Signal.h" +#include "react/Observer.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 1 - Hello world +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example1 +{ + using namespace std; + using namespace react; + + // Defines a domain. + // Each domain represents a separate dependency graph, managed by a dedicated propagation engine. + // Reactives of different domains can not be combined. + REACTIVE_DOMAIN(D) + + // Define type aliases for the given domain in this namespace. + // Now we can use VarSignalT instead of D::VarSignalT. + USING_REACTIVE_DOMAIN(D) + + // The concat function + string concatFunc(string first, string second) { + return first + string(" ") + second; + }; + + // A signal that concatenates both words + namespace v1 + { + // The two words + VarSignalT firstWord = MakeVar(string("Change")); + VarSignalT secondWord = MakeVar(string("me!")); + + SignalT bothWords = MakeSignal(With(firstWord,secondWord), &concatFunc); + + void Run() + { + cout << "Example 1 - Hello world (MakeSignal)" << endl; + + // Imperative imperative value access + cout << bothWords.Value() << endl; + + // Imperative imperative change + firstWord <<= string("Hello"); + + cout << bothWords.Value() << endl; + + secondWord <<= string("World"); + + cout << bothWords.Value() << endl; + + cout << endl; + } + } + + // Using overloaded operator + instead of explicit MakeSignal + namespace v2 + { + // The two words + VarSignalT firstWord = MakeVar(string("Change")); + VarSignalT secondWord = MakeVar(string("me!")); + + SignalT bothWords = firstWord + string(" ") + secondWord; + + void Run() + { + cout << "Example 1 - Hello world (operators)" << endl; + + cout << bothWords.Value() << endl; + + firstWord <<= string("Hello"); + + cout << bothWords.Value() << endl; + + secondWord <<= string("World"); + + cout << bothWords.Value() << endl; + + cout << endl; + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 2 - Reacting to value changes +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example2 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + USING_REACTIVE_DOMAIN(D) + + VarSignalT x = MakeVar(1); + SignalT xAbs = MakeSignal(x, [] (int x) { return abs(x); }); + + void Run() + { + cout << "Example 2 - Reacting to value changes" << endl; + + Observe(xAbs, [] (int newValue) { + cout << "xAbs changed to " << newValue << endl; + }); + + // initially x is 1 + x <<= 2; // output: xAbs changed to 2 + x <<= -3; // output: xAbs changed to 3 + x <<= 3; // no output, xAbs is still 3 + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 3 - Changing multiple inputs +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example3 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + USING_REACTIVE_DOMAIN(D) + + VarSignalT a = MakeVar(1); + VarSignalT b = MakeVar(1); + + SignalT x = a + b; + SignalT y = a + b; + SignalT z = x + y; + + void Run() + { + cout << "Example 3 - Changing multiple inputs" << endl; + + Observe(z, [] (int newValue) { + std::cout << "z changed to " << newValue << std::endl; + }); + + a <<= 2; // output: z changed to 6 + b <<= 2; // output: z changed to 8 + + D::DoTransaction([] { + a <<= 4; + b <<= 4; + }); // output: z changed to 16 + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 4 - Complex signals +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example4 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + USING_REACTIVE_DOMAIN(D) + + // Helpers + using ExprPairT = pair; + using ExprVectT = vector; + + string makeExprStr(int a, int b, const char* op) + { + return to_string(a) + string(op) + to_string(b); + } + + ExprPairT makeExprPair(const string& s, int v) + { + return make_pair(s, v); + } + + void printExpressions(const ExprVectT& expressions) + { + cout << "Expressions: " << endl; + for (const auto& p : expressions) + cout << "\t" << p.first << " is " << p.second << endl; + } + + // Version 1 - Intermediate signals + namespace v1 + { + // Input operands + VarSignalT a = MakeVar(1); + VarSignalT b = MakeVar(2); + + // Calculations + SignalT sum = a + b; + SignalT diff = a - b; + SignalT prod = a * b; + + using std::placeholders::_1; + using std::placeholders::_2; + + // Stringified expressions + SignalT sumExpr = + MakeSignal(With(a,b), bind(makeExprStr, _1, _2, "+")); + + SignalT diffExpr = + MakeSignal(With(a,b), bind(makeExprStr, _1, _2, "-")); + + SignalT prodExpr = + MakeSignal(With(a,b), bind(makeExprStr, _1, _2, "*")); + + // The expression vector + SignalT expressions = MakeSignal( + With( + MakeSignal(With(sumExpr, sum), &makeExprPair), + MakeSignal(With(diffExpr, diff), &makeExprPair), + MakeSignal(With(prodExpr, prod), &makeExprPair) + ), + [] (const ExprPairT& sumP, const ExprPairT& diffP, const ExprPairT& prodP) { + return ExprVectT{ sumP, diffP, prodP}; + }); + + void Run() + { + cout << "Example 4 - Complex signals (v1)" << endl; + + Observe(expressions, printExpressions); + + a <<= 10; + b <<= 20; + + cout << endl; + } + } + + // Version 2 - Intermediate signals in a function + namespace v2 + { + SignalT createExpressionSignal(const SignalT& a, const SignalT& b) + { + using std::placeholders::_1; + using std::placeholders::_2; + + // Inside a function, we can use auto + auto sumExpr = + MakeSignal(With(a,b), bind(makeExprStr, _1, _2, "+")); + + auto diffExpr = + MakeSignal(With(a,b), bind(makeExprStr, _1, _2, "-")); + + auto prodExpr = + MakeSignal(With(a,b), bind(makeExprStr, _1, _2, "*")); + + return MakeSignal( + With( + MakeSignal(With(sumExpr, a + b), &makeExprPair), + MakeSignal(With(diffExpr, a - b), &makeExprPair), + MakeSignal(With(prodExpr, a * b), &makeExprPair) + ), + [] (const ExprPairT& sumP, const ExprPairT& diffP, const ExprPairT& prodP) { + return ExprVectT{ sumP, diffP, prodP }; + }); + } + + // Input operands + VarSignalT a = MakeVar(1); + VarSignalT b = MakeVar(2); + + // The expression vector + SignalT expressions = createExpressionSignal(a, b); + + void Run() + { + cout << "Example 4 - Complex signals (v2)" << endl; + + Observe(expressions, printExpressions); + + a <<= 30; + b <<= 40; + + cout << endl; + } + } + + // Version 3 - Imperative function + namespace v3 + { + // Input operands + VarSignalT a = MakeVar(1); + VarSignalT b = MakeVar(2); + + // The expression vector + SignalT expressions = MakeSignal(With(a,b), [] (int a, int b) { + ExprVectT result; + + result.push_back( + make_pair( + makeExprStr(a, b, "+"), + a + b)); + + result.push_back( + make_pair( + makeExprStr(a, b, "-"), + a - b)); + + result.push_back( + make_pair( + makeExprStr(a, b, "*"), + a * b)); + + return result; + }); + + void Run() + { + cout << "Example 4 - Complex signals (v3)" << endl; + + Observe(expressions, printExpressions); + + a <<= 50; + b <<= 60; + + cout << endl; + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Run examples +/////////////////////////////////////////////////////////////////////////////////////////////////// +int main() +{ + example1::v1::Run(); + example1::v2::Run(); + + example2::Run(); + + example3::Run(); + + example4::v1::Run(); + example4::v2::Run(); + example4::v3::Run(); + + return 0; +} \ No newline at end of file diff --git a/examples/src/BasicSynchronization.cpp b/examples/src/BasicSynchronization.cpp new file mode 100644 index 00000000..e27d1a93 --- /dev/null +++ b/examples/src/BasicSynchronization.cpp @@ -0,0 +1,5 @@ + + +int main() +{ +} \ No newline at end of file diff --git a/examples/src/Main.cpp b/examples/src/Main.cpp new file mode 100644 index 00000000..ab6574ae --- /dev/null +++ b/examples/src/Main.cpp @@ -0,0 +1,54 @@ + +// 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) + +//#define REACT_ENABLE_LOGGING + +#include "react/Domain.h" +#include "react/Signal.h" + +using namespace std; +using namespace react; + +// Defines a domain. +// Each domain represents a separate dependency graph, managed by a dedicated propagation engine. +// Reactives of different domains can not be combined. +REACTIVE_DOMAIN(D) + +void SignalExample3() +{ + cout << "Signal Example 3" << endl; + + auto src = MakeVar(0); + + // Input values can be manipulated imperatively in observers. + // Inputs are implicitly thread-safe, buffered and executed in a continuation turn. + // This continuation turn is queued just like a regular turn. + // If other turns are already queued, they are executed before the continuation. + Observe(src, [&] (int v) { + cout << "V: " << v << endl; + if (v < 10) + src <<= v+1; + }); + + src <<= 1; + + cout << endl; +} + +int main() +{ + SignalExample3(); + +#ifdef REACT_ENABLE_LOGGING + std::ofstream logfile; + logfile.open("log.txt"); + + D::Log().Write(logfile); + logfile.close(); +#endif + + return 0; +} \ No newline at end of file diff --git a/project/msvc/CppReact.sln b/project/msvc/CppReact.sln index 915cf396..08c58e9d 100644 --- a/project/msvc/CppReact.sln +++ b/project/msvc/CppReact.sln @@ -1,13 +1,37 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30501.0 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CppReact", "CppReact.vcxproj", "{5E56AAB9-4E33-4B9E-A315-E85CEDB75CF1}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CppReactBenchmark", "CppReactBenchmark.vcxproj", "{F9115FB9-61DD-4B3C-BCE8-7D26372B05F7}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CppReactTest", "CppReactTest.vcxproj", "{52A9EC67-C6A7-453B-AD65-F027CA07AF44}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CppReactSandbox", "CppReactSandbox.vcxproj", "{CC0CD982-AE7D-4797-A122-58E6BBE70DDB}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3_Examples", "3_Examples", "{518ACABC-E4A7-4E2D-9A04-FFA669A30DBF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1_Core", "1_Core", "{91AFD614-F7E6-48CE-9808-642EAF476B66}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "4_Benchmarks", "4_Benchmarks", "{D6F88FF5-E55C-4E65-ABBB-B4298AB84D40}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2_Tests", "2_Tests", "{3F97AA87-0A03-4428-94C1-C9B4007C2C80}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CppReactExample", "CppReactExample.vcxproj", "{CC0CD982-AE7D-4797-A122-58E6BBE70DDB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example_BasicAlgorithms", "Example_BasicAlgorithms.vcxproj", "{617019A2-97BE-4A60-8EC4-3547D8C54533}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example_BasicComposition", "Example_BasicComposition.vcxproj", "{D7B70D3B-F14D-4A85-B164-EAB88C358E85}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example_BasicEvents", "Example_BasicEvents.vcxproj", "{BD777649-97F1-4810-BF21-CB27F7672BF4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example_BasicObservers", "Example_BasicObservers.vcxproj", "{CC66BFA0-D609-46E0-9FD1-F9CC902410B1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example_BasicReactors", "Example_BasicReactors.vcxproj", "{D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example_BasicSignals", "Example_BasicSignals.vcxproj", "{230C9137-CCD0-47E2-8F1F-2E1DD19984A1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example_BasicSynchronization", "Example_BasicSynchronization.vcxproj", "{269329F8-A9E1-41AC-9C37-3A82A082A62C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -31,8 +55,49 @@ Global {CC0CD982-AE7D-4797-A122-58E6BBE70DDB}.Debug|Win32.Build.0 = Debug|Win32 {CC0CD982-AE7D-4797-A122-58E6BBE70DDB}.Release|Win32.ActiveCfg = Release|Win32 {CC0CD982-AE7D-4797-A122-58E6BBE70DDB}.Release|Win32.Build.0 = Release|Win32 + {617019A2-97BE-4A60-8EC4-3547D8C54533}.Debug|Win32.ActiveCfg = Debug|Win32 + {617019A2-97BE-4A60-8EC4-3547D8C54533}.Debug|Win32.Build.0 = Debug|Win32 + {617019A2-97BE-4A60-8EC4-3547D8C54533}.Release|Win32.ActiveCfg = Release|Win32 + {617019A2-97BE-4A60-8EC4-3547D8C54533}.Release|Win32.Build.0 = Release|Win32 + {D7B70D3B-F14D-4A85-B164-EAB88C358E85}.Debug|Win32.ActiveCfg = Debug|Win32 + {D7B70D3B-F14D-4A85-B164-EAB88C358E85}.Debug|Win32.Build.0 = Debug|Win32 + {D7B70D3B-F14D-4A85-B164-EAB88C358E85}.Release|Win32.ActiveCfg = Release|Win32 + {D7B70D3B-F14D-4A85-B164-EAB88C358E85}.Release|Win32.Build.0 = Release|Win32 + {BD777649-97F1-4810-BF21-CB27F7672BF4}.Debug|Win32.ActiveCfg = Debug|Win32 + {BD777649-97F1-4810-BF21-CB27F7672BF4}.Debug|Win32.Build.0 = Debug|Win32 + {BD777649-97F1-4810-BF21-CB27F7672BF4}.Release|Win32.ActiveCfg = Release|Win32 + {BD777649-97F1-4810-BF21-CB27F7672BF4}.Release|Win32.Build.0 = Release|Win32 + {CC66BFA0-D609-46E0-9FD1-F9CC902410B1}.Debug|Win32.ActiveCfg = Debug|Win32 + {CC66BFA0-D609-46E0-9FD1-F9CC902410B1}.Debug|Win32.Build.0 = Debug|Win32 + {CC66BFA0-D609-46E0-9FD1-F9CC902410B1}.Release|Win32.ActiveCfg = Release|Win32 + {CC66BFA0-D609-46E0-9FD1-F9CC902410B1}.Release|Win32.Build.0 = Release|Win32 + {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Debug|Win32.ActiveCfg = Debug|Win32 + {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Debug|Win32.Build.0 = Debug|Win32 + {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Release|Win32.ActiveCfg = Release|Win32 + {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Release|Win32.Build.0 = Release|Win32 + {230C9137-CCD0-47E2-8F1F-2E1DD19984A1}.Debug|Win32.ActiveCfg = Debug|Win32 + {230C9137-CCD0-47E2-8F1F-2E1DD19984A1}.Debug|Win32.Build.0 = Debug|Win32 + {230C9137-CCD0-47E2-8F1F-2E1DD19984A1}.Release|Win32.ActiveCfg = Release|Win32 + {230C9137-CCD0-47E2-8F1F-2E1DD19984A1}.Release|Win32.Build.0 = Release|Win32 + {269329F8-A9E1-41AC-9C37-3A82A082A62C}.Debug|Win32.ActiveCfg = Debug|Win32 + {269329F8-A9E1-41AC-9C37-3A82A082A62C}.Debug|Win32.Build.0 = Debug|Win32 + {269329F8-A9E1-41AC-9C37-3A82A082A62C}.Release|Win32.ActiveCfg = Release|Win32 + {269329F8-A9E1-41AC-9C37-3A82A082A62C}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {5E56AAB9-4E33-4B9E-A315-E85CEDB75CF1} = {91AFD614-F7E6-48CE-9808-642EAF476B66} + {F9115FB9-61DD-4B3C-BCE8-7D26372B05F7} = {D6F88FF5-E55C-4E65-ABBB-B4298AB84D40} + {52A9EC67-C6A7-453B-AD65-F027CA07AF44} = {3F97AA87-0A03-4428-94C1-C9B4007C2C80} + {CC0CD982-AE7D-4797-A122-58E6BBE70DDB} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} + {617019A2-97BE-4A60-8EC4-3547D8C54533} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} + {D7B70D3B-F14D-4A85-B164-EAB88C358E85} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} + {BD777649-97F1-4810-BF21-CB27F7672BF4} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} + {CC66BFA0-D609-46E0-9FD1-F9CC902410B1} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} + {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} + {230C9137-CCD0-47E2-8F1F-2E1DD19984A1} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} + {269329F8-A9E1-41AC-9C37-3A82A082A62C} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} + EndGlobalSection EndGlobal diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index 12ea4a97..bb01d929 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -39,11 +39,11 @@ - $(SolutionName)/$(Configuration)\ + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ - $(SolutionName)/$(Configuration)\ + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ @@ -51,13 +51,16 @@ Level3 Disabled true - ../../include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) true %(PreprocessorDefinitions) true + + $(OutDir)$(TargetName)$(TargetExt) + @@ -66,7 +69,7 @@ true true true - ../../include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) true %(PreprocessorDefinitions) @@ -75,6 +78,9 @@ true true + + $(OutDir)$(TargetName)$(TargetExt) + diff --git a/project/msvc/CppReactBenchmark.vcxproj b/project/msvc/CppReactBenchmark.vcxproj index f2b6b327..348829f8 100644 --- a/project/msvc/CppReactBenchmark.vcxproj +++ b/project/msvc/CppReactBenchmark.vcxproj @@ -39,11 +39,11 @@ - $(SolutionName)/$(Configuration)\ + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ - $(SolutionName)/$(Configuration)\ + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ @@ -52,7 +52,7 @@ Disabled true true - ..\..\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;_VARIADIC_MAX=10;%(PreprocessorDefinitions) @@ -68,7 +68,7 @@ true true true - ..\..\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;_VARIADIC_MAX=10;%(PreprocessorDefinitions) @@ -79,21 +79,21 @@ false - - - {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} - - - - - - + + + + + + + + + diff --git a/project/msvc/CppReactBenchmark.vcxproj.filters b/project/msvc/CppReactBenchmark.vcxproj.filters index c3f6165a..cc27f988 100644 --- a/project/msvc/CppReactBenchmark.vcxproj.filters +++ b/project/msvc/CppReactBenchmark.vcxproj.filters @@ -15,28 +15,28 @@ - - Source Files - - - - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files + + + Source Files + + \ No newline at end of file diff --git a/project/msvc/CppReactSandbox.vcxproj b/project/msvc/CppReactExample.vcxproj similarity index 89% rename from project/msvc/CppReactSandbox.vcxproj rename to project/msvc/CppReactExample.vcxproj index c1646c99..b0277b9b 100644 --- a/project/msvc/CppReactSandbox.vcxproj +++ b/project/msvc/CppReactExample.vcxproj @@ -13,6 +13,7 @@ {CC0CD982-AE7D-4797-A122-58E6BBE70DDB} CppReactSandbox + CppReactExample @@ -39,11 +40,11 @@ - $(SolutionName)/$(Configuration)\ + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ - $(SolutionName)/$(Configuration)\ + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ @@ -51,7 +52,7 @@ Level3 Disabled true - ..\..\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) true _VARIADIC_MAX=10;%(PreprocessorDefinitions) @@ -67,7 +68,7 @@ true true true - ..\..\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) true _VARIADIC_MAX=10;%(PreprocessorDefinitions) @@ -79,14 +80,14 @@ false - - - {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} + + + diff --git a/project/msvc/CppReactSandbox.vcxproj.filters b/project/msvc/CppReactExample.vcxproj.filters similarity index 94% rename from project/msvc/CppReactSandbox.vcxproj.filters rename to project/msvc/CppReactExample.vcxproj.filters index a7a25c8a..4c1c8f03 100644 --- a/project/msvc/CppReactSandbox.vcxproj.filters +++ b/project/msvc/CppReactExample.vcxproj.filters @@ -15,7 +15,7 @@ - + Source Files diff --git a/project/msvc/CppReactTest.vcxproj b/project/msvc/CppReactTest.vcxproj index 1604e0b9..21a8d9e1 100644 --- a/project/msvc/CppReactTest.vcxproj +++ b/project/msvc/CppReactTest.vcxproj @@ -39,11 +39,11 @@ - $(SolutionName)/$(Configuration)\ + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ - $(SolutionName)/$(Configuration)\ + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ @@ -51,7 +51,7 @@ Level3 Disabled true - ..\..\include;$(GTestDir)\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\include;$(GTestDir)\include;%(AdditionalIncludeDirectories) _VARIADIC_MAX=10;%(PreprocessorDefinitions) true 4503;%(DisableSpecificWarnings) @@ -70,7 +70,7 @@ true true true - ..\..\include;$(GTestDir)\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\include;$(GTestDir)\include;%(AdditionalIncludeDirectories) _VARIADIC_MAX=10;%(PreprocessorDefinitions) true 4503;%(DisableSpecificWarnings) @@ -86,29 +86,29 @@ - - - - - - + + {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} + - - - - - - - - - - + + + + + + + + + + - - {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} - + + + + + + diff --git a/project/msvc/CppReactTest.vcxproj.filters b/project/msvc/CppReactTest.vcxproj.filters index ce2887ec..d3522f4a 100644 --- a/project/msvc/CppReactTest.vcxproj.filters +++ b/project/msvc/CppReactTest.vcxproj.filters @@ -15,55 +15,55 @@ - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + \ No newline at end of file diff --git a/project/msvc/Example_BasicAlgorithms.vcxproj b/project/msvc/Example_BasicAlgorithms.vcxproj new file mode 100644 index 00000000..a53d10f8 --- /dev/null +++ b/project/msvc/Example_BasicAlgorithms.vcxproj @@ -0,0 +1,87 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {617019A2-97BE-4A60-8EC4-3547D8C54533} + Example_BasicAlgorithms + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + + + + Level3 + Disabled + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + Console + + + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + true + true + + + + + {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} + + + + + + + + + \ No newline at end of file diff --git a/project/msvc/Example_BasicAlgorithms.vcxproj.filters b/project/msvc/Example_BasicAlgorithms.vcxproj.filters new file mode 100644 index 00000000..b71fc9ed --- /dev/null +++ b/project/msvc/Example_BasicAlgorithms.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/project/msvc/Example_BasicComposition.vcxproj b/project/msvc/Example_BasicComposition.vcxproj new file mode 100644 index 00000000..ba21df79 --- /dev/null +++ b/project/msvc/Example_BasicComposition.vcxproj @@ -0,0 +1,87 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {D7B70D3B-F14D-4A85-B164-EAB88C358E85} + Example_BasicComposition + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + + + + Level3 + Disabled + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + Console + + + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + true + true + + + + + {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} + + + + + + + + + \ No newline at end of file diff --git a/project/msvc/Example_BasicComposition.vcxproj.filters b/project/msvc/Example_BasicComposition.vcxproj.filters new file mode 100644 index 00000000..c7a5255c --- /dev/null +++ b/project/msvc/Example_BasicComposition.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/project/msvc/Example_BasicEvents.vcxproj b/project/msvc/Example_BasicEvents.vcxproj new file mode 100644 index 00000000..2de69525 --- /dev/null +++ b/project/msvc/Example_BasicEvents.vcxproj @@ -0,0 +1,87 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {BD777649-97F1-4810-BF21-CB27F7672BF4} + Example_BasicEvents + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + + + + Level3 + Disabled + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + Console + + + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + true + true + + + + + {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} + + + + + + + + + \ No newline at end of file diff --git a/project/msvc/Example_BasicEvents.vcxproj.filters b/project/msvc/Example_BasicEvents.vcxproj.filters new file mode 100644 index 00000000..54888eca --- /dev/null +++ b/project/msvc/Example_BasicEvents.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/project/msvc/Example_BasicObservers.vcxproj b/project/msvc/Example_BasicObservers.vcxproj new file mode 100644 index 00000000..4eddc801 --- /dev/null +++ b/project/msvc/Example_BasicObservers.vcxproj @@ -0,0 +1,87 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {CC66BFA0-D609-46E0-9FD1-F9CC902410B1} + Example_BasicObservers + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + + + + Level3 + Disabled + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + Console + + + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + true + true + + + + + + + + {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} + + + + + + \ No newline at end of file diff --git a/project/msvc/Example_BasicObservers.vcxproj.filters b/project/msvc/Example_BasicObservers.vcxproj.filters new file mode 100644 index 00000000..a809b186 --- /dev/null +++ b/project/msvc/Example_BasicObservers.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/project/msvc/Example_BasicReactors.vcxproj b/project/msvc/Example_BasicReactors.vcxproj new file mode 100644 index 00000000..92ab34cd --- /dev/null +++ b/project/msvc/Example_BasicReactors.vcxproj @@ -0,0 +1,87 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96} + Example_BasicReactors + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + + + + Level3 + Disabled + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + Console + + + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + true + true + + + + + + + + {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} + + + + + + \ No newline at end of file diff --git a/project/msvc/Example_BasicReactors.vcxproj.filters b/project/msvc/Example_BasicReactors.vcxproj.filters new file mode 100644 index 00000000..7398067f --- /dev/null +++ b/project/msvc/Example_BasicReactors.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/project/msvc/Example_BasicSignals.vcxproj b/project/msvc/Example_BasicSignals.vcxproj new file mode 100644 index 00000000..495b1813 --- /dev/null +++ b/project/msvc/Example_BasicSignals.vcxproj @@ -0,0 +1,87 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {230C9137-CCD0-47E2-8F1F-2E1DD19984A1} + Example_BasicSignals + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + + + + Level3 + Disabled + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + Console + + + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + true + true + + + + + {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} + + + + + + + + + \ No newline at end of file diff --git a/project/msvc/Example_BasicSignals.vcxproj.filters b/project/msvc/Example_BasicSignals.vcxproj.filters new file mode 100644 index 00000000..eafb525a --- /dev/null +++ b/project/msvc/Example_BasicSignals.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/project/msvc/Example_BasicSynchronization.vcxproj b/project/msvc/Example_BasicSynchronization.vcxproj new file mode 100644 index 00000000..5369559a --- /dev/null +++ b/project/msvc/Example_BasicSynchronization.vcxproj @@ -0,0 +1,87 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {269329F8-A9E1-41AC-9C37-3A82A082A62C} + Example_BasicSynchronization + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + + + + Level3 + Disabled + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + Console + + + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + true + true + + + + + + + + {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} + + + + + + \ No newline at end of file diff --git a/project/msvc/Example_BasicSynchronization.vcxproj.filters b/project/msvc/Example_BasicSynchronization.vcxproj.filters new file mode 100644 index 00000000..f19c5061 --- /dev/null +++ b/project/msvc/Example_BasicSynchronization.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/src/sandbox/Main.cpp b/src/sandbox/Main.cpp deleted file mode 100644 index 31f44ea5..00000000 --- a/src/sandbox/Main.cpp +++ /dev/null @@ -1,369 +0,0 @@ - -// 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) - -#include -#include -#include -#include -#include - -//#define REACT_ENABLE_LOGGING - -// Experimental. Requires boost::coroutine. -//#define REACT_ENABLE_REACTORS - -#include "react/Domain.h" -#include "react/Signal.h" -#include "react/Event.h" -#include "react/Algorithm.h" - -#ifdef REACT_ENABLE_REACTORS - #include "react/Reactor.h" -#endif - -using namespace std; -using namespace react; - -// Defines a domain. -// Each domain represents a separate dependency graph, managed by a dedicated propagation engine. -// Reactives of different domains can not be combined. -REACTIVE_DOMAIN(D) - -// Explicit engine: -//#include "react/engine/SubtreeEngine.h" -//#include "react/engine/PulsecountEngine.h" - -//REACTIVE_DOMAIN(D, PulsecountEngine); - -void SignalExample1() -{ - cout << "Signal Example 1" << endl; - - D::VarSignalT width = MakeVar(60); - // Note: Using auto from here on - auto height = MakeVar(70); - auto depth = MakeVar(8); - - auto area = width * height; - auto volume = area * depth; - - cout << "t0" << endl; - cout << "\tArea: " << area() << endl; - cout << "\tVolume: " << volume() << endl; - - width <<= 90; - depth <<= 80; - - cout << "t1" << endl; - cout << "\tArea: " << area() << endl; - cout << "\tVolume: " << volume() << endl; - - cout << endl; -} - -void SignalExample2() -{ - cout << "Signal Example 2" << endl; - - auto width = MakeVar(60); - auto height = MakeVar(70); - auto depth = MakeVar(8); - - auto volume = MakeSignal( - With(width,height,depth), - [] (int w, int h, int d) { - return w * h * d; - }); - - // Observe returns an observer handle, which can be used to detach the observer explicitly. - // This observer handle holds a shared_ptr to the subject, so as long as it exists, - // the subject will not be destroyed. - // The lifetime of the observer itself is tied to the subject. - Observe(volume, [] (int v) { - cout << "Volume changed to: " << v << endl; - }); - - // Change multiple inputs at once - D::DoTransaction([&] { - width <<= 90; - depth <<= 80; - }); - - cout << endl; -} - -void SignalExample3() -{ - cout << "Signal Example 3" << endl; - - auto src = MakeVar(0); - - // Input values can be manipulated imperatively in observers. - // Inputs are implicitly thread-safe, buffered and executed in a continuation turn. - // This continuation turn is queued just like a regular turn. - // If other turns are already queued, they are executed before the continuation. - Observe(src, [&] (int v) { - cout << "V: " << v << endl; - if (v < 10) - src <<= v+1; - }); - - src <<= 1; - - cout << endl; -} - -void EventExample1() -{ - cout << "Event Example 1" << endl; - - auto numbers1 = MakeEventSource(); - auto numbers2 = MakeEventSource(); - - auto anyNumber = numbers1 | numbers2; - - Observe(anyNumber, [] (int v) { - cout << "Number: " << v << endl; - }); - - numbers1 << 10 << 20 << 30; - numbers2 << 40 << 50 << 60; - - cout << endl; -} - -void EventExample2() -{ - cout << "Event Example 2" << endl; - - // The event type can be omitted if not required, in which case the event - // stream just indicates that it has fired, i.e. it behaves like a token stream. - auto emitter = MakeEventSource(); - - auto counter = Iterate(emitter, 0, [] (Token, int v) { return v+1; }); - - // The observer func declares a parameter of type Token - Observe(emitter, [] (Token) { - cout << "Emitter fired!" << endl; - }); - - // Using .Emit() to fire rather than "<< value" - for (int i=0; i<5; i++) - emitter.Emit(); - - cout << "Counted " << counter() << " events" << endl; - cout << endl; -} - -class Person -{ -public: - USING_REACTIVE_DOMAIN(D) - - VarSignalT Age = MakeVar(1); - SignalT Health = 100 - Age; - SignalT Wisdom = Age * Age / 100; - - ObserverT wisdomObs; - ObserverT weaknessObs; - - Person() - { - wisdomObs = Observe(Wisdom > 50, [] (bool isWise) - { - if (isWise) cout << "I'll do it next week!" << endl; - else cout << "I'll do it next month!" << endl; - }); - - weaknessObs = Observe(Health < 25, [] (bool isWeak) - { - if (isWeak) cout << ":<" << endl; - else cout << ":D" << endl; - }); - } - - bool operator==(const Person& other) const - { - return this == &other; - } -}; - -void ObjectExample1() -{ - cout << "Object Example 1" << endl; - - Person somePerson; - - somePerson.Age <<= 30; - somePerson.Age <<= 60; - somePerson.Age <<= 90; - - cout << "Health: " << somePerson.Health() << endl; - cout << "Wisdom: " << somePerson.Wisdom() << endl; - - cout << endl; -} - -class Company -{ - USING_REACTIVE_DOMAIN(D) - -public: - VarSignalT Name; - - Company(const char* name) : - Name( MakeVar(string(name)) ) - {} - - inline bool operator==(const Company& other) const - { - return this == &other; - } -}; - -class Manager -{ - USING_REACTIVE_DOMAIN(D) - - ObserverT nameObs; - -public: - VarSignalT CurrentCompany; - - Manager(Company& c) : - CurrentCompany( MakeVar(ref(c)) ) - { - nameObs = Observe(REACTIVE_REF(CurrentCompany, Name), [] (string name) { - cout << "Manager: Now managing " << name << endl; - }); - } -}; - -void ObjectExample2() -{ - cout << "Object Example 2" << endl; - - Company company1{ "Cellnet" }; - Company company2{ "Borland" }; - - Manager manager{ company1 }; - - company1.Name <<= string("BT Cellnet"); - company2.Name <<= string("Inprise"); - - manager.CurrentCompany <<= ref(company2); - - company1.Name <<= string("O2"); - company2.Name <<= string("Borland"); - - cout << endl; -} - -void IterateExample1() -{ - cout << "Iterate Example 1" << endl; - - auto src = MakeEventSource(); - auto it = Iterate(src, 0, [] (int d, int v) { - return v + d; - }); - - for (auto i=1; i<=100; i++) - src << i; - - cout << it() << endl; - - auto charSrc = MakeEventSource(); - auto str = Iterate(charSrc, string(""), [] (char c, string s) { - return s + c; - }); - - charSrc << 'T' << 'e' << 's' << 't'; - - cout << "Str: " << str() << endl; -} - -#ifdef REACT_ENABLE_REACTORS - -void LoopTest() -{ - cout << "ReactiveLoop Example 1" << endl; - - using PointT = pair; - using PathT = vector; - - vector paths; - - auto mouseDown = MakeEventSource(); - auto mouseUp = MakeEventSource(); - auto mouseMove = MakeEventSource(); - - D::ReactiveLoopT loop - { - [&] (D::ReactiveLoopT::Context& ctx) - { - PathT points; - - points.emplace_back(ctx.Await(mouseDown)); - - ctx.RepeatUntil(mouseUp, [&] { - points.emplace_back(ctx.Await(mouseMove)); - }); - - points.emplace_back(ctx.Await(mouseUp)); - - paths.push_back(points); - } - }; - - mouseDown << PointT(1,1); - mouseMove << PointT(2,2) << PointT(3,3) << PointT(4,4); - mouseUp << PointT(5,5); - - mouseMove << PointT(999,999); - - mouseDown << PointT(10,10); - mouseMove << PointT(20,20); - mouseUp << PointT(30,30); - - for (const auto& path : paths) - { - cout << "Path: "; - for (const auto& point : path) - cout << "(" << point.first << "," << point.second << ") "; - cout << endl; - } -} -#endif //REACT_ENABLE_REACTORS - -int main() -{ - SignalExample1(); - SignalExample2(); - SignalExample3(); - - EventExample1(); - EventExample2(); - - ObjectExample1(); - ObjectExample2(); - - IterateExample1(); - -#ifdef REACT_ENABLE_REACTORS - LoopTest(); -#endif - -#ifdef REACT_ENABLE_LOGGING - std::ofstream logfile; - logfile.open("log.txt"); - - D::Log().Write(logfile); - logfile.close(); -#endif - - return 0; -} \ No newline at end of file diff --git a/src/test/EventStreamTest.cpp b/tests/src/EventStreamTest.cpp similarity index 100% rename from src/test/EventStreamTest.cpp rename to tests/src/EventStreamTest.cpp diff --git a/src/test/EventStreamTest.h b/tests/src/EventStreamTest.h similarity index 100% rename from src/test/EventStreamTest.h rename to tests/src/EventStreamTest.h diff --git a/src/test/EventStreamTestQ.cpp b/tests/src/EventStreamTestQ.cpp similarity index 100% rename from src/test/EventStreamTestQ.cpp rename to tests/src/EventStreamTestQ.cpp diff --git a/src/test/MoveTest.cpp b/tests/src/MoveTest.cpp similarity index 100% rename from src/test/MoveTest.cpp rename to tests/src/MoveTest.cpp diff --git a/src/test/MoveTest.h b/tests/src/MoveTest.h similarity index 100% rename from src/test/MoveTest.h rename to tests/src/MoveTest.h diff --git a/src/test/ObserverTest.cpp b/tests/src/ObserverTest.cpp similarity index 100% rename from src/test/ObserverTest.cpp rename to tests/src/ObserverTest.cpp diff --git a/src/test/ObserverTest.h b/tests/src/ObserverTest.h similarity index 100% rename from src/test/ObserverTest.h rename to tests/src/ObserverTest.h diff --git a/src/test/ObserverTestQ.cpp b/tests/src/ObserverTestQ.cpp similarity index 100% rename from src/test/ObserverTestQ.cpp rename to tests/src/ObserverTestQ.cpp diff --git a/src/test/OperationsTest.cpp b/tests/src/OperationsTest.cpp similarity index 100% rename from src/test/OperationsTest.cpp rename to tests/src/OperationsTest.cpp diff --git a/src/test/OperationsTest.h b/tests/src/OperationsTest.h similarity index 100% rename from src/test/OperationsTest.h rename to tests/src/OperationsTest.h diff --git a/src/test/OperationsTestQ.cpp b/tests/src/OperationsTestQ.cpp similarity index 100% rename from src/test/OperationsTestQ.cpp rename to tests/src/OperationsTestQ.cpp diff --git a/src/test/SignalTest.cpp b/tests/src/SignalTest.cpp similarity index 100% rename from src/test/SignalTest.cpp rename to tests/src/SignalTest.cpp diff --git a/src/test/SignalTest.h b/tests/src/SignalTest.h similarity index 100% rename from src/test/SignalTest.h rename to tests/src/SignalTest.h diff --git a/src/test/SignalTestQ.cpp b/tests/src/SignalTestQ.cpp similarity index 100% rename from src/test/SignalTestQ.cpp rename to tests/src/SignalTestQ.cpp diff --git a/src/test/TransactionTest.cpp b/tests/src/TransactionTest.cpp similarity index 100% rename from src/test/TransactionTest.cpp rename to tests/src/TransactionTest.cpp diff --git a/src/test/TransactionTest.h b/tests/src/TransactionTest.h similarity index 100% rename from src/test/TransactionTest.h rename to tests/src/TransactionTest.h From 26a50aeb2882315269f82e9158ecf5533fb2c143 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 2 Jun 2014 02:29:39 +0200 Subject: [PATCH 128/266] Implemented non-windows version of timer. --- include/react/common/Timing.h | 44 ++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/include/react/common/Timing.h b/include/react/common/Timing.h index 71413477..cb1ae953 100644 --- a/include/react/common/Timing.h +++ b/include/react/common/Timing.h @@ -14,9 +14,15 @@ #include #if _WIN32 || _WIN64 + #define REACT_FIXME_CUSTOM_TIMER 1 +#else + #define REACT_FIXME_CUSTOM_TIMER 0 +#endif + +#if REACT_FIXME_CUSTOM_TIMER #include #else - #include + #include #endif /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -25,7 +31,7 @@ /// GetPerformanceFrequency /////////////////////////////////////////////////////////////////////////////////////////////////// // Todo: Initialization not thread-safe -#if _WIN32 || _WIN64 +#if REACT_FIXME_CUSTOM_TIMER inline const LARGE_INTEGER& GetPerformanceFrequency() { static bool init = false; @@ -87,10 +93,11 @@ template class ConditionalTimer { public: -#if _WIN32 || _WIN64 +#if REACT_FIXME_CUSTOM_TIMER using TimestampT = LARGE_INTEGER; -#elif __linux__ - using TimestampT = long long; +#else + using ClockT = std::chrono::high_resolution_clock; + using TimestampT = std::chrono::time_point; #endif class ScopedTimer @@ -123,19 +130,22 @@ class ConditionalTimer void endMeasure() { +#if REACT_FIXME_CUSTOM_TIMER TimestampT endTime = now(); -#if _WIN32 || _WIN64 - LARGE_INTEGER durationMS; + LARGE_INTEGER durationUS; - durationMS.QuadPart = endTime.QuadPart - startTime_.QuadPart; - durationMS.QuadPart *= 1000000; - durationMS.QuadPart /= GetPerformanceFrequency().QuadPart; + durationUS.QuadPart = endTime.QuadPart - startTime_.QuadPart; + durationUS.QuadPart *= 1000000; + durationUS.QuadPart /= GetPerformanceFrequency().QuadPart; - parent_.isThresholdExceeded_ = durationMS.QuadPart > threshold; + parent_.isThresholdExceeded_ = durationUS.QuadPart > threshold; #else - // TODO - parent_.isThresholdExceeded_ = false; + using std::chrono::duration_cast; + using std::chrono::microseconds; + + parent_.isThresholdExceeded_ = + duration_cast(now() - startTime_).count() > threshold; #endif } @@ -145,16 +155,12 @@ class ConditionalTimer static TimestampT now() { -#if _WIN32 || _WIN64 +#if REACT_FIXME_CUSTOM_TIMER TimestampT result; QueryPerformanceCounter(&result); return result; #else - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - return static_cast(1000000000UL) - * static_cast(ts.tv_sec) - + static_cast(ts.tv_nsec); + return ClockT::now(); #endif } From c78f32f252c382930f2b1a248234ed4dbe5a4721 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 2 Jun 2014 03:25:14 +0200 Subject: [PATCH 129/266] Fixed file name #1 --- include/react/engine/{PulseCountEngine.h => Pulse_countEngine.h} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename include/react/engine/{PulseCountEngine.h => Pulse_countEngine.h} (100%) diff --git a/include/react/engine/PulseCountEngine.h b/include/react/engine/Pulse_countEngine.h similarity index 100% rename from include/react/engine/PulseCountEngine.h rename to include/react/engine/Pulse_countEngine.h From 24b2b725ac5a7b2edf7d0fb4de9fa9d7817abd45 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 2 Jun 2014 03:25:46 +0200 Subject: [PATCH 130/266] Fixed file name #2 --- .../engine/{Pulse_countEngine.h => PulsecountEngine.h} | 0 project/msvc/CppReact.vcxproj | 2 +- project/msvc/CppReact.vcxproj.filters | 6 +++--- 3 files changed, 4 insertions(+), 4 deletions(-) rename include/react/engine/{Pulse_countEngine.h => PulsecountEngine.h} (100%) diff --git a/include/react/engine/Pulse_countEngine.h b/include/react/engine/PulsecountEngine.h similarity index 100% rename from include/react/engine/Pulse_countEngine.h rename to include/react/engine/PulsecountEngine.h diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index bb01d929..ff75468f 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -101,7 +101,7 @@ - + diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index 52282a78..1050b4b4 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -60,9 +60,6 @@ Header Files - - Header Files\engine - Header Files\engine @@ -144,6 +141,9 @@ Header Files\common + + Header Files\engine + From 3999db18a0d2172dd55643d84deb764af8c2201a Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 2 Jun 2014 16:51:29 +0200 Subject: [PATCH 131/266] Fixed some compiler warnings. --- benchmarks/src/Main.cpp | 8 ++++---- examples/src/BasicSignals.cpp | 2 +- include/react/detail/ObserverBase.h | 10 +++++----- include/react/detail/graph/AlgorithmNodes.h | 6 +++--- include/react/detail/graph/EventNodes.h | 4 ++-- include/react/detail/graph/ObserverNodes.h | 2 +- src/engine/PulsecountEngine.cpp | 19 +++++++++---------- src/engine/SubtreeEngine.cpp | 9 ++++----- tests/src/MoveTest.h | 2 +- tests/src/ObserverTest.h | 2 +- tests/src/OperationsTest.h | 2 +- tests/src/SignalTest.h | 2 +- tests/src/TransactionTest.h | 2 +- 13 files changed, 34 insertions(+), 36 deletions(-) diff --git a/benchmarks/src/Main.cpp b/benchmarks/src/Main.cpp index 3219cd89..0722b13e 100644 --- a/benchmarks/src/Main.cpp +++ b/benchmarks/src/Main.cpp @@ -32,10 +32,10 @@ namespace { using namespace react; -REACTIVE_DOMAIN(ToposortSTDomain, ToposortEngine); -REACTIVE_DOMAIN(ToposortDomain, ToposortEngine); -REACTIVE_DOMAIN(PulsecountDomain, PulsecountEngine); -REACTIVE_DOMAIN(SubtreeDomain, SubtreeEngine); +REACTIVE_DOMAIN(ToposortSTDomain, ToposortEngine) +REACTIVE_DOMAIN(ToposortDomain, ToposortEngine) +REACTIVE_DOMAIN(PulsecountDomain, PulsecountEngine) +REACTIVE_DOMAIN(SubtreeDomain, SubtreeEngine) void runBenchmarkGrid(std::ostream& out) { diff --git a/examples/src/BasicSignals.cpp b/examples/src/BasicSignals.cpp index b6e906e4..55e18ea7 100644 --- a/examples/src/BasicSignals.cpp +++ b/examples/src/BasicSignals.cpp @@ -35,7 +35,7 @@ namespace example1 // The concat function string concatFunc(string first, string second) { return first + string(" ") + second; - }; + } // A signal that concatenates both words namespace v1 diff --git a/include/react/detail/ObserverBase.h b/include/react/detail/ObserverBase.h index 4c197579..c7cb5fc2 100644 --- a/include/react/detail/ObserverBase.h +++ b/include/react/detail/ObserverBase.h @@ -60,15 +60,15 @@ class ObserverRegistry { public: Entry(std::unique_ptr&& nodePtr, const Observable* subjectPtr) : - nodePtr_( std::move(nodePtr) ), - SubjectPtr( subjectPtr ) + SubjectPtr( subjectPtr ), + nodePtr_( std::move(nodePtr) ) {} Entry(const Entry&) = delete; Entry(Entry&& other) : - nodePtr_( std::move(other.nodePtr_) ), - SubjectPtr( other.SubjectPtr ) + SubjectPtr( other.SubjectPtr ), + nodePtr_( std::move(other.nodePtr_) ) {} const Observable* SubjectPtr; @@ -85,7 +85,7 @@ class ObserverRegistry // Use raw ptr copy as index to find owned version of itself auto* rawObsPtr = obsPtr.get(); - observerMap_.emplace(rawObsPtr, Entry{ std::move(obsPtr), subjectPtr }); + observerMap_.emplace(rawObsPtr, Entry( std::move(obsPtr), subjectPtr )); return rawObsPtr; } diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index aad77a2b..a9c4cc4a 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -245,8 +245,8 @@ class SyncedIterateNode : public SignalNode std::shared_ptr> events_; - DepHolderT deps_; TFunc func_; + DepHolderT deps_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -345,8 +345,8 @@ class SyncedIterateByRefNode : public SignalNode std::shared_ptr> events_; - DepHolderT deps_; TFunc func_; + DepHolderT deps_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -588,7 +588,7 @@ class PulseNode : public EventStreamNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - for (const auto& e : trigger_->Events()) + for (uint i=0; iEvents().size(); i++) this->events_.push_back(target_->ValueRef()); REACT_LOG(D::Log().template Append( diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 792185dc..7d750123 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -625,8 +625,8 @@ class SyncedEventTransformNode : public EventStreamNode std::shared_ptr> source_; - DepHolderT deps_; TFunc func_; + DepHolderT deps_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -719,8 +719,8 @@ class SyncedEventFilterNode : public EventStreamNode std::shared_ptr> source_; - DepHolderT deps_; TFunc filter_; + DepHolderT deps_; }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index 170cb0da..60273633 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -284,8 +284,8 @@ class SyncedObserverNode : public ObserverNode std::weak_ptr> subject_; - DepHolderT deps_; TFunc func_; + DepHolderT deps_; virtual void detachObserver() { diff --git a/src/engine/PulsecountEngine.cpp b/src/engine/PulsecountEngine.cpp index d56b22eb..a93dacc4 100644 --- a/src/engine/PulsecountEngine.cpp +++ b/src/engine/PulsecountEngine.cpp @@ -19,7 +19,6 @@ namespace pulsecount { /////////////////////////////////////////////////////////////////////////////////////////////////// static const uint chunk_size = 8; static const uint dfs_threshold = 3; -static const uint heavy_weight = 1000; /////////////////////////////////////////////////////////////////////////////////////////////////// /// MarkerTask @@ -31,16 +30,16 @@ class MarkerTask: public task template MarkerTask(TInput srcBegin, TInput srcEnd) : - nodes_{ srcBegin, srcEnd } + nodes_( srcBegin, srcEnd ) {} MarkerTask(MarkerTask& other, SplitTag) : - nodes_{ other.nodes_, SplitTag{} } + nodes_( other.nodes_, SplitTag( ) ) {} task* execute() { - int splitCount = 0; + uint splitCount = 0; while (! nodes_.IsEmpty()) { @@ -88,18 +87,18 @@ class UpdaterTask: public task template UpdaterTask(TTurn& turn, TInput srcBegin, TInput srcEnd) : - turn_( turn ), // For now, GCC requires still parenthesis here - nodes_{ srcBegin, srcEnd } + turn_( turn ), + nodes_( srcBegin, srcEnd ) {} UpdaterTask(TTurn& turn, Node* node) : turn_( turn ), - nodes_{ node } + nodes_( node ) {} UpdaterTask(UpdaterTask& other, SplitTag) : turn_( other.turn_ ), - nodes_{ other.nodes_, SplitTag{} } + nodes_( other.nodes_, SplitTag( ) ) {} task* execute() @@ -258,7 +257,7 @@ void EngineBase::OnDynamicNodeAttach(Node& node, Node& parent, TTurn& tur bool shouldTick = false; {// parent.ShiftMutex (write) - NodeShiftMutexT::scoped_lock lock(parent.ShiftMutex, true); + NodeShiftMutexT::scoped_lock lock(parent.ShiftMutex, true); parent.Successors.Add(node); @@ -282,7 +281,7 @@ void EngineBase::OnDynamicNodeAttach(Node& node, Node& parent, TTurn& tur template void EngineBase::OnDynamicNodeDetach(Node& node, Node& parent, TTurn& turn) {// parent.ShiftMutex (write) - NodeShiftMutexT::scoped_lock lock(parent.ShiftMutex, true); + NodeShiftMutexT::scoped_lock lock(parent.ShiftMutex, true); parent.Successors.Remove(node); }// ~parent.ShiftMutex (write) diff --git a/src/engine/SubtreeEngine.cpp b/src/engine/SubtreeEngine.cpp index 7debdb4a..f56f0b80 100644 --- a/src/engine/SubtreeEngine.cpp +++ b/src/engine/SubtreeEngine.cpp @@ -19,7 +19,6 @@ namespace subtree { /////////////////////////////////////////////////////////////////////////////////////////////////// static const uint chunk_size = 8; static const uint dfs_threshold = 3; -static const uint heavy_weight = 1000; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Turn @@ -63,18 +62,18 @@ class UpdaterTask: public task using BufferT = NodeBuffer; UpdaterTask(TTurn& turn, Node* node) : - turn_( turn ), // For now, GCC requires still parenthesis here - nodes_{ node } + turn_( turn ), + nodes_( node ) {} UpdaterTask(UpdaterTask& other, SplitTag) : turn_( other.turn_ ), - nodes_{ other.nodes_, SplitTag{} } + nodes_( other.nodes_, SplitTag( ) ) {} task* execute() { - int splitCount = 0; + uint splitCount = 0; while (!nodes_.IsEmpty()) { diff --git a/tests/src/MoveTest.h b/tests/src/MoveTest.h index 90717ea2..e6d91c3e 100644 --- a/tests/src/MoveTest.h +++ b/tests/src/MoveTest.h @@ -23,7 +23,7 @@ template class MoveTest : public testing::Test { public: - REACTIVE_DOMAIN(MyDomain, TEngine); + REACTIVE_DOMAIN(MyDomain, TEngine) struct Stats { diff --git a/tests/src/ObserverTest.h b/tests/src/ObserverTest.h index 19dc8d04..9dbec07c 100644 --- a/tests/src/ObserverTest.h +++ b/tests/src/ObserverTest.h @@ -27,7 +27,7 @@ template class ObserverTest : public testing::Test { public: - REACTIVE_DOMAIN(MyDomain, TEngine); + REACTIVE_DOMAIN(MyDomain, TEngine) }; TYPED_TEST_CASE_P(ObserverTest); diff --git a/tests/src/OperationsTest.h b/tests/src/OperationsTest.h index 5075674e..5afc3979 100644 --- a/tests/src/OperationsTest.h +++ b/tests/src/OperationsTest.h @@ -37,7 +37,7 @@ template class OperationsTest : public testing::Test { public: - REACTIVE_DOMAIN(MyDomain, TEngine); + REACTIVE_DOMAIN(MyDomain, TEngine) }; TYPED_TEST_CASE_P(OperationsTest); diff --git a/tests/src/SignalTest.h b/tests/src/SignalTest.h index 5acfae2b..7cba5bb6 100644 --- a/tests/src/SignalTest.h +++ b/tests/src/SignalTest.h @@ -26,7 +26,7 @@ template class SignalTest : public testing::Test { public: - REACTIVE_DOMAIN(MyDomain, TEngine); + REACTIVE_DOMAIN(MyDomain, TEngine) }; TYPED_TEST_CASE_P(SignalTest); diff --git a/tests/src/TransactionTest.h b/tests/src/TransactionTest.h index c6c7c866..c7963cd9 100644 --- a/tests/src/TransactionTest.h +++ b/tests/src/TransactionTest.h @@ -27,7 +27,7 @@ template class TransactionTest : public testing::Test { public: - REACTIVE_DOMAIN(MyDomain, TEngine); + REACTIVE_DOMAIN(MyDomain, TEngine) }; TYPED_TEST_CASE_P(TransactionTest); From 52953565d70395a7f1c0a79a5f1ee9e941432c01 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 2 Jun 2014 16:52:29 +0200 Subject: [PATCH 132/266] GCC rejects the enum classes here. Replacing them with ints for now. --- benchmarks/src/BenchmarkLifeSim.h | 33 ++++++++++++++++--------------- examples/src/BasicAlgorithms.cpp | 20 +++++++++---------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/benchmarks/src/BenchmarkLifeSim.h b/benchmarks/src/BenchmarkLifeSim.h index 168192f1..26e5760d 100644 --- a/benchmarks/src/BenchmarkLifeSim.h +++ b/benchmarks/src/BenchmarkLifeSim.h @@ -30,8 +30,9 @@ using std::string; using std::unique_ptr; using std::vector; -enum class Seasons { summer, winter }; -enum class Migration { enter, leave }; +// FIXME: GCC doesn't like enum class in MakeEventSource +enum { summer, winter }; +enum { enter, leave }; template struct Incrementer { T operator()(Token,T v) const { return v+1; } }; @@ -53,10 +54,10 @@ class Time SignalT TotalDays = Iterate(NewDay, 0, Incrementer()); SignalT DayOfYear = TotalDays % 365; - SignalT Season = MakeSignal( + SignalT Season = MakeSignal( DayOfYear, - [] (int day) { - return day < 180 ? Seasons::winter : Seasons::summer; + [] (int day) -> int { + return day < 180 ? winter : summer; }); }; @@ -71,19 +72,19 @@ class Region BoundsT Bounds; - EventSourceT EnterOrLeave = MakeEventSource(); + EventSourceT EnterOrLeave = MakeEventSource(); SignalT AnimalCount = Iterate( EnterOrLeave, 0, - [] (Migration m, int count) { - return m == Migration::enter ? count + 1 : count - 1; + [] (int m, int count) { + return m == enter ? count + 1 : count - 1; }); SignalT FoodPerDay = MakeSignal( theTime.Season, - [] (Seasons season) { - return season == Seasons::summer ? 20 : 10; + [] (int season) { + return season == summer ? 20 : 10; }); SignalT FoodOutputPerDay = MakeSignal( @@ -95,8 +96,8 @@ class Region EventsT FoodOutput = Pulse(theTime.NewDay, FoodOutputPerDay); Region(Time& time, int x, int y) : - theTime( time ), - Bounds( x*10, x*10+9, y*10, y*10+9 ) + Bounds( x*10, x*10+9, y*10, y*10+9 ), + theTime( time ) {} PositionT Center() const @@ -195,7 +196,7 @@ class Animal SignalT Position; SignalT*> NewRegion; - EventsT*> RegionChanged = Monitor(NewRegion);; + EventsT*> RegionChanged = Monitor(NewRegion); SignalT Age = Iterate(theTime.NewDay, 0, Incrementer()); @@ -240,14 +241,14 @@ class Animal }) ) { - initRegion->EnterOrLeave(Migration::enter); + initRegion->EnterOrLeave(enter); Observe( RegionChanged, With(CurrentRegion), [this] (Region* newRegion, Region* oldRegion) { - oldRegion->EnterOrLeave(Migration::leave); - newRegion->EnterOrLeave(Migration::enter); + oldRegion->EnterOrLeave(leave); + newRegion->EnterOrLeave(enter); // Change region in continuation CurrentRegion <<= newRegion; diff --git a/examples/src/BasicAlgorithms.cpp b/examples/src/BasicAlgorithms.cpp index d0f0f8b9..a01c8c2d 100644 --- a/examples/src/BasicAlgorithms.cpp +++ b/examples/src/BasicAlgorithms.cpp @@ -183,14 +183,14 @@ namespace example5 REACTIVE_DOMAIN(D) - enum class ECmd { increment, decrement, reset }; + enum ECmd { increment, decrement, reset }; class Counter { public: USING_REACTIVE_DOMAIN(D) - EventSourceT Update = MakeEventSource(); + EventSourceT Update = MakeEventSource(); VarSignalT Delta = MakeVar(1); VarSignalT Start = MakeVar(0); @@ -198,10 +198,10 @@ namespace example5 Update, Start.Value(), With(Delta, Start), - [] (ECmd cmd, int oldCount, int delta, int start) { - if (cmd == ECmd::increment) + [] (int cmd, int oldCount, int delta, int start) { + if (cmd == increment) return oldCount + delta; - else if (cmd == ECmd::decrement) + else if (cmd == decrement) return oldCount - delta; else return start; @@ -216,19 +216,19 @@ namespace example5 cout << "Start: " << myCounter.Count() << endl; // output: 0 - myCounter.Update(ECmd::increment); - myCounter.Update(ECmd::increment); - myCounter.Update(ECmd::increment); + myCounter.Update(increment); + myCounter.Update(increment); + myCounter.Update(increment); cout << "3x increment by 1: " << myCounter.Count() << endl; // output: 3 myCounter.Delta <<= 5; - myCounter.Update(ECmd::decrement); + myCounter.Update(decrement); cout << "1x decrement by 5: " << myCounter.Count() << endl; // output: -2 myCounter.Start <<= 100; - myCounter.Update(ECmd::reset); + myCounter.Update(reset); cout << "reset to 100: " << myCounter.Count() << endl; // output: 100 From 88201db84526f6cde9c57721dad4bf1bf17ffc30 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 2 Jun 2014 16:53:53 +0200 Subject: [PATCH 133/266] Added CMakeLists. --- CMakeLists.txt | 51 +++++++++++++++++++++++++++++++++++++++ benchmarks/CMakeLists.txt | 4 +++ examples/CMakeLists.txt | 31 ++++++++++++++++++++++++ tests/CMakeLists.txt | 14 +++++++++++ 4 files changed, 100 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 benchmarks/CMakeLists.txt create mode 100644 examples/CMakeLists.txt create mode 100644 tests/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..82c1fa3c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,51 @@ +### Configuration +cmake_minimum_required (VERSION 2.6) + +project (CppReact) + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wpedantic") + +include_directories ("${PROJECT_SOURCE_DIR}/include") + +### CppReact +add_library(CppReact + src/engine/PulsecountEngine.cpp + src/engine/SubtreeEngine.cpp + src/engine/ToposortEngine.cpp + src/logging/EventLog.cpp + src/logging/EventRecords.cpp) + +target_link_libraries(CppReact tbb) + +### examples/ +option(build_examples "Build examples?" ON) +if(build_examples) + add_subdirectory(examples) +endif() + +### benchmarks/ +option(build_benchmarks "Build benchmarks?" OFF) +if(build_benchmarks) + add_subdirectory(benchmarks) +endif() + +### tests/ +option(build_tests "Build unit tests?" OFF) +if(build_tests) + add_subdirectory(tests) +endif() + +# cmake -Dbuild_tests=BOOL:ON +if(build_tests) + if(DEFINED ENV{GTEST_DIR}) + message("Using gtest found in $ENV{GTEST_DIR}.") + else() + message("GTEST_DIR is not defined. You must tell CMake where to find the gtest source.") + return() + endif() + add_subdirectory($ENV{GTEST_DIR} ${CMAKE_CURRENT_BINARY_DIR}/gtest) +endif() diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt new file mode 100644 index 00000000..b74640e9 --- /dev/null +++ b/benchmarks/CMakeLists.txt @@ -0,0 +1,4 @@ +### CppReactBenchmark + +add_executable(CppReactBenchmark src/Main.cpp) +target_link_libraries(CppReactBenchmark CppReact tbbmalloc_proxy) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 00000000..4a5fc96d --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,31 @@ +### Example_BasicAlgorithms +add_executable(Example_BasicAlgorithms src/BasicAlgorithms.cpp) +target_link_libraries(Example_BasicAlgorithms CppReact) + +### Example_BasicComposition +add_executable(Example_BasicComposition src/BasicComposition.cpp) +target_link_libraries(Example_BasicComposition CppReact) + +### Example_BasicEvents +add_executable(Example_BasicEvents src/BasicEvents.cpp) +target_link_libraries(Example_BasicEvents CppReact) + +### Example_BasicObservers +add_executable(Example_BasicObservers src/BasicObservers.cpp) +target_link_libraries(Example_BasicObservers CppReact) + +### Example_BasicReactors +#add_executable(Example_BasicReactors src/BasicReactors.cpp) +#target_link_libraries(Example_BasicReactors CppReact) + +### Example_BasicSignals +add_executable(Example_BasicSignals src/BasicSignals.cpp) +target_link_libraries(Example_BasicSignals CppReact) + +### Example_BasicSynchronization +add_executable(Example_BasicSynchronization src/BasicSynchronization.cpp) +target_link_libraries(Example_BasicSynchronization CppReact) + +### Example_Sandbox +add_executable(Example_Sandbox src/Main.cpp) +target_link_libraries(Example_Sandbox CppReact) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 00000000..50d60d26 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,14 @@ +### CppReactTest +add_executable(CppReactTest + src/EventStreamTest.cpp + src/EventStreamTestQ.cpp + src/MoveTest.cpp + src/ObserverTest.cpp + src/ObserverTestQ.cpp + src/OperationsTest.cpp + src/OperationsTestQ.cpp + src/SignalTest.cpp + src/SignalTestQ.cpp + src/TransactionTest.cpp) + +target_link_libraries(CppReactTest CppReact gtest gtest_main) From 3b11a133179c3485019a46aeb92d82bafdc9fcfb Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 2 Jun 2014 16:59:32 +0200 Subject: [PATCH 134/266] Fixed typos. --- include/react/detail/ReactiveInput.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index 9a4f60bb..9eeda84f 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -21,14 +21,15 @@ #include "tbb/concurrent_vector.h" -#include "react/detail/IReactiveNode.h." -#include "react/detail/Options.h." +#include "react/detail/Options.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ using TurnIdT = uint; using TurnFlagsT = uint; +struct IInputNode; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// ContinuationInput /////////////////////////////////////////////////////////////////////////////////////////////////// From d1a773bce853adc8a322d78c0313a13fd197ea8c Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 3 Jun 2014 02:50:57 +0200 Subject: [PATCH 135/266] Refactored Reactor.h so it compiles with gcc/clang. --- examples/src/BasicReactors.cpp | 18 +-- include/react/Domain.h | 10 +- include/react/Reactor.h | 11 +- include/react/detail/graph/ReactorNodes.h | 141 +++++++++++----------- 4 files changed, 90 insertions(+), 90 deletions(-) diff --git a/examples/src/BasicReactors.cpp b/examples/src/BasicReactors.cpp index 4d5cddfb..990ea589 100644 --- a/examples/src/BasicReactors.cpp +++ b/examples/src/BasicReactors.cpp @@ -32,9 +32,9 @@ namespace example1 EventSourceT mouseUp = MakeEventSource(); EventSourceT mouseMove = MakeEventSource(); - ReactiveLoopT loop + ReactorT loop { - [&] (ReactiveLoopT::Context& ctx) + [&] (ReactorT::Context ctx) { PathT points; @@ -54,15 +54,15 @@ namespace example1 { cout << "Example 1 - Creating reactive loops" << endl; - mouseDown << PointT(1,1); - mouseMove << PointT(2,2) << PointT(3,3) << PointT(4,4); - mouseUp << PointT(5,5); + mouseDown << PointT( 1,1 ); + mouseMove << PointT( 2,2 ) << PointT( 3,3 ) << PointT( 4,4 ); + mouseUp << PointT( 5,5 ); - mouseMove << PointT(999,999); + mouseMove << PointT( 999,999 ); - mouseDown << PointT(10,10); - mouseMove << PointT(20,20); - mouseUp << PointT(30,30); + mouseDown << PointT( 10,10 ); + mouseMove << PointT( 20,20 ); + mouseUp << PointT( 30,30 ); for (const auto& path : paths) { diff --git a/include/react/Domain.h b/include/react/Domain.h index 34fe75a3..ec7eca03 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -49,15 +49,15 @@ class TempEvents; enum class Token; -template -class ReactiveLoop; - template class Observer; template class ScopedObserver; +template +class Reactor; + using REACT_IMPL::TurnFlagsT; #ifdef REACT_ENABLE_LOGGING @@ -109,7 +109,7 @@ class DomainBase using ScopedObserverT = ScopedObserver; - using ReactiveLoopT = ReactiveLoop; + using ReactorT = Reactor; /////////////////////////////////////////////////////////////////////////////////////////////////// /// DoTransaction @@ -201,6 +201,6 @@ class DomainInitializer \ using ScopedObserverT = ScopedObserver; \ \ - using ReactiveLoopT = ReactiveLoop; + using ReactorT = Reactor; #endif // REACT_DOMAIN_H_INCLUDED \ No newline at end of file diff --git a/include/react/Reactor.h b/include/react/Reactor.h index a5890acf..ceec738d 100644 --- a/include/react/Reactor.h +++ b/include/react/Reactor.h @@ -13,6 +13,7 @@ #include #include +#include #include "react/common/Util.h" @@ -23,7 +24,7 @@ /*****************************************/ REACT_BEGIN /*****************************************/ template -class ReactiveLoop +class Reactor { public: class Context; @@ -40,13 +41,13 @@ class ReactiveLoop template E& Await(const Events& evn) { - return node_.Await(evn.NodePtr()); + return node_.Await(evn.NodePtr()); } template - void RepeatUntil(const Events& evn, F func) + void RepeatUntil(const Events& evn, F&& func) { - node_.RepeatUntil(evn.NodePtr(), func); + node_.RepeatUntil(evn.NodePtr(), std::forward(func)); } private: @@ -54,7 +55,7 @@ class ReactiveLoop }; template - ReactiveLoop(F&& func) : + explicit Reactor(F&& func) : nodePtr_( new REACT_IMPL::ReactorNode(std::forward(func)) ) { nodePtr_->StartLoop(); diff --git a/include/react/detail/graph/ReactorNodes.h b/include/react/detail/graph/ReactorNodes.h index 0e737c58..aa56287a 100644 --- a/include/react/detail/graph/ReactorNodes.h +++ b/include/react/detail/graph/ReactorNodes.h @@ -9,8 +9,6 @@ #pragma once -#ifndef REACT_DISABLE_REACTORS - #include "react/detail/Defs.h" #include @@ -37,19 +35,18 @@ template class ReactorNode : public ReactiveNode { -public: - using NodeBasePtrT = std::shared_ptr>; + using Engine = typename ReactorNode::Engine; - using CoroutineT = boost::coroutines::coroutine; - using LoopT = typename CoroutineT::pull_type; - using OutT = typename CoroutineT::push_type; - +public: + using CoroutineT = boost::coroutines::coroutine*>; + using PullT = typename CoroutineT::pull_type; + using PushT = typename CoroutineT::push_type; using TurnT = typename D::Engine::TurnT; template ReactorNode(F&& func) : - ReactiveNode(), - func_{ std::forward(func) } + ReactorNode::ReactiveNode( ), + func_( std::forward(func) ) { Engine::OnNodeCreate(*this); } @@ -61,17 +58,16 @@ class ReactorNode : void StartLoop() { - // Could already have started it in ctor, - // but lets make sure node is fully constructed before calls to - // context in coroutine can happen. - mainLoop_ = LoopT + mainLoop_ = PullT ( - [&] (OutT& out) + [&] (PushT& out) { curOutPtr_ = &out; - TContext ctx{ *this }; + TContext ctx( *this ); + // Start the loop function. + // It will reach it its first Await at some point. while (true) { func_(ctx); @@ -80,11 +76,12 @@ class ReactorNode : ); // First blocking event is not initiated by Tick() but after loop creation. - const auto* p = mainLoop_.get(); + NodeBase* p = mainLoop_.get(); REACT_ASSERT(p != nullptr, "StartLoop: first depPtr was null"); - Engine::OnNodeAttach(*this, **p); + Engine::OnNodeAttach(*this, *p); + ++depCount_; } @@ -96,20 +93,22 @@ class ReactorNode : virtual void Tick(void* turnPtr) override { turnPtr_ = reinterpret_cast(turnPtr); - REACT_SCOPE_EXIT{ turnPtr_ = nullptr; }; mainLoop_(); - if (mainLoop_.get() != nullptr) + NodeBase* p = mainLoop_.get(); + + if (p != nullptr) { - const auto& depPtr = *mainLoop_.get(); - Engine::OnDynamicNodeAttach(*this, *depPtr, *turnPtr_); + Engine::OnDynamicNodeAttach(*this, *p, *turnPtr_); ++depCount_; - - return; } - - offsets_.clear(); + else + { + offsets_.clear(); + } + + turnPtr_ = nullptr; } virtual int DependencyCount() const override @@ -121,73 +120,73 @@ class ReactorNode : E& Await(const std::shared_ptr>& events) { // First attach to target event node - (*curOutPtr_)(&std::static_pointer_cast>(events)); + (*curOutPtr_)(events.get()); - while (! checkEvent(events)) + while (! checkEvent(events)) (*curOutPtr_)(nullptr); - REACT_ASSERT(turnPtr_ != nullptr, "Take: turnPtr_ was null"); + REACT_ASSERT(turnPtr_ != nullptr, "Await: turnPtr_ was null"); Engine::OnDynamicNodeDetach(*this, *events, *turnPtr_); --depCount_; - return events->Events()[offsets_[reinterpret_cast(&events)]++]; + auto index = offsets_[reinterpret_cast(events.get())]++; + return events->Events()[index]; } template - void RepeatUntil(const std::shared_ptr>& events, F func) + void RepeatUntil(const std::shared_ptr>& eventsPtr, const F& func) { // First attach to target event node if (turnPtr_ != nullptr) { - (*curOutPtr_)(&std::static_pointer_cast>(events)); + (*curOutPtr_)(eventsPtr.get()); } else { // Non-dynamic attach in case first loop until is encountered before the loop was // suspended for the first time. - Engine::OnNodeAttach(*this, *events); + Engine::OnNodeAttach(*this, *eventsPtr); ++depCount_; } - // Detach when this function is exited - REACT_SCOPE_EXIT + // Don't enter loop if event already present + if (! checkEvent(eventsPtr)) { - if (turnPtr_ != nullptr) - Engine::OnDynamicNodeDetach(*this, *events, *turnPtr_); - else - Engine::OnNodeDetach(*this, *events); - --depCount_; - }; + auto* parentOutPtr = curOutPtr_; - // Don't enter loop if event already present - if (checkEvent(events)) - return; + // Create and start loop + PullT nestedLoop_ + { + [&] (PushT& out) + { + curOutPtr_ = &out; + while (true) + func(); + } + }; - auto* parentOutPtr = curOutPtr_; - REACT_SCOPE_EXIT{ curOutPtr_ = parentOutPtr; }; + // First suspend from initial loop run + (*parentOutPtr)(nestedLoop_.get()); - // Create and start loop - LoopT nestedLoop_ - { - [&] (OutT& out) + // Further iterations + while (! checkEvent(eventsPtr)) { - curOutPtr_ = &out; - while (true) - func(); + // Advance loop, forward blocking event to parent, and suspend + nestedLoop_(); + (*parentOutPtr)(nestedLoop_.get()); } - }; - // First suspend from initial loop run - (*parentOutPtr)(nestedLoop_.get()); - - // Further iterations - while (! checkEvent(events)) - { - // Advance loop, forward blocking event to parent, and suspend - nestedLoop_(); - (*parentOutPtr)(nestedLoop_.get()); + curOutPtr_ = parentOutPtr; } + + // Detach when this function is exited + if (turnPtr_ != nullptr) + Engine::OnDynamicNodeDetach(*this, *eventsPtr, *turnPtr_); + else + Engine::OnNodeDetach(*this, *eventsPtr); + + --depCount_; } private: @@ -198,23 +197,23 @@ class ReactorNode : return false; events->SetCurrentTurn(*turnPtr_); - return offsets_[reinterpret_cast(&events)] < events->Events().size(); + + auto index = reinterpret_cast(events.get()); + return offsets_[index] < events->Events().size(); } - std::function func_; + std::function func_; - LoopT mainLoop_; + PullT mainLoop_; TurnT* turnPtr_; - OutT* curOutPtr_ = nullptr; + PushT* curOutPtr_ = nullptr; - int depCount_ = 0; + uint depCount_ = 0; - std::unordered_map offsets_; + std::unordered_map offsets_; }; /****************************************/ REACT_IMPL_END /***************************************/ -#endif // REACT_DISABLE_REACTORS - #endif // REACT_DETAIL_GRAPH_REACTORNODES_H_INCLUDED \ No newline at end of file From 0741199d334db423ebe4222815b8507704336a93 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 3 Jun 2014 02:52:25 +0200 Subject: [PATCH 136/266] Improved build scripts, enabled Example_BasicReactors. --- CMakeLists.txt | 11 ----------- examples/CMakeLists.txt | 10 +++++++++- tests/CMakeLists.txt | 9 +++++++++ 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 82c1fa3c..ae21be06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,14 +38,3 @@ option(build_tests "Build unit tests?" OFF) if(build_tests) add_subdirectory(tests) endif() - -# cmake -Dbuild_tests=BOOL:ON -if(build_tests) - if(DEFINED ENV{GTEST_DIR}) - message("Using gtest found in $ENV{GTEST_DIR}.") - else() - message("GTEST_DIR is not defined. You must tell CMake where to find the gtest source.") - return() - endif() - add_subdirectory($ENV{GTEST_DIR} ${CMAKE_CURRENT_BINARY_DIR}/gtest) -endif() diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 4a5fc96d..940479ae 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -15,7 +15,15 @@ add_executable(Example_BasicObservers src/BasicObservers.cpp) target_link_libraries(Example_BasicObservers CppReact) ### Example_BasicReactors -#add_executable(Example_BasicReactors src/BasicReactors.cpp) +find_package(Boost 1.55 COMPONENTS coroutine context system) +if(Boost_FOUND) + include_directories(${Boost_INCLUDE_DIRS}) + add_executable(Example_BasicReactors src/BasicReactors.cpp) + target_link_libraries(Example_BasicReactors CppReact ${Boost_LIBRARIES}) +else() + message("boost::coroutine not found. Skipping build of Example_BasicReactors.") +endif() +#add_exyecutable(Example_BasicReactors src/BasicReactors.cpp) #target_link_libraries(Example_BasicReactors CppReact) ### Example_BasicSignals diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 50d60d26..1946c5e0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,3 +1,12 @@ +### Configuration +if(DEFINED ENV{GTEST_DIR}) + message("Using gtest found in $ENV{GTEST_DIR}.") +else() + message("GTEST_DIR is not defined. You must tell CMake where to find the gtest source.") + return() +endif() +add_subdirectory($ENV{GTEST_DIR} ${CMAKE_CURRENT_BINARY_DIR}/gtest) + ### CppReactTest add_executable(CppReactTest src/EventStreamTest.cpp From 9b864a3d3f4256c0c55cf9d814cdb9c4169a57f9 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 3 Jun 2014 02:52:49 +0200 Subject: [PATCH 137/266] Removed obsolete stuff from Util.h. --- include/react/common/Util.h | 83 ------------------------------------- 1 file changed, 83 deletions(-) diff --git a/include/react/common/Util.h b/include/react/common/Util.h index 14eee051..1c85731c 100644 --- a/include/react/common/Util.h +++ b/include/react/common/Util.h @@ -11,7 +11,6 @@ #include "react/detail/Defs.h" -#include #include #include #include @@ -98,62 +97,6 @@ struct Identity using Type = T; }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Generic RAII scope guard -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ScopeGuard -{ -public: - explicit ScopeGuard(F func) : - func_( std::move(func) ) - { - } - - ~ScopeGuard() { func_(); } - - ScopeGuard() = delete; - ScopeGuard(const ScopeGuard&) = delete; - ScopeGuard& operator=(const ScopeGuard&) = delete; - -private: - F func_; -}; - -enum class ScopeGuardDummy_ {}; - -template -ScopeGuard operator+(ScopeGuardDummy_, F&& func) -{ - return ScopeGuard(std::forward(func)); -} - -// - -template -class MoveBindWrapper -{ -public: - explicit MoveBindWrapper(T&& v): - v_( std::forward(v) ) - {} - - template - T&& operator()(U&& ...) - { - return std::forward(v_); - } - -private: - T v_; -}; - -template -MoveBindWrapper MoveIntoBind(T&& t) -{ - return MoveBindWrapper{std::forward(t)}; -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// DontMove! /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -200,34 +143,8 @@ struct AddDummyArgWrapper /****************************************/ REACT_IMPL_END /***************************************/ -namespace std -{ - template - struct is_bind_expression> : std::true_type {}; -} - // Expand args by wrapping them in a dummy function // Use comma operator to replace potential void return value with 0 #define REACT_EXPAND_PACK(...) REACT_IMPL::pass((__VA_ARGS__ , 0) ...) -// Concat -#define REACT_CONCAT_(l, r) l##r -#define REACT_CONCAT(l, r) REACT_CONCAT_(l, r) - -// Anon var -#ifdef __COUNTER__ -#define REACT_ANON_VAR(prefix) \ - REACT_CONCAT(prefix, __COUNTER__) -#else -#define REACT_ANON_VAR(s) \ - REACT_CONCAT(prefix, __LINE__) -#endif - -// Scope guard helper -#define REACT_SCOPE_EXIT_PREFIX scopeExitGuard_ - -#define REACT_SCOPE_EXIT \ - auto REACT_ANON_VAR(REACT_SCOPE_EXIT_PREFIX) \ - = REACT_IMPL::ScopeGuardDummy_() + [&]() - #endif // REACT_COMMON_UTIL_H_INCLUDED \ No newline at end of file From 87c03b96f1dd2b7d0ba7382eefdbc7f4710c64ad Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 3 Jun 2014 21:32:50 +0200 Subject: [PATCH 138/266] Updated readme. --- README.md | 62 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 62b9204d..0159017f 100644 --- a/README.md +++ b/README.md @@ -23,24 +23,30 @@ Cpp.React is a work-in-progress and should not be considered release quality yet The project has been actively developed for about 6 months and has seen a fair share of testing and tuning during that time, so it's in a usable state. ### Compiling -At the moment, the only support build environment is Visual Studio 2013. +Cpp.React has been tested to compile with -You are welcome to try other C++11 compilers/platforms and report any issues you encounter. +* Visual Studio 2013.2 +* GCC 4.8.2 +* Clang 3.4 -### Projects -The VS solution contains the following pojects: +To build with Visual Studio, use the pre-made solution found in `project/msvc/`. -* `CppReact` - The library itself. -* `CppReactBenchmark` - A number of benchmarks used to compare the different propagation strategies. -* `CppReactTest` - The unit tests. -* `CppReactSandbox` - A project containing several basic examples. You can use this to start experimenting with the library. +To build with GCC or Clang, use [CMake](http://www.cmake.org/): +``` +mkdir build +cd build +cmake .. +make +``` + +For more details, see [Build instructions](). ### Dependencies Cpp.React uses several external dependencies, but only one of them is mandatory: * [Intel TBB 4.2](https://www.threadingbuildingblocks.org/) (required) * [Google test framework](https://code.google.com/p/googletest/) (optional, to compile the unit tests) -* [Boost C++ Libraries](http://www.boost.org/) (optional, to use ReactiveLoop, which requires boost::coroutine) +* [Boost 1.55 C++ Libraries](http://www.boost.org/) (optional, to include Reactor.h, which requires boost::coroutines) TBB is required, because it enables the parallel propagation strategies. Future plans are to separate the multi-threaded and single-threaded propagation engines more cleanly to remove the TBB dependency if parallelism is not used. @@ -56,14 +62,15 @@ They can be combined to expressions to create new signals, which are automatical using namespace std; using namespace react; -REACTIVE_DOMAIN(D); +REACTIVE_DOMAIN(D) +USING_REACTIVE_DOMAIN(D) // Two reactive variables that can be manipulated imperatively -D::VarSignalT width = D::MakeVar(1); -D::VarSignalT height = D::MakeVar(2); +VarSignalT width = MakeVar(1); +VarSignalT height = MakeVar(2); // A signal that depends on width and height and multiplies their values -D::SignalT area = MakeSignal(With(width, height), +SignalT area = MakeSignal(With(width, height), [] (int w, int h) { return w * h; }); @@ -79,7 +86,7 @@ cout << "area: " << area.Value() << endl; // => area: 20 For expressions that use operators only, `MakeSignal` can be omitted completely: ```C++ // Lift as reactive expression -D::SignalT area = width * height; +SignalT area = width * height; ``` ## Events and Observers @@ -91,14 +98,15 @@ They are first-class objects and can be merged, filtered, transformed or compose using namespace std; using namespace react; -REACTIVE_DOMAIN(D); +REACTIVE_DOMAIN(D) +USING_REACTIVE_DOMAIN(D) // Two event sources -D::EventSourceT leftClicked = D::MakeEventSource(); -D::EventSourceT rightClicked = D::MakeEventSource(); +EventSourceT leftClicked = MakeEventSource(); +EventSourceT rightClicked = MakeEventSource(); // Merge both event streams -D::EventsT merged = leftClicked | rightClicked; +EventsT merged = leftClicked | rightClicked; // React to events auto obs = Observe(merged, [] (Token) { @@ -121,11 +129,11 @@ using namespace react; // Sequential updating { - REACTIVE_DOMAIN(D, ToposortEngine); + REACTIVE_DOMAIN(D, ToposortEngine) - auto a = D::MakeVar(1); - auto b = D::MakeVar(2); - auto c = D::MakeVar(3); + auto a = MakeVar(1); + auto b = MakeVar(2); + auto c = MakeVar(3); auto x = (a + b) * c; @@ -134,9 +142,9 @@ using namespace react; // Parallel updating { - REACTIVE_DOMAIN(D, ToposortEngine); + REACTIVE_DOMAIN(D, ToposortEngine) - auto in = D::MakeVar(0); + auto in = MakeVar(0); auto op1 = MakeSignal(in, [] (int in) { @@ -159,9 +167,9 @@ using namespace react; ## More examples -* [Examples](https://github.com/schlangster/cpp.react/blob/master/src/sandbox/Main.cpp) -* [Test cases](https://github.com/schlangster/cpp.react/tree/master/src/test) -* [Benchmark](https://github.com/schlangster/cpp.react/blob/master/src/benchmark/BenchmarkLifeSim.h) +* [Examples](https://github.com/schlangster/cpp.react/tree/master/examples/src) +* [Test cases](https://github.com/schlangster/cpp.react/tree/master/tests/src) +* [Benchmarks](https://github.com/schlangster/cpp.react/blob/master/benchmarks/src/BenchmarkLifeSim.h) # Acknowledgements From 860aadacdac940a0160b1d768f5c60a0ad308272 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 4 Jun 2014 13:35:27 +0200 Subject: [PATCH 139/266] Updated readme. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0159017f..5fab6cc8 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Cpp.React is a work-in-progress and should not be considered release quality yet The project has been actively developed for about 6 months and has seen a fair share of testing and tuning during that time, so it's in a usable state. ### Compiling -Cpp.React has been tested to compile with +Cpp.React has been tested with the following compilers: * Visual Studio 2013.2 * GCC 4.8.2 @@ -39,7 +39,7 @@ cmake .. make ``` -For more details, see [Build instructions](). +For more details, see [Build instructions](https://github.com/schlangster/cpp.react/wiki/Build-instructions). ### Dependencies Cpp.React uses several external dependencies, but only one of them is mandatory: From 0eea52c9ec415a121c05d27cba38e7830295f7f4 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 4 Jun 2014 15:54:43 +0200 Subject: [PATCH 140/266] Added another example. --- examples/src/BasicSignals.cpp | 52 +++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/examples/src/BasicSignals.cpp b/examples/src/BasicSignals.cpp index 55e18ea7..dfb02f23 100644 --- a/examples/src/BasicSignals.cpp +++ b/examples/src/BasicSignals.cpp @@ -44,7 +44,7 @@ namespace example1 VarSignalT firstWord = MakeVar(string("Change")); VarSignalT secondWord = MakeVar(string("me!")); - SignalT bothWords = MakeSignal(With(firstWord,secondWord), &concatFunc); + SignalT bothWords = MakeSignal(With(firstWord,secondWord), concatFunc); void Run() { @@ -164,7 +164,7 @@ namespace example3 } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 4 - Complex signals +/// Example 4 - Modifying signal values in place /////////////////////////////////////////////////////////////////////////////////////////////////// namespace example4 { @@ -174,6 +174,40 @@ namespace example4 REACTIVE_DOMAIN(D) USING_REACTIVE_DOMAIN(D) + VarSignalT> data = MakeVar(vector{ }); + + void Run() + { + cout << "Example 4 - Modifying signal values in place" << endl; + + data.Modify([] (vector& data) { + data.push_back("Hello"); + }); + + data.Modify([] (vector& data) { + data.push_back("World"); + }); + + for (const auto& s : data.Value()) + cout << s << " "; + cout << endl; + // output: Hell World + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 5 - Complex signals +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example5 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D) + USING_REACTIVE_DOMAIN(D) + // Helpers using ExprPairT = pair; using ExprVectT = vector; @@ -233,7 +267,7 @@ namespace example4 void Run() { - cout << "Example 4 - Complex signals (v1)" << endl; + cout << "Example 5 - Complex signals (v1)" << endl; Observe(expressions, printExpressions); @@ -282,7 +316,7 @@ namespace example4 void Run() { - cout << "Example 4 - Complex signals (v2)" << endl; + cout << "Example 5 - Complex signals (v2)" << endl; Observe(expressions, printExpressions); @@ -324,7 +358,7 @@ namespace example4 void Run() { - cout << "Example 4 - Complex signals (v3)" << endl; + cout << "Example 5 - Complex signals (v3)" << endl; Observe(expressions, printExpressions); @@ -348,9 +382,11 @@ int main() example3::Run(); - example4::v1::Run(); - example4::v2::Run(); - example4::v3::Run(); + example4::Run(); + + example5::v1::Run(); + example5::v2::Run(); + example5::v3::Run(); return 0; } \ No newline at end of file From ad883943128d401903b7edb861387db91efcfd95 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 11 Jun 2014 21:28:58 +0200 Subject: [PATCH 141/266] Removed pipelining toposort engine. It was somewhat broken and adding async (which I'm planning) would have introduced further complications. --- include/react/engine/ToposortEngine.h | 157 ------------- src/engine/PulsecountEngine.cpp | 6 +- src/engine/ToposortEngine.cpp | 312 -------------------------- tests/src/EventStreamTestQ.cpp | 1 - tests/src/ObserverTestQ.cpp | 1 - tests/src/OperationsTestQ.cpp | 1 - tests/src/SignalTestQ.cpp | 1 - tests/src/TransactionTest.cpp | 1 - 8 files changed, 3 insertions(+), 477 deletions(-) diff --git a/include/react/engine/ToposortEngine.h b/include/react/engine/ToposortEngine.h index 8de56945..546a824a 100644 --- a/include/react/engine/ToposortEngine.h +++ b/include/react/engine/ToposortEngine.h @@ -209,156 +209,6 @@ class QueuingSeqEngine : public DefaultQueuingEngine {}; class QueuingParEngine : public DefaultQueuingEngine {}; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// PipeliningTurn -/////////////////////////////////////////////////////////////////////////////////////////////////// -class PipeliningTurn : public TurnBase -{ -public: - using ConcNodeVectT = concurrent_vector; - using NodeVectT = vector; - using IntervalSetT = set>; - using DynRequestVectT = concurrent_vector; - using TopoQueueT = ConcurrentTopoQueue - < - ParNode*, - grain_size, - GetLevelFunctor, - GetWeightFunctor - >; - - PipeliningTurn(TurnIdT id, TurnFlagsT flags); - - int CurrentLevel() const { return currentLevel_; } - - bool AdvanceLevel(); - void SetMaxLevel(int level); - void WaitForMaxLevel(int targetLevel); - - void UpdateSuccessor(); - - void Append(PipeliningTurn* turn); - - void Remove(); - - void AdjustUpperBound(int level); - - template - bool TryMerge(F&& inputFunc, BlockingCondition& caller) - { - if (!isMergeable_) - return false; - - bool merged = false; - - {// advMutex_ - std::lock_guard scopedLock(advMutex_); - - // Only merge if target is still blocked - if (currentLevel_ == -1) - { - merged = true; - caller.Block(); - merged_.emplace_back(std::make_pair(std::forward(inputFunc), &caller)); - } - }// ~advMutex_ - - return merged; - } - - void RunMergedInputs() const; - - TopoQueueT TopoQueue; - DynRequestVectT DynRequests; - -private: - using MergedDataVectT = vector,BlockingCondition*>>; - - const bool isMergeable_; - MergedDataVectT merged_; - - IntervalSetT levelIntervals_; - - PipeliningTurn* predecessor_ { nullptr }; - PipeliningTurn* successor_ { nullptr }; - - int currentLevel_ { -1 }; - - /// This turn may only advance up to maxLevel - int maxLevel_ { (numeric_limits::max)() }; - - /// successor.maxLevel = this.minLevel - 1 - int minLevel_ { -1 }; - - int curUpperBound_ { -1 }; - - mutex advMutex_; - condition_variable advCondition_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// PipeliningEngine -/////////////////////////////////////////////////////////////////////////////////////////////////// -class PipeliningEngine : public IReactiveEngine -{ -public: - using SeqMutexT = queuing_rw_mutex; - using NodeSetT = set; - using MaxDynamicLevelMutexT = spin_mutex; - - void OnNodeAttach(ParNode& node, ParNode& parent); - void OnNodeDetach(ParNode& node, ParNode& parent); - - void OnTurnAdmissionStart(PipeliningTurn& turn); - void OnTurnAdmissionEnd(PipeliningTurn& turn); - void OnTurnEnd(PipeliningTurn& turn); - - void OnTurnInputChange(ParNode& node, PipeliningTurn& turn); - void OnNodePulse(ParNode& node, PipeliningTurn& turn); - - void OnTurnPropagate(PipeliningTurn& turn); - - void OnDynamicNodeAttach(ParNode& node, ParNode& parent, PipeliningTurn& turn); - void OnDynamicNodeDetach(ParNode& node, ParNode& parent, PipeliningTurn& turn); - - template - inline bool TryMerge(F&& inputFunc) - { - bool merged = false; - - BlockingCondition caller; - - {// seqMutex_ - SeqMutexT::scoped_lock lock(seqMutex_); - - if (tail_) - merged = tail_->TryMerge(std::forward(inputFunc), caller); - }// ~seqMutex_ - - if (merged) - caller.WaitForUnblock(); - - return merged; - } - -private: - void applyDynamicAttach(ParNode& node, ParNode& parent, PipeliningTurn& turn); - void applyDynamicDetach(ParNode& node, ParNode& parent, PipeliningTurn& turn); - - void processChildren(ParNode& node, PipeliningTurn& turn); - void invalidateSuccessors(ParNode& node); - - void advanceTurn(PipeliningTurn& turn); - - SeqMutexT seqMutex_; - PipeliningTurn* tail_ { nullptr }; - - NodeSetT dynamicNodes_; - int maxDynamicLevel_; - - MaxDynamicLevelMutexT maxDynamicLevelMutex_; -}; - } // ~namespace toposort /****************************************/ REACT_IMPL_END /***************************************/ @@ -369,7 +219,6 @@ struct sequential; struct sequential_queue; struct parallel; struct parallel_queue; -struct parallel_pipeline; template class ToposortEngine; @@ -386,9 +235,6 @@ template <> class ToposortEngine : template <> class ToposortEngine : public REACT_IMPL::toposort::QueuingParEngine {}; -template <> class ToposortEngine : - public REACT_IMPL::toposort::PipeliningEngine {}; - /******************************************/ REACT_END /******************************************/ /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -396,17 +242,14 @@ template <> class ToposortEngine : template struct EnableNodeUpdateTimer; template <> struct EnableNodeUpdateTimer> : std::true_type {}; template <> struct EnableNodeUpdateTimer> : std::true_type {}; -template <> struct EnableNodeUpdateTimer> : std::true_type {}; template struct EnableParallelUpdating; template <> struct EnableParallelUpdating> : std::true_type {}; template <> struct EnableParallelUpdating> : std::true_type {}; -template <> struct EnableParallelUpdating> : std::true_type {}; template struct EnableConcurrentInput; template <> struct EnableConcurrentInput> : std::true_type {}; template <> struct EnableConcurrentInput> : std::true_type {}; -template <> struct EnableConcurrentInput> : std::true_type {}; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/src/engine/PulsecountEngine.cpp b/src/engine/PulsecountEngine.cpp index a93dacc4..907dfc1e 100644 --- a/src/engine/PulsecountEngine.cpp +++ b/src/engine/PulsecountEngine.cpp @@ -204,13 +204,13 @@ template void spawnHelper ( task* rootTask, task_list& spawnList, - const int count, TIt start, TIt end, + const uint count, TIt start, TIt end, TArgs& ... args ) { rootTask->set_ref_count(1 + count); - for (int i=0; i < (count - 1); i++) + for (uint i=0; i < (count - 1); i++) { spawnList.push_back(*new(rootTask->allocate_child()) TTask(args ..., start, start + chunk_size)); @@ -228,7 +228,7 @@ void spawnHelper template void EngineBase::OnTurnPropagate(TTurn& turn) { - const int initialTaskCount = (changedInputs_.size() - 1) / chunk_size + 1; + const uint initialTaskCount = (changedInputs_.size() - 1) / chunk_size + 1; spawnHelper(rootTask_, spawnList_, initialTaskCount, changedInputs_.begin(), changedInputs_.end()); diff --git a/src/engine/ToposortEngine.cpp b/src/engine/ToposortEngine.cpp index 392f2e95..60cfd9cc 100644 --- a/src/engine/ToposortEngine.cpp +++ b/src/engine/ToposortEngine.cpp @@ -237,317 +237,5 @@ void ParEngineBase::invalidateSuccessors(ParNode& node) template class ParEngineBase; template class ParEngineBase>; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// PipeliningTurn -/////////////////////////////////////////////////////////////////////////////////////////////////// -PipeliningTurn::PipeliningTurn(TurnIdT id, TurnFlagsT flags) : - TurnBase(id, flags), - isMergeable_{ (flags & enable_input_merging) != 0 } -{ -} - -bool PipeliningTurn::AdvanceLevel() -{ - std::unique_lock lock(advMutex_); - - advCondition_.wait(lock, [this] { - return !(currentLevel_ + 1 > maxLevel_); - }); - - // Remove the intervals we're going to leave - auto it = levelIntervals_.begin(); - while (it != levelIntervals_.end()) - { - if (it->second <= currentLevel_) - it = levelIntervals_.erase(it); - else - ++it; - } - - // Add new interval for current level - if (currentLevel_ < curUpperBound_) - levelIntervals_.emplace(currentLevel_, curUpperBound_); - - currentLevel_++; - - curUpperBound_ = currentLevel_; - - // Min level is the minimum over all interval lower bounds - int newMinLevel = - levelIntervals_.begin() != levelIntervals_.end() ? - levelIntervals_.begin()->first : - -1; - - if (minLevel_ != newMinLevel) - { - minLevel_ = newMinLevel; - return true; - } - else - { - return false; - } -} - -void PipeliningTurn::SetMaxLevel(int level) -{ - std::lock_guard lock(advMutex_); - - maxLevel_ = level; - advCondition_.notify_all(); -} - -void PipeliningTurn::WaitForMaxLevel(int targetLevel) -{ - std::unique_lock lock(advMutex_); - - advCondition_.wait(lock, [this, targetLevel] { - return !(targetLevel < maxLevel_); - }); -} - -void PipeliningTurn::Append(PipeliningTurn* turn) -{ - successor_ = turn; - - if (turn) - turn->predecessor_ = this; - - UpdateSuccessor(); -} - -void PipeliningTurn::UpdateSuccessor() -{ - if (successor_) - successor_->SetMaxLevel(minLevel_ - 1); -} - -void PipeliningTurn::Remove() -{ - if (predecessor_) - { - predecessor_->Append(successor_); - } - else if (successor_) - { - successor_->SetMaxLevel((numeric_limits::max)()); - successor_->predecessor_ = nullptr; - } - - for (const auto& e : merged_) - e.second->Unblock(); -} - -void PipeliningTurn::AdjustUpperBound(int level) -{ - if (curUpperBound_ < level) - curUpperBound_ = level; -} - -void PipeliningTurn::RunMergedInputs() const -{ - for (const auto& e : merged_) - e.first(); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// PipeliningEngine -/////////////////////////////////////////////////////////////////////////////////////////////////// -void PipeliningEngine::OnNodeAttach(ParNode& node, ParNode& parent) -{ - parent.Successors.Add(node); - - if (node.Level <= parent.Level) - node.Level = parent.Level + 1; - - if (node.IsDynamicNode()) - { - dynamicNodes_.insert(&node); - - if (maxDynamicLevel_ < node.Level) - maxDynamicLevel_ = node.Level; - } -} - -void PipeliningEngine::OnNodeDetach(ParNode& node, ParNode& parent) -{ - parent.Successors.Remove(node); - - // Recalc maxdynamiclevel? - if (node.IsDynamicNode()) - { - dynamicNodes_.erase(&node); - if (maxDynamicLevel_ == node.Level) - { - maxDynamicLevel_ = 0; - for (auto e : dynamicNodes_) - if (maxDynamicLevel_ < e->Level) - maxDynamicLevel_ = e->Level; - } - } -} - -void PipeliningEngine::OnTurnAdmissionStart(PipeliningTurn& turn) -{ - {// seqMutex_ - SeqMutexT::scoped_lock lock(seqMutex_); - - if (tail_) - tail_->Append(&turn); - - tail_ = &turn; - }// ~seqMutex_ - - advanceTurn(turn); -} - -void PipeliningEngine::OnTurnAdmissionEnd(PipeliningTurn& turn) -{ - turn.RunMergedInputs(); -} - -void PipeliningEngine::OnTurnEnd(PipeliningTurn& turn) -{// seqMutex_ (write) - SeqMutexT::scoped_lock lock(seqMutex_, true); - - turn.Remove(); - - if (tail_ == &turn) - tail_ = nullptr; -}// ~seqMutex_ - -void PipeliningEngine::OnTurnInputChange(ParNode& node, PipeliningTurn& turn) -{ - processChildren(node, turn); -} - -void PipeliningEngine::OnNodePulse(ParNode& node, PipeliningTurn& turn) -{ - processChildren(node, turn); -} - -void PipeliningEngine::OnTurnPropagate(PipeliningTurn& turn) -{ - if (maxDynamicLevel_ > 0) - turn.AdjustUpperBound(maxDynamicLevel_); - - while (turn.TopoQueue.FetchNext()) - { - using RangeT = PipeliningTurn::TopoQueueT::NextRangeT; - - for (const auto& e : turn.TopoQueue.NextRange()) - turn.AdjustUpperBound(e.first->Level); - - advanceTurn(turn); - - // Iterate all nodes of current level and start processing them in parallel - tbb::parallel_for( - turn.TopoQueue.NextRange(), - [&] (const RangeT& range) - { - for (const auto& e : range) - { - auto* curNode = e.first; - if (curNode->Level < curNode->NewLevel) - { - curNode->Level = curNode->NewLevel; - invalidateSuccessors(*curNode); - turn.TopoQueue.Push(curNode); - continue; - } - - curNode->Collected = false; - - // Tick -> if changed: OnNodePulse -> adds child nodes to the queue - curNode->Tick(&turn); - } - } - ); - - if (turn.DynRequests.size() > 0) - { - for (auto req : turn.DynRequests) - { - if (req.ShouldAttach) - applyDynamicAttach(*req.Node, *req.Parent, turn); - else - applyDynamicDetach(*req.Node, *req.Parent, turn); - } - turn.DynRequests.clear(); - } - } -} - -void PipeliningEngine::OnDynamicNodeAttach(ParNode& node, ParNode& parent, PipeliningTurn& turn) -{ - DynRequestData data{ true, &node, &parent }; - turn.DynRequests.push_back(data); -} - -void PipeliningEngine::OnDynamicNodeDetach(ParNode& node, ParNode& parent, PipeliningTurn& turn) -{ - DynRequestData data{ false, &node, &parent }; - turn.DynRequests.push_back(data); -} - -void PipeliningEngine::applyDynamicAttach(ParNode& node, ParNode& parent, PipeliningTurn& turn) -{ - turn.WaitForMaxLevel((numeric_limits::max)()); - - OnNodeAttach(node, parent); - - invalidateSuccessors(node); - - // Re-schedule this node - node.Collected = true; - turn.TopoQueue.Push(&node); -} - -void PipeliningEngine::applyDynamicDetach(ParNode& node, ParNode& parent, PipeliningTurn& turn) -{ - OnNodeDetach(node, parent); -} - -void PipeliningEngine::processChildren(ParNode& node, PipeliningTurn& turn) -{ - // Add children to queue - for (auto* succ : node.Successors) - if (!succ->Collected.exchange(true, std::memory_order_relaxed)) - turn.TopoQueue.Push(succ); -} - -void PipeliningEngine::invalidateSuccessors(ParNode& node) -{ - for (auto* succ : node.Successors) - {// succ->InvalidateMutex - ParNode::InvalidateMutexT::scoped_lock lock(succ->InvalidateMutex); - - if (succ->NewLevel <= node.Level) - { - auto newLevel = node.Level + 1; - succ->NewLevel = newLevel; - - {// maxDynamicLevelMutex_ - MaxDynamicLevelMutexT::scoped_lock lock(maxDynamicLevelMutex_); - if (succ->IsDynamicNode() && maxDynamicLevel_ < newLevel) - maxDynamicLevel_ = newLevel; - }// ~maxDynamicLevelMutex_ - } - }// ~succ->InvalidateMutex -} - -void PipeliningEngine::advanceTurn(PipeliningTurn& turn) -{ - // No need to wake up successor if min level did not change - if (turn.AdvanceLevel()) - return; - - {// seqMutex_ (read) - SeqMutexT::scoped_lock lock(seqMutex_, false); - - turn.UpdateSuccessor(); - }// ~seqMutex_ -} - } // ~namespace toposort /****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file diff --git a/tests/src/EventStreamTestQ.cpp b/tests/src/EventStreamTestQ.cpp index 4a625420..c4581898 100644 --- a/tests/src/EventStreamTestQ.cpp +++ b/tests/src/EventStreamTestQ.cpp @@ -18,7 +18,6 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, EventStreamTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, EventStreamTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, EventStreamTest, PulsecountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposortP, EventStreamTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, EventStreamTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/tests/src/ObserverTestQ.cpp b/tests/src/ObserverTestQ.cpp index 4d1d36f6..8798e0cb 100644 --- a/tests/src/ObserverTestQ.cpp +++ b/tests/src/ObserverTestQ.cpp @@ -18,7 +18,6 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, ObserverTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, ObserverTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, ObserverTest, PulsecountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposortP, ObserverTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, ObserverTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/tests/src/OperationsTestQ.cpp b/tests/src/OperationsTestQ.cpp index c77ea687..a501ace9 100644 --- a/tests/src/OperationsTestQ.cpp +++ b/tests/src/OperationsTestQ.cpp @@ -18,7 +18,6 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, OperationsTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, OperationsTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, OperationsTest, PulsecountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposortP, OperationsTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, OperationsTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/tests/src/SignalTestQ.cpp b/tests/src/SignalTestQ.cpp index fc45d8ea..3fd36a5f 100644 --- a/tests/src/SignalTestQ.cpp +++ b/tests/src/SignalTestQ.cpp @@ -18,7 +18,6 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, SignalTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, SignalTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, SignalTest, PulsecountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposortP, SignalTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, SignalTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/tests/src/TransactionTest.cpp b/tests/src/TransactionTest.cpp index 7b5f4fc0..c44370bd 100644 --- a/tests/src/TransactionTest.cpp +++ b/tests/src/TransactionTest.cpp @@ -18,7 +18,6 @@ using namespace react; INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, TransactionTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, TransactionTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, TransactionTest, PulsecountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposortP, TransactionTest, ToposortEngine); INSTANTIATE_TYPED_TEST_CASE_P(Subtree, TransactionTest, SubtreeEngine); } // ~namespace \ No newline at end of file From 79888e5921260da05cdaec986b9488a28b837232 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 15 Jun 2014 11:49:06 +0200 Subject: [PATCH 142/266] Added more graph figures. --- README.md | 5 +- doc/media/graphs/flow_eventflatten.graphml | 149 +++++++++ doc/media/graphs/flow_eventflatten.png | Bin 0 -> 5218 bytes doc/media/graphs/flow_filter.graphml | 110 +++++++ doc/media/graphs/flow_filter.png | Bin 0 -> 4369 bytes doc/media/graphs/flow_filter2.graphml | 197 ++++++++++++ doc/media/graphs/flow_filter2.png | Bin 0 -> 8051 bytes doc/media/graphs/flow_flatten.graphml | 149 +++++++++ doc/media/graphs/flow_flatten.png | Bin 0 -> 4622 bytes doc/media/graphs/flow_hold.graphml | 111 +++++++ doc/media/graphs/flow_hold.png | Bin 0 -> 4216 bytes doc/media/graphs/flow_iterate.graphml | 111 +++++++ doc/media/graphs/flow_iterate.png | Bin 0 -> 4677 bytes doc/media/graphs/flow_iterate2.graphml | 198 ++++++++++++ doc/media/graphs/flow_iterate2.png | Bin 0 -> 8052 bytes doc/media/graphs/flow_makesignal.graphml | 206 ++++++++++++ doc/media/graphs/flow_makesignal.png | Bin 0 -> 6945 bytes doc/media/graphs/flow_makesource.graphml | 109 +++++++ doc/media/graphs/flow_makesource.png | Bin 0 -> 1521 bytes doc/media/graphs/flow_makevar.graphml | 111 +++++++ doc/media/graphs/flow_makevar.png | Bin 0 -> 1675 bytes doc/media/graphs/flow_merge.graphml | 205 ++++++++++++ doc/media/graphs/flow_merge.png | Bin 0 -> 7452 bytes doc/media/graphs/flow_monitor.graphml | 110 +++++++ doc/media/graphs/flow_monitor.png | Bin 0 -> 3515 bytes doc/media/graphs/flow_observe.graphml | 109 +++++++ doc/media/graphs/flow_observe.png | Bin 0 -> 4412 bytes doc/media/graphs/flow_observe2.graphml | 109 +++++++ doc/media/graphs/flow_observe2.png | Bin 0 -> 3987 bytes doc/media/graphs/flow_observe3.graphml | 196 +++++++++++ doc/media/graphs/flow_observe3.png | Bin 0 -> 8143 bytes doc/media/graphs/flow_pulse.graphml | 141 ++++++++ doc/media/graphs/flow_pulse.png | Bin 0 -> 5700 bytes doc/media/graphs/flow_snapshot.graphml | 142 ++++++++ doc/media/graphs/flow_snapshot.png | Bin 0 -> 5731 bytes doc/media/graphs/flow_transform.graphml | 110 +++++++ doc/media/graphs/flow_transform.png | Bin 0 -> 4321 bytes doc/media/graphs/flow_transform2.graphml | 197 ++++++++++++ doc/media/graphs/flow_transform2.png | Bin 0 -> 7915 bytes doc/media/helloworld.graphml | 222 +++++++++++++ doc/media/helloworld.png | Bin 0 -> 7126 bytes doc/media/reactives1.graphml | 85 +++-- doc/media/reactives1.png | Bin 6920 -> 0 bytes doc/media/reactives2.graphml | 212 +++++++----- doc/media/reactives2.png | Bin 13167 -> 0 bytes doc/media/signals1.graphml | 178 ++++++++++ doc/media/signals1.png | Bin 0 -> 3869 bytes doc/media/signals2.graphml | 358 +++++++++++++++++++++ doc/media/signals2.png | Bin 0 -> 9576 bytes 49 files changed, 3716 insertions(+), 114 deletions(-) create mode 100644 doc/media/graphs/flow_eventflatten.graphml create mode 100644 doc/media/graphs/flow_eventflatten.png create mode 100644 doc/media/graphs/flow_filter.graphml create mode 100644 doc/media/graphs/flow_filter.png create mode 100644 doc/media/graphs/flow_filter2.graphml create mode 100644 doc/media/graphs/flow_filter2.png create mode 100644 doc/media/graphs/flow_flatten.graphml create mode 100644 doc/media/graphs/flow_flatten.png create mode 100644 doc/media/graphs/flow_hold.graphml create mode 100644 doc/media/graphs/flow_hold.png create mode 100644 doc/media/graphs/flow_iterate.graphml create mode 100644 doc/media/graphs/flow_iterate.png create mode 100644 doc/media/graphs/flow_iterate2.graphml create mode 100644 doc/media/graphs/flow_iterate2.png create mode 100644 doc/media/graphs/flow_makesignal.graphml create mode 100644 doc/media/graphs/flow_makesignal.png create mode 100644 doc/media/graphs/flow_makesource.graphml create mode 100644 doc/media/graphs/flow_makesource.png create mode 100644 doc/media/graphs/flow_makevar.graphml create mode 100644 doc/media/graphs/flow_makevar.png create mode 100644 doc/media/graphs/flow_merge.graphml create mode 100644 doc/media/graphs/flow_merge.png create mode 100644 doc/media/graphs/flow_monitor.graphml create mode 100644 doc/media/graphs/flow_monitor.png create mode 100644 doc/media/graphs/flow_observe.graphml create mode 100644 doc/media/graphs/flow_observe.png create mode 100644 doc/media/graphs/flow_observe2.graphml create mode 100644 doc/media/graphs/flow_observe2.png create mode 100644 doc/media/graphs/flow_observe3.graphml create mode 100644 doc/media/graphs/flow_observe3.png create mode 100644 doc/media/graphs/flow_pulse.graphml create mode 100644 doc/media/graphs/flow_pulse.png create mode 100644 doc/media/graphs/flow_snapshot.graphml create mode 100644 doc/media/graphs/flow_snapshot.png create mode 100644 doc/media/graphs/flow_transform.graphml create mode 100644 doc/media/graphs/flow_transform.png create mode 100644 doc/media/graphs/flow_transform2.graphml create mode 100644 doc/media/graphs/flow_transform2.png create mode 100644 doc/media/helloworld.graphml create mode 100644 doc/media/helloworld.png delete mode 100644 doc/media/reactives1.png delete mode 100644 doc/media/reactives2.png create mode 100644 doc/media/signals1.graphml create mode 100644 doc/media/signals1.png create mode 100644 doc/media/signals2.graphml create mode 100644 doc/media/signals2.png diff --git a/README.md b/README.md index 5fab6cc8..8c4dbe37 100644 --- a/README.md +++ b/README.md @@ -46,10 +46,7 @@ Cpp.React uses several external dependencies, but only one of them is mandatory: * [Intel TBB 4.2](https://www.threadingbuildingblocks.org/) (required) * [Google test framework](https://code.google.com/p/googletest/) (optional, to compile the unit tests) -* [Boost 1.55 C++ Libraries](http://www.boost.org/) (optional, to include Reactor.h, which requires boost::coroutines) - -TBB is required, because it enables the parallel propagation strategies. -Future plans are to separate the multi-threaded and single-threaded propagation engines more cleanly to remove the TBB dependency if parallelism is not used. +* [Boost 1.55.0 C++ Libraries](http://www.boost.org/) (optional, to include Reactor.h, which requires `boost::coroutine`) # Features by example diff --git a/doc/media/graphs/flow_eventflatten.graphml b/doc/media/graphs/flow_eventflatten.graphml new file mode 100644 index 00000000..a0385cfe --- /dev/null +++ b/doc/media/graphs/flow_eventflatten.graphml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <E> + r + + + + + + + + + + + + + + + + + <E> + inner + + + + + + + + + + + + + + + + + <Events<E>> + + + + + + + + + outer + + + + + + + + + + + + + + + + + [e,...] + + + + + + + + + + + + [e,...] + + + + + + + + + + + + + inner + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/graphs/flow_eventflatten.png b/doc/media/graphs/flow_eventflatten.png new file mode 100644 index 0000000000000000000000000000000000000000..d9911717c271b1ccdcf995ef337759581c3c8012 GIT binary patch literal 5218 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 gQ~&K_+J!^4j(e + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <E> + r + + + + + + + + + + + + + + + + + <E> + source + + + + + + + + + + + + + + + + + [e if func(e), ...] + + + + + + + + + + + + [e,...] + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/graphs/flow_filter.png b/doc/media/graphs/flow_filter.png new file mode 100644 index 0000000000000000000000000000000000000000..fd3454b2e7af195d8f27f0f7976068a422dd1cb1 GIT binary patch literal 4369 zcmZ8lc_38l8$KvXMx`rauC;r!6lKdGDQQe($(mi(vP{OlD~XiBHN%xjQM zU-ud_LLo~-mN6zV#!?stzvI?j=a2KvnfcE5zTf+P@AJIRd1Ph+hwzK>0{{R)7+$#n z0NlsG@0|y@!7HN@+X(j72gy;XdeE*<^^Z0AOU-qTnu$+yH%p+|_01<>h4yyX8nI zDsl@7!dM*w*!F^1-6g~GrKP3EPA588RIx%@Zngkvq4Wd$xItk>29R_epO-@at)pVG_Dg-WoY{i4g`6 z7bQ<}?FvAbl#~=xq=&QYVNgMIAfOk6)qguYY-4Mysj8ac0-Jft8-K6QnoDmdQ~wDX z-PqKWh?O16jx#9@^!0rLZZ6#MUxE5}gyK*q=ay-t?e|k(BEsE)Uyo+dR_NWN`l6PW z95^ok^?7t^dX+6vARfc-)6?a_^ z{}LG;O<>ZSvJ5V>vVs~t4&PQ4SNJ^H!IMqRIAz)%wtSz&2S6{u{KsBOi%%cc-O$rN zRPy{eVwU~sUAc}fAT_)f^oE88yl{R_&cK_??8O#Vkd~u|#}Bch`~b=kZQtaN(CE#D z6%pt%tEU5%w2gJH08*v&u=Vj!k1cI=^%4RdUsEF$xYMi6s-bcL1Vz$#OQ?F=vK9VR z%XOV=fE1i=_F?ISgv8LtLiXl-wfd&DIN*(kQoQWdqtjp;;nN8T`z7Rnv@fXo#zu-p zsEdodABZhjoBtgOt; zJpM|N(VIsd8{^!*>dYRkNaS20wAR;K-nenA))piAp(V8MP6I{xC)ga53CYXO%gbwQ z)S!!y6TJHJe}B(Rqf#z(D}9$L40PRsVcz1DoZF$WxuR5EesQNa%n-#4utsiJTOZd# zv_t#{j>*HLqoXffx&()Vc3xOGI6xziJv}`XY_u+J)K@Ui($Vqz`6mG$AHORdAiM1c z@K$I>A1*61vr|m1rl#iXhR{xLo;K>H#x$ikKVO1vy(Z+|-QLnt4t2?r{LxA9c4tS& zy_JlrDkb)#S5shy>gwuHHwz1mQ3Qu;hBG2e7E6)G3L+@j$!2{F(t7B`7w3!BhiORV zyf3rBifo0nL`Otuy~NsBT3L~c8yeK<_NgS<32RHslTPR_U%u2tMt=TiKBF`E?j89g z`Qb|C1S<;}&k8X3iNBqTd4<8(1v1yu7>v5z(ew59t?X zTgUty2D@DPaNK^T`0Oc@+@!ibX&Vj}<$jF3qn6drBc(0c5A_xCn}R=H%1jI1&$sD?9Py;aD9v{F=fV9jg!vNU$6^)zJ7inVEm>G2R}D<1(^PGq$9SIjCV5AoOWI*IHj#yi`Ia7)X4GFp2A~Z;)iW8yp z^Ygs%(hEgRwK3^V)y_^%=&>oYUg_bXBg;U+I~252v}}yE9vU2cBBk^GeeMkM$Hj?C2`Ej905(hQh>u;BV2-$pL-3?)0R zU*PfY_?It5VY^T4_X7l_#34R~SXSG*n${H^z`FsOXeUvYO9b79TL^}CRLv0&mW_W6 zZ<@$LsYzb{m>!+;Jqpmoqmt%_t9=&ZE3aR_E^wY3fLo&{z3dxQ%m^s7D`!BID-PwJ z)yS)0^RPE%Z*j1UN2pW_UI-@av)AAji-;T5w!Au)tnMJY_umm*)C&0=^W);;Y&vM& zL9lw5irY^~YCS=U`vlzmOz#>Rlhp21*auK9!9eB!38erEGKL)ApX>sXjvfr86Ei`O zfiUs^SmG&HzfIRf)u)14{dV`Vu<(DZ^>Uv_pfhHHFkU`BKaCs`4W_A?6mjbK zUaf% zw0_|HxpU73=EnTjyR($BsD0dAWQ4i-+oh$m<;MUyxaQmgvinaime$rEnuCp$28V_y zM37b^BO@zJz?_?ZUSEf!kM@s`_cVit;pjMxlp3t%XbT$}9=@5i7v(#CevqEdY*jDY zbntfirmgLPGo-=6?-YdqIg*bYU#CHMc(~LN7X#3ju#{)dq)VW=oqLIs25o?rwDK0` z21+XoKw}39VatW%{$9e|zveijFK$mQzc-BCblx4DQ19P5e}bUM-lVbGbi@F?68h4Z zs3`lf+np^t_O#93PbqZGjdN$;23>0$42{T9X=q8C9VR*vq*hmRobx%$iCH>QfL<_t zWQW;=&(3F4d9BXuC;$BV9c$5e=GG&gIoG;`RuDW=oQT1WRz_3AOwYg})qM8kG7lyF zLG;AHTcXa+-b%P_kvsNBXt9p+%Ok5*tFOU|u(U+SO18<^Gg^r4W&hh>tI?0kq0Q*5 zcF_1RvIi-(%h1*;(+=Fu(nds^5+8{iJ{wOuEC(;dxY3$&S;h<< z0%ch_7TMPk0^4rv3CW+zu15Lz_y{J-b|~U5#_oJ*i^TA}ae`si@B8PfT*vNt8tebhVpn;l$S>+6)W~R`_iii%~Zd z=tkVZ{MbOM4GHdt>{VJ{WSReO=uOJ`s~@Wwzjs{nQp$m6~Fv)wR$)U zizPTzUHNc2dDS!Gj=sZh1gnt?4`LK+aoO47^Oa5RvmlIQW@YJD%V*4##2F`RiCdRj zIVly_s2vv(5f>91-0>}j7C@7uCoJeAVV->%W*Un9Po5A>QA=)?=ls*_cApvNekGPEC` znnW)>7D&BOIxx2ovNbm5aFrr$Ph~QhxRq8SQcXH$DbeBqkf9yMFv~7md7BOv` zcQn$_^>$Z;ypqx{&^QfBz|VQ-Uf0>6)kyaaOGt}AAP@@+3s)Aws~9RQEF43;NCWqo zDH}vR6DfKy^Odg#($DXAwj+pv0RiU{N@`nMeH-lL`3ka8?rs9B3s~8_fXOx|Q6DZu{p1+t0ZD=V*_?Q$4-+NxL7A=XXV{$TcXk02PMa5%>VlD z5{1+a@RF)(YM8;;QU9j-FPn{xjmFmt!PJ?%bH)}ga zBc#+6Cm6#k-;rX%Ic(-p16>hwzAs(3zD7k(v`1@y&MVZUYeofYcwE1pvVgymr#}~f zKp>|`2!W08h$p-z(A;mQxJ@$kLx1dTqyDxQkcOt>j-iY(L50oyp zxXn0Qc`aYYJZOF|rR!Tr7Y-qV$N)%6E@yF8zsTx6!WoRxokC zd@tQF4k(h=n|1U0T0O0L|9Xau2ozX7P43iO3dm>cX^r?wv%tDsC9A$uRgYaf1gtbc zJI=jk>{JE&6+1(mLt~@OXHU^+zw!X|E>U_Esv-LMF0z;hlx?(B;`$4Br0i#-*YAFno}NE;pt{r{ygax{?l{hwMA7fR_d_BNE5a*Ox$;aHBO{`Fn`{0%C}2Hlx90eFBk=N*y6BSg;bksT!Xsslh6m|Us4>~jDAaZN%O literal 0 HcmV?d00001 diff --git a/doc/media/graphs/flow_filter2.graphml b/doc/media/graphs/flow_filter2.graphml new file mode 100644 index 00000000..beec03af --- /dev/null +++ b/doc/media/graphs/flow_filter2.graphml @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <E> + r + + + + + + + + + + + + + + + + + <TDepValue1> + dep1 + + + + + + + + + + + + + + + + + <TDepValueN> + + + + + + + + + depN + + + + + + + + + + + + + + + + + ... + + + + + + + + + + + + + + + + + <E> + source + + + + + + + + + + + + + + + + + [e1 if func(e1,v1,...,vN), ...] + + + + + + + + + + + + v1 + + + + + + + + + + + + + vN + + + + + + + + + + + + + [e1,...] + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/graphs/flow_filter2.png b/doc/media/graphs/flow_filter2.png new file mode 100644 index 0000000000000000000000000000000000000000..ae73e1fa178a0016e6d2ae3d0314dd96f8daa67a GIT binary patch literal 8051 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 literal 0 HcmV?d00001 diff --git a/doc/media/graphs/flow_flatten.graphml b/doc/media/graphs/flow_flatten.graphml new file mode 100644 index 00000000..ace5b909 --- /dev/null +++ b/doc/media/graphs/flow_flatten.graphml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <S> + r + + + + + + + + + + + + + + + + + <S> + inner + + + + + + + + + + + + + + + + + <Signal<S>> + + + + + + + + + outer + + + + + + + + + + + + + + + + + v = x + + + + + + + + + + + + x + + + + + + + + + + + + + inner + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/graphs/flow_flatten.png b/doc/media/graphs/flow_flatten.png new file mode 100644 index 0000000000000000000000000000000000000000..561430cc1887dfd24f0a8e8f86ebea7de6154c10 GIT binary patch literal 4622 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 F{{`srXvhEn literal 0 HcmV?d00001 diff --git a/doc/media/graphs/flow_hold.graphml b/doc/media/graphs/flow_hold.graphml new file mode 100644 index 00000000..16f1c40e --- /dev/null +++ b/doc/media/graphs/flow_hold.graphml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <S> + r + + + + + + + + + + + + + + + + + <E> + events + + + + + + + + + + + + + + + + + INIT: v = init +STEP: v = e + + + + + + + + + + + + [e,...] + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/graphs/flow_hold.png b/doc/media/graphs/flow_hold.png new file mode 100644 index 0000000000000000000000000000000000000000..544e59cd8ae0166c985006cb12101007748548fa GIT binary patch literal 4216 zcmcgwc{r47AATJtZKjeEO_nH>ETzU8+AP_!rP3h`C8OaG#>mJyMKYF=efw%cmdH$` ztR-XYA@te?V<(e+%rL&EPTzITcYXhV*X4CR^E}V>ywCi8_wT;%-#ofxW`NkXe;Wir z2qVMumJsyIZZMwM@(cKXsy?|Af_CW|oj+sk-}ANK@s{;?dbQ(0F{evydRt&AY0EaL z;W&{=0qj(0a9BwAwljy2&kc>A3^*B|Kg%C<=}fp}h~cd;&&Hn1LPeGr=bky$a-Qz$ z8<3K|V3A{wlpaP%A4G2~Vs3VzcN?UjT}k5y|4gb+ooh;+TOUn})lhZb1yN8n=b&H= zB!z_N0uY7o$57%I2+a?{kRA$(M*J9l+x6c&Ao{~l=)ZQ*>p8?5DSUL=h8q^kD0c6D zg^BPTP1Ex8_3`=gC)9Bg+1%WmV?EWBq*iLpmoLi}#o=7em8k8I= zwFB2_fi_dawU52WnjX0@MKH#}ZZX94^z@#do`!}7q}mF(Yii^q+_VErahr-5(#;fdMV zg52D2JC$B2Q3Mt}K*2H_;uH#>5KDuO72o6fa#2v01!^~Ug{6Gth=$+n^>{eUJw863 zV|+B&Et&YKt*!0TCzWGJaz%e(QBe_<+9CUEV&y*~_Rsh^?^$6nu!;x#X8Yy@A-W4{ z`D?El*>h%~qE{7?x(WNwj~KbQxCqt}iA0=EtN%*BpY!RPEvaJDGe%H!3g&>Ok34Ny zT+4UQ$J0?u5yiwm{dYqnQpnZ}-3(nqkl;r#U6jnu^~DbJqLR_k(bn|S3))cP3?^0G zOIm*jfkf^3l*V}NW|hAXBV(Yw6OuiJTwh-|PEaBqWPfB5jOiReIBzdKNlBa0(my?v{pKh&I}W+1@f za7g@m;yQuqK|Z{-)x|5iK7m*)Rucc-2YK7woz-8arlR8cZScLi7J$%GT~h|e#GqX` zj8(~1GBb##Rch7N)<$OSP&o)2E^q#*o-&u++1WYQAhDiWCAu&%N8=h2iRdm%2M5AfQ_=`yzuh4f!g2>> zQjF0KWP2{pz~p9>lf2~Ws>ozjGxsv3B7(Qp(*{{w)Cmc^Hgh5)U1V*(QTf7!3n^;Y$PNSo;l-Kl zFN2S=8D2|hg%m1;$uo~wLL2htS23YmDPPwuFc7NvjLrvsHUc-HTE;N@7!ro?=&1Z zY=6bz-e!3^MldpHX0FVSCClo0>{XSOqsk*>`go# z&me59zc940umA*cQidsX=|JqZ80Mrz7|8A12Ay(Ko%p zz`Kn-z-`Kd4k4jU(Zr%OU4o8Cer|4ZIbI3OBohJ7yE{8;L+T>M!8e_ZBa#kM${)11 zw*w}$$~H6oAl_0MK;es~V)l%XL1HKekq|B!k#Ts36w;OdKem7_5RCcz`HxfhJ7V7> z_tQuGBhTOS{WI6#R7yo@ zL@XGC_T1pD12uDmOK}TX9$-DmqyOFATdH08SFH@Aj;%Hmf)YdEQlH88PaEUe<{3)) z82pv7MbAi5P+}dw9Pde5T)*Gf!_yQ7bGhWrNHK z$YR8!i3xaC#mdz5G{WSIrphU5Tld1Zu5P&?3|1VV;2HzV;5sn<|CrnB&Snw%)l}*Q zKuxSI+=ZrN^;o*9{0t4BUEszi%fD%1f9Y2s9~G!psi1rRisEGi*nU? zK_9E1c%Q0DO3lQG(-Sa@OWG!5V!9h-m4V-cro~3X3bzIn-=4Ye_WDhF?W7tb(ED|- zm~&1@kXG+`G_z)>Zu#${;TS!hD-&FfgwyBiUrqG>s9Bdhe!JxOyrx$@5K9n!-Q~Lx z50!0fZRL>B-7veT;?%=Iw!NNT(An7D9Woz-^0Vh z#EEi!LDYlkTVP+2T=_gNPeDn{c@Hmk=jU66xy@->Y7(I?jq#b5IsPUO3))4ET^2}b z;2e`2OyKHh!sOPvQnXC6&?r4*M-$%Iqjf-N+8}3>{)eeS^uMu-6mJq3LM( zqen@IXar0TG%``YoPQ-I4EGmrT3QLmu97ZhIyI-Hq;R=huwO=U^eFPpo&MZ{ z0<#^*k>oW67v~p%Cmn5htK%7J*nvP`h#a?%EsT4dS9R(qRTTw+3E-?j)D)0_jgI#L z58_lB!6*#n*>ChviRc72#ibFrQQo+1&4@AV^L$(PHz&2z!KZ z0?90!k6pzfW#r`H8Ey;!12Fxf|5BZO-{OH1kegdTJu?T+Dt5g*0j!;E(kBoI-rfuG zigvP^$kjC*@_2SmULGL!*C^<*WUtfYT7eYj^_V8gIJj!nnp+yCx2_*Q2%Y$ zigwom)uwiC=?ANrmW@}uHc?nK_Q>ha(P95NjaqCC>XUm!S36q}t7QnT_ZEcRiznFAj zPO7N^SyR2)@?2Sr%M2J!_F9jR7qJt%5#U0us;UCX$j_evHtd|TLuDY&+A$)^vDANV z7#MdaxO7wR?i<(8+(H(P4ndXJy*&3yY7*BGyOPC4R#TE=f13M*y6hG%7EIpS-PNTimQmdDxP$&|m^ u?C%}--%a<=;m;QQ?}dN#mRXS-o90PwL0N7ko!}P*$moLE`GT`ILjDc@!D9IU literal 0 HcmV?d00001 diff --git a/doc/media/graphs/flow_iterate.graphml b/doc/media/graphs/flow_iterate.graphml new file mode 100644 index 00000000..ac293f3a --- /dev/null +++ b/doc/media/graphs/flow_iterate.graphml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <S> + r + + + + + + + + + + + + + + + + + <E> + events + + + + + + + + + + + + + + + + + INIT: v = init +STEP: v = func(e,v) + + + + + + + + + + + + [e,...] + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/graphs/flow_iterate.png b/doc/media/graphs/flow_iterate.png new file mode 100644 index 0000000000000000000000000000000000000000..5f2e08e9b166ebfdb60f894b254efed6a0511516 GIT binary patch literal 4677 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <S> + r + + + + + + + + + + + + + + + + + <TDepValue1> + dep1 + + + + + + + + + + + + + + + + + <TDepValueN> + + + + + + + + + depN + + + + + + + + + + + + + + + + + ... + + + + + + + + + + + + + + + + + <E> + events + + + + + + + + + + + + + + + + + INIT: v = init +STEP: v = func(e,v,v1,...,vN) + + + + + + + + + + + + v1 + + + + + + + + + + + + + vN + + + + + + + + + + + + + [e,...] + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/graphs/flow_iterate2.png b/doc/media/graphs/flow_iterate2.png new file mode 100644 index 0000000000000000000000000000000000000000..8ee567e09a1c4934fc01edb91018fbca5e9055d6 GIT binary patch literal 8052 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 literal 0 HcmV?d00001 diff --git a/doc/media/graphs/flow_makesignal.graphml b/doc/media/graphs/flow_makesignal.graphml new file mode 100644 index 00000000..74177539 --- /dev/null +++ b/doc/media/graphs/flow_makesignal.graphml @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <S> + r + + + + + + + + + + + + + + + + + <TValue1> + arg1 + + + + + + + + + + + + + + + + + <TValue2> + + + + + + + + + arg2 + + + + + + + + + + + + + + + + + <TValueN> + + + + + + + + + argN + + + + + + + + + + + + + + + + + ... + + + + + + + + + + + + + + + + + v = func(v1,v2,...,vN) + + + + + + + + + + + + v1 + + + + + + + + + + + + + v2 + + + + + + + + + + + + + vN + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/graphs/flow_makesignal.png b/doc/media/graphs/flow_makesignal.png new file mode 100644 index 0000000000000000000000000000000000000000..09412e5f5ee09c9f4b8476078fbd40e246194567 GIT binary patch literal 6945 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 Pf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <E> + r + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [...] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/graphs/flow_makesource.png b/doc/media/graphs/flow_makesource.png new file mode 100644 index 0000000000000000000000000000000000000000..89423d5ed5837cdb8637de68f5c74e10e8231d48 GIT binary patch literal 1521 zcmY*ZeLT~79RH%zQlnQVrm0mPZk0TQal>xPh7Mz7od}tSY@VWRn#Cb1C(}-Q={zKH zB-BWh$9gpiNu`=4u@2Q48l&@czv*?={qg<2-rv{v^ZI<=pQra3cQ;44+9ovs0NmNh zfee811_-lZ%Ftb9v*ZcDclFK=_Fk<1@d2|i&!eDqD!F<(^>j>jUR-qa*hzQm^5Hkz zv1o6YdO;j_OWb}&%96dDK zr_wc0;i59{iXww@uka&B?$#=G2q}B@_vE^i-CtUqj~Z;={8|TrfJHrh7gs_yivR)- z6MpARLJ*zal?T~kFYx)r=&PN=;o-|as&F%B;2Ij$5yEUeM;AmAoj%r-MurQyw!lgZDn9s6@o{J-oYUjXGT$5 zl)#x_J&R(7LqQYkazjQ+X%y2Wf1L=n&#&H0Y7e-0%8CW2u#d z(25gS9%@ZMhTw*U`GG^BagQIRTRI|Q5%C#<-uNt45vVgllGGZaVd_yfVWWqzAtFVS zs47sE6@~ta@tE6|$V2kl7s8foI@HwjW?5NTBoZlVX|FVb_1gtF9&=tNw!zn}gq-VQ zcuq3etgtc`mF4^778VXRv1g&WiS?0feH9d1P{!pj7K;@b>4pKKFg|^qy1Mk`jUX!Z z3P<2k>8+t_eO)fFp9{qmR(tIfD0L9;iePPXb5lW2nMVIF*i2apor|@QNtF^08N63&iEX_$q2c-{}amA+0xYXItH=0xEQ0G_^F2$ ztjKci1^?&aw#Sd%%e}6$aa9oMZOCObzw=#>P$x$7OxwtgO%Ss6?{e?IVcGa6_<(}RDa1}W>2Stj5YF8XV)P>&87$k>Q8)t=FCMr^y z4=LV^fWbysO zw?$soYAv@Z?@!xlNE&g7Q{w|4g0obTDjyLi7ea8<;<^zQx#Olx>$%Q0L}1xkE1?|> zoPR*9)V=SYc=dj9_bo2G8#q}b3vq>&J{NPtWqm|MQLHD3$X?m@Zx4qX_M$LRh+NRmDz-t_4X zsbS0ij-Q(hT%5z$?K-}^wCHz-ajFOI-{z|vN5FVfC>khS-UF(Z7Q}V%#knnZKFpLq zNrlpZJIt`Kv2pRO((eQ2LnY8u6s^ue{d71n0q(1kI6dZ3FPf6HyZ+EB?;##ENbMsY zG)p~xZ@EL5p=}}`7i%{s_o?D3RLRqeG^9)>Ti$^jk`L89DzN*|uZq_WE*^Q9vx~#w zFr}}KDoEQfrPD4ulZ?BG?nYWY`8E_N$E8;j3!h{>9 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <S> + r + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + v = init + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/graphs/flow_makevar.png b/doc/media/graphs/flow_makevar.png new file mode 100644 index 0000000000000000000000000000000000000000..07349c52aa0f6d03ba7bbc569f8ed2236beedfd8 GIT binary patch literal 1675 zcmZ8idpwj`7(U1)&Cn8UrioH1m6G*!am{5Anp}#?r81Iy2<4K|D5a(v*Q5!nOu0ld zGqu>py~ebnwG!hRle?j*#%0Lt$5y-TALsnu^E=OZ-gDmPoad1D@3TV7tH}cZ&^Fc< zjsQq1A#tRfB+?fe%sBxd<7i{C$2q)>-?7K~W`ugBK*EZssd-t>b20gX23bLH@k9BO z#?@ChoV=%CF6&}wy*0FTP~*}sf8JZ9h+S)T5T*JySB_rCD004PamAF#T}ocZiGfk% zqSd;nGLz6&56_TSs#Y1)*H`vr=lEoc^~8ediOfbDB8b+v4VME6Xh4txTwn?xtAiG^^LK_4Q@+eh z*0#jP#!gR1G|3>Vy|+`JYw5U^9Bn}MEX`yxUlM{3LD@Mu^(&&`g3(bf(>5*W^Pqz= z0=6_dKO61~jdfQ9RwHQr%VE_uzZyjbm!>b4N}x2OP^e{$o)&#T;C<>J7{I8hCB1y# z(UH!TOrX3rAk7hXXFvx)3Y8MZEu~&5&Qv_{b|iSmr|~M=?gAc`y$&}u6UfX~Kbp)G z)OB9yc|0sDPHYvWj<+R=r+Z?3KxMYK<{gKfn&K&bp3r?|!AxI{t)~@qJlCmvGExb^ zW>A;+fc(e}Pm)f2cj46_+^$~7ESG$x=GtOq4tx3Si124c4I5}N@%mObcHum-VCNP+ z8^{zY=X|QNOTWnTTY)9o)NaNgdc-#6@}#*5Xzpp&{0Ih&)fmRSm^axKb49wvNoZs) zfuiEZ3J)R@-4GH2O_J##UmJ%_Mki3VM9J&Sih|M~hK_Yiw|E#PqPgIq4#wZ#UteE8 zBO{|Pjk2?yeTz&c#~)d*rM1Lzb7O_asFcZ?N-r-jS%sCkj~*dd_ucmo1PHl!<-~c) zn-n>mai-^FO!vEH_C50}ZSCzQbM*ZDO~EA-VD>Veq^%n5tSv+YD>2@_U8&D?=J%>o zQXAr7J3KzM_np_1oVoHKb^h{!!70VLv>k!HqUvfhH+3v_3x8DgCGHqXntmf0efKYh zL)Lr>+~(Vd4<8nZM7`%oByoAv%wFSXmJGVc7%2n$76(@n#KUQ)T-ziN_SdKRkrut3 zwAAJT0?m*?FDYqlZ4DO=H~07VJF<>A4a4`FhQ`LmCda$$WRM!d&o>&ll?Fd3EG+!s zc)%vc0lvW43mHEvi1;EB3V5|%v*(lK3FZ*A0i!Zww@~WSYG+xdRBzgSLXAN3n-(18 zN+R)Bf#@=Nih;Eor)mT7DUDU)GeaXkhumOU{mays?(4+(EK0ThKa!*yO zRlM24Eh|#HNohhu&^icT@*GpJfaLw+24bj>h~%@lMVhg@g;5n+5|#Y~+WxqEq`5~+ zDg-rVWFW4Xf)Lz5W~yRjY4R@)VtN{BRtBM?SKHY}w{4>za&Sb=a1(#42kT@& zKwM-|8_2x|6J0-cv8SfO=}{l2iSF*53<{mpG2Bra($x-fv*2ZJ<-}dp+aTz-<1XjU zooh(n#XRFRTI$~pnb=70f54S9H#g_=`C?2*W+=a{xwEru)LnphR!`3{C%OG)-#uCC p|Naa9_3N+J{nfqaM|Xo~m!lkSIVE+xq9UIRu(8}{QD}Db;@^*RA=Cf> literal 0 HcmV?d00001 diff --git a/doc/media/graphs/flow_merge.graphml b/doc/media/graphs/flow_merge.graphml new file mode 100644 index 00000000..ea36b912 --- /dev/null +++ b/doc/media/graphs/flow_merge.graphml @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <E> + r + + + + + + + + + + + + + + + + + <TValue1> + arg1 + + + + + + + + + + + + + + + + + <TValue2> + + + + + + + + + arg2 + + + + + + + + + + + + + + + + + <TValueN> + + + + + + + + + argN + + + + + + + + + + + + + + + + + ... + + + + + + + + + + + + + + + + + [e1,...,e2,...,eN,...] + + + + + + + + + + + + [e1,...] + + + + + + + + + + + + + [e2,...] + + + + + + + + + + + + + [eN,...] + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/graphs/flow_merge.png b/doc/media/graphs/flow_merge.png new file mode 100644 index 0000000000000000000000000000000000000000..79f11631daab7ba7fa0d0ff72429ae9d4c2b64c5 GIT binary patch literal 7452 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 literal 0 HcmV?d00001 diff --git a/doc/media/graphs/flow_monitor.graphml b/doc/media/graphs/flow_monitor.graphml new file mode 100644 index 00000000..a5e67669 --- /dev/null +++ b/doc/media/graphs/flow_monitor.graphml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <S> + r + + + + + + + + + + + + + + + + + <S> + target + + + + + + + + + + + + + + + + + [t] + + + + + + + + + + + + t + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/graphs/flow_monitor.png b/doc/media/graphs/flow_monitor.png new file mode 100644 index 0000000000000000000000000000000000000000..d4f3d67406f88c73faf81cbb5bd3159edc2580c5 GIT binary patch literal 3515 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 bk*an;nx6rp&=WLe&`&3=&0hX^`Ir9yY8=fF literal 0 HcmV?d00001 diff --git a/doc/media/graphs/flow_observe.graphml b/doc/media/graphs/flow_observe.graphml new file mode 100644 index 00000000..66187620 --- /dev/null +++ b/doc/media/graphs/flow_observe.graphml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + r + + + + + + + + + + + + + + + + + <E> + subject + + + + + + + + + + + + + + + + + func(e), ... + + + + + + + + + + + + [e,...] + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/graphs/flow_observe.png b/doc/media/graphs/flow_observe.png new file mode 100644 index 0000000000000000000000000000000000000000..de427401c7a7fe8fa5f9808903758fcda8e5af6d GIT binary patch literal 4412 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 W@4O{T$ber(07E^vZt+#yi2njeGJ+8R literal 0 HcmV?d00001 diff --git a/doc/media/graphs/flow_observe2.graphml b/doc/media/graphs/flow_observe2.graphml new file mode 100644 index 00000000..37013734 --- /dev/null +++ b/doc/media/graphs/flow_observe2.graphml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + r + + + + + + + + + + + + + + + + + <S> + subject + + + + + + + + + + + + + + + + + func(s) + + + + + + + + + + + + s + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/graphs/flow_observe2.png b/doc/media/graphs/flow_observe2.png new file mode 100644 index 0000000000000000000000000000000000000000..82f519c70caeff46206f998976096663cb7732cb GIT binary patch literal 3987 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + r + + + + + + + + + + + + + + + + + <TDepValue1> + dep1 + + + + + + + + + + + + + + + + + <TDepValueN> + + + + + + + + + depN + + + + + + + + + + + + + + + + + ... + + + + + + + + + + + + + + + + + <E> + subject + + + + + + + + + + + + + + + + + func(e,d1,...,dN), ... + + + + + + + + + + + + d1 + + + + + + + + + + + + + dN + + + + + + + + + + + + + [e,...] + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/graphs/flow_observe3.png b/doc/media/graphs/flow_observe3.png new file mode 100644 index 0000000000000000000000000000000000000000..ad86b97679fd95b23fa300064ad29bdb02f175fd GIT binary patch literal 8143 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 LYv~e6;}8D@tAUo4 literal 0 HcmV?d00001 diff --git a/doc/media/graphs/flow_pulse.graphml b/doc/media/graphs/flow_pulse.graphml new file mode 100644 index 00000000..c89f4ee3 --- /dev/null +++ b/doc/media/graphs/flow_pulse.graphml @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <S> + r + + + + + + + + + + + + + + + + + <S> + target + + + + + + + + + + + + + + + + + <E> + trigger + + + + + + + + + + + + + + + + + [t,t,...] + + + + + + + + + + + + t + + + + + + + + + + + + + [...] + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/graphs/flow_pulse.png b/doc/media/graphs/flow_pulse.png new file mode 100644 index 0000000000000000000000000000000000000000..b669ce4e7a2f5ad3624f66d4fe19b1dd1626a1d6 GIT binary patch literal 5700 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 literal 0 HcmV?d00001 diff --git a/doc/media/graphs/flow_snapshot.graphml b/doc/media/graphs/flow_snapshot.graphml new file mode 100644 index 00000000..daf1bc3a --- /dev/null +++ b/doc/media/graphs/flow_snapshot.graphml @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <S> + r + + + + + + + + + + + + + + + + + <S> + target + + + + + + + + + + + + + + + + + <E> + trigger + + + + + + + + + + + + + + + + + INIT: v = t +STEP: v = t + + + + + + + + + + + + t + + + + + + + + + + + + + [...] + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/graphs/flow_snapshot.png b/doc/media/graphs/flow_snapshot.png new file mode 100644 index 0000000000000000000000000000000000000000..db28dfdb457766633da5dede781b59c6a2f85146 GIT binary patch literal 5731 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 Sx&nsB5G{2bwUX<$VgCh%RLar- literal 0 HcmV?d00001 diff --git a/doc/media/graphs/flow_transform.graphml b/doc/media/graphs/flow_transform.graphml new file mode 100644 index 00000000..957fe523 --- /dev/null +++ b/doc/media/graphs/flow_transform.graphml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <T> + r + + + + + + + + + + + + + + + + + <E> + source + + + + + + + + + + + + + + + + + [func(e), ...] + + + + + + + + + + + + [e,...] + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/graphs/flow_transform.png b/doc/media/graphs/flow_transform.png new file mode 100644 index 0000000000000000000000000000000000000000..9e76db2e74adf639c67ce7743b1d320e363c63e4 GIT binary patch literal 4321 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 literal 0 HcmV?d00001 diff --git a/doc/media/graphs/flow_transform2.graphml b/doc/media/graphs/flow_transform2.graphml new file mode 100644 index 00000000..85baa006 --- /dev/null +++ b/doc/media/graphs/flow_transform2.graphml @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <T> + r + + + + + + + + + + + + + + + + + <TDepValue1> + dep1 + + + + + + + + + + + + + + + + + <TDepValueN> + + + + + + + + + depN + + + + + + + + + + + + + + + + + ... + + + + + + + + + + + + + + + + + <E> + source + + + + + + + + + + + + + + + + + [func(e,d1,...,dN), ...] + + + + + + + + + + + + d1 + + + + + + + + + + + + + dN + + + + + + + + + + + + + [e,...] + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/graphs/flow_transform2.png b/doc/media/graphs/flow_transform2.png new file mode 100644 index 0000000000000000000000000000000000000000..0af4bdd1301b2b4207186b8a6e565a3f9855dbeb GIT binary patch literal 7915 zcmZ{J2UHWxvo97vK>|vb2nZt5ix>!1dX?T=K$KpD&_WYMjEIINT|s&i_)tO*y%Ufs z5}LHoL8=tNyZGJzedoRNF6Y2WhTWaIGryUQ(A8F@qPR{$LPA2NuBN0a=8f&V*m9avpp@Yb2}!70xvn~G@;omcVoNU@P%~Rf?BUwzb zlfP6za~?0WQ&=t*AD_d^H!_s> zK%wl5^>sV12^Cg-5BJ-c*qE#h!(VlpTUl9YZ&xv4+dwW3@2;sx>%G-cQ5jCY%PWW2 z+ig94+R=ga=iVZ`zZRv6L&dGJ`|RZM3K~xb(T1dtjPM?EX=QYlXlNR_xEy@=s-8hY ze5K~*R@^b?C@m^NE|--QkIXjjuJu8)z3YGLOFeUFX@6Vz$#rEtLBM&3 zc!xu?F1oPBx(8CL1DEi66>HW^_qx8wWpp^@-i%+`Z-sOCtIGjxO6)Y9y}dZ(qIPH0 zcV6{M_sQ<=?%2(4R!i@9TxHKfyL*jpj$v7{$;b?CXNJcq8HcwQ2#5S5))B$&O`UxS zirFJ@d%MfS_jXyt%R{*?-~C=~u($6_nJ84$7U{r}&9Cp6*F7m&+y@oF!i_Llc-F}Glql4(J*v5OZFa37(7iHrLU{Y9{5Bv zRWX+5s{D$}%x4#IL1|Q`)n{R2Qe1Y4q+O1w#1os(E4%|MYhwjpb69IoUwq*T`?UMM zUmeewttTmpTIhbfQQjIa9DWTJKDgG8q^sGvMMq(SVHQ4u6UN80MA5E7(}kZtc{|Ce zssXDOoE(oZBD4fGKSp@(zkFF?RFpe4Rg>e^PbfUOXCfE*dN@bZoJz8ArPz#2KM&?% z7t_q#dlRh&F?{@Z`v`uAZpS{hqN3u*2kv5B5+oi!kfnN8RJ4Q{=XEz(v0^s>w}Go% zS<)-6Lmx`x|4D0qv({OXIwKU= zV-~-rvmvR2gM&8&3Y+9dhO;p@kh_}t2ftAiALPy)W-8$|-+BJmKOWPvi3tY5xNiIv zIg)Xw(%oIByT5wv?tm*)CJciScMflHm>3zs>#%wT2As>5RZlXq$KGDmkozNdGQ2!8 zUTlG7=Tesm4mfoQJ4*(X+&XFRp45R%VDiXlkf(q?vT7sXm30};tL?_+1aFP^v7^%U z3JPcGC}y-2!N|a%!N2j@zx7$1|L=pcY?zSHX1qvK0gIz^%BSx&mkQ=JQn(g&=)nzA z#dGV#U7Xdi^4WKttT7r4A{f2(b>i&0yjhYE%QB>uL2_#XN=r-2XZ$Jp&cwHG#LS7Q zhNp5eGPwNwPG#zs9BMK06r{umc!@QRmvBc&$j|B}S8we0?O$Sq%leO^6`r9UVtiJ= zssF~!CY;LCR(eb)ynA{ugLZfFrOEDO4?n-t1ohjT@-g{IRV3pC^K6c@ryyllC-w^+ zh>3|+iG5`hvi2Uxsrasa@Dcr{o%AFl?_vRyC{yWd|1SdsTTeqHR5+2?zv<>2!N>;^bS#v|d4M#S%BcIeiLIBuO9$#I9TX+AvyZAf>_fS!>V z{9Y6(F$sz6bZH^OpW6^oMIfe|!zo)JxpA+5&$OU}Wz#$6%sxNr+M4U=bvZsh20S)` zI}!ve0v;IM4pu2xUThrZKJp3-4D|K&RacKV1)HdQy1VDu)HNWi(vI5r#>V@~%HgL` zk7n8;8DmmzY+H?e5z8Q|MhEPw#V0sZ3kvWV#4dl75EcH&%lmG$2=}Nd$Z%IcRmBih zz3o3tz^*ifIR*Ch8lg}tM?Uzg%4D>L&?7<%8rzVkZ3}r40pA@<$3WrNYF2i>%2B6S z|5H+;jo53UmYZK(Of~7U;LIHt={VY39U2@gEGk-{Ez>qOPC$8#SGWy{vx2OC=ZA93%hst^#c~3wF^&H-~MPy=S?d$4_5OcCoQF*Bs-hkT4<=3Pc zHCdl-Kul@y$#Wd{_eW1P39=Ey(Gqj(|V_WT5C*>>)bCFJts%CqV<$HHMiml{(yMw++3Vq zmjv%og3C8YdfUSiL!X89q|@db@xg&C+(Y@LR^}MSJjpTGJ7IT@89DIe^Aw!0S^$rXT#igaW z%jg|0Eje0QeHf#v(d4iy9(-W)1~SkQX_};1eAzLi$SOX1HUoglb28p)TI zS~T9t`4tt$h`rgi$o^U{axyZ!wTOiO&yQGmO>HgC+M~a?GUha5|)%CA&^5 z5#>4%%9(E*376O`DzqXwuZ7}W_FOpI^+eo>BHtM3UX)(7gQPNRS$e+cZ63-aEbyPj z4EnY3nZ=i;aDhl95_mC4Hr=u1Q^M5Llz>IWqdHESOS`+fNpn@5oKjL2#y0>KX%B(m z9%M!RG%a_GDHq1iQRIwdMqQ+Hc=ztzL^YCI>}1DliWi<4oAKsNSE0Kuba^u!8gN@) z118KyAKc^cwcL>lF~grCb-s3^hzn zCcrmi1DnddbMstE7r!p9J1L%3&<|y0BEf_AV>-&37Na@~4%ipNjQKy?b5!f?M6W^VWqODyi`8F^83`sM(+wJakWR@E3|`h`_GcueU??etmk1bkYjiA9J|tzWnp@h(n#i;4s}H zA%E)e^^U}B;hYk*5-*(-eqlZz7r0p86!Sd%yr3{zvC4B!MOz#5J4Y#t@01i8cJRd^ z>gcE9173h=P&SaHXQQQ9_oyg2`iKXj8;u8miwgI+4(#P{_k$j;|gAWLq;L5TO>$! zYWz8vqr0(ey4Of~`d^-y>Z}ZT7q_54GOFi=JA&Sk`&rmxs*v-q%*zz2QI0&ox>qV! z9%8Bt46-IerKIsOf6pbyzYzHrCC?HHk0vLECNr}btbTKfixhUeAgo##3Z!=J?>O4h zk_jCaML&E2C%%d=5nSIGe@TY9cz-2X(X&X9_=+|76Pp}!=nNzA4Cy9UVr z&)Cv%`_WeD?Z2v_1yWM6-^lDvK#vRrtB^qHr*=e>i=DH<9CUKhZ&YY_B7d)#NPoNx zA~cGw+DQCyuZV~U{LCExnlu@;r@^wyE%fr$1sllxGq`;&aF%J4socNk%LD|JkLUZB zu#pheVx@@-lg2!cG2@j+)~shdRX-<{R<7h?;Mq$-vT3X2q3}$~n4M=#t-?Kt$SdN< zjDHsm6V;md@uEW*QSxFwa4cMyoCvf_x_|^NJ6i9th>BWzK+LRbVZY7s=R)DasuOt6|MbrGcH${n<=%wiN-!Ki z0@YKqKI_W_F@*s79N`|;p6ZOBx>!dzC1ZjRVq?pHduPYR#f6&~;GQLs0)k2aQDPO_ z-3MV)v3i72Lk%S6T;@6jvx6i~W+IK-yZCq?Kfj)BfU+JxUT6vx=D}^DH|%XX;aqa;+lcUw$cyG<=`TzNloPzF#HQ z`mV?7D6wWBChCK0gIoZV+cKv|Ag@pryk)_%*9KsHsojmgmAi2~h*{M_nl84z5t9Ow z8lqYi{F9TLTf7hh9UWf%b8@9~$UG?tGCLan^5x6lAq_!WtECO?!2N*s9xkf&eIjgh zRe*=5I_S{rnm!8*1p)&oUoEEk`q8<+A@U5aKLEOfvmJQ7?+fsG_aI2MSxt0Xd(BAIPAs~@(oHX&DuPtz5et{laNd8Oo zq2Aev5!LI$e$V07IDL!C!VgkdQn2)gHws@3Pm>!>xo%|i_uA9MNyknvic}JpmIsbn zqM(1T>cfZHN7BT8sFf9AvU2gO!8TCKa%4M+52?nW%mQKRKTnbrmVjo#>mf?5vTL5E z50W)PX+{%Nz2;+rkWi^y3QFd3aB&*3+iIWT`)dQa4eP5}FU|>*R~fUa-|hIGbaY_1 z-u96S=(7QK;cpU5E44|_|4kjC#|tqRksxt7J2Raf!d|_t)>>S1ZKw^pE$+EQ?I;<1 z-3=(HCV08VXmoS5Il##&vpK*bwYy7?ii(aMG{2~UZ3&c@S8P+t^>yvBD&$t?zU9A( zuhCN=YG!6;ywXFpwrI0qH0^~5ELR{>`VqW-ZhK|*o?GYBb3iDJ3rg(XJ<{{f@_jRl zW;Z6CILvCi^>2MyY`yH}F*!-cNJ1o0t&oP?kbIYxCMzM~_>|gKNN1U`bnUM6rw3+t z3LG7EeS8SACn0-r>5f(#|06vlgPLXy-8`|_V&*?-uWG(y~P8D0Y&RH z`&T-0BQL`>Ip=<(isdX%?H#htOayU9S=RaMU57rzG&eV24ir~Y?-xhpi6Z~hSNOI! z1w4G^v)WuL8Ek$s%SZ(8J6T$=08Py~_*EecYU214 zMUO#jV`f}dwOJ|!BPbmbi4hSIfZM@INDCAWhuc+~};Ei#56Qr=_b#LM(1(ls<*)aV{TDs-ooTXOf?u8LH#UQ`}A4Bf@mNB(G7Xi24Yd z2aJ1;oAQS%PzfTV;)jsg%r`tS33M3@O5kv~U3D+}A>_&RInJ=RPjNr}*cMKv5BTzO z)(43kF&(A|afjPB5a8O`X%-zd@A~5fR`T59or$vf-8ehQS-r}l&ai7mnMhrcNbdUW9l>-W{6l>tqW#um#Z0x>Owg z>j4IX5wxzW%E<}Or&FA5IN7&I|0*sjIy+ydbd&?YG0jnx=VH{*x8t97+KZrlOifKK zPWNzh!>AR(+z5syCgL%l%zl7L2=3{hQzddlS05NQfT^6N!&y=FEmr=vzcFcHWVE1i z1DJ>=!0Pnr>B09yx2&-V@rlvV#N=e2y$2E7pP{PA?3|plDg!6asH_wRqo0wYgGxv* za~*2O-qUkSd^%_@L6`m!W^i!OcxX3SzS{qA`?0&byN(VuuoMmFg+JdIe${*8GA5^{ z(ms5!($~M%A+vfi{|9yQ&=n!)Ir}250yGsjq}37^Ld4M1AT^uFEdy3zwZiGeyO)AU;k`#aKF?FwDuaibh>MKe;jHQMM&$Z&V3f zpm^Qqe-m|OC3tS@26qNp+EZnsbf)gabM{v%g#*%Uz5Ojq#*FOUyGVc#qS)jxcBw7R z(9d;`x?;(xSv$9LG!t*NGZ)n#Z`5XHX0j(HZ3W;mRE^4SstlwbNC^wG3%m>?rzS+y zg*1R4#Giy(ylsqsuo)&BjIDdyQpG*y)gFQfXLv)`n`5D=)2yDX9MtNd z&L7dfFP%AgdF1Dxff;RfvYn%2r$3*-T{|=W!Lc!BBe8*oy|u9~1r$vO{_gG@Z&dYp z*KjzT-a<3n^wyMVg7M@?kqPI50>R<0rnekjeP`XZ?-8G|p$PQjBZc&Q#z`OaOifKe z$Vo^rrLsPRsb(A5M=CiZ)q>$`=fv;Mg(&<<{42GcS;~zflTt`3~M;?e9_tArOc@DUc5t zmo?0xy?U0GLCb?&X8u2aG<|;DJKB##yyB92NQ5;ob*7XFgKve2`rDb-Z8^ zBbm@DK4@A{S}Ok3)XFOON8`CejljU#sakirQCq)!+DjK7nb6bI1GwZi+w0`+rp@|{s4rLpG4mlPGk3k!7dc$zpjkWYUKuF@S%vAH z!m0yB_+&;OQ1Ed&0B(_^g-q8xrt~pS`?=@l=48`_T*v!35Dcv{Jpe`ApI8*uiH64t zQ`aeyxtSV(>70Q9?T85&-Ef_dl9Fy|C{Fk#;s0Pk>X@wc`--ZsG7pDOJeWA{qJ4z9 zg}<;#fqe7kjavNSxJLuF8fWXNLBqZtp#LQ5 zTMm$&vFP`x9|C>&E40XIt+lmsxz@qey@Yq~EbD{d)_-I+q_l{C2w-%deVi7TEJVp4 z;skO&QzESl`p{LaPx_~H`UU6>eGzhr*YkK=*1PJd z)O+t}Y+@%xHx@=MO+0Hr*d^`ZXw-AHR_UJ%4!1+&nxH=>NDCw zb}AaC2T&VHwAOwN6P(Kt$I?H+f7soZ=DI%Yd!61LJO==*;Gg5^V2G1j5+auOHQE3E z4tX}>2b=W^@>U*6flrNsi+vv`j|~_9Y0fVxad&l{|9B2SXDeqH7a3L^P0h#$$j*e% z!B$o`&=Alkbc3et>ZOZbNaW^3HEsO?r)74H&#Ez)u?Cs!H`-nVO#m-&0ccbXgX%%1 zROUbJoMf@GvN}9G%*n|CEd*2xU`?K;+y|*jSAK0we&G%2!E9kLkg*?eIQ!u0sDIkI@Yq&Y+z{gFPhb%GydL3O3LG3v>Vv%>V!Z literal 0 HcmV?d00001 diff --git a/doc/media/helloworld.graphml b/doc/media/helloworld.graphml new file mode 100644 index 00000000..00b98c9f --- /dev/null +++ b/doc/media/helloworld.graphml @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bothWords + + + + + + + + + + + + + + + + + secondWord + + + + + + + + + + + + + + + + + + + + + + + + + firstWord + + + + + + + + + + + + + + + + + v = "Hello" + " " + "World + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "World" + + + + + + + + + + + + + "Hello" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/helloworld.png b/doc/media/helloworld.png new file mode 100644 index 0000000000000000000000000000000000000000..3e23d09a257e0fde697f5804cdd63b1b4577b1f3 GIT binary patch literal 7126 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 - + - - a + + + - + - + - - b + + + a - + - + - - c + + + b - + - + - - - + + + c - + - + + + + + + + + + + + + + + + + + + + + - - x + + + x - + - + + - + - + + - + - + + - + - + + - + diff --git a/doc/media/reactives1.png b/doc/media/reactives1.png deleted file mode 100644 index 705e0557670b2940842656351e01bfc6e6a01245..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6920 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 OFjOHLil5}o!~O@&A!(xk diff --git a/doc/media/reactives2.graphml b/doc/media/reactives2.graphml index 1c902632..dda20611 100644 --- a/doc/media/reactives2.graphml +++ b/doc/media/reactives2.graphml @@ -15,139 +15,185 @@ - - + - - 2 + + + - + - - + - - 2 + + a - + - - + - - 2 + + b - + - - + - - 1 + + c - + - - + - - 1 + + x - + - - + - - a + + 2 - + - - + - - b + + 2 - + + + + + + + 2 + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + - + - - c + + + @@ -158,14 +204,15 @@ - + - + - - x + + + @@ -176,27 +223,25 @@ - + - - + - + - + - + - - + @@ -204,103 +249,108 @@ - + - - + - + - + - + - - + - + - + - + - - + - + - + - + - - + - + - - + - + - - - + + - + - + - - + - + - + + + + + + + + + + + + + diff --git a/doc/media/reactives2.png b/doc/media/reactives2.png deleted file mode 100644 index f8fab071c131a69d684a81baa5fe33a032682f43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13167 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 diff --git a/doc/media/signals1.graphml b/doc/media/signals1.graphml new file mode 100644 index 00000000..5f2c894a --- /dev/null +++ b/doc/media/signals1.graphml @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + a + + + + + + + + + + + + + + + + + + b + + + + + + + + + + + + + + + + + + c + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/signals1.png b/doc/media/signals1.png new file mode 100644 index 0000000000000000000000000000000000000000..372383bf2d5bef80f80fdfc8fa243f177c7a485d GIT binary patch literal 3869 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 Hrw9K4*hpy? literal 0 HcmV?d00001 diff --git a/doc/media/signals2.graphml b/doc/media/signals2.graphml new file mode 100644 index 00000000..03a90b8a --- /dev/null +++ b/doc/media/signals2.graphml @@ -0,0 +1,358 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + a + + + + + + + + + + + + + + + + + b + + + + + + + + + + + + + + + + + c + + + + + + + + + + + + + + + + + x + + + + + + + + + + + + + + + + + 2 + + + + + + + + + + + + + + + + + 2 + + + + + + + + + + + + + + + + + 2 + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/media/signals2.png b/doc/media/signals2.png new file mode 100644 index 0000000000000000000000000000000000000000..019f747b18dbd5860253d67e48ae8221a661a0a4 GIT binary patch literal 9576 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 literal 0 HcmV?d00001 From b70e052799ab1f5df7891234217a1c1921e3020a Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 15 Jun 2014 11:58:49 +0200 Subject: [PATCH 143/266] Added AsyncTransaction (async equivalent of DoTransaction). --- include/react/Domain.h | 49 ++++- include/react/detail/EngineBase.h | 59 ++++-- include/react/detail/EventBase.h | 2 +- include/react/detail/IReactiveEngine.h | 123 ++++++----- include/react/detail/ReactiveInput.h | 258 ++++++++++++++++++++--- include/react/detail/SignalBase.h | 4 +- include/react/detail/graph/EventNodes.h | 2 +- include/react/detail/graph/SignalNodes.h | 4 +- include/react/engine/PulsecountEngine.h | 6 +- include/react/engine/SubtreeEngine.h | 4 +- include/react/engine/ToposortEngine.h | 20 +- src/engine/PulsecountEngine.cpp | 20 +- src/engine/SubtreeEngine.cpp | 4 +- src/engine/ToposortEngine.cpp | 6 +- 14 files changed, 410 insertions(+), 151 deletions(-) diff --git a/include/react/Domain.h b/include/react/Domain.h index ec7eca03..d41d4631 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -59,6 +59,7 @@ template class Reactor; using REACT_IMPL::TurnFlagsT; +using REACT_IMPL::TransactionStatus; #ifdef REACT_ENABLE_LOGGING using REACT_IMPL::EventLog; @@ -111,19 +112,56 @@ class DomainBase using ReactorT = Reactor; - /////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////// /// DoTransaction - /////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////// template static void DoTransaction(F&& func) { - REACT_IMPL::InputManager::DoTransaction(0, std::forward(func)); + using REACT_IMPL::DomainSpecificInputManager; + DomainSpecificInputManager::Instance().DoTransaction(0, std::forward(func)); } template static void DoTransaction(TurnFlagsT flags, F&& func) { - REACT_IMPL::InputManager::DoTransaction(flags, std::forward(func)); + using REACT_IMPL::DomainSpecificInputManager; + DomainSpecificInputManager::Instance().DoTransaction(flags, std::forward(func)); + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// AsyncTransaction + /////////////////////////////////////////////////////////////////////////////////////////////// + template + static void AsyncTransaction(F&& func) + { + using REACT_IMPL::DomainSpecificInputManager; + DomainSpecificInputManager::Instance() + .AsyncTransaction(0, nullptr, std::forward(func)); + } + + template + static void AsyncTransaction(TurnFlagsT flags, F&& func) + { + using REACT_IMPL::DomainSpecificInputManager; + DomainSpecificInputManager::Instance() + .AsyncTransaction(flags, nullptr, std::forward(func)); + } + + template + static void AsyncTransaction(TransactionStatus& status, F&& func) + { + using REACT_IMPL::DomainSpecificInputManager; + DomainSpecificInputManager::Instance() + .AsyncTransaction(0, &status, std::forward(func)); + } + + template + static void AsyncTransaction(TurnFlagsT flags, TransactionStatus& status, F&& func) + { + using REACT_IMPL::DomainSpecificInputManager; + DomainSpecificInputManager::Instance() + .AsyncTransaction(flags, &status, std::forward(func)); } #ifdef REACT_ENABLE_LOGGING @@ -167,8 +205,9 @@ class DomainInitializer D::Log(); #endif //REACT_ENABLE_LOGGING - D::Engine::Engine(); + D::Engine::Instance(); DomainSpecificObserverRegistry::Instance(); + DomainSpecificInputManager::Instance(); } }; diff --git a/include/react/detail/EngineBase.h b/include/react/detail/EngineBase.h index 3ee59992..50251ecc 100644 --- a/include/react/detail/EngineBase.h +++ b/include/react/detail/EngineBase.h @@ -185,22 +185,24 @@ class TurnQueueManager inline void UnblockSuccessors() { for (const auto& e : merged_) - e.second->Unblock(); + if (e.second != nullptr) + e.second->Unblock(); if (successor_) successor_->blockCondition_.Unblock(); } template - inline bool TryMerge(F&& inputFunc, BlockingCondition& caller) + inline bool TryMerge(F&& inputFunc, BlockingCondition* caller) { if (!isMergeable_) return false; // Only merge if target is still blocked bool merged = blockCondition_.RunIfBlocked([&] { - caller.Block(); - merged_.emplace_back(std::make_pair(std::forward(inputFunc), &caller)); + if (caller) + caller->Block(); + merged_.emplace_back(std::make_pair(std::forward(inputFunc), caller)); }); return merged; @@ -230,7 +232,7 @@ class TurnQueueManager SeqMutexT::scoped_lock lock(seqMutex_); if (tail_) - merged = tail_->TryMerge(std::forward(inputFunc), caller); + merged = tail_->TryMerge(std::forward(inputFunc), &caller); }// ~seqMutex_ if (merged) @@ -239,7 +241,22 @@ class TurnQueueManager return merged; } - inline void StartTurn(QueueEntry& turn) + template + inline bool TryMergeAsync(F&& inputFunc) + { + bool merged = false; + + {// seqMutex_ + SeqMutexT::scoped_lock lock(seqMutex_); + + if (tail_) + merged = tail_->TryMerge(std::forward(inputFunc), nullptr); + }// ~seqMutex_ + + return merged; + } + + inline void EnterQueue(QueueEntry& turn) { {// seqMutex_ SeqMutexT::scoped_lock lock(seqMutex_); @@ -253,7 +270,7 @@ class TurnQueueManager turn.WaitForUnblock(); } - inline void EndTurn(QueueEntry& turn) + inline void ExitQueue(QueueEntry& turn) {// seqMutex_ SeqMutexT::scoped_lock lock(seqMutex_); @@ -273,10 +290,7 @@ class TurnQueueManager /////////////////////////////////////////////////////////////////////////////////////////////////// /// DefaultQueueableTurn /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename TTurnBase -> +template class DefaultQueueableTurn : public TTurnBase, public TurnQueueManager::QueueEntry @@ -301,29 +315,32 @@ class DefaultQueuingEngine : public TTEngineBase public: using TurnT = DefaultQueueableTurn; - void OnTurnAdmissionStart(TurnT& turn) + template + bool TryMergeInput(F&& f, bool isBlocking) { - queueManager_.StartTurn(turn); + if (isBlocking) + return queueManager_.TryMerge(std::forward(f)); + else + return queueManager_.TryMergeAsync(std::forward(f)); } - void OnTurnAdmissionEnd(TurnT& turn) + void ApplyMergedInputs(TurnT& turn) { turn.RunMergedInputs(); } - void OnTurnEnd(TurnT& turn) + void EnterTurnQueue(TurnT& turn) { - queueManager_.EndTurn(turn); + queueManager_.EnterQueue(turn); } - - template - bool TryMerge(F&& f) + + void ExitTurnQueue(TurnT& turn) { - return queueManager_.TryMerge(std::forward(f)); + queueManager_.ExitQueue(turn); } private: - TurnQueueManager queueManager_; + TurnQueueManager queueManager_; }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/EventBase.h b/include/react/detail/EventBase.h index ac2b6afb..907365f6 100644 --- a/include/react/detail/EventBase.h +++ b/include/react/detail/EventBase.h @@ -42,7 +42,7 @@ class EventStreamBase : public ReactiveBase> template void emit(T&& e) const { - InputManager::AddInput( + DomainSpecificInputManager::Instance().AddInput( *reinterpret_cast*>(this->ptr_.get()), std::forward(e)); } diff --git a/include/react/detail/IReactiveEngine.h b/include/react/detail/IReactiveEngine.h index b687b8d6..824a55c1 100644 --- a/include/react/detail/IReactiveEngine.h +++ b/include/react/detail/IReactiveEngine.h @@ -30,27 +30,32 @@ struct IReactiveEngine using NodeT = TNode; using TurnT = TTurn; - void OnNodeCreate(NodeT& node) {} - void OnNodeDestroy(NodeT& node) {} + template + bool TryMergeInput(F&& f, bool isBlocking) { return false; } - void OnNodeAttach(NodeT& node, NodeT& parent) {} - void OnNodeDetach(NodeT& node, NodeT& parent) {} + void ApplyMergedInputs(TurnT& turn) {} + + void EnterTurnQueue(TurnT& turn) {} + void ExitTurnQueue(TurnT& turn) {} void OnTurnAdmissionStart(TurnT& turn) {} void OnTurnAdmissionEnd(TurnT& turn) {} - void OnTurnEnd(TurnT& turn) {} - void OnTurnInputChange(NodeT& node, TurnT& turn) {} - void OnTurnPropagate(TurnT& turn) {} + void OnInputChange(NodeT& node, TurnT& turn) {} + + void Propagate(TurnT& turn) {} + + void OnNodeCreate(NodeT& node) {} + void OnNodeDestroy(NodeT& node) {} + + void OnNodeAttach(NodeT& node, NodeT& parent) {} + void OnNodeDetach(NodeT& node, NodeT& parent) {} void OnNodePulse(NodeT& node, TurnT& turn) {} void OnNodeIdlePulse(NodeT& node, TurnT& turn) {} void OnDynamicNodeAttach(NodeT& node, NodeT& parent, TurnT& turn) {} void OnDynamicNodeDetach(NodeT& node, NodeT& parent, TurnT& turn) {} - - template - bool TryMerge(F&& f) { return false; } }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -66,101 +71,111 @@ struct EngineInterface using NodeT = typename TEngine::NodeT; using TurnT = typename TEngine::TurnT; - static TEngine& Engine() + static TEngine& Instance() { static TEngine engine; return engine; } + template + static bool TryMergeInput(F&& f, bool isBlocking) + { + return Instance().TryMergeInput(std::forward(f), isBlocking); + } + + static void ApplyMergedInputs(TurnT& turn) + { + Instance().ApplyMergedInputs(turn); + } + + static void EnterTurnQueue(TurnT& turn) + { + REACT_LOG(D::Log().template Append(turn.Id())); + Instance().EnterTurnQueue(turn); + } + + static void ExitTurnQueue(TurnT& turn) + { + REACT_LOG(D::Log().template Append(turn.Id())); + Instance().ExitTurnQueue(turn); + } + + static void OnTurnAdmissionStart(TurnT& turn) + { + Instance().OnTurnAdmissionEnd(turn); + } + + static void OnTurnAdmissionEnd(TurnT& turn) + { + Instance().OnTurnAdmissionEnd(turn); + } + + static void OnInputChange(NodeT& node, TurnT& turn) + { + REACT_LOG(D::Log().template Append( + GetObjectId(node), turn.Id())); + Instance().OnInputChange(node, turn); + } + + static void Propagate(TurnT& turn) + { + Instance().Propagate(turn); + } + static void OnNodeCreate(NodeT& node) { REACT_LOG(D::Log().template Append( GetObjectId(node), node.GetNodeType())); - Engine().OnNodeCreate(node); + Instance().OnNodeCreate(node); } static void OnNodeDestroy(NodeT& node) { REACT_LOG(D::Log().template Append( GetObjectId(node))); - Engine().OnNodeDestroy(node); + Instance().OnNodeDestroy(node); } static void OnNodeAttach(NodeT& node, NodeT& parent) { REACT_LOG(D::Log().template Append( GetObjectId(node), GetObjectId(parent))); - Engine().OnNodeAttach(node, parent); + Instance().OnNodeAttach(node, parent); } static void OnNodeDetach(NodeT& node, NodeT& parent) { REACT_LOG(D::Log().template Append( GetObjectId(node), GetObjectId(parent))); - Engine().OnNodeDetach(node, parent); + Instance().OnNodeDetach(node, parent); } static void OnNodePulse(NodeT& node, TurnT& turn) { REACT_LOG(D::Log().template Append( GetObjectId(node), turn.Id())); - Engine().OnNodePulse(node, turn); + Instance().OnNodePulse(node, turn); } static void OnNodeIdlePulse(NodeT& node, TurnT& turn) { REACT_LOG(D::Log().template Append( GetObjectId(node), turn.Id())); - Engine().OnNodeIdlePulse(node, turn); + Instance().OnNodeIdlePulse(node, turn); } static void OnDynamicNodeAttach(NodeT& node, NodeT& parent, TurnT& turn) { REACT_LOG(D::Log().template Append( GetObjectId(node), GetObjectId(parent), turn.Id())); - Engine().OnDynamicNodeAttach(node, parent, turn); + Instance().OnDynamicNodeAttach(node, parent, turn); } static void OnDynamicNodeDetach(NodeT& node, NodeT& parent, TurnT& turn) { REACT_LOG(D::Log().template Append( GetObjectId(node), GetObjectId(parent), turn.Id())); - Engine().OnDynamicNodeDetach(node, parent, turn); - } - - static void OnTurnAdmissionStart(TurnT& turn) - { - REACT_LOG(D::Log().template Append(turn.Id())); - Engine().OnTurnAdmissionStart(turn); - } - - static void OnTurnAdmissionEnd(TurnT& turn) - { - Engine().OnTurnAdmissionEnd(turn); - } - - static void OnTurnEnd(TurnT& turn) - { - REACT_LOG(D::Log().template Append(turn.Id())); - Engine().OnTurnEnd(turn); - } - - static void OnTurnInputChange(NodeT& node, TurnT& turn) - { - REACT_LOG(D::Log().template Append( - GetObjectId(node), turn.Id())); - Engine().OnTurnInputChange(node, turn); - } - - static void OnTurnPropagate(TurnT& turn) - { - Engine().OnTurnPropagate(turn); - } - - template - static bool TryMerge(F&& f) - { - return Engine().TryMerge(std::forward(f)); + Instance().OnDynamicNodeDetach(node, parent, turn); } }; diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index 9eeda84f..679cc2f3 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -17,8 +17,10 @@ #include #include #include +#include #include +#include "tbb/concurrent_queue.h" #include "tbb/concurrent_vector.h" #include "react/detail/Options.h" @@ -160,6 +162,58 @@ class ContinuationHolder template REACT_TLS typename ContinuationHolder::ContinuationT* ContinuationHolder::ptr_(nullptr); +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ContinuationInput +/////////////////////////////////////////////////////////////////////////////////////////////////// +class TransactionStatus +{ +public: + TransactionStatus() = default; + TransactionStatus(const TransactionStatus&) = delete; + TransactionStatus(TransactionStatus&&) = delete; + + inline void Wait() + { + std::unique_lock lock(mutex_); + condition_.wait(lock, [this] { return !isWaiting_; }); + } + +private: + inline void incWaitCount() + { + auto oldVal = waitCount_.fetch_add(1, std::memory_order_relaxed); + + if (oldVal == 0) + {// mutex_ + std::lock_guard scopedLock(mutex_); + isWaiting_ = true; + }// ~mutex_ + } + + + inline void decWaitCount() + { + auto oldVal = waitCount_.fetch_sub(1, std::memory_order_relaxed); + + if (oldVal == 1) + {// mutex_ + std::lock_guard scopedLock(mutex_); + isWaiting_ = false; + condition_.notify_all(); + }// ~mutex_ + } + + + std::atomic waitCount_{ 0 }; + + std::condition_variable condition_; + std::mutex mutex_; + bool isWaiting_{ false }; + + template + friend class InputManager; +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// InputManager /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -167,25 +221,50 @@ template class InputManager { public: + using TransactionFuncT = std::function; + + + struct AsyncItem + { + TurnFlagsT Flags; + TransactionStatus* StatusPtr; + TransactionFuncT Func; + }; + + using AsyncQueueT = tbb::concurrent_bounded_queue; + + InputManager() : + asyncWorker_( [this] { processAsyncQueue(); } ) + { + asyncWorker_.detach(); + } + + AsyncQueueT asyncQueue_; + std::thread asyncWorker_; + using TurnT = typename D::TurnT; using Engine = typename D::Engine; template - static void DoTransaction(TurnFlagsT flags, F&& func) + void DoTransaction(TurnFlagsT flags, F&& func) { // Attempt to add input to another turn. // If successful, blocks until other turn is done and returns. - if (Engine::TryMerge(std::forward(func))) + bool canMerge = (flags & enable_input_merging) != 0; + if (canMerge && Engine::TryMergeInput(std::forward(func), true)) return; bool shouldPropagate = false; TurnT turn( nextTurnId(), flags ); + Engine::EnterTurnQueue(turn); + // Phase 1 - Input admission transactionState_.Active = true; Engine::OnTurnAdmissionStart(turn); func(); + Engine::ApplyMergedInputs(turn); Engine::OnTurnAdmissionEnd(turn); transactionState_.Active = false; @@ -197,15 +276,122 @@ class InputManager // Phase 3 - Propagate changes if (shouldPropagate) - Engine::OnTurnPropagate(turn); + Engine::Propagate(turn); - Engine::OnTurnEnd(turn); + Engine::ExitTurnQueue(turn); postProcessTurn(turn); } + template + void AsyncTransaction(TurnFlagsT flags, TransactionStatus* statusPtr, F&& func) + { + if (statusPtr != nullptr) + statusPtr->incWaitCount(); + + asyncQueue_.push(AsyncItem{ flags, statusPtr, func } ); + } + + void processAsyncQueue() + { + AsyncItem item; + + TransactionStatus* savedStatusPtr = nullptr; + std::vector statusPtrStash; + + bool skipPop = false; + + while (true) + { + // Blocks if queue is empty + if (!skipPop) + asyncQueue_.pop(item); + else + skipPop = false; + + // First try to merge to an existing synchronous item in the queue + bool canMerge = (item.Flags & enable_input_merging) != 0; + if (canMerge && Engine::TryMergeInput(std::move(item.Func), false)) + return; + + bool shouldPropagate = false; + + TurnT turn( nextTurnId(), item.Flags ); + + // Blocks until turn is at the front of the queue + Engine::EnterTurnQueue(turn); + + // Phase 1 - Input admission + transactionState_.Active = true; + Engine::OnTurnAdmissionStart(turn); + + // Input of current item + item.Func(); + + // Merged inputs that arrived while this item was queued + Engine::ApplyMergedInputs(turn); + + // Save status ptr, because item might be re-used for next input + savedStatusPtr = item.StatusPtr; + + // If the current item supports merging, try to add more mergeable inputs + // to this turn + if (canMerge) + { + uint extraCount = 0; + while (extraCount < 100 && asyncQueue_.try_pop(item)) + { + bool canMergeNext = (item.Flags & enable_input_merging) != 0; + if (canMergeNext) + { + item.Func(); + if (item.StatusPtr != nullptr) + statusPtrStash.push_back(item.StatusPtr); + + ++extraCount; + } + else + { + // We already popped an item we could not merge + // Process it in the next iteration + skipPop = true; + + // Break at first item that cannot be merged. + // We only allow merging of continuous ranges. + break; + } + } + } + + Engine::OnTurnAdmissionEnd(turn); + transactionState_.Active = false; + + // Phase 2 - Apply input node changes + for (auto* p : transactionState_.Inputs) + if (p->ApplyInput(&turn)) + shouldPropagate = true; + transactionState_.Inputs.clear(); + + // Phase 3 - Propagate changes + if (shouldPropagate) + Engine::Propagate(turn); + + Engine::ExitTurnQueue(turn); + + postProcessTurn(turn); + + // Decrement transaction status counts of processed items + if (savedStatusPtr != nullptr) + savedStatusPtr->decWaitCount(); + + for (auto* p : statusPtrStash) + p->decWaitCount(); + statusPtrStash.clear(); + } + } + template - static void AddInput(R& r, V&& v) + void AddInput(R& r, V&& v) { if (ContinuationHolder::Get() != nullptr) { @@ -222,7 +408,7 @@ class InputManager } template - static void ModifyInput(R& r, const F& func) + void ModifyInput(R& r, const F& func) { if (ContinuationHolder::Get() != nullptr) { @@ -239,9 +425,9 @@ class InputManager } private: - static std::atomic nextTurnId_; + std::atomic nextTurnId_ { 0 }; - static TurnIdT nextTurnId() + TurnIdT nextTurnId() { auto curId = nextTurnId_.fetch_add(1, std::memory_order_relaxed); @@ -257,55 +443,61 @@ class InputManager std::vector Inputs; }; - static TransactionState transactionState_; + TransactionState transactionState_; // Create a turn with a single input template - static void addSimpleInput(R& r, V&& v) + void addSimpleInput(R& r, V&& v) { TurnT turn( nextTurnId(), 0 ); + Engine::EnterTurnQueue(turn); + Engine::OnTurnAdmissionStart(turn); r.AddInput(std::forward(v)); + Engine::ApplyMergedInputs(turn); Engine::OnTurnAdmissionEnd(turn); if (r.ApplyInput(&turn)) - Engine::OnTurnPropagate(turn); + Engine::Propagate(turn); - Engine::OnTurnEnd(turn); + Engine::ExitTurnQueue(turn); postProcessTurn(turn); } template - static void modifySimpleInput(R& r, const F& func) + void modifySimpleInput(R& r, const F& func) { TurnT turn( nextTurnId(), 0 ); + Engine::EnterTurnQueue(turn); + Engine::OnTurnAdmissionStart(turn); r.ModifyInput(func); + Engine::ApplyMergedInputs(turn); Engine::OnTurnAdmissionEnd(turn); // Return value, will always be true r.ApplyInput(&turn); - Engine::OnTurnPropagate(turn); + Engine::Propagate(turn); - Engine::OnTurnEnd(turn); + Engine::ExitTurnQueue(turn); postProcessTurn(turn); } // This input is part of an active transaction template - static void addTransactionInput(R& r, V&& v) + void addTransactionInput(R& r, V&& v) { r.AddInput(std::forward(v)); transactionState_.Inputs.push_back(&r); } template - static void modifyTransactionInput(R& r, const F& func) + void modifyTransactionInput(R& r, const F& func) { r.ModifyInput(func); transactionState_.Inputs.push_back(&r); @@ -313,24 +505,24 @@ class InputManager // Input happened during a turn - buffer in continuation template - static void addContinuationInput(R& r, const V& v) + void addContinuationInput(R& r, const V& v) { // Copy v ContinuationHolder::Get()->Add( - [&r,v] { addTransactionInput(r, std::move(v)); } + [this,&r,v] { addTransactionInput(r, std::move(v)); } ); } template - static void modifyContinuationInput(R& r, const F& func) + void modifyContinuationInput(R& r, const F& func) { // Copy func ContinuationHolder::Get()->Add( - [&r,func] { modifyTransactionInput(r, func); } + [this,&r,func] { modifyTransactionInput(r, func); } ); } - static void postProcessTurn(TurnT& turn) + void postProcessTurn(TurnT& turn) { turn.template detachObservers(); @@ -339,7 +531,7 @@ class InputManager processContinuations(std::move(turn.continuation_), 0); } - static void processContinuations(typename TurnT::ContinuationT&& cont, TurnFlagsT flags) + void processContinuations(typename TurnT::ContinuationT&& cont, TurnFlagsT flags) { // No merging for continuations flags &= ~enable_input_merging; @@ -349,9 +541,12 @@ class InputManager bool shouldPropagate = false; TurnT turn( nextTurnId(), flags ); + Engine::EnterTurnQueue(turn); + transactionState_.Active = true; Engine::OnTurnAdmissionStart(turn); cont.Execute(); + Engine::ApplyMergedInputs(turn); Engine::OnTurnAdmissionEnd(turn); transactionState_.Active = false; @@ -361,9 +556,9 @@ class InputManager transactionState_.Inputs.clear(); if (shouldPropagate) - Engine::OnTurnPropagate(turn); + Engine::Propagate(turn); - Engine::OnTurnEnd(turn); + Engine::ExitTurnQueue(turn); turn.template detachObservers(); @@ -376,10 +571,17 @@ class InputManager }; template -std::atomic InputManager::nextTurnId_( 0 ); +class DomainSpecificInputManager +{ +public: + DomainSpecificInputManager() = delete; -template -typename InputManager::TransactionState InputManager::transactionState_; + static InputManager& Instance() + { + static InputManager instance; + return instance; + } +}; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/SignalBase.h b/include/react/detail/SignalBase.h index 4e504294..3ba0abb8 100644 --- a/include/react/detail/SignalBase.h +++ b/include/react/detail/SignalBase.h @@ -47,7 +47,7 @@ class SignalBase : public ReactiveBase> template void setValue(T&& newValue) const { - InputManager::AddInput( + DomainSpecificInputManager::Instance().AddInput( *reinterpret_cast*>(this->ptr_.get()), std::forward(newValue)); } @@ -55,7 +55,7 @@ class SignalBase : public ReactiveBase> template void modifyValue(const F& func) const { - InputManager::ModifyInput( + DomainSpecificInputManager::Instance().ModifyInput( *reinterpret_cast*>(this->ptr_.get()), func); } }; diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 7d750123..5b20acaf 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -144,7 +144,7 @@ class EventSourceNode : this->SetCurrentTurn(turn, true, true); changedFlag_ = true; - Engine::OnTurnInputChange(*this, turn); + Engine::OnInputChange(*this, turn); return true; } else diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index 28bd80ed..c9ff4f26 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -136,7 +136,7 @@ class VarNode : if (! Equals(this->value_, newValue_)) { this->value_ = std::move(newValue_); - Engine::OnTurnInputChange(*this, turn); + Engine::OnInputChange(*this, turn); return true; } else @@ -148,7 +148,7 @@ class VarNode : { isInputModified_ = false; - Engine::OnTurnInputChange(*this, turn); + Engine::OnInputChange(*this, turn); return true; } diff --git a/include/react/engine/PulsecountEngine.h b/include/react/engine/PulsecountEngine.h index 6c0ae9ae..2b2c62ed 100644 --- a/include/react/engine/PulsecountEngine.h +++ b/include/react/engine/PulsecountEngine.h @@ -107,8 +107,8 @@ class EngineBase : public IReactiveEngine void OnNodeAttach(Node& node, Node& parent); void OnNodeDetach(Node& node, Node& parent); - void OnTurnInputChange(Node& node, TTurn& turn); - void OnTurnPropagate(TTurn& turn); + void OnInputChange(Node& node, TTurn& turn); + void Propagate(TTurn& turn); void OnNodePulse(Node& node, TTurn& turn); void OnNodeIdlePulse(Node& node, TTurn& turn); @@ -118,7 +118,7 @@ class EngineBase : public IReactiveEngine private: NodeVectT changedInputs_; - empty_task* rootTask_ { new(task::allocate_root()) empty_task }; + empty_task& rootTask_ { *new(task::allocate_root()) empty_task }; task_list spawnList_; }; diff --git a/include/react/engine/SubtreeEngine.h b/include/react/engine/SubtreeEngine.h index fafd9bd5..ca2329ba 100644 --- a/include/react/engine/SubtreeEngine.h +++ b/include/react/engine/SubtreeEngine.h @@ -138,8 +138,8 @@ class EngineBase : public IReactiveEngine void OnNodeAttach(Node& node, Node& parent); void OnNodeDetach(Node& node, Node& parent); - void OnTurnInputChange(Node& node, TTurn& turn); - void OnTurnPropagate(TTurn& turn); + void OnInputChange(Node& node, TTurn& turn); + void Propagate(TTurn& turn); void OnNodePulse(Node& node, TTurn& turn); void OnNodeIdlePulse(Node& node, TTurn& turn); diff --git a/include/react/engine/ToposortEngine.h b/include/react/engine/ToposortEngine.h index 546a824a..86aafe43 100644 --- a/include/react/engine/ToposortEngine.h +++ b/include/react/engine/ToposortEngine.h @@ -12,20 +12,13 @@ #include "react/detail/Defs.h" #include -#include -#include -#include -#include -#include #include #include #include #include "tbb/concurrent_vector.h" -#include "tbb/queuing_rw_mutex.h" #include "tbb/spin_mutex.h" -#include "react/common/Concurrency.h" #include "react/common/Containers.h" #include "react/common/TopoQueue.h" #include "react/common/Types.h" @@ -39,15 +32,8 @@ class TurnBase; namespace toposort { using std::atomic; -using std::condition_variable; -using std::function; -using std::mutex; -using std::numeric_limits; -using std::pair; -using std::set; using std::vector; using tbb::concurrent_vector; -using tbb::queuing_rw_mutex; using tbb::spin_mutex; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -138,7 +124,7 @@ class EngineBase : public IReactiveEngine void OnNodeAttach(TNode& node, TNode& parent); void OnNodeDetach(TNode& node, TNode& parent); - void OnTurnInputChange(TNode& node, TTurn& turn); + void OnInputChange(TNode& node, TTurn& turn); void OnNodePulse(TNode& node, TTurn& turn); protected: @@ -154,7 +140,7 @@ class SeqEngineBase : public EngineBase public: using TopoQueueT = TopoQueue>; - void OnTurnPropagate(TTurn& turn); + void Propagate(TTurn& turn); void OnDynamicNodeAttach(SeqNode& node, SeqNode& parent, TTurn& turn); void OnDynamicNodeDetach(SeqNode& node, SeqNode& parent, TTurn& turn); @@ -183,7 +169,7 @@ class ParEngineBase : public EngineBase GetWeightFunctor >; - void OnTurnPropagate(TTurn& turn); + void Propagate(TTurn& turn); void OnDynamicNodeAttach(ParNode& node, ParNode& parent, TTurn& turn); void OnDynamicNodeDetach(ParNode& node, ParNode& parent, TTurn& turn); diff --git a/src/engine/PulsecountEngine.cpp b/src/engine/PulsecountEngine.cpp index 907dfc1e..b5bf3274 100644 --- a/src/engine/PulsecountEngine.cpp +++ b/src/engine/PulsecountEngine.cpp @@ -194,46 +194,46 @@ void EngineBase::OnNodeDetach(Node& node, Node& parent) } template -void EngineBase::OnTurnInputChange(Node& node, TTurn& turn) +void EngineBase::OnInputChange(Node& node, TTurn& turn) { changedInputs_.push_back(&node); node.State = ENodeState::changed; } template -void spawnHelper +void spawnTasks ( - task* rootTask, task_list& spawnList, + task& rootTask, task_list& spawnList, const uint count, TIt start, TIt end, TArgs& ... args ) { - rootTask->set_ref_count(1 + count); + rootTask.set_ref_count(1 + count); for (uint i=0; i < (count - 1); i++) { - spawnList.push_back(*new(rootTask->allocate_child()) + spawnList.push_back(*new(rootTask.allocate_child()) TTask(args ..., start, start + chunk_size)); start += chunk_size; } - spawnList.push_back(*new(rootTask->allocate_child()) + spawnList.push_back(*new(rootTask.allocate_child()) TTask(args ..., start, end)); - rootTask->spawn_and_wait_for_all(spawnList); + rootTask.spawn_and_wait_for_all(spawnList); spawnList.clear(); } template -void EngineBase::OnTurnPropagate(TTurn& turn) +void EngineBase::Propagate(TTurn& turn) { const uint initialTaskCount = (changedInputs_.size() - 1) / chunk_size + 1; - spawnHelper(rootTask_, spawnList_, initialTaskCount, + spawnTasks(rootTask_, spawnList_, initialTaskCount, changedInputs_.begin(), changedInputs_.end()); - spawnHelper>(rootTask_, spawnList_, initialTaskCount, + spawnTasks>(rootTask_, spawnList_, initialTaskCount, changedInputs_.begin(), changedInputs_.end(), turn); changedInputs_.clear(); diff --git a/src/engine/SubtreeEngine.cpp b/src/engine/SubtreeEngine.cpp index f56f0b80..6d03a3db 100644 --- a/src/engine/SubtreeEngine.cpp +++ b/src/engine/SubtreeEngine.cpp @@ -47,7 +47,7 @@ void EngineBase::OnNodeDetach(Node& node, Node& parent) } template -void EngineBase::OnTurnInputChange(Node& node, TTurn& turn) +void EngineBase::OnInputChange(Node& node, TTurn& turn) { processChildren(node, turn); } @@ -148,7 +148,7 @@ class UpdaterTask: public task }; template -void EngineBase::OnTurnPropagate(TTurn& turn) +void EngineBase::Propagate(TTurn& turn) { // Phase 1 while (scheduledNodes_.FetchNext()) diff --git a/src/engine/ToposortEngine.cpp b/src/engine/ToposortEngine.cpp index 60cfd9cc..2db5971c 100644 --- a/src/engine/ToposortEngine.cpp +++ b/src/engine/ToposortEngine.cpp @@ -43,7 +43,7 @@ void EngineBase::OnNodeDetach(TNode& node, TNode& parent) } template -void EngineBase::OnTurnInputChange(TNode& node, TTurn& turn) +void EngineBase::OnInputChange(TNode& node, TTurn& turn) { processChildren(node, turn); } @@ -64,7 +64,7 @@ template class EngineBase>; /// SeqEngineBase /////////////////////////////////////////////////////////////////////////////////////////////////// template -void SeqEngineBase::OnTurnPropagate(TTurn& turn) +void SeqEngineBase::Propagate(TTurn& turn) { while (scheduledNodes_.FetchNext()) { @@ -134,7 +134,7 @@ template class SeqEngineBase>; /// ParEngineBase /////////////////////////////////////////////////////////////////////////////////////////////////// template -void ParEngineBase::OnTurnPropagate(TTurn& turn) +void ParEngineBase::Propagate(TTurn& turn) { while (topoQueue_.FetchNext()) { From acc074f72c1643a3d4add2e76ddebc421bae447d Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 16 Jun 2014 02:26:05 +0200 Subject: [PATCH 144/266] Removed Options.h. --- include/react/Domain.h | 4 +++- include/react/detail/Options.h | 26 -------------------------- project/msvc/CppReact.vcxproj | 1 - project/msvc/CppReact.vcxproj.filters | 3 --- 4 files changed, 3 insertions(+), 31 deletions(-) delete mode 100644 include/react/detail/Options.h diff --git a/include/react/Domain.h b/include/react/Domain.h index d41d4631..0e8bac94 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -14,7 +14,6 @@ #include #include "react/detail/ReactiveInput.h" -#include "react/detail/Options.h" #ifdef REACT_ENABLE_LOGGING #include "react/logging/EventLog.h" @@ -61,6 +60,9 @@ class Reactor; using REACT_IMPL::TurnFlagsT; using REACT_IMPL::TransactionStatus; +//ETurnFlags +using REACT_IMPL::enable_input_merging; + #ifdef REACT_ENABLE_LOGGING using REACT_IMPL::EventLog; #endif //REACT_ENABLE_LOGGING diff --git a/include/react/detail/Options.h b/include/react/detail/Options.h deleted file mode 100644 index 3c93cd16..00000000 --- a/include/react/detail/Options.h +++ /dev/null @@ -1,26 +0,0 @@ - -// 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_OPTIONS_H_INCLUDED -#define REACT_DETAIL_OPTIONS_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ETurnFlags -/////////////////////////////////////////////////////////////////////////////////////////////////// -enum ETurnFlags -{ - enable_input_merging = 1 << 0 -}; - -/******************************************/ REACT_END /******************************************/ - -#endif // REACT_DETAIL_OPTIONS_H_INCLUDED \ No newline at end of file diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index ff75468f..46f68193 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -97,7 +97,6 @@ - diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index 1050b4b4..71168417 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -75,9 +75,6 @@ Header Files\detail - - Header Files\detail - Header Files\detail From 3433c2c7ca024094d00898bd5ab17fe66bd8ebe8 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 16 Jun 2014 02:26:55 +0200 Subject: [PATCH 145/266] Fixed root task init. --- include/react/engine/PulsecountEngine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/react/engine/PulsecountEngine.h b/include/react/engine/PulsecountEngine.h index 2b2c62ed..80edb9ea 100644 --- a/include/react/engine/PulsecountEngine.h +++ b/include/react/engine/PulsecountEngine.h @@ -118,7 +118,7 @@ class EngineBase : public IReactiveEngine private: NodeVectT changedInputs_; - empty_task& rootTask_ { *new(task::allocate_root()) empty_task }; + empty_task& rootTask_ = *new(task::allocate_root()) empty_task; task_list spawnList_; }; From 55d76c10667b0788a46d212b2201b1c6afb647da Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 16 Jun 2014 02:31:43 +0200 Subject: [PATCH 146/266] Refactored continuatons. Having a single active turn means it's no longer necessary to store continuation input and detached observers per turn, but rather per input manager. --- include/react/Observer.h | 2 +- include/react/detail/EngineBase.h | 174 ++++------- include/react/detail/IReactiveEngine.h | 20 +- include/react/detail/ObserverBase.h | 4 +- include/react/detail/ReactiveInput.h | 317 ++++++++++++--------- include/react/detail/graph/ObserverNodes.h | 37 ++- 6 files changed, 270 insertions(+), 284 deletions(-) diff --git a/include/react/Observer.h b/include/react/Observer.h index e808f8c8..c715f4df 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -248,7 +248,7 @@ void DetachAllObservers(const TSubject& subject) /////////////////////////////////////////////////////////////////////////////////////////////////// inline void DetachThisObserver() { - REACT_IMPL::GlobalObserverState<>::ShouldDetach = true; + REACT_IMPL::ThreadLocalObserverState<>::ShouldDetach = true; } /******************************************/ REACT_END /******************************************/ diff --git a/include/react/detail/EngineBase.h b/include/react/detail/EngineBase.h index 50251ecc..1b1b44cf 100644 --- a/include/react/detail/EngineBase.h +++ b/include/react/detail/EngineBase.h @@ -17,7 +17,6 @@ #include #include -#include "tbb/concurrent_vector.h" #include "tbb/queuing_mutex.h" #include "react/common/Concurrency.h" @@ -26,7 +25,6 @@ #include "react/detail/IReactiveNode.h" #include "react/detail/IReactiveEngine.h" #include "react/detail/ObserverBase.h" -#include "react/detail/Options.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -36,74 +34,6 @@ template class TurnBase { -public: - TurnBase(TurnIdT id, TurnFlagsT flags); - - TurnIdT Id() const; - - void QueueForDetach(IObserver& obs); - - template - friend class InputManager; - - template - friend class ContinuationHolder; - -private: - using ContinuationT = ContinuationInput; - - template - void detachObservers(); -}; - -// Not thread-safe -template <> -class TurnBase -{ -public: - TurnBase(TurnIdT id, TurnFlagsT flags) : - id_( id ) - {} - - TurnIdT Id() const { return id_; } - - void QueueForDetach(IObserver& obs) - { - detachedObservers_.push_back(&obs); - } - - template - friend class InputManager; - - template - friend class ContinuationHolder; - -private: - using ObsVectT = std::vector; - using ContinuationT = ContinuationInput; - - TurnIdT id_; - - template - void detachObservers() - { - - auto& registry = DomainSpecificObserverRegistry::Instance(); - - for (auto* o : detachedObservers_) - registry.Unregister(o); - - detachedObservers_.clear(); - } - - ObsVectT detachedObservers_; - ContinuationT continuation_; -}; - -// Thread-safe -template <> -class TurnBase -{ public: TurnBase(TurnIdT id, TurnFlagsT flags) : id_( id ) @@ -111,45 +41,8 @@ class TurnBase TurnIdT Id() const { return id_; } - void QueueForDetach(IObserver& obs) - { - // Allocation of concurrent vector is not cheap -> create on demand - std::call_once(detachedObserversInit_, [this] { - detachedObserversPtr_.reset(new ObsVectT()); - }); - - detachedObserversPtr_->push_back(&obs); - } - - template - friend class InputManager; - - template - friend class ContinuationHolder; - private: - using ObsVectT = tbb::concurrent_vector; - using ContinuationT = ContinuationInput; - TurnIdT id_; - - template - void detachObservers() - { - if (detachedObserversPtr_ != nullptr) - { - auto& registry = DomainSpecificObserverRegistry::Instance(); - - for (auto* o : *detachedObserversPtr_) - registry.Unregister(o); - - detachedObserversPtr_->clear(); - } - } - - std::once_flag detachedObserversInit_; - std::unique_ptr detachedObserversPtr_; - ContinuationT continuation_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -179,21 +72,33 @@ class TurnQueueManager inline void RunMergedInputs() const { for (const auto& e : merged_) - e.first(); + e.InputFunc(); } inline void UnblockSuccessors() { + // Release merged for (const auto& e : merged_) - if (e.second != nullptr) - e.second->Unblock(); + { + // Note: Since a merged input is either sync or async, + // either cond or status will be null + // Sync + if (e.Cond != nullptr) + e.Cond->Unblock(); + + // Async + else if (e.Status != nullptr) + TransactionStatusInterface::DecrementWaitCount(*e.Status); + } + + // Release next thread in queue if (successor_) successor_->blockCondition_.Unblock(); } template - inline bool TryMerge(F&& inputFunc, BlockingCondition* caller) + inline bool TryMerge(F&& inputFunc, BlockingCondition* caller, TransactionStatus* status) { if (!isMergeable_) return false; @@ -202,18 +107,32 @@ class TurnQueueManager bool merged = blockCondition_.RunIfBlocked([&] { if (caller) caller->Block(); - merged_.emplace_back(std::make_pair(std::forward(inputFunc), caller)); + merged_.emplace_back(std::forward(inputFunc), caller, status); }); return merged; } private: - using MergedDataVectT = - std::vector< - std::pair< - std::function, - BlockingCondition*>>; + struct MergedData + { + template + MergedData(F&& func, BlockingCondition* cond, TransactionStatus* status) : + InputFunc( std::forward(func) ), + Cond( cond ), + Status( status ) + {} + + std::function InputFunc; + + // Blocking condition variable for sync merged + BlockingCondition* Cond; + + // Status for async merged + TransactionStatus* Status; + }; + + using MergedDataVectT = std::vector; bool isMergeable_; QueueEntry* successor_ = nullptr; @@ -222,7 +141,7 @@ class TurnQueueManager }; template - inline bool TryMerge(F&& inputFunc) + inline bool TryMergeSync(F&& inputFunc) { bool merged = false; @@ -232,7 +151,7 @@ class TurnQueueManager SeqMutexT::scoped_lock lock(seqMutex_); if (tail_) - merged = tail_->TryMerge(std::forward(inputFunc), &caller); + merged = tail_->TryMerge(std::forward(inputFunc), &caller, nullptr); }// ~seqMutex_ if (merged) @@ -242,7 +161,7 @@ class TurnQueueManager } template - inline bool TryMergeAsync(F&& inputFunc) + inline bool TryMergeAsync(F&& inputFunc, TransactionStatus* status) { bool merged = false; @@ -250,7 +169,7 @@ class TurnQueueManager SeqMutexT::scoped_lock lock(seqMutex_); if (tail_) - merged = tail_->TryMerge(std::forward(inputFunc), nullptr); + merged = tail_->TryMerge(std::forward(inputFunc), nullptr, status); }// ~seqMutex_ return merged; @@ -316,12 +235,15 @@ class DefaultQueuingEngine : public TTEngineBase using TurnT = DefaultQueueableTurn; template - bool TryMergeInput(F&& f, bool isBlocking) + bool TryMergeSync(F&& f) + { + return queueManager_.TryMergeSync(std::forward(f)); + } + + template + bool TryMergeAsync(F&& f, TransactionStatus* statusPtr) { - if (isBlocking) - return queueManager_.TryMerge(std::forward(f)); - else - return queueManager_.TryMergeAsync(std::forward(f)); + return queueManager_.TryMergeAsync(std::forward(f), statusPtr); } void ApplyMergedInputs(TurnT& turn) diff --git a/include/react/detail/IReactiveEngine.h b/include/react/detail/IReactiveEngine.h index 824a55c1..cfcfbf6c 100644 --- a/include/react/detail/IReactiveEngine.h +++ b/include/react/detail/IReactiveEngine.h @@ -17,6 +17,11 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Forward declarations +/////////////////////////////////////////////////////////////////////////////////////////////////// +class TransactionStatus; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// IReactiveEngine /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -31,7 +36,10 @@ struct IReactiveEngine using TurnT = TTurn; template - bool TryMergeInput(F&& f, bool isBlocking) { return false; } + bool TryMergeSync(F&& f) { return false; } + + template + bool TryMergeAsync(F&& f, TransactionStatus* status) { return false; } void ApplyMergedInputs(TurnT& turn) {} @@ -78,9 +86,15 @@ struct EngineInterface } template - static bool TryMergeInput(F&& f, bool isBlocking) + static bool TryMergeSync(F&& f) + { + return Instance().TryMergeSync(std::forward(f)); + } + + template + static bool TryMergeAsync(F&& f, TransactionStatus* status) { - return Instance().TryMergeInput(std::forward(f), isBlocking); + return Instance().TryMergeAsync(std::forward(f), status); } static void ApplyMergedInputs(TurnT& turn) diff --git a/include/react/detail/ObserverBase.h b/include/react/detail/ObserverBase.h index c7cb5fc2..4fe43bd5 100644 --- a/include/react/detail/ObserverBase.h +++ b/include/react/detail/ObserverBase.h @@ -40,13 +40,13 @@ class IObserver // tbb tasks are non-preemptible, thread local flag for each worker template -struct GlobalObserverState +struct ThreadLocalObserverState { static REACT_TLS bool ShouldDetach; }; template -REACT_TLS bool GlobalObserverState::ShouldDetach(false); +REACT_TLS bool ThreadLocalObserverState::ShouldDetach(false); /////////////////////////////////////////////////////////////////////////////////////////////////// /// ObserverRegistry diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index 679cc2f3..5d144e79 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -12,6 +12,7 @@ #include "react/detail/Defs.h" #include +#include #include #include #include @@ -21,149 +22,174 @@ #include #include "tbb/concurrent_queue.h" -#include "tbb/concurrent_vector.h" +#include "tbb/enumerable_thread_specific.h" -#include "react/detail/Options.h" +#include "ObserverBase.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Forward declarations +/////////////////////////////////////////////////////////////////////////////////////////////////// +struct IInputNode; +class IObserver; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Common types & constants +/////////////////////////////////////////////////////////////////////////////////////////////////// using TurnIdT = uint; using TurnFlagsT = uint; -struct IInputNode; +enum ETurnFlags +{ + enable_input_merging = 1 << 0 +}; + +enum class EInputContext +{ + none, + transaction, + continuation +}; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// ContinuationInput +/// ThreadLocalInputState +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct ThreadLocalInputState +{ + static REACT_TLS EInputContext Context; +}; + +template +REACT_TLS EInputContext ThreadLocalInputState::Context(EInputContext::none); + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ContinuationData /////////////////////////////////////////////////////////////////////////////////////////////////// template -class ContinuationInput +class ContinuationData { public: - ContinuationInput() = default; - ContinuationInput(const ContinuationInput&) = delete; - ContinuationInput(ContinuationInput&&); - - bool IsEmpty() const; + bool HasInput() const; template - void Add(F&& input); + void AddInput(F&& input); - void Execute(); + void CommitInput(); + + void QueueObserverForDetach(IObserver& obs); + + template + void DetachQueuedObservers(); }; // Not thread-safe template <> -class ContinuationInput +class ContinuationData { -public: using InputClosureT = std::function; using InputVectT = std::vector; + using ObsVectT = std::vector; - ContinuationInput() = default; - ContinuationInput(const ContinuationInput&) = delete; - - ContinuationInput(ContinuationInput&& other) : - bufferedInputs_( std::move(other.bufferedInputs_) ) - {} - - ContinuationInput& operator=(ContinuationInput&& other) +public: + bool HasInput() const { - bufferedInputs_ = std::move(other.bufferedInputs_); - return *this; + return !bufferedInputs_.empty(); } - bool IsEmpty() const { return bufferedInputs_.empty(); } - template - void Add(F&& input) + void AddInput(F&& input) { bufferedInputs_.push_back(std::forward(input)); } - void Execute() + void CommitInput() { for (auto& f : bufferedInputs_) f(); bufferedInputs_.clear(); } + void QueueObserverForDetach(IObserver& obs) + { + detachedObservers_.push_back(&obs); + } + + template + void DetachQueuedObservers() + { + auto& registry = DomainSpecificObserverRegistry::Instance(); + + for (auto* o : detachedObservers_) + registry.Unregister(o); + detachedObservers_.clear(); + } + private: InputVectT bufferedInputs_; + ObsVectT detachedObservers_; }; // Thread-safe template <> -class ContinuationInput +class ContinuationData { -public: using InputClosureT = std::function; - using InputVectT = tbb::concurrent_vector; - - ContinuationInput() = default; - ContinuationInput(const ContinuationInput&) = delete; + using InputVectT = std::vector; + using ObsVectT = std::vector; - ContinuationInput(ContinuationInput&& other) : - bufferedInputsPtr_( std::move(other.bufferedInputsPtr_) ) - {} - - ContinuationInput& operator=(ContinuationInput&& other) +public: + bool HasInput() const { - bufferedInputsPtr_ = std::move(other.bufferedInputsPtr_); - return *this; + return hasInput_; } - bool IsEmpty() const { return bufferedInputsPtr_ == nullptr; } - template - void Add(F&& input) + void AddInput(F&& input) { - // Allocation of concurrent vector is not cheap -> create on demand - std::call_once(bufferedInputsInit_, [this] { - bufferedInputsPtr_.reset(new InputVectT()); - }); - bufferedInputsPtr_->push_back(std::forward(input)); + bufferedInputs_.local().push_back(std::forward(input)); + hasInput_ = true; } - void Execute() + void CommitInput() { - if (bufferedInputsPtr_ != nullptr) + for (auto& v : bufferedInputs_) { - for (auto& f : *bufferedInputsPtr_) + for (auto& f : v) f(); - bufferedInputsPtr_->clear(); + v.clear(); } + hasInput_ = false; } -private: - std::once_flag bufferedInputsInit_; - std::unique_ptr bufferedInputsPtr_ = nullptr; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ContinuationHolder -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ContinuationHolder -{ -public: - using TurnT = typename D::TurnT; - using ContinuationT = ContinuationInput; + void QueueObserverForDetach(IObserver& obs) + { + detachedObservers_.local().push_back(&obs); + } - ContinuationHolder() = delete; + template + void DetachQueuedObservers() + { + auto& registry = DomainSpecificObserverRegistry::Instance(); - static void SetTurn(TurnT& turn) { ptr_ = &turn.continuation_; } - static void Clear() { ptr_ = nullptr; } - static ContinuationT* Get() { return ptr_; } + for (auto& v : detachedObservers_) + { + for (auto* o : v) + registry.Unregister(o); + v.clear(); + } + } private: - static REACT_TLS ContinuationT* ptr_; -}; + tbb::enumerable_thread_specific bufferedInputs_; + tbb::enumerable_thread_specific detachedObservers_; -template -REACT_TLS typename ContinuationHolder::ContinuationT* ContinuationHolder::ptr_(nullptr); + bool hasInput_ = false; +}; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// ContinuationInput +/// TransactionStatus /////////////////////////////////////////////////////////////////////////////////////////////////// class TransactionStatus { @@ -210,8 +236,21 @@ class TransactionStatus std::mutex mutex_; bool isWaiting_{ false }; - template - friend class InputManager; + friend class TransactionStatusInterface; +}; + +class TransactionStatusInterface +{ +public: + inline static void IncrementWaitCount(TransactionStatus& status) + { + status.incWaitCount(); + } + + inline static void DecrementWaitCount(TransactionStatus& status) + { + status.decWaitCount(); + } }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -220,9 +259,8 @@ class TransactionStatus template class InputManager { -public: +private: using TransactionFuncT = std::function; - struct AsyncItem { @@ -232,6 +270,17 @@ class InputManager }; using AsyncQueueT = tbb::concurrent_bounded_queue; + using ContinuationT = ContinuationData; + + struct TransactionState + { + bool Active = false; + std::vector Inputs; + }; + +public: + using TurnT = typename D::TurnT; + using Engine = typename D::Engine; InputManager() : asyncWorker_( [this] { processAsyncQueue(); } ) @@ -239,19 +288,13 @@ class InputManager asyncWorker_.detach(); } - AsyncQueueT asyncQueue_; - std::thread asyncWorker_; - - using TurnT = typename D::TurnT; - using Engine = typename D::Engine; - template void DoTransaction(TurnFlagsT flags, F&& func) { // Attempt to add input to another turn. // If successful, blocks until other turn is done and returns. bool canMerge = (flags & enable_input_merging) != 0; - if (canMerge && Engine::TryMergeInput(std::forward(func), true)) + if (canMerge && Engine::TryMergeSync(std::forward(func))) return; bool shouldPropagate = false; @@ -261,12 +304,14 @@ class InputManager Engine::EnterTurnQueue(turn); // Phase 1 - Input admission - transactionState_.Active = true; + ThreadLocalInputState<>::Context = EInputContext::transaction; + Engine::OnTurnAdmissionStart(turn); func(); Engine::ApplyMergedInputs(turn); Engine::OnTurnAdmissionEnd(turn); - transactionState_.Active = false; + + ThreadLocalInputState<>::Context = EInputContext::none; // Phase 2 - Apply input node changes for (auto* p : transactionState_.Inputs) @@ -280,14 +325,14 @@ class InputManager Engine::ExitTurnQueue(turn); - postProcessTurn(turn); + processContinuationData(flags); } template void AsyncTransaction(TurnFlagsT flags, TransactionStatus* statusPtr, F&& func) { if (statusPtr != nullptr) - statusPtr->incWaitCount(); + TransactionStatusInterface::IncrementWaitCount(*statusPtr); asyncQueue_.push(AsyncItem{ flags, statusPtr, func } ); } @@ -296,7 +341,9 @@ class InputManager { AsyncItem item; - TransactionStatus* savedStatusPtr = nullptr; + TransactionStatus* savedStatusPtr = nullptr; + TurnFlagsT savedFlags = 0; + std::vector statusPtrStash; bool skipPop = false; @@ -311,7 +358,7 @@ class InputManager // First try to merge to an existing synchronous item in the queue bool canMerge = (item.Flags & enable_input_merging) != 0; - if (canMerge && Engine::TryMergeInput(std::move(item.Func), false)) + if (canMerge && Engine::TryMergeAsync(std::move(item.Func), item.StatusPtr)) return; bool shouldPropagate = false; @@ -322,7 +369,8 @@ class InputManager Engine::EnterTurnQueue(turn); // Phase 1 - Input admission - transactionState_.Active = true; + ThreadLocalInputState<>::Context = EInputContext::transaction; + Engine::OnTurnAdmissionStart(turn); // Input of current item @@ -331,8 +379,9 @@ class InputManager // Merged inputs that arrived while this item was queued Engine::ApplyMergedInputs(turn); - // Save status ptr, because item might be re-used for next input + // Save data, because item might be re-used for next input savedStatusPtr = item.StatusPtr; + savedFlags = item.Flags; // If the current item supports merging, try to add more mergeable inputs // to this turn @@ -364,7 +413,8 @@ class InputManager } Engine::OnTurnAdmissionEnd(turn); - transactionState_.Active = false; + + ThreadLocalInputState<>::Context = EInputContext::none; // Phase 2 - Apply input node changes for (auto* p : transactionState_.Inputs) @@ -378,14 +428,14 @@ class InputManager Engine::ExitTurnQueue(turn); - postProcessTurn(turn); + processContinuationData(savedFlags); // Decrement transaction status counts of processed items if (savedStatusPtr != nullptr) - savedStatusPtr->decWaitCount(); + TransactionStatusInterface::DecrementWaitCount(*savedStatusPtr); for (auto* p : statusPtrStash) - p->decWaitCount(); + TransactionStatusInterface::DecrementWaitCount(*p); statusPtrStash.clear(); } } @@ -393,11 +443,11 @@ class InputManager template void AddInput(R& r, V&& v) { - if (ContinuationHolder::Get() != nullptr) + if (ThreadLocalInputState<>::Context == EInputContext::continuation) { addContinuationInput(r, std::forward(v)); } - else if (transactionState_.Active) + else if (ThreadLocalInputState<>::Context == EInputContext::transaction) { addTransactionInput(r, std::forward(v)); } @@ -410,11 +460,11 @@ class InputManager template void ModifyInput(R& r, const F& func) { - if (ContinuationHolder::Get() != nullptr) + if (ThreadLocalInputState<>::Context == EInputContext::continuation) { modifyContinuationInput(r, func); } - else if (transactionState_.Active) + else if (ThreadLocalInputState<>::Context == EInputContext::transaction) { modifyTransactionInput(r, func); } @@ -424,9 +474,12 @@ class InputManager } } -private: - std::atomic nextTurnId_ { 0 }; + ContinuationT& Continuation() + { + return continuation_; + } +private: TurnIdT nextTurnId() { auto curId = nextTurnId_.fetch_add(1, std::memory_order_relaxed); @@ -437,14 +490,6 @@ class InputManager return curId; } - struct TransactionState - { - bool Active = false; - std::vector Inputs; - }; - - TransactionState transactionState_; - // Create a turn with a single input template void addSimpleInput(R& r, V&& v) @@ -463,7 +508,7 @@ class InputManager Engine::ExitTurnQueue(turn); - postProcessTurn(turn); + processContinuationData(0); } template @@ -485,7 +530,7 @@ class InputManager Engine::ExitTurnQueue(turn); - postProcessTurn(turn); + processContinuationData(0); } // This input is part of an active transaction @@ -508,7 +553,7 @@ class InputManager void addContinuationInput(R& r, const V& v) { // Copy v - ContinuationHolder::Get()->Add( + continuation_.AddInput( [this,&r,v] { addTransactionInput(r, std::move(v)); } ); } @@ -517,38 +562,33 @@ class InputManager void modifyContinuationInput(R& r, const F& func) { // Copy func - ContinuationHolder::Get()->Add( + continuation_.AddInput( [this,&r,func] { modifyTransactionInput(r, func); } ); } - void postProcessTurn(TurnT& turn) - { - turn.template detachObservers(); - - // Steal continuation from current turn - if (! turn.continuation_.IsEmpty()) - processContinuations(std::move(turn.continuation_), 0); - } - - void processContinuations(typename TurnT::ContinuationT&& cont, TurnFlagsT flags) + void processContinuationData(TurnFlagsT flags) { // No merging for continuations flags &= ~enable_input_merging; - while (true) + continuation_.template DetachQueuedObservers(); + + while (continuation_.HasInput()) { bool shouldPropagate = false; TurnT turn( nextTurnId(), flags ); Engine::EnterTurnQueue(turn); - transactionState_.Active = true; + ThreadLocalInputState<>::Context = EInputContext::transaction; + Engine::OnTurnAdmissionStart(turn); - cont.Execute(); + continuation_.CommitInput(); Engine::ApplyMergedInputs(turn); Engine::OnTurnAdmissionEnd(turn); - transactionState_.Active = false; + + ThreadLocalInputState<>::Context = EInputContext::none; for (auto* p : transactionState_.Inputs) if (p->ApplyInput(&turn)) @@ -560,14 +600,17 @@ class InputManager Engine::ExitTurnQueue(turn); - turn.template detachObservers(); - - if (turn.continuation_.IsEmpty()) - break; - - cont = std::move(turn.continuation_); + continuation_.template DetachQueuedObservers(); } } + + AsyncQueueT asyncQueue_; + std::thread asyncWorker_; + + std::atomic nextTurnId_ { 0 }; + + TransactionState transactionState_; + ContinuationT continuation_; }; template diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index 60273633..910bafec 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -78,23 +78,26 @@ class SignalObserverNode : public ObserverNode virtual void Tick(void* turnPtr) override { +#ifdef REACT_ENABLE_LOGGING using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); +#endif REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - GlobalObserverState<>::ShouldDetach = false; + ThreadLocalObserverState<>::ShouldDetach = false; - ContinuationHolder::SetTurn(turn); + ThreadLocalInputState<>::Context = EInputContext::continuation; if (auto p = subject_.lock()) func_(p->ValueRef()); - ContinuationHolder::Clear(); + ThreadLocalInputState<>::Context = EInputContext::none; - if (GlobalObserverState<>::ShouldDetach) - turn.QueueForDetach(*this); + if (ThreadLocalObserverState<>::ShouldDetach) + DomainSpecificInputManager::Instance().Continuation() + .QueueObserverForDetach(*this); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); @@ -151,15 +154,17 @@ class EventObserverNode : public ObserverNode virtual void Tick(void* turnPtr) override { +#ifdef REACT_ENABLE_LOGGING using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); +#endif REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - GlobalObserverState<>::ShouldDetach = false; + ThreadLocalObserverState<>::ShouldDetach = false; - ContinuationHolder::SetTurn(turn); + ThreadLocalInputState<>::Context = EInputContext::continuation; if (auto p = subject_.lock()) { @@ -167,10 +172,11 @@ class EventObserverNode : public ObserverNode func_(e); } - ContinuationHolder::Clear(); + ThreadLocalInputState<>::Context = EInputContext::none; - if (GlobalObserverState<>::ShouldDetach) - turn.QueueForDetach(*this); + if (ThreadLocalObserverState<>::ShouldDetach) + DomainSpecificInputManager::Instance().Continuation() + .QueueObserverForDetach(*this); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); @@ -256,9 +262,9 @@ class SyncedObserverNode : public ObserverNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - GlobalObserverState<>::ShouldDetach = false; + ThreadLocalObserverState<>::ShouldDetach = false; - ContinuationHolder::SetTurn(turn); + ThreadLocalInputState<>::Context = EInputContext::continuation; if (auto p = subject_.lock()) { @@ -270,10 +276,11 @@ class SyncedObserverNode : public ObserverNode apply(EvalFunctor_{ e, func_ }, deps_); } - ContinuationHolder::Clear(); + ThreadLocalInputState<>::Context = EInputContext::none; - if (GlobalObserverState<>::ShouldDetach) - turn.QueueForDetach(*this); + if (ThreadLocalObserverState<>::ShouldDetach) + DomainSpecificInputManager::Instance().Continuation() + .QueueObserverForDetach(*this); REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); From e28fedeeade0f8bf847dac4dc9e2503accff5acb Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 16 Jun 2014 15:07:00 +0200 Subject: [PATCH 147/266] Misc fixes. --- include/react/detail/ReactiveInput.h | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index 5d144e79..41f3e4dc 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -272,10 +272,9 @@ class InputManager using AsyncQueueT = tbb::concurrent_bounded_queue; using ContinuationT = ContinuationData; - struct TransactionState + struct TransactionData { - bool Active = false; - std::vector Inputs; + std::vector ChangedInputs; }; public: @@ -359,7 +358,7 @@ class InputManager // First try to merge to an existing synchronous item in the queue bool canMerge = (item.Flags & enable_input_merging) != 0; if (canMerge && Engine::TryMergeAsync(std::move(item.Func), item.StatusPtr)) - return; + continue; bool shouldPropagate = false; @@ -417,10 +416,10 @@ class InputManager ThreadLocalInputState<>::Context = EInputContext::none; // Phase 2 - Apply input node changes - for (auto* p : transactionState_.Inputs) + for (auto* p : transaction_.ChangedInputs) if (p->ApplyInput(&turn)) shouldPropagate = true; - transactionState_.Inputs.clear(); + transaction_.ChangedInputs.clear(); // Phase 3 - Propagate changes if (shouldPropagate) @@ -538,21 +537,21 @@ class InputManager void addTransactionInput(R& r, V&& v) { r.AddInput(std::forward(v)); - transactionState_.Inputs.push_back(&r); + transaction_.ChangedInputs.push_back(&r); } template void modifyTransactionInput(R& r, const F& func) { r.ModifyInput(func); - transactionState_.Inputs.push_back(&r); + transaction_.ChangedInputs.push_back(&r); } // Input happened during a turn - buffer in continuation template void addContinuationInput(R& r, const V& v) { - // Copy v + // Capture v by value to store it in lambda continuation_.AddInput( [this,&r,v] { addTransactionInput(r, std::move(v)); } ); @@ -561,7 +560,7 @@ class InputManager template void modifyContinuationInput(R& r, const F& func) { - // Copy func + // Capture func by value to store it in lambda continuation_.AddInput( [this,&r,func] { modifyTransactionInput(r, func); } ); @@ -590,10 +589,10 @@ class InputManager ThreadLocalInputState<>::Context = EInputContext::none; - for (auto* p : transactionState_.Inputs) + for (auto* p : transaction_.ChangedInputs) if (p->ApplyInput(&turn)) shouldPropagate = true; - transactionState_.Inputs.clear(); + transaction_.ChangedInputs.clear(); if (shouldPropagate) Engine::Propagate(turn); @@ -609,7 +608,7 @@ class InputManager std::atomic nextTurnId_ { 0 }; - TransactionState transactionState_; + TransactionData transaction_; ContinuationT continuation_; }; From 70246160b2c77a9287a0a8776d804edb471551ea Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 16 Jun 2014 15:07:00 +0200 Subject: [PATCH 148/266] Misc fixes. --- include/react/detail/ReactiveInput.h | 29 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index 5d144e79..7c9dcd65 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -272,10 +272,9 @@ class InputManager using AsyncQueueT = tbb::concurrent_bounded_queue; using ContinuationT = ContinuationData; - struct TransactionState + struct TransactionData { - bool Active = false; - std::vector Inputs; + std::vector ChangedInputs; }; public: @@ -314,10 +313,10 @@ class InputManager ThreadLocalInputState<>::Context = EInputContext::none; // Phase 2 - Apply input node changes - for (auto* p : transactionState_.Inputs) + for (auto* p : transaction_.ChangedInputs) if (p->ApplyInput(&turn)) shouldPropagate = true; - transactionState_.Inputs.clear(); + transaction_.ChangedInputs.clear(); // Phase 3 - Propagate changes if (shouldPropagate) @@ -359,7 +358,7 @@ class InputManager // First try to merge to an existing synchronous item in the queue bool canMerge = (item.Flags & enable_input_merging) != 0; if (canMerge && Engine::TryMergeAsync(std::move(item.Func), item.StatusPtr)) - return; + continue; bool shouldPropagate = false; @@ -417,10 +416,10 @@ class InputManager ThreadLocalInputState<>::Context = EInputContext::none; // Phase 2 - Apply input node changes - for (auto* p : transactionState_.Inputs) + for (auto* p : transaction_.ChangedInputs) if (p->ApplyInput(&turn)) shouldPropagate = true; - transactionState_.Inputs.clear(); + transaction_.ChangedInputs.clear(); // Phase 3 - Propagate changes if (shouldPropagate) @@ -538,21 +537,21 @@ class InputManager void addTransactionInput(R& r, V&& v) { r.AddInput(std::forward(v)); - transactionState_.Inputs.push_back(&r); + transaction_.ChangedInputs.push_back(&r); } template void modifyTransactionInput(R& r, const F& func) { r.ModifyInput(func); - transactionState_.Inputs.push_back(&r); + transaction_.ChangedInputs.push_back(&r); } // Input happened during a turn - buffer in continuation template void addContinuationInput(R& r, const V& v) { - // Copy v + // Capture v by value to store it in lambda continuation_.AddInput( [this,&r,v] { addTransactionInput(r, std::move(v)); } ); @@ -561,7 +560,7 @@ class InputManager template void modifyContinuationInput(R& r, const F& func) { - // Copy func + // Capture func by value to store it in lambda continuation_.AddInput( [this,&r,func] { modifyTransactionInput(r, func); } ); @@ -590,10 +589,10 @@ class InputManager ThreadLocalInputState<>::Context = EInputContext::none; - for (auto* p : transactionState_.Inputs) + for (auto* p : transaction_.ChangedInputs) if (p->ApplyInput(&turn)) shouldPropagate = true; - transactionState_.Inputs.clear(); + transaction_.ChangedInputs.clear(); if (shouldPropagate) Engine::Propagate(turn); @@ -609,7 +608,7 @@ class InputManager std::atomic nextTurnId_ { 0 }; - TransactionState transactionState_; + TransactionData transaction_; ContinuationT continuation_; }; From b4762041f0105b13711aa8a754501b6d11420e01 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 16 Jun 2014 16:27:44 +0200 Subject: [PATCH 149/266] Removed DetachAllObservers as it's too dangerous. Having observer groups would be a safer approach. --- examples/src/BasicObservers.cpp | 64 +++------------------------------ include/react/Observer.h | 12 ------- tests/src/ObserverTest.h | 7 ++-- 3 files changed, 9 insertions(+), 74 deletions(-) diff --git a/examples/src/BasicObservers.cpp b/examples/src/BasicObservers.cpp index 4bd6bf61..56e5adf4 100644 --- a/examples/src/BasicObservers.cpp +++ b/examples/src/BasicObservers.cpp @@ -96,65 +96,13 @@ namespace example1 } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 2 - Detaching observers manually (1) +/// Example 2 - Detaching observers manually /////////////////////////////////////////////////////////////////////////////////////////////////// namespace example2 { using namespace std; using namespace react; - REACTIVE_DOMAIN(D) - - class DelayedExecutor - { - public: - USING_REACTIVE_DOMAIN(D) - - template - void ScheduleForExecution(const F& func) - { - Observe(trigger_, [=] (Token) { func(); }); - } - - void Execute() - { - trigger_.Emit(); - DetachAllObservers(trigger_); - } - - private: - EventSourceT<> trigger_ = MakeEventSource(); - }; - - void Run() - { - cout << "Example 2 - Detaching observers manually (1)" << endl; - - DelayedExecutor exec; - - exec.ScheduleForExecution([] { - cout << "one" << endl; - }); - exec.ScheduleForExecution([] { - cout << "two" << endl; - }); - - exec.Execute(); // output: one, two - - exec.Execute(); // no output - - cout << endl; - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 3 - Detaching observers manually (2) -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace example3 -{ - using namespace std; - using namespace react; - REACTIVE_DOMAIN(D) USING_REACTIVE_DOMAIN(D) @@ -162,7 +110,7 @@ namespace example3 void Run() { - cout << "Example 3 - Detaching observers manually (2)" << endl; + cout << "Example 2 - Detaching observers manually" << endl; ObserverT obs = Observe(trigger, [] (Token) { cout << "Triggered!" << endl; @@ -179,9 +127,9 @@ namespace example3 } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 4 - Using scoped observers +/// Example 3 - Using scoped observers /////////////////////////////////////////////////////////////////////////////////////////////////// -namespace example4 +namespace example3 { using namespace std; using namespace react; @@ -193,7 +141,7 @@ namespace example4 void Run() { - cout << "Example 4 - Using scoped observers" << endl; + cout << "Example 3 - Using scoped observers" << endl; // Inner scope { @@ -236,7 +184,5 @@ int main() example3::Run(); - example4::Run(); - return 0; } \ No newline at end of file diff --git a/include/react/Observer.h b/include/react/Observer.h index c715f4df..a5ba41c0 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -231,18 +231,6 @@ auto Observe(const Events& subject, return Observer(rawObsPtr, subject.NodePtr()); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DetachAllObservers -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -void DetachAllObservers(const TSubject& subject) -{ - using D = typename TSubject::DomainT; - - REACT_IMPL::DomainSpecificObserverRegistry::Instance().UnregisterFrom( - subject.NodePtr().get()); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// DetachThisObserver /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tests/src/ObserverTest.h b/tests/src/ObserverTest.h index 9dbec07c..39a8d079 100644 --- a/tests/src/ObserverTest.h +++ b/tests/src/ObserverTest.h @@ -62,7 +62,7 @@ TYPED_TEST_P(ObserverTest, Detach) ASSERT_TRUE(false); }); - Observe(result, [&] (int v) + auto obs2 = Observe(result, [&] (int v) { observeCount2++; @@ -74,7 +74,7 @@ TYPED_TEST_P(ObserverTest, Detach) ASSERT_TRUE(false); }); - Observe(result, [&] (int v) + auto obs3 = Observe(result, [&] (int v) { observeCount3++; @@ -100,7 +100,8 @@ TYPED_TEST_P(ObserverTest, Detach) ASSERT_EQ(observeCount3,2); phase = 2; - DetachAllObservers(result); + obs2.Detach(); + obs3.Detach(); a1 <<= 4; ASSERT_EQ(observeCount1,1); ASSERT_EQ(observeCount2,2); From 568e2494f1989f2c06787fc52e53b600af405219 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 17 Jun 2014 02:32:29 +0200 Subject: [PATCH 150/266] TransactionStatus now has value semantics and uses a shared_ptr to heap allocated state. This is safer and enables conditional waiting. --- include/react/Domain.h | 46 +++++++++++++++++-- include/react/detail/EngineBase.h | 23 +++++----- include/react/detail/IReactiveEngine.h | 9 ++-- include/react/detail/ReactiveInput.h | 63 +++++++++----------------- 4 files changed, 81 insertions(+), 60 deletions(-) diff --git a/include/react/Domain.h b/include/react/Domain.h index 0e8bac94..78ecd5c3 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -11,6 +11,7 @@ #include "react/detail/Defs.h" +#include #include #include "react/detail/ReactiveInput.h" @@ -58,7 +59,6 @@ template class Reactor; using REACT_IMPL::TurnFlagsT; -using REACT_IMPL::TransactionStatus; //ETurnFlags using REACT_IMPL::enable_input_merging; @@ -67,6 +67,45 @@ using REACT_IMPL::enable_input_merging; using REACT_IMPL::EventLog; #endif //REACT_ENABLE_LOGGING +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// TransactionStatus +/////////////////////////////////////////////////////////////////////////////////////////////////// +class TransactionStatus +{ + using StateT = REACT_IMPL::AsyncState; + +public: + TransactionStatus() : + state_( std::make_shared() ) + {} + + TransactionStatus(const TransactionStatus&) = default; + + TransactionStatus(TransactionStatus&& other) : + state_( std::move(other.state_) ) + {} + + TransactionStatus& operator=(const TransactionStatus&) = default; + + TransactionStatus& operator=(TransactionStatus&& other) + { + state_ = std::move(other.state_); + return *this; + } + + inline void Wait() + { + assert(state_.get() != nullptr); + state_->Wait(); + } + +private: + std::shared_ptr state_; + + template + friend class DomainBase; +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// DomainBase /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -154,8 +193,9 @@ class DomainBase static void AsyncTransaction(TransactionStatus& status, F&& func) { using REACT_IMPL::DomainSpecificInputManager; + DomainSpecificInputManager::Instance() - .AsyncTransaction(0, &status, std::forward(func)); + .AsyncTransaction(0, status.state_, std::forward(func)); } template @@ -163,7 +203,7 @@ class DomainBase { using REACT_IMPL::DomainSpecificInputManager; DomainSpecificInputManager::Instance() - .AsyncTransaction(flags, &status, std::forward(func)); + .AsyncTransaction(flags, status.state_, std::forward(func)); } #ifdef REACT_ENABLE_LOGGING diff --git a/include/react/detail/EngineBase.h b/include/react/detail/EngineBase.h index 1b1b44cf..f056b71b 100644 --- a/include/react/detail/EngineBase.h +++ b/include/react/detail/EngineBase.h @@ -88,8 +88,8 @@ class TurnQueueManager e.Cond->Unblock(); // Async - else if (e.Status != nullptr) - TransactionStatusInterface::DecrementWaitCount(*e.Status); + else if (e.StatusPtr != nullptr) + e.StatusPtr->DecWaitCount(); } // Release next thread in queue @@ -98,7 +98,7 @@ class TurnQueueManager } template - inline bool TryMerge(F&& inputFunc, BlockingCondition* caller, TransactionStatus* status) + inline bool TryMerge(F&& inputFunc, BlockingCondition* caller, AsyncStatePtrT&& statusPtr) { if (!isMergeable_) return false; @@ -107,7 +107,7 @@ class TurnQueueManager bool merged = blockCondition_.RunIfBlocked([&] { if (caller) caller->Block(); - merged_.emplace_back(std::forward(inputFunc), caller, status); + merged_.emplace_back(std::forward(inputFunc), caller, std::move(statusPtr)); }); return merged; @@ -117,10 +117,10 @@ class TurnQueueManager struct MergedData { template - MergedData(F&& func, BlockingCondition* cond, TransactionStatus* status) : + MergedData(F&& func, BlockingCondition* cond, AsyncStatePtrT&& statusPtr) : InputFunc( std::forward(func) ), Cond( cond ), - Status( status ) + StatusPtr( std::move(statusPtr) ) {} std::function InputFunc; @@ -129,7 +129,7 @@ class TurnQueueManager BlockingCondition* Cond; // Status for async merged - TransactionStatus* Status; + AsyncStatePtrT StatusPtr; }; using MergedDataVectT = std::vector; @@ -161,7 +161,7 @@ class TurnQueueManager } template - inline bool TryMergeAsync(F&& inputFunc, TransactionStatus* status) + inline bool TryMergeAsync(F&& inputFunc, AsyncStatePtrT&& statusPtr) { bool merged = false; @@ -169,7 +169,8 @@ class TurnQueueManager SeqMutexT::scoped_lock lock(seqMutex_); if (tail_) - merged = tail_->TryMerge(std::forward(inputFunc), nullptr, status); + merged = tail_->TryMerge( + std::forward(inputFunc), nullptr, std::move(statusPtr)); }// ~seqMutex_ return merged; @@ -241,9 +242,9 @@ class DefaultQueuingEngine : public TTEngineBase } template - bool TryMergeAsync(F&& f, TransactionStatus* statusPtr) + bool TryMergeAsync(F&& f, std::shared_ptr&& statusPtr) { - return queueManager_.TryMergeAsync(std::forward(f), statusPtr); + return queueManager_.TryMergeAsync(std::forward(f), std::move(statusPtr)); } void ApplyMergedInputs(TurnT& turn) diff --git a/include/react/detail/IReactiveEngine.h b/include/react/detail/IReactiveEngine.h index cfcfbf6c..a612b7c7 100644 --- a/include/react/detail/IReactiveEngine.h +++ b/include/react/detail/IReactiveEngine.h @@ -9,6 +9,7 @@ #pragma once +#include #include #include "react/detail/Defs.h" @@ -20,7 +21,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// -class TransactionStatus; +class AsyncState; /////////////////////////////////////////////////////////////////////////////////////////////////// /// IReactiveEngine @@ -39,7 +40,7 @@ struct IReactiveEngine bool TryMergeSync(F&& f) { return false; } template - bool TryMergeAsync(F&& f, TransactionStatus* status) { return false; } + bool TryMergeAsync(F&& f, std::shared_ptr&& statusPtr) { return false; } void ApplyMergedInputs(TurnT& turn) {} @@ -92,9 +93,9 @@ struct EngineInterface } template - static bool TryMergeAsync(F&& f, TransactionStatus* status) + static bool TryMergeAsync(F&& f, std::shared_ptr&& statusPtr) { - return Instance().TryMergeAsync(std::forward(f), status); + return Instance().TryMergeAsync(std::forward(f), std::move(statusPtr)); } static void ApplyMergedInputs(TurnT& turn) diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index 7c9dcd65..f55f2e4b 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -189,23 +189,18 @@ class ContinuationData }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// TransactionStatus +/// AsyncState /////////////////////////////////////////////////////////////////////////////////////////////////// -class TransactionStatus +class AsyncState { public: - TransactionStatus() = default; - TransactionStatus(const TransactionStatus&) = delete; - TransactionStatus(TransactionStatus&&) = delete; - inline void Wait() { std::unique_lock lock(mutex_); condition_.wait(lock, [this] { return !isWaiting_; }); } -private: - inline void incWaitCount() + inline void IncWaitCount() { auto oldVal = waitCount_.fetch_add(1, std::memory_order_relaxed); @@ -216,8 +211,7 @@ class TransactionStatus }// ~mutex_ } - - inline void decWaitCount() + inline void DecWaitCount() { auto oldVal = waitCount_.fetch_sub(1, std::memory_order_relaxed); @@ -228,30 +222,15 @@ class TransactionStatus condition_.notify_all(); }// ~mutex_ } - +private: std::atomic waitCount_{ 0 }; - std::condition_variable condition_; std::mutex mutex_; - bool isWaiting_{ false }; - - friend class TransactionStatusInterface; + bool isWaiting_ = false; }; -class TransactionStatusInterface -{ -public: - inline static void IncrementWaitCount(TransactionStatus& status) - { - status.incWaitCount(); - } - - inline static void DecrementWaitCount(TransactionStatus& status) - { - status.decWaitCount(); - } -}; +using AsyncStatePtrT = std::shared_ptr; /////////////////////////////////////////////////////////////////////////////////////////////////// /// InputManager @@ -264,9 +243,9 @@ class InputManager struct AsyncItem { - TurnFlagsT Flags; - TransactionStatus* StatusPtr; - TransactionFuncT Func; + TurnFlagsT Flags; + AsyncStatePtrT StatusPtr; + TransactionFuncT Func; }; using AsyncQueueT = tbb::concurrent_bounded_queue; @@ -328,10 +307,10 @@ class InputManager } template - void AsyncTransaction(TurnFlagsT flags, TransactionStatus* statusPtr, F&& func) + void AsyncTransaction(TurnFlagsT flags, const AsyncStatePtrT& statusPtr, F&& func) { if (statusPtr != nullptr) - TransactionStatusInterface::IncrementWaitCount(*statusPtr); + statusPtr->IncWaitCount(); asyncQueue_.push(AsyncItem{ flags, statusPtr, func } ); } @@ -340,10 +319,10 @@ class InputManager { AsyncItem item; - TransactionStatus* savedStatusPtr = nullptr; - TurnFlagsT savedFlags = 0; + AsyncStatePtrT savedStatusPtr = nullptr; + TurnFlagsT savedFlags = 0; - std::vector statusPtrStash; + std::vector statusPtrStash; bool skipPop = false; @@ -357,7 +336,7 @@ class InputManager // First try to merge to an existing synchronous item in the queue bool canMerge = (item.Flags & enable_input_merging) != 0; - if (canMerge && Engine::TryMergeAsync(std::move(item.Func), item.StatusPtr)) + if (canMerge && Engine::TryMergeAsync(std::move(item.Func), std::move(item.StatusPtr))) continue; bool shouldPropagate = false; @@ -379,7 +358,7 @@ class InputManager Engine::ApplyMergedInputs(turn); // Save data, because item might be re-used for next input - savedStatusPtr = item.StatusPtr; + savedStatusPtr = std::move(item.StatusPtr); savedFlags = item.Flags; // If the current item supports merging, try to add more mergeable inputs @@ -394,7 +373,7 @@ class InputManager { item.Func(); if (item.StatusPtr != nullptr) - statusPtrStash.push_back(item.StatusPtr); + statusPtrStash.push_back(std::move(item.StatusPtr)); ++extraCount; } @@ -431,10 +410,10 @@ class InputManager // Decrement transaction status counts of processed items if (savedStatusPtr != nullptr) - TransactionStatusInterface::DecrementWaitCount(*savedStatusPtr); + savedStatusPtr->DecWaitCount(); - for (auto* p : statusPtrStash) - TransactionStatusInterface::DecrementWaitCount(*p); + for (auto& p : statusPtrStash) + p->DecWaitCount(); statusPtrStash.clear(); } } From 126bbbaf117591eb856dcac4c131d20961dc285e Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 17 Jun 2014 14:25:40 +0200 Subject: [PATCH 151/266] Improved handling of dynamic attach for Pulsecount and Subtree engines. It's no longer fixed to a single dynamic predecessor and doesn't grow the stack for successive shifts. --- include/react/engine/PulsecountEngine.h | 3 +- include/react/engine/SubtreeEngine.h | 15 +++-- src/engine/PulsecountEngine.cpp | 46 ++++++++-------- src/engine/SubtreeEngine.cpp | 73 +++++++++++++------------ 4 files changed, 72 insertions(+), 65 deletions(-) diff --git a/include/react/engine/PulsecountEngine.h b/include/react/engine/PulsecountEngine.h index 80edb9ea..cfcd581b 100644 --- a/include/react/engine/PulsecountEngine.h +++ b/include/react/engine/PulsecountEngine.h @@ -57,7 +57,8 @@ enum class ENodeState { unchanged, changed, - deferred + dyn_defer, + dyn_repeat }; class Node : public IReactiveNode diff --git a/include/react/engine/SubtreeEngine.h b/include/react/engine/SubtreeEngine.h index ca2329ba..077cf826 100644 --- a/include/react/engine/SubtreeEngine.h +++ b/include/react/engine/SubtreeEngine.h @@ -67,6 +67,10 @@ class Node : public IReactiveNode inline void SetDeferredFlag() { flags_.Set(); } inline void ClearDeferredFlag() { flags_.Clear(); } + inline bool IsRepeated() const { return flags_.Test(); } + inline void SetRepeatedFlag() { flags_.Set(); } + inline void ClearRepeatedFlag() { flags_.Clear(); } + inline bool IsInitial() const { return flags_.Test(); } inline void SetInitialFlag() { flags_.Set(); } inline void ClearInitialFlag() { flags_.Clear(); } @@ -96,9 +100,9 @@ class Node : public IReactiveNode NodeVector Successors; ShiftMutexT ShiftMutex; - uint16_t Level { 0 }; - uint16_t NewLevel { 0 }; - uint16_t WaitCount { 0 }; + uint16_t Level = 0; + uint16_t NewLevel = 0; + uint16_t WaitCount = 0; private: enum EFlags : uint16_t @@ -107,6 +111,7 @@ class Node : public IReactiveNode flag_marked, flag_changed, flag_deferred, + flag_repeated, flag_initial, flag_root }; @@ -159,10 +164,10 @@ class EngineBase : public IReactiveEngine TopoQueueT scheduledNodes_; vector subtreeRoots_; - empty_task* rootTask_ { new(task::allocate_root()) empty_task }; + empty_task& rootTask_ = *new(task::allocate_root()) empty_task; task_list spawnList_; - bool isInPhase2_ { false }; + bool isInPhase2_ = false; }; class BasicEngine : public EngineBase {}; diff --git a/src/engine/PulsecountEngine.cpp b/src/engine/PulsecountEngine.cpp index b5bf3274..5059b870 100644 --- a/src/engine/PulsecountEngine.cpp +++ b/src/engine/PulsecountEngine.cpp @@ -112,9 +112,16 @@ class UpdaterTask: public task if (node.Mark() == ENodeMark::should_update) node.Tick(&turn_); - if (node.State == ENodeState::deferred) + // Defer if node was dynamically attached to a predecessor that + // has not pulsed yet + if (node.State == ENodeState::dyn_defer) continue; + // Repeat the update if a node was dynamically attached to a + // predecessor that has already pulsed + while (node.State == ENodeState::dyn_repeat) + node.Tick(&turn_); + // Mark successors for update? bool update = node.State == ENodeState::changed; node.State = ENodeState::unchanged; @@ -253,30 +260,23 @@ void EngineBase::OnNodeIdlePulse(Node& node, TTurn& turn) template void EngineBase::OnDynamicNodeAttach(Node& node, Node& parent, TTurn& turn) -{ - bool shouldTick = false; - - {// parent.ShiftMutex (write) - NodeShiftMutexT::scoped_lock lock(parent.ShiftMutex, true); +{// parent.ShiftMutex (write) + NodeShiftMutexT::scoped_lock lock(parent.ShiftMutex, true); - parent.Successors.Add(node); - - // Has already nudged its neighbors? - if (parent.Mark() == ENodeMark::unmarked) - { - shouldTick = true; - } - else - { - node.State = ENodeState::deferred; - node.SetCounter(1); - node.SetMark(ENodeMark::should_update); - } - }// ~parent.ShiftMutex (write) + parent.Successors.Add(node); - if (shouldTick) - node.Tick(&turn); -} + // Has already nudged its neighbors? + if (parent.Mark() == ENodeMark::unmarked) + { + node.State = ENodeState::dyn_repeat; + } + else + { + node.State = ENodeState::dyn_defer; + node.IncCounter(); + node.SetMark(ENodeMark::should_update); + } +}// ~parent.ShiftMutex (write) template void EngineBase::OnDynamicNodeDetach(Node& node, Node& parent, TTurn& turn) diff --git a/src/engine/SubtreeEngine.cpp b/src/engine/SubtreeEngine.cpp index 6d03a3db..096de5ba 100644 --- a/src/engine/SubtreeEngine.cpp +++ b/src/engine/SubtreeEngine.cpp @@ -85,13 +85,24 @@ class UpdaterTask: public task node.ClearInitialFlag(); node.SetShouldUpdate(false); - // Skip deferred node + // Defer if node was dynamically attached to a predecessor that + // has not pulsed yet if (node.IsDeferred()) { node.ClearDeferredFlag(); continue; } + // Repeat the update if a node was dynamically attached to a + // predecessor that has already pulsed + while (node.IsRepeated()) + { + node.ClearRepeatedFlag(); + node.Tick(&turn_); + } + + node.SetReadyCount(0); + // Mark successors for update? bool update = node.IsChanged(); @@ -107,8 +118,6 @@ class UpdaterTask: public task if (succ->IncReadyCount()) continue; - succ->SetReadyCount(0); - // Heavyweight - spawn new task if (succ->IsHeavyweight()) { @@ -124,11 +133,11 @@ class UpdaterTask: public task if (nodes_.IsFull()) { - splitCount++; + ++splitCount; //Delegate half the work to new task auto& t = *new(task::allocate_additional_child_of(*parent())) - UpdaterTask(*this, SplitTag{}); + UpdaterTask(*this, SplitTag()); spawn(t); } @@ -171,24 +180,24 @@ void EngineBase::Propagate(TTurn& turn) // Phase 2 isInPhase2_ = true; - rootTask_->set_ref_count(1 + subtreeRoots_.size()); + rootTask_.set_ref_count(1 + subtreeRoots_.size()); for (auto* node : subtreeRoots_) { // Ignore if root flag has been cleared because node was part of another subtree if (! node->IsRoot()) { - rootTask_->decrement_ref_count(); + rootTask_.decrement_ref_count(); continue; } - spawnList_.push_back(*new(rootTask_->allocate_child()) + spawnList_.push_back(*new(rootTask_.allocate_child()) UpdaterTask(turn, node)); node->ClearRootFlag(); } - rootTask_->spawn_and_wait_for_all(spawnList_); + rootTask_.spawn_and_wait_for_all(spawnList_); subtreeRoots_.clear(); spawnList_.clear(); @@ -242,36 +251,28 @@ void EngineBase::OnDynamicNodeDetach(Node& node, Node& parent, TTurn& tur template void EngineBase::applyAsyncDynamicAttach(Node& node, Node& parent, TTurn& turn) -{ - bool shouldTick = false; - - {// parent.ShiftMutex (write) - NodeShiftMutexT::scoped_lock lock(parent.ShiftMutex, true); +{// parent.ShiftMutex (write) + NodeShiftMutexT::scoped_lock lock(parent.ShiftMutex, true); - parent.Successors.Add(node); - - // Level recalulation applied when added to topoqueue next time. - // During the async phase 2 it's not needed. - if (node.NewLevel <= parent.Level) - node.NewLevel = parent.Level + 1; - - // Has already nudged its neighbors? - if (! parent.IsMarked()) - { - shouldTick = true; - } - else - { - node.SetDeferredFlag(); - node.SetShouldUpdate(true); + parent.Successors.Add(node); - node.WaitCount = 1; - } - }// ~parent.ShiftMutex (write) + // Level recalulation applied when added to topoqueue next time. + // During the async phase 2 it's not needed. + if (node.NewLevel <= parent.Level) + node.NewLevel = parent.Level + 1; - if (shouldTick) - node.Tick(&turn); -} + // Has already nudged its neighbors? + if (! parent.IsMarked()) + { + node.SetRepeatedFlag(); + } + else + { + node.SetDeferredFlag(); + node.SetShouldUpdate(true); + node.DecReadyCount(); + } +}// ~parent.ShiftMutex (write) template void EngineBase::applyAsyncDynamicDetach(Node& node, Node& parent, TTurn& turn) From 89124867d252bd2718d78c844549b7d85b3fa3cc Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 19 Jun 2014 18:38:56 +0200 Subject: [PATCH 152/266] Removed DetachThisObserver(). Instead, self-detaching observer functions can use a return value to request a detach. Detached observers process further events from the same turn. --- include/react/Observer.h | 65 +++-- include/react/common/Util.h | 24 ++ include/react/detail/ObserverBase.h | 13 +- include/react/detail/graph/ObserverNodes.h | 56 +++-- tests/src/ObserverTest.h | 8 +- tests/src/OperationsTest.h | 272 +++++++++++---------- tests/src/SignalTest.h | 12 +- 7 files changed, 254 insertions(+), 196 deletions(-) diff --git a/include/react/Observer.h b/include/react/Observer.h index a5ba41c0..5bcd9957 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -33,6 +33,8 @@ class SignalPack; template class Events; +using REACT_IMPL::ObserverAction; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Observer /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -89,7 +91,7 @@ class Observer NodeT* nodePtr_; // While the observer handle exists, the subject is not destroyed - SubjectT subject_; + SubjectT subject_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -141,17 +143,27 @@ auto Observe(const Signal& subject, FIn&& func) using REACT_IMPL::IObserver; using REACT_IMPL::SignalObserverNode; using REACT_IMPL::DomainSpecificObserverRegistry; + using REACT_IMPL::AddDefaultReturnValueWrapper; using F = typename std::decay::type; + using R = typename std::result_of::type; + using WrapperT = AddDefaultReturnValueWrapper; + + // If return value of passed function is void, add ObserverAction::next as + // default return value. + using TNode = typename std::conditional< + std::is_same::value, + SignalObserverNode, + SignalObserverNode + >::type; - std::unique_ptr obsPtr{ - new SignalObserverNode{ - subject.NodePtr(), std::forward(func)}}; + std::unique_ptr obsPtr(new TNode( + subject.NodePtr(), std::forward(func))); auto* rawObsPtr = DomainSpecificObserverRegistry::Instance() .Register(std::move(obsPtr), subject.NodePtr().get()); - return Observer{ rawObsPtr, subject.NodePtr() }; + return Observer( rawObsPtr, subject.NodePtr() ); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -169,12 +181,22 @@ auto Observe(const Events& subject, FIn&& func) using REACT_IMPL::IObserver; using REACT_IMPL::EventObserverNode; using REACT_IMPL::DomainSpecificObserverRegistry; + using REACT_IMPL::AddDefaultReturnValueWrapper; using F = typename std::decay::type; - - std::unique_ptr obsPtr{ - new EventObserverNode{ - subject.NodePtr(), std::forward(func)}}; + using R = typename std::result_of::type; + using WrapperT = AddDefaultReturnValueWrapper; + + // If return value of passed function is void, add ObserverAction::next as + // default return value. + using TNode = typename std::conditional< + std::is_same::value, + EventObserverNode, + EventObserverNode + >::type; + + std::unique_ptr obsPtr(new TNode( + subject.NodePtr(), std::forward(func))); auto* rawObsPtr = DomainSpecificObserverRegistry::Instance() .Register(std::move(obsPtr), subject.NodePtr().get()); @@ -199,8 +221,19 @@ auto Observe(const Events& subject, using REACT_IMPL::IObserver; using REACT_IMPL::SyncedObserverNode; using REACT_IMPL::DomainSpecificObserverRegistry; + using REACT_IMPL::AddDefaultReturnValueWrapper; using F = typename std::decay::type; + using R = typename std::result_of::type; + using WrapperT = AddDefaultReturnValueWrapper; + + // If return value of passed function is void, add ObserverAction::next as + // default return value. + using TNode = typename std::conditional< + std::is_same::value, + SyncedObserverNode, + SyncedObserverNode + >::type; struct NodeBuilder_ { @@ -212,9 +245,9 @@ auto Observe(const Events& subject, auto operator()(const Signal& ... deps) -> std::unique_ptr { - return std::unique_ptr { - new SyncedObserverNode { - MySubject.NodePtr(), std::forward(MyFunc), deps.NodePtr() ...}}; + return std::unique_ptr( + new TNode( + MySubject.NodePtr(), std::forward(MyFunc), deps.NodePtr() ... )); } const Events& MySubject; @@ -231,14 +264,6 @@ auto Observe(const Events& subject, return Observer(rawObsPtr, subject.NodePtr()); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DetachThisObserver -/////////////////////////////////////////////////////////////////////////////////////////////////// -inline void DetachThisObserver() -{ - REACT_IMPL::ThreadLocalObserverState<>::ShouldDetach = true; -} - /******************************************/ REACT_END /******************************************/ #endif // REACT_OBSERVER_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/Util.h b/include/react/common/Util.h index 1c85731c..35e8e771 100644 --- a/include/react/common/Util.h +++ b/include/react/common/Util.h @@ -141,6 +141,30 @@ struct AddDummyArgWrapper F MyFunc; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// AddDefaultReturnValueWrapper +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename F, + typename TRet, + TRet return_value +> +struct AddDefaultReturnValueWrapper +{ + template + AddDefaultReturnValueWrapper(FIn&& func) : MyFunc( std::forward(func) ) {} + + template + TRet operator()(TArgs&& ... args) + { + MyFunc(std::forward(args) ...); + return return_value; + } + + F MyFunc; +}; + /****************************************/ REACT_IMPL_END /***************************************/ // Expand args by wrapping them in a dummy function diff --git a/include/react/detail/ObserverBase.h b/include/react/detail/ObserverBase.h index 4fe43bd5..ec0f987f 100644 --- a/include/react/detail/ObserverBase.h +++ b/include/react/detail/ObserverBase.h @@ -20,6 +20,9 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Forward declarations +/////////////////////////////////////////////////////////////////////////////////////////////////// template class Observable; @@ -38,16 +41,6 @@ class IObserver friend class ObserverRegistry; }; -// tbb tasks are non-preemptible, thread local flag for each worker -template -struct ThreadLocalObserverState -{ - static REACT_TLS bool ShouldDetach; -}; - -template -REACT_TLS bool ThreadLocalObserverState::ShouldDetach(false); - /////////////////////////////////////////////////////////////////////////////////////////////////// /// ObserverRegistry /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index 910bafec..e434c1ac 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -29,6 +29,15 @@ class SignalNode; template class EventStreamNode; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ObserverAction +/////////////////////////////////////////////////////////////////////////////////////////////////// +enum class ObserverAction +{ + next, + stop_and_detach +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// ObserverNode /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -86,16 +95,13 @@ class SignalObserverNode : public ObserverNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - ThreadLocalObserverState<>::ShouldDetach = false; - - ThreadLocalInputState<>::Context = EInputContext::continuation; + bool shouldDetach = false; if (auto p = subject_.lock()) - func_(p->ValueRef()); + if (func_(p->ValueRef()) == ObserverAction::stop_and_detach) + shouldDetach = true; - ThreadLocalInputState<>::Context = EInputContext::none; - - if (ThreadLocalObserverState<>::ShouldDetach) + if (shouldDetach) DomainSpecificInputManager::Instance().Continuation() .QueueObserverForDetach(*this); @@ -161,20 +167,22 @@ class EventObserverNode : public ObserverNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - - ThreadLocalObserverState<>::ShouldDetach = false; - ThreadLocalInputState<>::Context = EInputContext::continuation; + bool shouldDetach = false; if (auto p = subject_.lock()) { for (const auto& e : p->Events()) - func_(e); + { + if (func_(e) == ObserverAction::stop_and_detach) + { + shouldDetach = true; + break; + } + } } - ThreadLocalInputState<>::Context = EInputContext::none; - - if (ThreadLocalObserverState<>::ShouldDetach) + if (shouldDetach) DomainSpecificInputManager::Instance().Continuation() .QueueObserverForDetach(*this); @@ -247,9 +255,9 @@ class SyncedObserverNode : public ObserverNode MyFunc( f ) {} - void operator()(const std::shared_ptr>& ... args) + ObserverAction operator()(const std::shared_ptr>& ... args) { - MyFunc(MyEvent, args->ValueRef() ...); + return MyFunc(MyEvent, args->ValueRef() ...); } const E& MyEvent; @@ -262,9 +270,7 @@ class SyncedObserverNode : public ObserverNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - ThreadLocalObserverState<>::ShouldDetach = false; - - ThreadLocalInputState<>::Context = EInputContext::continuation; + bool shouldDetach = false; if (auto p = subject_.lock()) { @@ -273,12 +279,16 @@ class SyncedObserverNode : public ObserverNode p->SetCurrentTurn(turn); for (const auto& e : p->Events()) - apply(EvalFunctor_{ e, func_ }, deps_); + { + if (apply(EvalFunctor_( e, func_ ), deps_) == ObserverAction::stop_and_detach) + { + shouldDetach = true; + break; + } + } } - ThreadLocalInputState<>::Context = EInputContext::none; - - if (ThreadLocalObserverState<>::ShouldDetach) + if (shouldDetach) DomainSpecificInputManager::Instance().Continuation() .QueueObserverForDetach(*this); diff --git a/tests/src/ObserverTest.h b/tests/src/ObserverTest.h index 39a8d079..1cdbb955 100644 --- a/tests/src/ObserverTest.h +++ b/tests/src/ObserverTest.h @@ -181,9 +181,9 @@ TYPED_TEST_P(ObserverTest, DetachThisObserver1) int count = 0; - Observe(src, [&] (Token) { + Observe(src, [&] (Token) -> ObserverAction { ++count; - DetachThisObserver(); + return ObserverAction::stop_and_detach; }); src.Emit(); @@ -211,9 +211,9 @@ TYPED_TEST_P(ObserverTest, DetachThisObserver2) int count = 0; - Observe(src, With(sum,prod,diff), [&] (Token, int sum, int prod, int diff) { + Observe(src, With(sum,prod,diff), [&] (Token, int sum, int prod, int diff) -> ObserverAction { ++count; - DetachThisObserver(); + return ObserverAction::stop_and_detach; }); in1 <<= 22; diff --git a/tests/src/OperationsTest.h b/tests/src/OperationsTest.h index 5afc3979..2b5a700e 100644 --- a/tests/src/OperationsTest.h +++ b/tests/src/OperationsTest.h @@ -315,65 +315,67 @@ TYPED_TEST_P(OperationsTest, SyncedTransform1) int obsCount1 = 0; int obsCount2 = 0; - Observe(out1, [&] (const tuple& t) { - ++obsCount1; - - ASSERT_EQ(get<0>(t), 33); - ASSERT_EQ(get<1>(t), 242); - ASSERT_EQ(get<2>(t), 11); - - DetachThisObserver(); - }); + { + auto obs1 = Observe(out1, [&] (const tuple& t) { + ++obsCount1; - Observe(out2, [&] (const tuple& t) { - ++obsCount2; + ASSERT_EQ(get<0>(t), 33); + ASSERT_EQ(get<1>(t), 242); + ASSERT_EQ(get<2>(t), 11); + }); - ASSERT_EQ(get<0>(t), 42); - ASSERT_EQ(get<1>(t), 33); - ASSERT_EQ(get<2>(t), 242); - ASSERT_EQ(get<3>(t), 11); + auto obs2 = Observe(out2, [&] (const tuple& t) { + ++obsCount2; - DetachThisObserver(); - }); + ASSERT_EQ(get<0>(t), 42); + ASSERT_EQ(get<1>(t), 33); + ASSERT_EQ(get<2>(t), 242); + ASSERT_EQ(get<3>(t), 11); + }); - in1 <<= 22; - in2 <<= 11; + in1 <<= 22; + in2 <<= 11; - src1.Emit(); - src2.Emit(42); + src1.Emit(); + src2.Emit(42); - ASSERT_EQ(obsCount1, 1); - ASSERT_EQ(obsCount2, 1); + ASSERT_EQ(obsCount1, 1); + ASSERT_EQ(obsCount2, 1); - Observe(out1, [&] (const tuple& t) { - ++obsCount1; + obs1.Detach(); + obs2.Detach(); + } - ASSERT_EQ(get<0>(t), 330); - ASSERT_EQ(get<1>(t), 24200); - ASSERT_EQ(get<2>(t), 110); + { + auto obs1 = Observe(out1, [&] (const tuple& t) { + ++obsCount1; - DetachThisObserver(); - }); + ASSERT_EQ(get<0>(t), 330); + ASSERT_EQ(get<1>(t), 24200); + ASSERT_EQ(get<2>(t), 110); + }); - Observe(out2, [&] (const tuple& t) { - ++obsCount2; + auto obs2 = Observe(out2, [&] (const tuple& t) { + ++obsCount2; - ASSERT_EQ(get<0>(t), 420); - ASSERT_EQ(get<1>(t), 330); - ASSERT_EQ(get<2>(t), 24200); - ASSERT_EQ(get<3>(t), 110); + ASSERT_EQ(get<0>(t), 420); + ASSERT_EQ(get<1>(t), 330); + ASSERT_EQ(get<2>(t), 24200); + ASSERT_EQ(get<3>(t), 110); + }); - DetachThisObserver(); - }); + in1 <<= 220; + in2 <<= 110; - in1 <<= 220; - in2 <<= 110; + src1.Emit(); + src2.Emit(420); - src1.Emit(); - src2.Emit(420); + ASSERT_EQ(obsCount1, 2); + ASSERT_EQ(obsCount2, 2); - ASSERT_EQ(obsCount1, 2); - ASSERT_EQ(obsCount2, 2); + obs1.Detach(); + obs2.Detach(); + } } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -411,61 +413,63 @@ TYPED_TEST_P(OperationsTest, SyncedIterate1) int obsCount1 = 0; int obsCount2 = 0; - Observe(out1, [&] (const tuple& t) { - ++obsCount1; - - ASSERT_EQ(get<0>(t), 33); - ASSERT_EQ(get<1>(t), 330); - - DetachThisObserver(); - }); + { + auto obs1 = Observe(out1, [&] (const tuple& t) { + ++obsCount1; - Observe(out2, [&] (const tuple& t) { - ++obsCount2; + ASSERT_EQ(get<0>(t), 33); + ASSERT_EQ(get<1>(t), 330); + }); - ASSERT_EQ(get<0>(t), 42); - ASSERT_EQ(get<1>(t), 33); - ASSERT_EQ(get<2>(t), 330); + auto obs2 = Observe(out2, [&] (const tuple& t) { + ++obsCount2; - DetachThisObserver(); - }); + ASSERT_EQ(get<0>(t), 42); + ASSERT_EQ(get<1>(t), 33); + ASSERT_EQ(get<2>(t), 330); + }); - in1 <<= 22; - in2 <<= 11; + in1 <<= 22; + in2 <<= 11; - src1.Emit(); - src2.Emit(42); + src1.Emit(); + src2.Emit(42); - ASSERT_EQ(obsCount1, 1); - ASSERT_EQ(obsCount2, 1); + ASSERT_EQ(obsCount1, 1); + ASSERT_EQ(obsCount2, 1); - Observe(out1, [&] (const tuple& t) { - ++obsCount1; + obs1.Detach(); + obs2.Detach(); + } - ASSERT_EQ(get<0>(t), 33 + 330); - ASSERT_EQ(get<1>(t), 330 + 3300); + { + auto obs1 = Observe(out1, [&] (const tuple& t) { + ++obsCount1; - DetachThisObserver(); - }); + ASSERT_EQ(get<0>(t), 33 + 330); + ASSERT_EQ(get<1>(t), 330 + 3300); + }); - Observe(out2, [&] (const tuple& t) { - ++obsCount2; + auto obs2 = Observe(out2, [&] (const tuple& t) { + ++obsCount2; - ASSERT_EQ(get<0>(t), 42 + 420); - ASSERT_EQ(get<1>(t), 33 + 330); - ASSERT_EQ(get<2>(t), 330 + 3300); + ASSERT_EQ(get<0>(t), 42 + 420); + ASSERT_EQ(get<1>(t), 33 + 330); + ASSERT_EQ(get<2>(t), 330 + 3300); + }); - DetachThisObserver(); - }); + in1 <<= 220; + in2 <<= 110; - in1 <<= 220; - in2 <<= 110; + src1.Emit(); + src2.Emit(420); - src1.Emit(); - src2.Emit(420); + ASSERT_EQ(obsCount1, 2); + ASSERT_EQ(obsCount2, 2); - ASSERT_EQ(obsCount1, 2); - ASSERT_EQ(obsCount2, 2); + obs1.Detach(); + obs2.Detach(); + } } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -506,75 +510,77 @@ TYPED_TEST_P(OperationsTest, SyncedIterate2) int obsCount1 = 0; int obsCount2 = 0; - Observe(out1, [&] (const vector& v) { - ++obsCount1; - - ASSERT_EQ(v.size(), 2); - - ASSERT_EQ(v[0], 33); - ASSERT_EQ(v[1], 330); + { + auto obs1 = Observe(out1, [&] (const vector& v) { + ++obsCount1; - DetachThisObserver(); - }); + ASSERT_EQ(v.size(), 2); - Observe(out2, [&] (const vector& v) { - ++obsCount2; + ASSERT_EQ(v[0], 33); + ASSERT_EQ(v[1], 330); + }); - ASSERT_EQ(v.size(), 3); + auto obs2 = Observe(out2, [&] (const vector& v) { + ++obsCount2; - ASSERT_EQ(v[0], 42); - ASSERT_EQ(v[1], 33); - ASSERT_EQ(v[2], 330); + ASSERT_EQ(v.size(), 3); - DetachThisObserver(); - }); + ASSERT_EQ(v[0], 42); + ASSERT_EQ(v[1], 33); + ASSERT_EQ(v[2], 330); + }); - in1 <<= 22; - in2 <<= 11; + in1 <<= 22; + in2 <<= 11; - src1.Emit(); - src2.Emit(42); + src1.Emit(); + src2.Emit(42); - ASSERT_EQ(obsCount1, 1); - ASSERT_EQ(obsCount2, 1); + ASSERT_EQ(obsCount1, 1); + ASSERT_EQ(obsCount2, 1); - Observe(out1, [&] (const vector& v) { - ++obsCount1; + obs1.Detach(); + obs2.Detach(); + } - ASSERT_EQ(v.size(), 4); + { + auto obs1 = Observe(out1, [&] (const vector& v) { + ++obsCount1; - ASSERT_EQ(v[0], 33); - ASSERT_EQ(v[1], 330); - ASSERT_EQ(v[2], 330); - ASSERT_EQ(v[3], 3300); + ASSERT_EQ(v.size(), 4); - DetachThisObserver(); - }); + ASSERT_EQ(v[0], 33); + ASSERT_EQ(v[1], 330); + ASSERT_EQ(v[2], 330); + ASSERT_EQ(v[3], 3300); + }); - Observe(out2, [&] (const vector& v) { - ++obsCount2; + auto obs2 = Observe(out2, [&] (const vector& v) { + ++obsCount2; - ASSERT_EQ(v.size(), 6); + ASSERT_EQ(v.size(), 6); - ASSERT_EQ(v[0], 42); - ASSERT_EQ(v[1], 33); - ASSERT_EQ(v[2], 330); + ASSERT_EQ(v[0], 42); + ASSERT_EQ(v[1], 33); + ASSERT_EQ(v[2], 330); - ASSERT_EQ(v[3], 420); - ASSERT_EQ(v[4], 330); - ASSERT_EQ(v[5], 3300); + ASSERT_EQ(v[3], 420); + ASSERT_EQ(v[4], 330); + ASSERT_EQ(v[5], 3300); + }); - DetachThisObserver(); - }); + in1 <<= 220; + in2 <<= 110; - in1 <<= 220; - in2 <<= 110; + src1.Emit(); + src2.Emit(420); - src1.Emit(); - src2.Emit(420); + ASSERT_EQ(obsCount1, 2); + ASSERT_EQ(obsCount2, 2); - ASSERT_EQ(obsCount1, 2); - ASSERT_EQ(obsCount2, 2); + obs1.Detach(); + obs2.Detach(); + } } /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tests/src/SignalTest.h b/tests/src/SignalTest.h index 7cba5bb6..f15c7735 100644 --- a/tests/src/SignalTest.h +++ b/tests/src/SignalTest.h @@ -622,12 +622,12 @@ TYPED_TEST_P(SignalTest, Modify3) auto vect = MakeVar(vector{}); - int obsCount = 0; + int count = 0; // Terrible, but lets test it - Observe(vect, [&] (const vector& v) + auto cont = MakeContinuation(vect, [&] (const vector& v) { - if (obsCount == 0) + if (count == 0) { ASSERT_EQ(v[0], 30); @@ -635,7 +635,7 @@ TYPED_TEST_P(SignalTest, Modify3) v.push_back(50); }); } - else if (obsCount == 1) + else if (count == 1) { ASSERT_EQ(v[1], 50); @@ -648,14 +648,14 @@ TYPED_TEST_P(SignalTest, Modify3) ASSERT_EQ(v[2], 70); } - obsCount++; + count++; }); vect.Modify([] (vector& v) { v.push_back(30); }); - ASSERT_EQ(obsCount, 3); + ASSERT_EQ(count, 3); } /////////////////////////////////////////////////////////////////////////////////////////////////// From ae4b597c33eab1f81c3c9a2a97d33f928499bcea Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 19 Jun 2014 18:45:27 +0200 Subject: [PATCH 153/266] Refactoring of engine mode names. X_queue -> X_concurrent. --- include/react/detail/IReactiveEngine.h | 6 +++--- include/react/detail/graph/EventNodes.h | 2 +- include/react/engine/PulsecountEngine.h | 23 +++++++++++++--------- include/react/engine/SubtreeEngine.h | 19 ++++++++++-------- include/react/engine/ToposortEngine.h | 26 ++++++++++++------------- tests/src/EventStreamTestQ.cpp | 8 ++++---- tests/src/ObserverTestQ.cpp | 8 ++++---- tests/src/OperationsTestQ.cpp | 8 ++++---- tests/src/SignalTestQ.cpp | 8 ++++---- tests/src/TransactionTest.cpp | 8 ++++---- 10 files changed, 62 insertions(+), 54 deletions(-) diff --git a/include/react/detail/IReactiveEngine.h b/include/react/detail/IReactiveEngine.h index a612b7c7..5b4cb651 100644 --- a/include/react/detail/IReactiveEngine.h +++ b/include/react/detail/IReactiveEngine.h @@ -197,9 +197,9 @@ struct EngineInterface /////////////////////////////////////////////////////////////////////////////////////////////////// /// Engine traits /////////////////////////////////////////////////////////////////////////////////////////////////// -template struct EnableNodeUpdateTimer : std::false_type {}; -template struct EnableParallelUpdating : std::false_type {}; -template struct EnableConcurrentInput : std::false_type {}; +template struct NodeUpdateTimerEnabled : std::false_type {}; +template struct IsParallelEngine : std::false_type {}; +template struct IsConcurrentEngine : std::false_type {}; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 5b20acaf..02576a08 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -38,7 +38,7 @@ class SignalNode; // Note: Weird design due to empty base class optimization template struct BufferClearAccessPolicy : - private ConditionalCriticalSection + private ConditionalCriticalSection { template void AccessBufferForClearing(const F& f) { this->Access(f); } diff --git a/include/react/engine/PulsecountEngine.h b/include/react/engine/PulsecountEngine.h index cfcd581b..5ff3b126 100644 --- a/include/react/engine/PulsecountEngine.h +++ b/include/react/engine/PulsecountEngine.h @@ -88,7 +88,7 @@ class Node : public IReactiveNode ShiftMutexT ShiftMutex; NodeVector Successors; - ENodeState State { ENodeState::unchanged }; + ENodeState State = ENodeState::unchanged; private: atomic counter_ { 0 }; @@ -132,7 +132,7 @@ class QueuingEngine : public DefaultQueuingEngine {}; /*****************************************/ REACT_BEGIN /*****************************************/ struct parallel; -struct parallel_queue; +struct parallel_concurrent; template class PulsecountEngine; @@ -140,20 +140,25 @@ class PulsecountEngine; template <> class PulsecountEngine : public REACT_IMPL::pulsecount::BasicEngine {}; -template <> class PulsecountEngine : +template <> class PulsecountEngine : public REACT_IMPL::pulsecount::QueuingEngine {}; /******************************************/ REACT_END /******************************************/ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -template struct EnableNodeUpdateTimer; -template <> struct EnableNodeUpdateTimer> : std::true_type {}; -template <> struct EnableNodeUpdateTimer> : std::true_type {}; +template struct NodeUpdateTimerEnabled; +template <> struct NodeUpdateTimerEnabled> : std::true_type {}; +template <> struct NodeUpdateTimerEnabled> : + std::true_type {}; -template struct EnableParallelUpdating; -template <> struct EnableParallelUpdating> : std::true_type {}; -template <> struct EnableParallelUpdating> : std::true_type {}; +template struct IsParallelEngine; +template <> struct IsParallelEngine> : std::true_type {}; +template <> struct IsParallelEngine> : std::true_type {}; + +template struct IsConcurrentEngine; +template <> struct IsConcurrentEngine> : std::false_type {}; +template <> struct IsConcurrentEngine> : std::true_type {}; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/engine/SubtreeEngine.h b/include/react/engine/SubtreeEngine.h index 077cf826..216aac91 100644 --- a/include/react/engine/SubtreeEngine.h +++ b/include/react/engine/SubtreeEngine.h @@ -179,7 +179,7 @@ class QueuingEngine : public DefaultQueuingEngine {}; /*****************************************/ REACT_BEGIN /*****************************************/ struct parallel; -struct parallel_queue; +struct parallel_concurrent; template class SubtreeEngine; @@ -187,20 +187,23 @@ class SubtreeEngine; template <> class SubtreeEngine : public REACT_IMPL::subtree::BasicEngine {}; -template <> class SubtreeEngine : +template <> class SubtreeEngine : public REACT_IMPL::subtree::QueuingEngine {}; /******************************************/ REACT_END /******************************************/ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -template struct EnableNodeUpdateTimer; -template <> struct EnableNodeUpdateTimer> : std::true_type {}; -template <> struct EnableNodeUpdateTimer> : std::true_type {}; +template struct NodeUpdateTimerEnabled; +template <> struct NodeUpdateTimerEnabled> : std::true_type {}; +template <> struct NodeUpdateTimerEnabled> : std::true_type {}; -template struct EnableParallelUpdating; -template <> struct EnableParallelUpdating> : std::true_type {}; -template <> struct EnableParallelUpdating> : std::true_type {}; +template struct IsParallelEngine; +template <> struct IsParallelEngine> : std::true_type {}; +template <> struct IsParallelEngine> : std::true_type {}; + +template struct IsConcurrentEngine; +template <> struct IsConcurrentEngine> : std::true_type {}; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/engine/ToposortEngine.h b/include/react/engine/ToposortEngine.h index 86aafe43..50510ca8 100644 --- a/include/react/engine/ToposortEngine.h +++ b/include/react/engine/ToposortEngine.h @@ -202,9 +202,9 @@ class QueuingParEngine : public DefaultQueuingEngine class ToposortEngine; @@ -212,30 +212,30 @@ class ToposortEngine; template <> class ToposortEngine : public REACT_IMPL::toposort::BasicSeqEngine {}; -template <> class ToposortEngine : +template <> class ToposortEngine : public REACT_IMPL::toposort::QueuingSeqEngine {}; template <> class ToposortEngine : public REACT_IMPL::toposort::BasicParEngine {}; -template <> class ToposortEngine : +template <> class ToposortEngine : public REACT_IMPL::toposort::QueuingParEngine {}; /******************************************/ REACT_END /******************************************/ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -template struct EnableNodeUpdateTimer; -template <> struct EnableNodeUpdateTimer> : std::true_type {}; -template <> struct EnableNodeUpdateTimer> : std::true_type {}; +template struct NodeUpdateTimerEnabled; +template <> struct NodeUpdateTimerEnabled> : std::true_type {}; +template <> struct NodeUpdateTimerEnabled> : std::true_type {}; -template struct EnableParallelUpdating; -template <> struct EnableParallelUpdating> : std::true_type {}; -template <> struct EnableParallelUpdating> : std::true_type {}; +template struct IsParallelEngine; +template <> struct IsParallelEngine> : std::true_type {}; +template <> struct IsParallelEngine> : std::true_type {}; -template struct EnableConcurrentInput; -template <> struct EnableConcurrentInput> : std::true_type {}; -template <> struct EnableConcurrentInput> : std::true_type {}; +template struct IsConcurrentEngine; +template <> struct IsConcurrentEngine> : std::true_type {}; +template <> struct IsConcurrentEngine> : std::true_type {}; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/tests/src/EventStreamTestQ.cpp b/tests/src/EventStreamTestQ.cpp index c4581898..328fe40e 100644 --- a/tests/src/EventStreamTestQ.cpp +++ b/tests/src/EventStreamTestQ.cpp @@ -15,9 +15,9 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, EventStreamTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, EventStreamTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, EventStreamTest, PulsecountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, EventStreamTest, SubtreeEngine); +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, EventStreamTest, ToposortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, EventStreamTest, ToposortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, EventStreamTest, PulsecountEngine); +INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, EventStreamTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/tests/src/ObserverTestQ.cpp b/tests/src/ObserverTestQ.cpp index 8798e0cb..c77b6fda 100644 --- a/tests/src/ObserverTestQ.cpp +++ b/tests/src/ObserverTestQ.cpp @@ -15,9 +15,9 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, ObserverTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, ObserverTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, ObserverTest, PulsecountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, ObserverTest, SubtreeEngine); +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, ObserverTest, ToposortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, ObserverTest, ToposortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, ObserverTest, PulsecountEngine); +INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, ObserverTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/tests/src/OperationsTestQ.cpp b/tests/src/OperationsTestQ.cpp index a501ace9..cda8cb7e 100644 --- a/tests/src/OperationsTestQ.cpp +++ b/tests/src/OperationsTestQ.cpp @@ -15,9 +15,9 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, OperationsTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, OperationsTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, OperationsTest, PulsecountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, OperationsTest, SubtreeEngine); +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, OperationsTest, ToposortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, OperationsTest, ToposortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, OperationsTest, PulsecountEngine); +INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, OperationsTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/tests/src/SignalTestQ.cpp b/tests/src/SignalTestQ.cpp index 3fd36a5f..b383a98b 100644 --- a/tests/src/SignalTestQ.cpp +++ b/tests/src/SignalTestQ.cpp @@ -15,9 +15,9 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, SignalTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, SignalTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, SignalTest, PulsecountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, SignalTest, SubtreeEngine); +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, SignalTest, ToposortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, SignalTest, ToposortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, SignalTest, PulsecountEngine); +INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, SignalTest, SubtreeEngine); } // ~namespace \ No newline at end of file diff --git a/tests/src/TransactionTest.cpp b/tests/src/TransactionTest.cpp index c44370bd..b5cfd7ec 100644 --- a/tests/src/TransactionTest.cpp +++ b/tests/src/TransactionTest.cpp @@ -15,9 +15,9 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, TransactionTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, TransactionTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, TransactionTest, PulsecountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(Subtree, TransactionTest, SubtreeEngine); +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, TransactionTest, ToposortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, TransactionTest, ToposortEngine); +INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, TransactionTest, PulsecountEngine); +INSTANTIATE_TYPED_TEST_CASE_P(Subtree, TransactionTest, SubtreeEngine); } // ~namespace \ No newline at end of file From 57004a29c4aff2fae1676a6511d0bf3f31e035fa Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 19 Jun 2014 19:07:36 +0200 Subject: [PATCH 154/266] Added explicit Continuations. Before, all input in observer nodes to reactives of the current domain was captured as a continuation. Input to other domains was not allowed. Now, MakeContinuation can be used to create explicit continuations. Each continuation is processed as a transaction. Continuations to other domains are supported. --- include/react/Domain.h | 140 +++++++- include/react/detail/ReactiveInput.h | 340 ++++++++---------- .../react/detail/graph/ContinuationNodes.h | 312 ++++++++++++++++ project/msvc/CppReact.vcxproj | 1 + project/msvc/CppReact.vcxproj.filters | 3 + 5 files changed, 605 insertions(+), 191 deletions(-) create mode 100644 include/react/detail/graph/ContinuationNodes.h diff --git a/include/react/Domain.h b/include/react/Domain.h index 78ecd5c3..f5b15a78 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -14,14 +14,15 @@ #include #include +#include "react/detail/IReactiveEngine.h" #include "react/detail/ReactiveInput.h" +#include "react/detail/graph/ContinuationNodes.h" #ifdef REACT_ENABLE_LOGGING #include "react/logging/EventLog.h" #include "react/logging/EventRecords.h" #endif //REACT_ENABLE_LOGGING -#include "react/detail/IReactiveEngine.h" #include "react/engine/ToposortEngine.h" /*****************************************/ REACT_BEGIN /*****************************************/ @@ -58,6 +59,9 @@ class ScopedObserver; template class Reactor; +template +class SignalPack; + using REACT_IMPL::TurnFlagsT; //ETurnFlags @@ -124,13 +128,13 @@ class DomainBase /// Domain traits /////////////////////////////////////////////////////////////////////////////////////////////// static const bool uses_node_update_timer = - REACT_IMPL::EnableNodeUpdateTimer::value; + REACT_IMPL::NodeUpdateTimerEnabled::value; - static const bool uses_concurrent_input = - REACT_IMPL::EnableConcurrentInput::value; + static const bool is_concurrent = + REACT_IMPL::IsConcurrentEngine::value; - static const bool uses_parallel_updating = - REACT_IMPL::EnableParallelUpdating::value; + static const bool is_parallel = + REACT_IMPL::IsParallelEngine::value; /////////////////////////////////////////////////////////////////////////////////////////////// /// Aliases for reactives of this domain @@ -218,6 +222,130 @@ class DomainBase #endif //REACT_ENABLE_LOGGING }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Continuation +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename TSourceDomain, + typename TTargetDomain +> +class Continuation +{ + using NodePtrT = REACT_IMPL::NodeBasePtrT; + +public: + using SourceDomainT = TSourceDomain; + using TargetDomainT = TTargetDomain; + + Continuation() = default; + Continuation(const Continuation&) = delete; + Continuation& operator=(const Continuation&) = delete; + + Continuation(Continuation&& other) : + nodePtr_( std::move(other.nodePtr_) ) + {} + + explicit Continuation(NodePtrT&& nodePtr) : + nodePtr_( std::move(nodePtr) ) + {} + + Continuation& operator=(Continuation&& other) + { + nodePtr_ = std::move(other.nodePtr_); + return *this; + } + +private: + NodePtrT nodePtr_; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// MakeContinuation - Signals +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename DOut = D, + typename S, + typename FIn +> +auto MakeContinuation(const Signal& trigger, FIn&& func) + -> Continuation +{ + using REACT_IMPL::SignalContinuationNode; + using F = typename std::decay::type; + + return Continuation( + std::make_shared>( + trigger.NodePtr(), std::forward(func))); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// MakeContinuation - Events +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename DOut = D, + typename E, + typename FIn +> +auto MakeContinuation(const Events& trigger, FIn&& func) + -> Continuation +{ + using REACT_IMPL::EventContinuationNode; + using F = typename std::decay::type; + + return Continuation( + std::make_shared>( + trigger.NodePtr(), std::forward(func))); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// MakeContinuation - Synced +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename DOut = D, + typename E, + typename FIn, + typename ... TDepValues +> +auto MakeContinuation(const Events& trigger, + const SignalPack& depPack, FIn&& func) + -> Continuation +{ + using REACT_IMPL::SyncedContinuationNode; + using F = typename std::decay::type; + + struct NodeBuilder_ + { + NodeBuilder_(const Events& trigger, FIn&& func) : + MyTrigger( trigger ), + MyFunc( std::forward(func) ) + {} + + auto operator()(const Signal& ... deps) + -> Continuation + { + return Continuation( + std::make_shared>( + MyTrigger.NodePtr(), + std::forward(MyFunc), deps.NodePtr() ...)); + } + + const Events& MyTrigger; + FIn MyFunc; + }; + + return REACT_IMPL::apply( + NodeBuilder_( trigger, std::forward(func) ), + depPack.Data); +} + /******************************************/ REACT_END /******************************************/ /***************************************/ REACT_IMPL_BEGIN /**************************************/ diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index f55f2e4b..b72b9374 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -13,12 +13,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include "tbb/concurrent_queue.h" @@ -39,17 +41,20 @@ class IObserver; /////////////////////////////////////////////////////////////////////////////////////////////////// using TurnIdT = uint; using TurnFlagsT = uint; +using TransactionFuncT = std::function; -enum ETurnFlags +enum { enable_input_merging = 1 << 0 }; -enum class EInputContext +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// IContinuationTarget +/////////////////////////////////////////////////////////////////////////////////////////////////// +struct IContinuationTarget { - none, - transaction, - continuation + virtual void DoContinuation(TransactionFuncT&&) = 0; + virtual void AsyncContinuation(TransactionFuncT&&) = 0; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -58,11 +63,11 @@ enum class EInputContext template struct ThreadLocalInputState { - static REACT_TLS EInputContext Context; + static REACT_TLS bool IsTransactionActive; }; template -REACT_TLS EInputContext ThreadLocalInputState::Context(EInputContext::none); +REACT_TLS bool ThreadLocalInputState::IsTransactionActive(false); /////////////////////////////////////////////////////////////////////////////////////////////////// /// ContinuationData @@ -71,12 +76,11 @@ template class ContinuationData { public: - bool HasInput() const; + bool HasNext() const; - template - void AddInput(F&& input); + void StoreContinuation(IContinuationTarget& target, TransactionFuncT&& cont); - void CommitInput(); + void RunNext(); void QueueObserverForDetach(IObserver& obs); @@ -88,29 +92,30 @@ class ContinuationData template <> class ContinuationData { - using InputClosureT = std::function; - using InputVectT = std::vector; + using ContDataT = std::pair; + using DataQueueT = std::deque; using ObsVectT = std::vector; public: - bool HasInput() const + bool HasNext() const { - return !bufferedInputs_.empty(); + return !storedContinuations_.empty(); } - template - void AddInput(F&& input) + void StoreContinuation(IContinuationTarget& target, TransactionFuncT&& cont) { - bufferedInputs_.push_back(std::forward(input)); + storedContinuations_.emplace_back(&target, std::move(cont)); } - void CommitInput() + void RunNext() { - for (auto& f : bufferedInputs_) - f(); - bufferedInputs_.clear(); + ContDataT t = std::move(storedContinuations_.front()); + storedContinuations_.pop_front(); + + t.first->DoContinuation(std::move(t.second)); } + // Todo: Move this somewhere else void QueueObserverForDetach(IObserver& obs) { detachedObservers_.push_back(&obs); @@ -127,7 +132,7 @@ class ContinuationData } private: - InputVectT bufferedInputs_; + DataQueueT storedContinuations_; ObsVectT detachedObservers_; }; @@ -135,32 +140,36 @@ class ContinuationData template <> class ContinuationData { - using InputClosureT = std::function; - using InputVectT = std::vector; + using ContDataT = std::pair; + using DataQueueT = std::deque; using ObsVectT = std::vector; public: - bool HasInput() const + bool HasNext() const { - return hasInput_; + return contCount_ != 0; } - template - void AddInput(F&& input) + void StoreContinuation(IContinuationTarget& target, TransactionFuncT&& cont) { - bufferedInputs_.local().push_back(std::forward(input)); - hasInput_ = true; + storedContinuations_.local().emplace_back(&target, std::move(cont)); + ++contCount_; } - void CommitInput() + void RunNext() { - for (auto& v : bufferedInputs_) + for (auto& v : storedContinuations_) { - for (auto& f : v) - f(); - v.clear(); + if (v.size() == 0) + continue; + + ContDataT t = std::move(v.front()); + v.pop_front(); + contCount_--; + + t.first->DoContinuation(std::move(t.second)); + break; } - hasInput_ = false; } void QueueObserverForDetach(IObserver& obs) @@ -182,10 +191,10 @@ class ContinuationData } private: - tbb::enumerable_thread_specific bufferedInputs_; + tbb::enumerable_thread_specific storedContinuations_; tbb::enumerable_thread_specific detachedObservers_; - bool hasInput_ = false; + size_t contCount_ = 0; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -236,11 +245,10 @@ using AsyncStatePtrT = std::shared_ptr; /// InputManager /////////////////////////////////////////////////////////////////////////////////////////////////// template -class InputManager +class InputManager : + public IContinuationTarget { private: - using TransactionFuncT = std::function; - struct AsyncItem { TurnFlagsT Flags; @@ -249,7 +257,7 @@ class InputManager }; using AsyncQueueT = tbb::concurrent_bounded_queue; - using ContinuationT = ContinuationData; + using ContinuationT = ContinuationData; struct TransactionData { @@ -282,14 +290,14 @@ class InputManager Engine::EnterTurnQueue(turn); // Phase 1 - Input admission - ThreadLocalInputState<>::Context = EInputContext::transaction; + ThreadLocalInputState<>::IsTransactionActive = true; Engine::OnTurnAdmissionStart(turn); func(); Engine::ApplyMergedInputs(turn); Engine::OnTurnAdmissionEnd(turn); - ThreadLocalInputState<>::Context = EInputContext::none; + ThreadLocalInputState<>::IsTransactionActive = false; // Phase 2 - Apply input node changes for (auto* p : transaction_.ChangedInputs) @@ -315,117 +323,10 @@ class InputManager asyncQueue_.push(AsyncItem{ flags, statusPtr, func } ); } - void processAsyncQueue() - { - AsyncItem item; - - AsyncStatePtrT savedStatusPtr = nullptr; - TurnFlagsT savedFlags = 0; - - std::vector statusPtrStash; - - bool skipPop = false; - - while (true) - { - // Blocks if queue is empty - if (!skipPop) - asyncQueue_.pop(item); - else - skipPop = false; - - // First try to merge to an existing synchronous item in the queue - bool canMerge = (item.Flags & enable_input_merging) != 0; - if (canMerge && Engine::TryMergeAsync(std::move(item.Func), std::move(item.StatusPtr))) - continue; - - bool shouldPropagate = false; - - TurnT turn( nextTurnId(), item.Flags ); - - // Blocks until turn is at the front of the queue - Engine::EnterTurnQueue(turn); - - // Phase 1 - Input admission - ThreadLocalInputState<>::Context = EInputContext::transaction; - - Engine::OnTurnAdmissionStart(turn); - - // Input of current item - item.Func(); - - // Merged inputs that arrived while this item was queued - Engine::ApplyMergedInputs(turn); - - // Save data, because item might be re-used for next input - savedStatusPtr = std::move(item.StatusPtr); - savedFlags = item.Flags; - - // If the current item supports merging, try to add more mergeable inputs - // to this turn - if (canMerge) - { - uint extraCount = 0; - while (extraCount < 100 && asyncQueue_.try_pop(item)) - { - bool canMergeNext = (item.Flags & enable_input_merging) != 0; - if (canMergeNext) - { - item.Func(); - if (item.StatusPtr != nullptr) - statusPtrStash.push_back(std::move(item.StatusPtr)); - - ++extraCount; - } - else - { - // We already popped an item we could not merge - // Process it in the next iteration - skipPop = true; - - // Break at first item that cannot be merged. - // We only allow merging of continuous ranges. - break; - } - } - } - - Engine::OnTurnAdmissionEnd(turn); - - ThreadLocalInputState<>::Context = EInputContext::none; - - // Phase 2 - Apply input node changes - for (auto* p : transaction_.ChangedInputs) - if (p->ApplyInput(&turn)) - shouldPropagate = true; - transaction_.ChangedInputs.clear(); - - // Phase 3 - Propagate changes - if (shouldPropagate) - Engine::Propagate(turn); - - Engine::ExitTurnQueue(turn); - - processContinuationData(savedFlags); - - // Decrement transaction status counts of processed items - if (savedStatusPtr != nullptr) - savedStatusPtr->DecWaitCount(); - - for (auto& p : statusPtrStash) - p->DecWaitCount(); - statusPtrStash.clear(); - } - } - template void AddInput(R& r, V&& v) { - if (ThreadLocalInputState<>::Context == EInputContext::continuation) - { - addContinuationInput(r, std::forward(v)); - } - else if (ThreadLocalInputState<>::Context == EInputContext::transaction) + if (ThreadLocalInputState<>::IsTransactionActive) { addTransactionInput(r, std::forward(v)); } @@ -438,11 +339,7 @@ class InputManager template void ModifyInput(R& r, const F& func) { - if (ThreadLocalInputState<>::Context == EInputContext::continuation) - { - modifyContinuationInput(r, func); - } - else if (ThreadLocalInputState<>::Context == EInputContext::transaction) + if (ThreadLocalInputState<>::IsTransactionActive) { modifyTransactionInput(r, func); } @@ -452,6 +349,18 @@ class InputManager } } + //IContinuationTarget + virtual void DoContinuation(TransactionFuncT&& cont) override + { + DoTransaction(0, std::move(cont)); + } + + virtual void AsyncContinuation(TransactionFuncT&& cont) override + { + AsyncTransaction(0, nullptr, std::move(cont)); + } + //~IContinuationTarget + ContinuationT& Continuation() { return continuation_; @@ -526,58 +435,119 @@ class InputManager transaction_.ChangedInputs.push_back(&r); } - // Input happened during a turn - buffer in continuation - template - void addContinuationInput(R& r, const V& v) + void processAsyncQueue() { - // Capture v by value to store it in lambda - continuation_.AddInput( - [this,&r,v] { addTransactionInput(r, std::move(v)); } - ); - } + AsyncItem item; - template - void modifyContinuationInput(R& r, const F& func) - { - // Capture func by value to store it in lambda - continuation_.AddInput( - [this,&r,func] { modifyTransactionInput(r, func); } - ); - } + AsyncStatePtrT savedStatusPtr = nullptr; + TurnFlagsT savedFlags = 0; - void processContinuationData(TurnFlagsT flags) - { - // No merging for continuations - flags &= ~enable_input_merging; + std::vector statusPtrStash; - continuation_.template DetachQueuedObservers(); + bool skipPop = false; - while (continuation_.HasInput()) + while (true) { + // Blocks if queue is empty + if (!skipPop) + asyncQueue_.pop(item); + else + skipPop = false; + + // First try to merge to an existing synchronous item in the queue + bool canMerge = (item.Flags & enable_input_merging) != 0; + if (canMerge && Engine::TryMergeAsync(std::move(item.Func), std::move(item.StatusPtr))) + continue; + bool shouldPropagate = false; - TurnT turn( nextTurnId(), flags ); + TurnT turn( nextTurnId(), item.Flags ); + + // Blocks until turn is at the front of the queue Engine::EnterTurnQueue(turn); - ThreadLocalInputState<>::Context = EInputContext::transaction; + // Phase 1 - Input admission + ThreadLocalInputState<>::IsTransactionActive = true; Engine::OnTurnAdmissionStart(turn); - continuation_.CommitInput(); + + // Input of current item + item.Func(); + + // Merged inputs that arrived while this item was queued Engine::ApplyMergedInputs(turn); + + // Save data, because item might be re-used for next input + savedStatusPtr = std::move(item.StatusPtr); + savedFlags = item.Flags; + + // If the current item supports merging, try to add more mergeable inputs + // to this turn + if (canMerge) + { + uint extraCount = 0; + while (extraCount < 100 && asyncQueue_.try_pop(item)) + { + bool canMergeNext = (item.Flags & enable_input_merging) != 0; + if (canMergeNext) + { + item.Func(); + if (item.StatusPtr != nullptr) + statusPtrStash.push_back(std::move(item.StatusPtr)); + + ++extraCount; + } + else + { + // We already popped an item we could not merge + // Process it in the next iteration + skipPop = true; + + // Break at first item that cannot be merged. + // We only allow merging of continuous ranges. + break; + } + } + } + Engine::OnTurnAdmissionEnd(turn); - ThreadLocalInputState<>::Context = EInputContext::none; + ThreadLocalInputState<>::IsTransactionActive = false; + // Phase 2 - Apply input node changes for (auto* p : transaction_.ChangedInputs) if (p->ApplyInput(&turn)) shouldPropagate = true; transaction_.ChangedInputs.clear(); + // Phase 3 - Propagate changes if (shouldPropagate) Engine::Propagate(turn); Engine::ExitTurnQueue(turn); + processContinuationData(savedFlags); + + // Decrement transaction status counts of processed items + if (savedStatusPtr != nullptr) + savedStatusPtr->DecWaitCount(); + + for (auto& p : statusPtrStash) + p->DecWaitCount(); + statusPtrStash.clear(); + } + } + + void processContinuationData(TurnFlagsT flags) + { + // No merging for continuations + flags &= ~enable_input_merging; + + continuation_.template DetachQueuedObservers(); + + while (continuation_.HasNext()) + { + continuation_.RunNext(); continuation_.template DetachQueuedObservers(); } } @@ -585,7 +555,7 @@ class InputManager AsyncQueueT asyncQueue_; std::thread asyncWorker_; - std::atomic nextTurnId_ { 0 }; + std::atomic nextTurnId_{ 0 }; TransactionData transaction_; ContinuationT continuation_; diff --git a/include/react/detail/graph/ContinuationNodes.h b/include/react/detail/graph/ContinuationNodes.h new file mode 100644 index 00000000..4e3749de --- /dev/null +++ b/include/react/detail/graph/ContinuationNodes.h @@ -0,0 +1,312 @@ + +// 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_CONTINUATIONNODES_H_INCLUDED +#define REACT_DETAIL_GRAPH_CONTINUATIONNODES_H_INCLUDED + +#pragma once + +#include "react/detail/Defs.h" + +#include +#include +#include + +#include "GraphBase.h" + +#include "react/detail/ReactiveInput.h" + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Forward declarations +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class SignalNode; + +template +class EventStreamNode; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ContinuationNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class ContinuationNode : + public ReactiveNode +{ +public: + ContinuationNode() = default; + + virtual bool IsOutputNode() const { return true; } +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SignalContinuationNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename DOut, + typename S, + typename TFunc +> +class SignalContinuationNode : public ContinuationNode +{ + using Engine = typename SignalContinuationNode::Engine; + +public: + template + SignalContinuationNode(const std::shared_ptr>& trigger, F&& func) : + SignalContinuationNode::ContinuationNode( ), + trigger_( trigger ), + func_( std::forward(func) ) + { + Engine::OnNodeCreate(*this); + Engine::OnNodeAttach(*this, *trigger); + } + + ~SignalContinuationNode() + { + Engine::OnNodeDestroy(*this); + } + + virtual const char* GetNodeType() const override { return "SignalContinuationNode"; } + virtual int DependencyCount() const override { return 1; } + + virtual void Tick(void* turnPtr) override + { +#ifdef REACT_ENABLE_LOGGING + using TurnT = typename D::Engine::TurnT; + TurnT& turn = *reinterpret_cast(turnPtr); +#endif + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + + auto& storedValue = trigger_->ValueRef(); + auto& storedFunc = func_; + + TransactionFuncT cont + ( + // Copy value and func + [storedFunc,storedValue] () mutable + { + storedFunc(storedValue); + } + ); + + DomainSpecificInputManager::Instance().Continuation() + .StoreContinuation( + DomainSpecificInputManager::Instance(), + std::move(cont)); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + } + +private: + std::shared_ptr> trigger_; + + TFunc func_; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventContinuationNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename DOut, + typename E, + typename TFunc +> +class EventContinuationNode : public ContinuationNode +{ + using Engine = typename EventContinuationNode::Engine; + +public: + template + EventContinuationNode(const std::shared_ptr>& trigger, F&& func) : + EventContinuationNode::ContinuationNode( ), + trigger_( trigger ), + func_( std::forward(func) ) + { + Engine::OnNodeCreate(*this); + Engine::OnNodeAttach(*this, *trigger); + } + + ~EventContinuationNode() + { + Engine::OnNodeDestroy(*this); + } + + virtual const char* GetNodeType() const override { return "EventContinuationNode"; } + virtual int DependencyCount() const override { return 1; } + + virtual void Tick(void* turnPtr) override + { +#ifdef REACT_ENABLE_LOGGING + using TurnT = typename D::Engine::TurnT; + TurnT& turn = *reinterpret_cast(turnPtr); +#endif + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + + auto& storedEvents = trigger_->Events(); + auto& storedFunc = func_; + + TransactionFuncT cont + ( + // Copy events and func + [storedFunc,storedEvents] () mutable + { + for (const auto& e : storedEvents) + storedFunc(e); + } + ); + + TransactionFuncT cont2 = [] { return; }; + + DomainSpecificInputManager::Instance().Continuation() + .StoreContinuation( + DomainSpecificInputManager::Instance(), + std::move(cont)); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + } + +private: + std::shared_ptr> trigger_; + + TFunc func_; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SyncedContinuationNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename DOut, + typename E, + typename TFunc, + typename ... TDepValues +> +class SyncedContinuationNode : public ContinuationNode +{ + using Engine = typename SyncedContinuationNode::Engine; + + using ValueTupleT = std::tuple; + + struct TupleBuilder_ + { + ValueTupleT operator()(const std::shared_ptr>& ... deps) + { + return ValueTupleT(deps->ValueRef() ...); + } + }; + + struct EvalFunctor_ + { + EvalFunctor_(const E& e, TFunc& f) : + MyEvent( e ), + MyFunc( f ) + {} + + void operator()(const TDepValues& ... vals) + { + MyFunc(MyEvent, vals ...); + } + + const E& MyEvent; + TFunc& MyFunc; + }; + +public: + template + SyncedContinuationNode(const std::shared_ptr>& trigger, F&& func, + const std::shared_ptr>& ... deps) : + SyncedContinuationNode::ContinuationNode( ), + trigger_( trigger ), + func_( std::forward(func) ), + deps_( deps ... ) + { + Engine::OnNodeCreate(*this); + Engine::OnNodeAttach(*this, *trigger); + + REACT_EXPAND_PACK(Engine::OnNodeAttach(*this, *deps)); + } + + ~SyncedContinuationNode() + { + Engine::OnNodeDetach(*this, *trigger_); + + apply( + DetachFunctor>...>( *this ), + deps_); + + Engine::OnNodeDestroy(*this); + } + + virtual const char* GetNodeType() const override { return "SyncedContinuationNode"; } + virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } + + 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())); + + // Update of this node could be triggered from deps, + // so make sure source doesnt contain events from last turn + trigger_->SetCurrentTurn(turn); + + auto& storedEvents = trigger_->Events(); + auto& storedFunc = func_; + + // Copy values to tuple + ValueTupleT storedValues = apply(TupleBuilder_( ), deps_); + + // Note: MSVC error, if using () initialization. + // Probably a compiler bug. + TransactionFuncT cont + { + // Copy events, func, value tuple (note: 2x copy) + [storedFunc,storedEvents,storedValues] () mutable + { + for (const auto& e : storedEvents) + { + apply(EvalFunctor_( e, storedFunc ), storedValues); + } + } + }; + + DomainSpecificInputManager::Instance().Continuation() + .StoreContinuation( + DomainSpecificInputManager::Instance(), + std::move(cont)); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + } + +private: + using DepHolderT = std::tuple>...>; + + std::shared_ptr> trigger_; + + TFunc func_; + DepHolderT deps_; +}; + +/****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_GRAPH_CONTINUATIONNODES_H_INCLUDED \ No newline at end of file diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index 46f68193..1a7496bb 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -86,6 +86,7 @@ + diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index 71168417..868e9439 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -141,6 +141,9 @@ Header Files\engine + + Header Files\detail\graph + From f9aaa09d58bb8b5b3052414e57e8bdf8d853c2cd Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 19 Jun 2014 19:10:02 +0200 Subject: [PATCH 155/266] Updated example. --- examples/src/Main.cpp | 68 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/examples/src/Main.cpp b/examples/src/Main.cpp index ab6574ae..acad8788 100644 --- a/examples/src/Main.cpp +++ b/examples/src/Main.cpp @@ -8,6 +8,8 @@ #include "react/Domain.h" #include "react/Signal.h" +#include "react/Event.h" +#include "react/Algorithm.h" using namespace std; using namespace react; @@ -15,7 +17,7 @@ using namespace react; // Defines a domain. // Each domain represents a separate dependency graph, managed by a dedicated propagation engine. // Reactives of different domains can not be combined. -REACTIVE_DOMAIN(D) +REACTIVE_DOMAIN(D, ToposortEngine) void SignalExample3() { @@ -27,7 +29,7 @@ void SignalExample3() // Inputs are implicitly thread-safe, buffered and executed in a continuation turn. // This continuation turn is queued just like a regular turn. // If other turns are already queued, they are executed before the continuation. - Observe(src, [&] (int v) { + auto cont = MakeContinuation(src, [&] (int v) { cout << "V: " << v << endl; if (v < 10) src <<= v+1; @@ -38,9 +40,71 @@ void SignalExample3() cout << endl; } +void SignalExample4() +{ + REACTIVE_DOMAIN(L, ToposortEngine) + REACTIVE_DOMAIN(R, ToposortEngine) + + cout << "Signal Example 4" << endl; + + auto srcL = MakeVar(0); + auto srcR = MakeVar(0); + + auto contL = MakeContinuation(srcL, [&] (int v) { + cout << "L->R: " << v << endl; + if (v < 10) + srcR <<= v+1; + }); + + auto contR = MakeContinuation(srcR, [&] (int v) { + cout << "R->L: " << v << endl; + if (v < 10) + srcL <<= v+1; + }); + + srcL <<= 1; + + cout << endl; +} + +void SignalExample5() +{ + REACTIVE_DOMAIN(L, ToposortEngine) + REACTIVE_DOMAIN(R, ToposortEngine) + + cout << "Signal Example 5" << endl; + + auto srcL = MakeVar(0); + auto depL1 = MakeVar(0); + auto depL2 = MakeVar(0); + auto srcR = MakeVar(0); + + auto contL = MakeContinuation( + Monitor(srcL), + With(depL1, depL2), + [&] (int v, int depL1, int depL2) { + cout << "L->R: " << v << endl; + if (v < 10) + srcR <<= v+1; + }); + + auto contR = MakeContinuation( + Monitor(srcR), + [&] (int v) { + cout << "R->L: " << v << endl; + if (v < 10) + srcL <<= v+1; + }); + + srcL <<= 1; + + cout << endl; +} + int main() { SignalExample3(); + SignalExample4(); #ifdef REACT_ENABLE_LOGGING std::ofstream logfile; From 45b3c56f8d87a561a93f24ff3da6a51fdb0b3f1f Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 20 Jun 2014 03:57:19 +0200 Subject: [PATCH 156/266] Minor fixes. --- include/react/Domain.h | 1 - include/react/detail/ReactiveInput.h | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/include/react/Domain.h b/include/react/Domain.h index f5b15a78..4fa6bc9d 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -302,7 +302,6 @@ auto MakeContinuation(const Events& trigger, FIn&& func) trigger.NodePtr(), std::forward(func))); } - /////////////////////////////////////////////////////////////////////////////////////////////////// /// MakeContinuation - Synced /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index b72b9374..fcd208a6 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -20,7 +20,6 @@ #include #include #include -#include #include #include "tbb/concurrent_queue.h" @@ -320,7 +319,7 @@ class InputManager : if (statusPtr != nullptr) statusPtr->IncWaitCount(); - asyncQueue_.push(AsyncItem{ flags, statusPtr, func } ); + asyncQueue_.push(AsyncItem{ flags, statusPtr, std::forward(func) } ); } template From 9880d033215b999847b732cb1a93a3132cb7324c Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 20 Jun 2014 03:58:03 +0200 Subject: [PATCH 157/266] Added some test cases for AsyncTransaction. --- tests/src/TransactionTest.h | 101 ++++++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 3 deletions(-) diff --git a/tests/src/TransactionTest.h b/tests/src/TransactionTest.h index c7963cd9..4dc0f99c 100644 --- a/tests/src/TransactionTest.h +++ b/tests/src/TransactionTest.h @@ -258,8 +258,7 @@ TYPED_TEST_P(TransactionTest, Merging1) auto n1 = MakeVar(1); auto n2 = n1 ->* f; - Observe(n2, [&] (int v) - { + Observe(n2, [&] (int v) { results.push_back(v); }); @@ -302,6 +301,100 @@ TYPED_TEST_P(TransactionTest, Merging1) ASSERT_TRUE(results[1] == 5); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Async1 +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(TransactionTest, Async1) +{ + using D = typename Async1::MyDomain; + + std::vector results; + + auto in = MakeVar(1); + auto s1 = in * 1000; + + Observe(s1, [&] (int v) { + results.push_back(v); + }); + + TransactionStatus st; + + D::AsyncTransaction(st, [&] { + in <<= 10; + }); + + D::AsyncTransaction(st, [&] { + in <<= 20; + }); + + D::AsyncTransaction(st, [&] { + in <<= 30; + }); + + st.Wait(); + + ASSERT_EQ(results.size(), 3); + ASSERT_TRUE(results[0] == 10000); + ASSERT_TRUE(results[1] == 20000); + ASSERT_TRUE(results[2] == 30000); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// AsyncMerging1 +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(TransactionTest, AsyncMerging1) +{ + using D = typename AsyncMerging1::MyDomain; + + std::vector results; + + auto in = MakeVar(1); + auto s1 = in * 1000; + + Observe(s1, [&] (int v) { + results.push_back(v); + }); + + TransactionStatus st; + + D::AsyncTransaction(enable_input_merging, st, [&] { + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + in <<= 10; + }); + + // Make sure other async transaction gets to start + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + // These two can still be pulled in after the first input function is done + D::AsyncTransaction(enable_input_merging, st, [&] { + in <<= 20; + }); + + D::AsyncTransaction(enable_input_merging, st, [&] { + in <<= 30; + }); + + // Can't be merged + D::AsyncTransaction(st, [&] { + in <<= 40; + }); + + // These two should be merged again + D::AsyncTransaction(enable_input_merging, st, [&] { + in <<= 50; + }); + + D::AsyncTransaction(enable_input_merging, st, [&] { + in <<= 60; + }); + + st.Wait(); + + ASSERT_EQ(results.size(), 3); + ASSERT_TRUE(results[0] == 30000); + ASSERT_TRUE(results[1] == 40000); + ASSERT_TRUE(results[2] == 60000); +} /////////////////////////////////////////////////////////////////////////////////////////////////// REGISTER_TYPED_TEST_CASE_P @@ -310,7 +403,9 @@ REGISTER_TYPED_TEST_CASE_P Concurrent1, Concurrent2, Concurrent3, - Merging1 + Merging1, + Async1, + AsyncMerging1 ); } // ~namespace From c84ca32c5b1f5b7472097cda0ee69ced2cc1f4dd Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 20 Jun 2014 13:50:32 +0200 Subject: [PATCH 158/266] Added timer to more node types. --- include/react/common/Timing.h | 16 +-- include/react/detail/graph/AlgorithmNodes.h | 110 ++++++++++++++------ include/react/detail/graph/EventNodes.h | 77 ++++++++++---- include/react/detail/graph/GraphBase.h | 5 +- include/react/detail/graph/ObserverNodes.h | 41 ++++++-- include/react/detail/graph/ReactorNodes.h | 18 +++- include/react/detail/graph/SignalNodes.h | 16 +-- 7 files changed, 205 insertions(+), 78 deletions(-) diff --git a/include/react/common/Timing.h b/include/react/common/Timing.h index cb1ae953..8c8b62a8 100644 --- a/include/react/common/Timing.h +++ b/include/react/common/Timing.h @@ -61,7 +61,9 @@ class ConditionalTimer class ScopedTimer { public: - ScopedTimer(const ConditionalTimer&); + // Note: + // Count is passed by ref so it can be set later if it's not known at time of creation + ScopedTimer(const ConditionalTimer&, const size_t& count); }; bool IsThresholdExceeded() const; @@ -79,7 +81,7 @@ class ConditionalTimer class ScopedTimer { public: - ScopedTimer(const ConditionalTimer&) {} + ScopedTimer(const ConditionalTimer&, const size_t& count) {} }; bool IsThresholdExceeded() const { return false; } @@ -103,8 +105,9 @@ class ConditionalTimer class ScopedTimer { public: - ScopedTimer(ConditionalTimer& parent) : - parent_( parent ) + ScopedTimer(ConditionalTimer& parent, const size_t& count) : + parent_( parent ), + count_( count ) { if (!parent_.shouldMeasure_) return; @@ -139,18 +142,19 @@ class ConditionalTimer durationUS.QuadPart *= 1000000; durationUS.QuadPart /= GetPerformanceFrequency().QuadPart; - parent_.isThresholdExceeded_ = durationUS.QuadPart > threshold; + parent_.isThresholdExceeded_ = durationUS.QuadPart > (threshold * count_); #else using std::chrono::duration_cast; using std::chrono::microseconds; parent_.isThresholdExceeded_ = - duration_cast(now() - startTime_).count() > threshold; + duration_cast(now() - startTime_).count() > (threshold * count_); #endif } ConditionalTimer& parent_; TimestampT startTime_; + const size_t& count_; }; static TimestampT now() diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index a9c4cc4a..d3559e27 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -29,7 +29,9 @@ template typename E, typename TFunc > -class IterateNode : public SignalNode +class IterateNode : + public SignalNode, + public UpdateTimingPolicy { using Engine = typename IterateNode::Engine; @@ -58,26 +60,40 @@ class IterateNode : public SignalNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - S newValue = this->value_; - for (const auto& e : events_->Events()) - newValue = func_(e, newValue); + bool changed = false; + + {// timer + using TimerT = typename IterateNode::ScopedUpdateTimer; + TimerT scopedTimer( *this, events_->Events().size() ); + + S newValue = this->value_; + + for (const auto& e : events_->Events()) + newValue = func_(e, newValue); + + if (! Equals(newValue, this->value_)) + { + this->value_ = std::move(newValue); + changed = true; + } + }// ~timer REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - if (! Equals(newValue, this->value_)) - { - this->value_ = std::move(newValue); + 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; } + virtual const char* GetNodeType() const override { return "IterateNode"; } + virtual int DependencyCount() const override { return 1; } + + virtual bool IsHeavyweight() const override + { + return this->IsUpdateThresholdExceeded(); + } private: std::shared_ptr> events_; @@ -95,7 +111,9 @@ template typename E, typename TFunc > -class IterateByRefNode : public SignalNode +class IterateByRefNode : + public SignalNode, + public UpdateTimingPolicy { using Engine = typename IterateByRefNode::Engine; @@ -124,8 +142,13 @@ class IterateByRefNode : public SignalNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - for (const auto& e : events_->Events()) - func_(e, this->value_); + {// timer + using TimerT = typename IterateByRefNode::ScopedUpdateTimer; + TimerT scopedTimer( *this, events_->Events().size() ); + + for (const auto& e : events_->Events()) + func_(e, this->value_); + }// ~timer REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); @@ -137,6 +160,11 @@ class IterateByRefNode : public SignalNode virtual const char* GetNodeType() const override { return "IterateByRefNode"; } virtual int DependencyCount() const override { return 1; } + virtual bool IsHeavyweight() const override + { + return this->IsUpdateThresholdExceeded(); + } + protected: TFunc func_; @@ -154,7 +182,9 @@ template typename TFunc, typename ... TDepValues > -class SyncedIterateNode : public SignalNode +class SyncedIterateNode : + public SignalNode, + public UpdateTimingPolicy { using Engine = typename SyncedIterateNode::Engine; @@ -215,7 +245,10 @@ class SyncedIterateNode : public SignalNode GetObjectId(*this), turn.Id())); if (! events_->Events().empty()) - { + {// timer + using TimerT = typename SyncedIterateNode::ScopedUpdateTimer; + TimerT scopedTimer( *this, events_->Events().size() ); + S newValue = this->value_; for (const auto& e : events_->Events()) @@ -226,7 +259,7 @@ class SyncedIterateNode : public SignalNode changed = true; this->value_ = std::move(newValue); } - } + }// ~timer REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); @@ -240,6 +273,11 @@ class SyncedIterateNode : public SignalNode virtual const char* GetNodeType() const override { return "SyncedIterateNode"; } virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } + virtual bool IsHeavyweight() const override + { + return this->IsUpdateThresholdExceeded(); + } + private: using DepHolderT = std::tuple>...>; @@ -260,7 +298,9 @@ template typename TFunc, typename ... TDepValues > -class SyncedIterateByRefNode : public SignalNode +class SyncedIterateByRefNode : + public SignalNode, + public UpdateTimingPolicy { using Engine = typename SyncedIterateByRefNode::Engine; @@ -321,12 +361,15 @@ class SyncedIterateByRefNode : public SignalNode GetObjectId(*this), turn.Id())); if (! events_->Events().empty()) - { + {// timer + using TimerT = typename SyncedIterateByRefNode::ScopedUpdateTimer; + TimerT scopedTimer( *this, events_->Events().size() ); + for (const auto& e : events_->Events()) apply(EvalFunctor_( e, this->value_, func_ ), deps_); changed = true; - } + }// ~timer REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); @@ -340,6 +383,11 @@ class SyncedIterateByRefNode : public SignalNode virtual const char* GetNodeType() const override { return "SyncedIterateByRefNode"; } virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } + virtual bool IsHeavyweight() const override + { + return this->IsUpdateThresholdExceeded(); + } + private: using DepHolderT = std::tuple>...>; @@ -409,7 +457,7 @@ class HoldNode : public SignalNode Engine::OnNodeIdlePulse(*this, turn); } - virtual int DependencyCount() const override { return 1; } + virtual int DependencyCount() const override { return 1; } private: const std::shared_ptr> events_; @@ -447,9 +495,6 @@ class SnapshotNode : public SignalNode Engine::OnNodeDestroy(*this); } - virtual const char* GetNodeType() const override { return "SnapshotNode"; } - virtual int DependencyCount() const override { return 2; } - virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; @@ -482,6 +527,9 @@ class SnapshotNode : public SignalNode 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_; @@ -514,9 +562,6 @@ class MonitorNode : public EventStreamNode Engine::OnNodeDestroy(*this); } - virtual const char* GetNodeType() const override { return "MonitorNode"; } - virtual int DependencyCount() const override { return 1; } - virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; @@ -538,6 +583,9 @@ class MonitorNode : public EventStreamNode 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_; }; @@ -574,9 +622,6 @@ class PulseNode : public EventStreamNode Engine::OnNodeDestroy(*this); } - virtual const char* GetNodeType() const override { return "PulseNode"; } - virtual int DependencyCount() const override { return 2; } - virtual void Tick(void* turnPtr) override { typedef typename D::Engine::TurnT TurnT; @@ -600,6 +645,9 @@ class PulseNode : public EventStreamNode 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_; diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 02576a08..8c8688e2 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -384,7 +384,9 @@ template typename E, typename TOp > -class EventOpNode : public EventStreamNode +class EventOpNode : + public EventStreamNode, + public UpdateTimingPolicy { using Engine = typename EventOpNode::Engine; @@ -405,9 +407,6 @@ class EventOpNode : public EventStreamNode Engine::OnNodeDestroy(*this); } - virtual const char* GetNodeType() const override { return "EventOpNode"; } - virtual int DependencyCount() const override { return TOp::dependency_count; } - virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; @@ -418,7 +417,17 @@ class EventOpNode : public EventStreamNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - op_.Collect(turn, EventCollector{ this->events_ }); + {// timer + size_t count = 0; + using TimerT = typename EventOpNode::ScopedUpdateTimer; + TimerT scopedTimer( *this, count ); + + op_.Collect(turn, EventCollector( this->events_ )); + + // Note: Count was passed by reference, so we can still change before the dtor + // of the scoped timer is called + count = this->events_.size(); + }// ~timer REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); @@ -429,6 +438,14 @@ class EventOpNode : public EventStreamNode Engine::OnNodeIdlePulse(*this, turn); } + virtual const char* GetNodeType() const override { return "EventOpNode"; } + virtual int DependencyCount() const override { return TOp::dependency_count; } + + virtual bool IsHeavyweight() const override + { + return this->IsUpdateThresholdExceeded(); + } + TOp StealOp() { REACT_ASSERT(wasOpStolen_ == false, "Op was already stolen."); @@ -546,7 +563,9 @@ template typename TFunc, typename ... TDepValues > -class SyncedEventTransformNode : public EventStreamNode +class SyncedEventTransformNode : + public EventStreamNode, + public UpdateTimingPolicy { using Engine = typename SyncedEventTransformNode::Engine; @@ -576,9 +595,6 @@ class SyncedEventTransformNode : public EventStreamNode Engine::OnNodeDestroy(*this); } - virtual const char* GetNodeType() const override { return "SyncedEventTransformNode"; } - virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } - virtual void Tick(void* turnPtr) override { struct EvalFunctor_ @@ -608,8 +624,13 @@ class SyncedEventTransformNode : public EventStreamNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - for (const auto& e : source_->Events()) - this->events_.push_back(apply(EvalFunctor_( e, func_ ), deps_)); + {// timer + using TimerT = typename SyncedEventTransformNode::ScopedUpdateTimer; + TimerT scopedTimer( *this, source_->Events().size() ); + + for (const auto& e : source_->Events()) + this->events_.push_back(apply(EvalFunctor_( e, func_ ), deps_)); + }// ~timer REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); @@ -620,6 +641,14 @@ class SyncedEventTransformNode : public EventStreamNode Engine::OnNodeIdlePulse(*this, turn); } + virtual const char* GetNodeType() const override { return "SyncedEventTransformNode"; } + virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } + + virtual bool IsHeavyweight() const override + { + return this->IsUpdateThresholdExceeded(); + } + private: using DepHolderT = std::tuple>...>; @@ -639,7 +668,9 @@ template typename TFunc, typename ... TDepValues > -class SyncedEventFilterNode : public EventStreamNode +class SyncedEventFilterNode : + public EventStreamNode, + public UpdateTimingPolicy { using Engine = typename SyncedEventFilterNode::Engine; @@ -669,9 +700,6 @@ class SyncedEventFilterNode : public EventStreamNode Engine::OnNodeDestroy(*this); } - virtual const char* GetNodeType() const override { return "SyncedEventFilterNode"; } - virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } - virtual void Tick(void* turnPtr) override { struct EvalFunctor_ @@ -701,9 +729,14 @@ class SyncedEventFilterNode : public EventStreamNode REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); - for (const auto& e : source_->Events()) - if (apply(EvalFunctor_( e, filter_ ), deps_)) - this->events_.push_back(e); + {// timer + using TimerT = typename SyncedEventFilterNode::ScopedUpdateTimer; + TimerT scopedTimer( *this, source_->Events().size() ); + + for (const auto& e : source_->Events()) + if (apply(EvalFunctor_( e, filter_ ), deps_)) + this->events_.push_back(e); + }// ~timer REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); @@ -714,6 +747,14 @@ class SyncedEventFilterNode : public EventStreamNode Engine::OnNodeIdlePulse(*this, turn); } + virtual const char* GetNodeType() const override { return "SyncedEventFilterNode"; } + virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } + + virtual bool IsHeavyweight() const override + { + return this->IsUpdateThresholdExceeded(); + } + private: using DepHolderT = std::tuple>...>; diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index e182d2bc..a8d1d1ad 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -39,8 +39,9 @@ class UpdateTimingPolicy : class ScopedUpdateTimer : private ScopedTimer { public: - ScopedUpdateTimer(UpdateTimingPolicy& parent) : - ScopedTimer( parent ) {} + ScopedUpdateTimer(UpdateTimingPolicy& parent, const size_t& count) : + ScopedTimer( parent, count ) + {} }; bool IsUpdateThresholdExceeded() const { return this->IsThresholdExceeded(); } diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index e434c1ac..2bc4b02b 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -61,7 +61,9 @@ template typename S, typename TFunc > -class SignalObserverNode : public ObserverNode +class SignalObserverNode : + public ObserverNode, + public UpdateTimingPolicy { using Engine = typename SignalObserverNode::Engine; @@ -98,8 +100,13 @@ class SignalObserverNode : public ObserverNode bool shouldDetach = false; if (auto p = subject_.lock()) + {// timer + using TimerT = typename SignalObserverNode::ScopedUpdateTimer; + TimerT scopedTimer( *this, 1 ); + if (func_(p->ValueRef()) == ObserverAction::stop_and_detach) shouldDetach = true; + }// ~timer if (shouldDetach) DomainSpecificInputManager::Instance().Continuation() @@ -134,7 +141,9 @@ template typename E, typename TFunc > -class EventObserverNode : public ObserverNode +class EventObserverNode : + public ObserverNode, + public UpdateTimingPolicy { using Engine = typename EventObserverNode::Engine; @@ -171,7 +180,10 @@ class EventObserverNode : public ObserverNode bool shouldDetach = false; if (auto p = subject_.lock()) - { + {// timer + using TimerT = typename EventObserverNode::ScopedUpdateTimer; + TimerT scopedTimer( *this, p->Events().size() ); + for (const auto& e : p->Events()) { if (func_(e) == ObserverAction::stop_and_detach) @@ -180,7 +192,7 @@ class EventObserverNode : public ObserverNode break; } } - } + }// ~timer if (shouldDetach) DomainSpecificInputManager::Instance().Continuation() @@ -217,7 +229,9 @@ template typename TFunc, typename ... TDepValues > -class SyncedObserverNode : public ObserverNode +class SyncedObserverNode : + public ObserverNode, + public UpdateTimingPolicy { using Engine = typename SyncedObserverNode::Engine; @@ -278,14 +292,19 @@ class SyncedObserverNode : public ObserverNode // so make sure source doesnt contain events from last turn p->SetCurrentTurn(turn); - for (const auto& e : p->Events()) - { - if (apply(EvalFunctor_( e, func_ ), deps_) == ObserverAction::stop_and_detach) + {// timer + using TimerT = typename SyncedObserverNode::ScopedUpdateTimer; + TimerT scopedTimer( *this, p->Events().size() ); + + for (const auto& e : p->Events()) { - shouldDetach = true; - break; + if (apply(EvalFunctor_( e, func_ ), deps_) == ObserverAction::stop_and_detach) + { + shouldDetach = true; + break; + } } - } + }// ~timer } if (shouldDetach) diff --git a/include/react/detail/graph/ReactorNodes.h b/include/react/detail/graph/ReactorNodes.h index aa56287a..3b300380 100644 --- a/include/react/detail/graph/ReactorNodes.h +++ b/include/react/detail/graph/ReactorNodes.h @@ -21,6 +21,7 @@ #include "GraphBase.h" #include "EventNodes.h" +#include "SignalNodes.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -78,7 +79,7 @@ class ReactorNode : // First blocking event is not initiated by Tick() but after loop creation. NodeBase* p = mainLoop_.get(); - REACT_ASSERT(p != nullptr, "StartLoop: first depPtr was null"); + assert(p != nullptr); Engine::OnNodeAttach(*this, *p); @@ -125,7 +126,7 @@ class ReactorNode : while (! checkEvent(events)) (*curOutPtr_)(nullptr); - REACT_ASSERT(turnPtr_ != nullptr, "Await: turnPtr_ was null"); + assert(turnPtr_ != nullptr); Engine::OnDynamicNodeDetach(*this, *events, *turnPtr_); --depCount_; @@ -189,6 +190,19 @@ class ReactorNode : --depCount_; } + //template + //S& Get(const std::shared_ptr>& sig) + //{ + // // Attach to target signal node + // (*curOutPtr_)(sig.get()); + + // Engine::OnDynamicNodeDetach(*this, *events, *turnPtr_); + // --depCount_; + + // auto index = offsets_[reinterpret_cast(events.get())]++; + // return events->Events()[index]; + //} + private: template bool checkEvent(const std::shared_ptr>& events) diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index c9ff4f26..7715cff2 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -257,9 +257,6 @@ class SignalOpNode : Engine::OnNodeDestroy(*this); } - virtual const char* GetNodeType() const override { return "SignalOpNode"; } - virtual int DependencyCount() const override { return TOp::dependency_count; } - virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; @@ -272,7 +269,7 @@ class SignalOpNode : {// timer using TimerT = typename SignalOpNode::ScopedUpdateTimer; - TimerT scopedTimer( *this ); + TimerT scopedTimer( *this, 1 ); S newValue = op_.Evaluate(); @@ -292,6 +289,9 @@ class SignalOpNode : Engine::OnNodeIdlePulse(*this, turn); } + virtual const char* GetNodeType() const override { return "SignalOpNode"; } + virtual int DependencyCount() const override { return TOp::dependency_count; } + virtual bool IsHeavyweight() const override { return this->IsUpdateThresholdExceeded(); @@ -342,10 +342,6 @@ class FlattenNode : public SignalNode Engine::OnNodeDestroy(*this); } - virtual const char* GetNodeType() const override { return "FlattenNode"; } - virtual bool IsDynamicNode() const override { return true; } - virtual int DependencyCount() const override { return 2; } - virtual void Tick(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; @@ -382,6 +378,10 @@ class FlattenNode : public SignalNode } } + virtual const char* GetNodeType() const override { return "FlattenNode"; } + virtual bool IsDynamicNode() const override { return true; } + virtual int DependencyCount() const override { return 2; } + private: std::shared_ptr> outer_; std::shared_ptr> inner_; From d38e94e6a5b65c8d595d7306336614372dd062fd Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 20 Jun 2014 13:51:03 +0200 Subject: [PATCH 159/266] Added tests for synced filter and synced transform. --- tests/src/OperationsTest.h | 90 +++++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/tests/src/OperationsTest.h b/tests/src/OperationsTest.h index 2b5a700e..60a8e98d 100644 --- a/tests/src/OperationsTest.h +++ b/tests/src/OperationsTest.h @@ -583,6 +583,92 @@ TYPED_TEST_P(OperationsTest, SyncedIterate2) } } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventFilter1 +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(OperationsTest, SyncedEventFilter1) +{ + using D = typename SyncedEventFilter1::MyDomain; + + using std::string; + + std::queue results; + + auto in = MakeEventSource(); + + auto sig1 = MakeVar(1338); + auto sig2 = MakeVar(1336); + + auto filtered = Filter( + in, + With(sig1, sig2), + [] (const string& s, int sig1, int sig2) { + return s == "Hello World" && sig1 > sig2; + }); + + + Observe(filtered, [&] (const string& s) + { + results.push(s); + }); + + in << string("Hello Worlt") << string("Hello World") << string("Hello Vorld"); + sig1 <<= 1335; + in << string("Hello Vorld"); + + ASSERT_FALSE(results.empty()); + ASSERT_EQ(results.front(), "Hello World"); + results.pop(); + + ASSERT_TRUE(results.empty()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventTransform1 +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(OperationsTest, SyncedEventTransform1) +{ + using D = typename SyncedEventTransform1::MyDomain; + + using std::string; + + std::vector results; + + auto in1 = MakeEventSource(); + auto in2 = MakeEventSource(); + + auto merged = Merge(in1, in2); + + auto first = MakeVar(string("Ace")); + auto last = MakeVar(string("McSteele")); + + auto transformed = Transform( + merged, + With(first, last), + [] (string s, const string& first, const string& last) -> string { + std::transform(s.begin(), s.end(), s.begin(), ::toupper); + s += string(", ") + first + string(" ") + last; + return s; + }); + + Observe(transformed, [&] (const string& s) { + results.push_back(s); + }); + + in1 << string("Hello Worlt") << string("Hello World"); + + D::DoTransaction([&] { + in2 << string("Hello Vorld"); + first.Set(string("Alice")); + last.Set(string("Anderson")); + }); + + ASSERT_EQ(results.size(), 3); + ASSERT_TRUE(std::find(results.begin(), results.end(), "HELLO WORLT, Ace McSteele") != results.end()); + ASSERT_TRUE(std::find(results.begin(), results.end(), "HELLO WORLD, Ace McSteele") != results.end()); + ASSERT_TRUE(std::find(results.begin(), results.end(), "HELLO VORLD, Alice Anderson") != results.end()); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// REGISTER_TYPED_TEST_CASE_P ( @@ -598,7 +684,9 @@ REGISTER_TYPED_TEST_CASE_P IterateByRef2, SyncedTransform1, SyncedIterate1, - SyncedIterate2 + SyncedIterate2, + SyncedEventFilter1, + SyncedEventTransform1 ); } // ~namespace From 8045263ab61603afec840726322b0aa7f16a9897 Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 20 Jun 2014 15:49:30 +0200 Subject: [PATCH 160/266] Added continuation test cases. --- tests/src/TransactionTest.h | 112 +++++++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/tests/src/TransactionTest.h b/tests/src/TransactionTest.h index 4dc0f99c..efecffe7 100644 --- a/tests/src/TransactionTest.h +++ b/tests/src/TransactionTest.h @@ -14,6 +14,9 @@ #include "react/Domain.h" #include "react/Signal.h" +#include "react/Event.h" +#include "react/Observer.h" +#include "react/Algorithm.h" /////////////////////////////////////////////////////////////////////////////////////////////////// namespace { @@ -396,6 +399,110 @@ TYPED_TEST_P(TransactionTest, AsyncMerging1) ASSERT_TRUE(results[2] == 60000); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Continuation1 +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(TransactionTest, Continuation1) +{ + using D = typename Continuation1::MyDomain; + + std::vector results; + + auto in = MakeVar(0); + + auto cont = MakeContinuation(in, [&] (int v) { + results.push_back(v); + + if (v < 10) + in <<= v + 1; + }); + + in <<= 1; + + ASSERT_EQ(results.size(), 10); + for (int i=0; i<10; i++) + ASSERT_TRUE(results[i] == i+1); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Continuation2 +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(TransactionTest, Continuation2) +{ + using L = typename Continuation2::MyDomain; + + REACTIVE_DOMAIN(R) + + std::vector results; + + auto srcL = MakeVar(0); + auto srcR = MakeVar(0); + + auto contL = MakeContinuation(srcL, [&] (int v) { + results.push_back(v); + if (v < 10) + srcR <<= v+1; + }); + + auto contR = MakeContinuation(Monitor(srcR), [&] (int v) { + results.push_back(v); + if (v < 10) + srcL <<= v+1; + }); + + srcL <<= 1; + + ASSERT_EQ(results.size(), 10); + for (int i=0; i<10; i++) + ASSERT_TRUE(results[i] == i+1); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Continuation3 +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(TransactionTest, Continuation3) +{ + using L = typename Continuation3::MyDomain; + + REACTIVE_DOMAIN(R) + + std::vector results; + + auto srcL = MakeVar(0); + auto depL1 = MakeVar(100); + auto depL2 = MakeVar(10); + auto srcR = MakeVar(0); + + auto contL = MakeContinuation( + Monitor(srcL), + With(depL1, depL2), + [&] (int v, int depL1, int depL2) { + ASSERT_EQ(depL1, v*100); + ASSERT_EQ(depL2, v*10); + results.push_back(v); + if (v < 10) + srcR <<= v+1; + }); + + auto contR = MakeContinuation( + Monitor(srcR), + [&] (int v) { + results.push_back(v); + + v++; + depL1 <<= v*100; + depL2 <<= v*10; + if (v < 10) + srcL <<= v; + }); + + srcL <<= 1; + + ASSERT_EQ(results.size(), 10); + for (int i=0; i<10; i++) + ASSERT_TRUE(results[i] == i+1); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// REGISTER_TYPED_TEST_CASE_P ( @@ -405,7 +512,10 @@ REGISTER_TYPED_TEST_CASE_P Concurrent3, Merging1, Async1, - AsyncMerging1 + AsyncMerging1, + Continuation1, + Continuation2, + Continuation3 ); } // ~namespace From d63c66a05118d761b7c62d146dfd8c5cfea731e2 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sat, 21 Jun 2014 14:03:15 +0200 Subject: [PATCH 161/266] enable_input_merging -> allow_merging --- include/react/Domain.h | 2 +- include/react/detail/ReactiveInput.h | 10 +++++----- tests/src/TransactionTest.h | 18 +++++++++--------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/include/react/Domain.h b/include/react/Domain.h index 4fa6bc9d..83ebcd70 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -65,7 +65,7 @@ class SignalPack; using REACT_IMPL::TurnFlagsT; //ETurnFlags -using REACT_IMPL::enable_input_merging; +using REACT_IMPL::allow_merging; #ifdef REACT_ENABLE_LOGGING using REACT_IMPL::EventLog; diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index fcd208a6..46318c8a 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -44,7 +44,7 @@ using TransactionFuncT = std::function; enum { - enable_input_merging = 1 << 0 + allow_merging = 1 << 0 }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -278,7 +278,7 @@ class InputManager : { // Attempt to add input to another turn. // If successful, blocks until other turn is done and returns. - bool canMerge = (flags & enable_input_merging) != 0; + bool canMerge = (flags & allow_merging) != 0; if (canMerge && Engine::TryMergeSync(std::forward(func))) return; @@ -454,7 +454,7 @@ class InputManager : skipPop = false; // First try to merge to an existing synchronous item in the queue - bool canMerge = (item.Flags & enable_input_merging) != 0; + bool canMerge = (item.Flags & allow_merging) != 0; if (canMerge && Engine::TryMergeAsync(std::move(item.Func), std::move(item.StatusPtr))) continue; @@ -487,7 +487,7 @@ class InputManager : uint extraCount = 0; while (extraCount < 100 && asyncQueue_.try_pop(item)) { - bool canMergeNext = (item.Flags & enable_input_merging) != 0; + bool canMergeNext = (item.Flags & allow_merging) != 0; if (canMergeNext) { item.Func(); @@ -540,7 +540,7 @@ class InputManager : void processContinuationData(TurnFlagsT flags) { // No merging for continuations - flags &= ~enable_input_merging; + flags &= ~allow_merging; continuation_.template DetachQueuedObservers(); diff --git a/tests/src/TransactionTest.h b/tests/src/TransactionTest.h index efecffe7..4452edb3 100644 --- a/tests/src/TransactionTest.h +++ b/tests/src/TransactionTest.h @@ -268,25 +268,25 @@ TYPED_TEST_P(TransactionTest, Merging1) // Todo: improve this as it'll fail occasionally shouldSpin = true; std::thread t1([&] { - D::DoTransaction(enable_input_merging, [&] { + D::DoTransaction(allow_merging, [&] { n1 <<= 2; }); }); std::this_thread::sleep_for(std::chrono::milliseconds(2000)); std::thread t2([&] { - D::DoTransaction(enable_input_merging, [&] { + D::DoTransaction(allow_merging, [&] { n1 <<= 3; }); }); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::thread t3([&] { - D::DoTransaction(enable_input_merging, [&] { + D::DoTransaction(allow_merging, [&] { n1 <<= 4; }); }); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::thread t4([&] { - D::DoTransaction(enable_input_merging, [&] { + D::DoTransaction(allow_merging, [&] { n1 <<= 5; }); @@ -360,7 +360,7 @@ TYPED_TEST_P(TransactionTest, AsyncMerging1) TransactionStatus st; - D::AsyncTransaction(enable_input_merging, st, [&] { + D::AsyncTransaction(allow_merging, st, [&] { std::this_thread::sleep_for(std::chrono::milliseconds(1000)); in <<= 10; }); @@ -369,11 +369,11 @@ TYPED_TEST_P(TransactionTest, AsyncMerging1) std::this_thread::sleep_for(std::chrono::milliseconds(10)); // These two can still be pulled in after the first input function is done - D::AsyncTransaction(enable_input_merging, st, [&] { + D::AsyncTransaction(allow_merging, st, [&] { in <<= 20; }); - D::AsyncTransaction(enable_input_merging, st, [&] { + D::AsyncTransaction(allow_merging, st, [&] { in <<= 30; }); @@ -383,11 +383,11 @@ TYPED_TEST_P(TransactionTest, AsyncMerging1) }); // These two should be merged again - D::AsyncTransaction(enable_input_merging, st, [&] { + D::AsyncTransaction(allow_merging, st, [&] { in <<= 50; }); - D::AsyncTransaction(enable_input_merging, st, [&] { + D::AsyncTransaction(allow_merging, st, [&] { in <<= 60; }); From 7e6235b565653c08b254a2f88ec4e59c452830fb Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 22 Jun 2014 17:31:14 +0200 Subject: [PATCH 162/266] Refactored transactions. Before, propagation engines could implement custom concurrency models, but that's not needed anymore. * Transaction and turn data are now decoupled. The engine interface no longer includes transaction related stuff. * REACTIVE_DOMAIN macro now takes mode and engine separately. --- include/react/Domain.h | 125 +++++- include/react/detail/EngineBase.h | 226 +---------- include/react/detail/IReactiveEngine.h | 56 +-- include/react/detail/ReactiveInput.h | 377 ++++++++++++++---- .../react/detail/graph/ContinuationNodes.h | 6 +- include/react/detail/graph/ObserverNodes.h | 6 +- include/react/engine/PulsecountEngine.h | 48 +-- include/react/engine/SubtreeEngine.h | 52 +-- include/react/engine/ToposortEngine.h | 82 ++-- src/engine/PulsecountEngine.cpp | 41 +- src/engine/SubtreeEngine.cpp | 55 +-- src/engine/ToposortEngine.cpp | 62 +-- 12 files changed, 545 insertions(+), 591 deletions(-) diff --git a/include/react/Domain.h b/include/react/Domain.h index 83ebcd70..13c7bfd0 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -23,6 +23,9 @@ #include "react/logging/EventRecords.h" #endif //REACT_ENABLE_LOGGING +// Include all engines for convenience +#include "react/engine/PulsecountEngine.h" +#include "react/engine/SubtreeEngine.h" #include "react/engine/ToposortEngine.h" /*****************************************/ REACT_BEGIN /*****************************************/ @@ -62,14 +65,32 @@ class Reactor; template class SignalPack; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Common types & constants +/////////////////////////////////////////////////////////////////////////////////////////////////// using REACT_IMPL::TurnFlagsT; -//ETurnFlags +// ETurnFlags +using REACT_IMPL::ETurnFlags; using REACT_IMPL::allow_merging; #ifdef REACT_ENABLE_LOGGING using REACT_IMPL::EventLog; -#endif //REACT_ENABLE_LOGGING +#endif //REACT_ENABLE_LOGGING + +// Domain modes +enum EDomainMode +{ + sequential, + sequential_concurrent, + parallel, + parallel_concurrent +}; + +// Expose enum types so aliases for engines can be declared, but don't +// expose the actual enum values as they are reserved for internal use. +using REACT_IMPL::EInputMode; +using REACT_IMPL::EPropagationMode; /////////////////////////////////////////////////////////////////////////////////////////////////// /// TransactionStatus @@ -131,10 +152,10 @@ class DomainBase REACT_IMPL::NodeUpdateTimerEnabled::value; static const bool is_concurrent = - REACT_IMPL::IsConcurrentEngine::value; + Policy::input_mode == REACT_IMPL::concurrent_input; static const bool is_parallel = - REACT_IMPL::IsParallelEngine::value; + Policy::propagation_mode == REACT_IMPL::parallel_propagation; /////////////////////////////////////////////////////////////////////////////////////////////// /// Aliases for reactives of this domain @@ -350,15 +371,98 @@ auto MakeContinuation(const Events& trigger, /***************************************/ REACT_IMPL_BEGIN /**************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Policy +/// ModeSelector - Translate domain mode to individual propagation and input modes +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct ModeSelector; + +template <> +struct ModeSelector +{ + static const EInputMode input = consecutive_input; + static const EPropagationMode propagation = sequential_propagation; +}; + +template <> +struct ModeSelector +{ + static const EInputMode input = concurrent_input; + static const EPropagationMode propagation = sequential_propagation; +}; + +template <> +struct ModeSelector +{ + static const EInputMode input = consecutive_input; + static const EPropagationMode propagation = parallel_propagation; +}; + +template <> +struct ModeSelector +{ + static const EInputMode input = concurrent_input; + static const EPropagationMode propagation = parallel_propagation; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// GetDefaultEngine - Get default engine type for given propagation mode /////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct GetDefaultEngine; + +template <> +struct GetDefaultEngine +{ + using Type = ToposortEngine; +}; + +template <> +struct GetDefaultEngine +{ + using Type = SubtreeEngine; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EngineTypeBuilder - Instantiate the given template engine type with mode. +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct DefaultEnginePlaceholder; + +// Concrete engine template type template < - typename TEngine = ToposortEngine + EPropagationMode mode, + template class TTEngine +> +struct EngineTypeBuilder +{ + using Type = TTEngine; +}; + +// Placeholder engine type - use default engine for given mode +template +< + EPropagationMode mode +> +struct EngineTypeBuilder +{ + using Type = typename GetDefaultEngine::Type; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// DomainPolicy +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + EDomainMode mode, + template class TTEngine = DefaultEnginePlaceholder > struct DomainPolicy { - using Engine = TEngine; + static const EInputMode input_mode = ModeSelector::input; + static const EPropagationMode propagation_mode = ModeSelector::propagation; + + using Engine = typename EngineTypeBuilder::Type; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -385,9 +489,10 @@ class DomainInitializer /////////////////////////////////////////////////////////////////////////////////////////////////// /// Domain definition macro /////////////////////////////////////////////////////////////////////////////////////////////////// -#define REACTIVE_DOMAIN(name, ...) \ - struct name : public REACT::DomainBase> {}; \ - REACT_IMPL::DomainInitializer< name > name ## _initializer_; +#define REACTIVE_DOMAIN(name, ...) \ + struct name : \ + public REACT::DomainBase> {}; \ + REACT_IMPL::DomainInitializer name ## _initializer_; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Define type aliases for given domain diff --git a/include/react/detail/EngineBase.h b/include/react/detail/EngineBase.h index f056b71b..ca9afad5 100644 --- a/include/react/detail/EngineBase.h +++ b/include/react/detail/EngineBase.h @@ -31,241 +31,19 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// TurnBase /////////////////////////////////////////////////////////////////////////////////////////////////// -template class TurnBase { public: - TurnBase(TurnIdT id, TurnFlagsT flags) : + inline TurnBase(TurnIdT id, TurnFlagsT flags) : id_( id ) {} - TurnIdT Id() const { return id_; } + inline TurnIdT Id() const { return id_; } private: TurnIdT id_; }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// TurnQueueManager -/////////////////////////////////////////////////////////////////////////////////////////////////// -class TurnQueueManager -{ -public: - class QueueEntry - { - public: - explicit QueueEntry(TurnFlagsT flags) : - isMergeable_( (flags & enable_input_merging) != 0 ) - {} - - inline void Append(QueueEntry& tr) - { - successor_ = &tr; - tr.blockCondition_.Block(); - } - - inline void WaitForUnblock() - { - blockCondition_.WaitForUnblock(); - } - - inline void RunMergedInputs() const - { - for (const auto& e : merged_) - e.InputFunc(); - } - - inline void UnblockSuccessors() - { - // Release merged - for (const auto& e : merged_) - { - // Note: Since a merged input is either sync or async, - // either cond or status will be null - - // Sync - if (e.Cond != nullptr) - e.Cond->Unblock(); - - // Async - else if (e.StatusPtr != nullptr) - e.StatusPtr->DecWaitCount(); - } - - // Release next thread in queue - if (successor_) - successor_->blockCondition_.Unblock(); - } - - template - inline bool TryMerge(F&& inputFunc, BlockingCondition* caller, AsyncStatePtrT&& statusPtr) - { - if (!isMergeable_) - return false; - - // Only merge if target is still blocked - bool merged = blockCondition_.RunIfBlocked([&] { - if (caller) - caller->Block(); - merged_.emplace_back(std::forward(inputFunc), caller, std::move(statusPtr)); - }); - - return merged; - } - - private: - struct MergedData - { - template - MergedData(F&& func, BlockingCondition* cond, AsyncStatePtrT&& statusPtr) : - InputFunc( std::forward(func) ), - Cond( cond ), - StatusPtr( std::move(statusPtr) ) - {} - - std::function InputFunc; - - // Blocking condition variable for sync merged - BlockingCondition* Cond; - - // Status for async merged - AsyncStatePtrT StatusPtr; - }; - - using MergedDataVectT = std::vector; - - bool isMergeable_; - QueueEntry* successor_ = nullptr; - MergedDataVectT merged_; - BlockingCondition blockCondition_; - }; - - template - inline bool TryMergeSync(F&& inputFunc) - { - bool merged = false; - - BlockingCondition caller; - - {// seqMutex_ - SeqMutexT::scoped_lock lock(seqMutex_); - - if (tail_) - merged = tail_->TryMerge(std::forward(inputFunc), &caller, nullptr); - }// ~seqMutex_ - - if (merged) - caller.WaitForUnblock(); - - return merged; - } - - template - inline bool TryMergeAsync(F&& inputFunc, AsyncStatePtrT&& statusPtr) - { - bool merged = false; - - {// seqMutex_ - SeqMutexT::scoped_lock lock(seqMutex_); - - if (tail_) - merged = tail_->TryMerge( - std::forward(inputFunc), nullptr, std::move(statusPtr)); - }// ~seqMutex_ - - return merged; - } - - inline void EnterQueue(QueueEntry& turn) - { - {// seqMutex_ - SeqMutexT::scoped_lock lock(seqMutex_); - - if (tail_) - tail_->Append(turn); - - tail_ = &turn; - }// ~seqMutex_ - - turn.WaitForUnblock(); - } - - inline void ExitQueue(QueueEntry& turn) - {// seqMutex_ - SeqMutexT::scoped_lock lock(seqMutex_); - - turn.UnblockSuccessors(); - - if (tail_ == &turn) - tail_ = nullptr; - }// ~seqMutex_ - -private: - using SeqMutexT = tbb::queuing_mutex; - - SeqMutexT seqMutex_; - QueueEntry* tail_ = nullptr; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DefaultQueueableTurn -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class DefaultQueueableTurn : - public TTurnBase, - public TurnQueueManager::QueueEntry -{ -public: - DefaultQueueableTurn(TurnIdT id, TurnFlagsT flags) : - TTurnBase( id, flags ), - TurnQueueManager::QueueEntry( flags ) - {} -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DefaultQueuingEngine -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - template class TTEngineBase, - typename TTurnBase -> -class DefaultQueuingEngine : public TTEngineBase> -{ -public: - using TurnT = DefaultQueueableTurn; - - template - bool TryMergeSync(F&& f) - { - return queueManager_.TryMergeSync(std::forward(f)); - } - - template - bool TryMergeAsync(F&& f, std::shared_ptr&& statusPtr) - { - return queueManager_.TryMergeAsync(std::forward(f), std::move(statusPtr)); - } - - void ApplyMergedInputs(TurnT& turn) - { - turn.RunMergedInputs(); - } - - void EnterTurnQueue(TurnT& turn) - { - queueManager_.EnterQueue(turn); - } - - void ExitTurnQueue(TurnT& turn) - { - queueManager_.ExitQueue(turn); - } - -private: - TurnQueueManager queueManager_; -}; - /****************************************/ REACT_IMPL_END /***************************************/ #endif // REACT_DETAIL_ENGINEBASE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/IReactiveEngine.h b/include/react/detail/IReactiveEngine.h index 5b4cb651..33aaf076 100644 --- a/include/react/detail/IReactiveEngine.h +++ b/include/react/detail/IReactiveEngine.h @@ -18,11 +18,6 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations -/////////////////////////////////////////////////////////////////////////////////////////////////// -class AsyncState; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// IReactiveEngine /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -36,17 +31,6 @@ struct IReactiveEngine using NodeT = TNode; using TurnT = TTurn; - template - bool TryMergeSync(F&& f) { return false; } - - template - bool TryMergeAsync(F&& f, std::shared_ptr&& statusPtr) { return false; } - - void ApplyMergedInputs(TurnT& turn) {} - - void EnterTurnQueue(TurnT& turn) {} - void ExitTurnQueue(TurnT& turn) {} - void OnTurnAdmissionStart(TurnT& turn) {} void OnTurnAdmissionEnd(TurnT& turn) {} @@ -86,35 +70,6 @@ struct EngineInterface return engine; } - template - static bool TryMergeSync(F&& f) - { - return Instance().TryMergeSync(std::forward(f)); - } - - template - static bool TryMergeAsync(F&& f, std::shared_ptr&& statusPtr) - { - return Instance().TryMergeAsync(std::forward(f), std::move(statusPtr)); - } - - static void ApplyMergedInputs(TurnT& turn) - { - Instance().ApplyMergedInputs(turn); - } - - static void EnterTurnQueue(TurnT& turn) - { - REACT_LOG(D::Log().template Append(turn.Id())); - Instance().EnterTurnQueue(turn); - } - - static void ExitTurnQueue(TurnT& turn) - { - REACT_LOG(D::Log().template Append(turn.Id())); - Instance().ExitTurnQueue(turn); - } - static void OnTurnAdmissionStart(TurnT& turn) { Instance().OnTurnAdmissionEnd(turn); @@ -198,8 +153,15 @@ struct EngineInterface /// Engine traits /////////////////////////////////////////////////////////////////////////////////////////////////// template struct NodeUpdateTimerEnabled : std::false_type {}; -template struct IsParallelEngine : std::false_type {}; -template struct IsConcurrentEngine : std::false_type {}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EPropagationMode +/////////////////////////////////////////////////////////////////////////////////////////////////// +enum EPropagationMode +{ + sequential_propagation, + parallel_propagation +}; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index 46318c8a..916e64fb 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -24,8 +24,11 @@ #include "tbb/concurrent_queue.h" #include "tbb/enumerable_thread_specific.h" +#include "tbb/queuing_mutex.h" +#include "IReactiveEngine.h" #include "ObserverBase.h" +#include "react/common/Concurrency.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -42,17 +45,25 @@ using TurnIdT = uint; using TurnFlagsT = uint; using TransactionFuncT = std::function; -enum +enum ETurnFlags { allow_merging = 1 << 0 }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EInputMode +/////////////////////////////////////////////////////////////////////////////////////////////////// +enum EInputMode +{ + consecutive_input, + concurrent_input +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// IContinuationTarget /////////////////////////////////////////////////////////////////////////////////////////////////// struct IContinuationTarget { - virtual void DoContinuation(TransactionFuncT&&) = 0; virtual void AsyncContinuation(TransactionFuncT&&) = 0; }; @@ -69,17 +80,17 @@ template REACT_TLS bool ThreadLocalInputState::IsTransactionActive(false); /////////////////////////////////////////////////////////////////////////////////////////////////// -/// ContinuationData +/// ContinuationManager /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ContinuationData +// Interface +template +class ContinuationManager { public: - bool HasNext() const; - void StoreContinuation(IContinuationTarget& target, TransactionFuncT&& cont); - void RunNext(); + bool HasNext() const; + void ProcessNext(); void QueueObserverForDetach(IObserver& obs); @@ -87,31 +98,31 @@ class ContinuationData void DetachQueuedObservers(); }; -// Not thread-safe +// Non thread-safe implementation template <> -class ContinuationData +class ContinuationManager { using ContDataT = std::pair; using DataQueueT = std::deque; using ObsVectT = std::vector; public: - bool HasNext() const + void StoreContinuation(IContinuationTarget& target, TransactionFuncT&& cont) { - return !storedContinuations_.empty(); + storedContinuations_.emplace_back(&target, std::move(cont)); } - void StoreContinuation(IContinuationTarget& target, TransactionFuncT&& cont) + bool HasNext() const { - storedContinuations_.emplace_back(&target, std::move(cont)); + return !storedContinuations_.empty(); } - void RunNext() + void ProcessNext() { ContDataT t = std::move(storedContinuations_.front()); storedContinuations_.pop_front(); - t.first->DoContinuation(std::move(t.second)); + t.first->AsyncContinuation(std::move(t.second)); } // Todo: Move this somewhere else @@ -135,27 +146,27 @@ class ContinuationData ObsVectT detachedObservers_; }; -// Thread-safe +// Thread-safe implementation template <> -class ContinuationData +class ContinuationManager { using ContDataT = std::pair; using DataQueueT = std::deque; using ObsVectT = std::vector; public: - bool HasNext() const - { - return contCount_ != 0; - } - void StoreContinuation(IContinuationTarget& target, TransactionFuncT&& cont) { storedContinuations_.local().emplace_back(&target, std::move(cont)); ++contCount_; } - void RunNext() + bool HasNext() const + { + return contCount_ != 0; + } + + void ProcessNext() { for (auto& v : storedContinuations_) { @@ -166,7 +177,7 @@ class ContinuationData v.pop_front(); contCount_--; - t.first->DoContinuation(std::move(t.second)); + t.first->AsyncContinuation(std::move(t.second)); break; } } @@ -240,6 +251,214 @@ class AsyncState using AsyncStatePtrT = std::shared_ptr; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// TransactionQueue +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface +template +class TransactionQueue +{ +public: + class QueueEntry + { + public: + explicit QueueEntry(TurnFlagsT flags); + void RunMergedInputs(); + }; + + template + bool TryMergeSync(F&& inputFunc); + + template + bool TryMergeAsync(F&& inputFunc, AsyncStatePtrT&& statusPtr); + + void EnterQueue(QueueEntry& turn); + void ExitQueue(QueueEntry& turn); +}; + +// Non thread-safe implementation +template <> +class TransactionQueue +{ +public: + class QueueEntry + { + public: + explicit QueueEntry(TurnFlagsT flags) {} + void RunMergedInputs() {} + }; + + template + bool TryMergeSync(F&& inputFunc) { return false; } + + template + bool TryMergeAsync(F&& inputFunc, AsyncStatePtrT&& statusPtr) { return false; } + + void EnterQueue(QueueEntry& turn) {} + void ExitQueue(QueueEntry& turn) {} +}; + +// Thread-safe implementation +template <> +class TransactionQueue +{ +public: + class QueueEntry + { + public: + explicit QueueEntry(TurnFlagsT flags) : + isMergeable_( (flags & allow_merging) != 0 ) + {} + + void Append(QueueEntry& tr) + { + successor_ = &tr; + tr.blockCondition_.Block(); + } + + void WaitForUnblock() + { + blockCondition_.WaitForUnblock(); + } + + void RunMergedInputs() const + { + for (const auto& e : merged_) + e.InputFunc(); + } + + void UnblockSuccessors() + { + // Release merged + for (const auto& e : merged_) + { + // Note: Since a merged input is either sync or async, + // either cond or status will be null + + // Sync + if (e.Cond != nullptr) + e.Cond->Unblock(); + + // Async + else if (e.StatusPtr != nullptr) + e.StatusPtr->DecWaitCount(); + } + + // Release next thread in queue + if (successor_) + successor_->blockCondition_.Unblock(); + } + + template + bool TryMerge(F&& inputFunc, BlockingCondition* caller, AsyncStatePtrT&& statusPtr) + { + if (!isMergeable_) + return false; + + // Only merge if target is still blocked + bool merged = blockCondition_.RunIfBlocked([&] { + if (caller) + caller->Block(); + merged_.emplace_back(std::forward(inputFunc), caller, std::move(statusPtr)); + }); + + return merged; + } + + private: + struct MergedData + { + template + MergedData(F&& func, BlockingCondition* cond, AsyncStatePtrT&& statusPtr) : + InputFunc( std::forward(func) ), + Cond( cond ), + StatusPtr( std::move(statusPtr) ) + {} + + std::function InputFunc; + + // Blocking condition variable for sync merged + BlockingCondition* Cond; + + // Status for async merged + AsyncStatePtrT StatusPtr; + }; + + using MergedDataVectT = std::vector; + + bool isMergeable_; + QueueEntry* successor_ = nullptr; + MergedDataVectT merged_; + BlockingCondition blockCondition_; + }; + + template + bool TryMergeSync(F&& inputFunc) + { + bool merged = false; + + BlockingCondition caller; + + {// seqMutex_ + SeqMutexT::scoped_lock lock(seqMutex_); + + if (tail_) + merged = tail_->TryMerge(std::forward(inputFunc), &caller, nullptr); + }// ~seqMutex_ + + if (merged) + caller.WaitForUnblock(); + + return merged; + } + + template + bool TryMergeAsync(F&& inputFunc, AsyncStatePtrT&& statusPtr) + { + bool merged = false; + + {// seqMutex_ + SeqMutexT::scoped_lock lock(seqMutex_); + + if (tail_) + merged = tail_->TryMerge( + std::forward(inputFunc), nullptr, std::move(statusPtr)); + }// ~seqMutex_ + + return merged; + } + + void EnterQueue(QueueEntry& turn) + { + {// seqMutex_ + SeqMutexT::scoped_lock lock(seqMutex_); + + if (tail_) + tail_->Append(turn); + + tail_ = &turn; + }// ~seqMutex_ + + turn.WaitForUnblock(); + } + + void ExitQueue(QueueEntry& turn) + {// seqMutex_ + SeqMutexT::scoped_lock lock(seqMutex_); + + turn.UnblockSuccessors(); + + if (tail_ == &turn) + tail_ = nullptr; + }// ~seqMutex_ + +private: + using SeqMutexT = tbb::queuing_mutex; + + SeqMutexT seqMutex_; + QueueEntry* tail_ = nullptr; +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// InputManager /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -256,12 +475,10 @@ class InputManager : }; using AsyncQueueT = tbb::concurrent_bounded_queue; - using ContinuationT = ContinuationData; - struct TransactionData - { - std::vector ChangedInputs; - }; + // Select between thread-safe and non thread-safe implementations + using TransactionQueueT = TransactionQueue; + using ContinuationManagerT = ContinuationManager; public: using TurnT = typename D::TurnT; @@ -279,38 +496,38 @@ class InputManager : // Attempt to add input to another turn. // If successful, blocks until other turn is done and returns. bool canMerge = (flags & allow_merging) != 0; - if (canMerge && Engine::TryMergeSync(std::forward(func))) + if (canMerge && transactionQueue_.TryMergeSync(std::forward(func))) return; bool shouldPropagate = false; - - TurnT turn( nextTurnId(), flags ); - - Engine::EnterTurnQueue(turn); + + typename TransactionQueueT::QueueEntry tr( flags ); + transactionQueue_.EnterQueue(tr); // Phase 1 - Input admission ThreadLocalInputState<>::IsTransactionActive = true; + TurnT turn( nextTurnId(), flags ); Engine::OnTurnAdmissionStart(turn); func(); - Engine::ApplyMergedInputs(turn); + tr.RunMergedInputs(); Engine::OnTurnAdmissionEnd(turn); ThreadLocalInputState<>::IsTransactionActive = false; // Phase 2 - Apply input node changes - for (auto* p : transaction_.ChangedInputs) + for (auto* p : changedInputs_) if (p->ApplyInput(&turn)) shouldPropagate = true; - transaction_.ChangedInputs.clear(); + changedInputs_.clear(); // Phase 3 - Propagate changes if (shouldPropagate) Engine::Propagate(turn); - Engine::ExitTurnQueue(turn); + transactionQueue_.ExitQueue(tr); - processContinuationData(flags); + processContinuations(flags); } template @@ -348,21 +565,23 @@ class InputManager : } } - //IContinuationTarget - virtual void DoContinuation(TransactionFuncT&& cont) override + //IContinuationTarget + virtual void AsyncContinuation(TransactionFuncT&& cont) override { DoTransaction(0, std::move(cont)); + // Todo: fixme + //AsyncTransaction(0, nullptr, std::move(cont)); } - - virtual void AsyncContinuation(TransactionFuncT&& cont) override + //~IContinuationTarget + + void StoreContinuation(IContinuationTarget& target, TransactionFuncT&& cont) { - AsyncTransaction(0, nullptr, std::move(cont)); + continuationManager_.StoreContinuation(target, std::move(cont)); } - //~IContinuationTarget - ContinuationT& Continuation() + void QueueObserverForDetach(IObserver& obs) { - return continuation_; + continuationManager_.QueueObserverForDetach(obs); } private: @@ -380,33 +599,34 @@ class InputManager : template void addSimpleInput(R& r, V&& v) { - TurnT turn( nextTurnId(), 0 ); + typename TransactionQueueT::QueueEntry tr( 0 ); - Engine::EnterTurnQueue(turn); + transactionQueue_.EnterQueue(tr); + TurnT turn( nextTurnId(), 0 ); Engine::OnTurnAdmissionStart(turn); r.AddInput(std::forward(v)); - Engine::ApplyMergedInputs(turn); + tr.RunMergedInputs(); Engine::OnTurnAdmissionEnd(turn); if (r.ApplyInput(&turn)) Engine::Propagate(turn); - Engine::ExitTurnQueue(turn); + transactionQueue_.ExitQueue(tr); - processContinuationData(0); + processContinuations(0); } template void modifySimpleInput(R& r, const F& func) { - TurnT turn( nextTurnId(), 0 ); + typename TransactionQueueT::QueueEntry tr( 0 ); - Engine::EnterTurnQueue(turn); + transactionQueue_.EnterQueue(tr); + TurnT turn( nextTurnId(), 0 ); Engine::OnTurnAdmissionStart(turn); r.ModifyInput(func); - Engine::ApplyMergedInputs(turn); Engine::OnTurnAdmissionEnd(turn); // Return value, will always be true @@ -414,9 +634,9 @@ class InputManager : Engine::Propagate(turn); - Engine::ExitTurnQueue(turn); + transactionQueue_.ExitQueue(tr); - processContinuationData(0); + processContinuations(0); } // This input is part of an active transaction @@ -424,14 +644,14 @@ class InputManager : void addTransactionInput(R& r, V&& v) { r.AddInput(std::forward(v)); - transaction_.ChangedInputs.push_back(&r); + changedInputs_.push_back(&r); } template void modifyTransactionInput(R& r, const F& func) { r.ModifyInput(func); - transaction_.ChangedInputs.push_back(&r); + changedInputs_.push_back(&r); } void processAsyncQueue() @@ -455,15 +675,18 @@ class InputManager : // First try to merge to an existing synchronous item in the queue bool canMerge = (item.Flags & allow_merging) != 0; - if (canMerge && Engine::TryMergeAsync(std::move(item.Func), std::move(item.StatusPtr))) + if (canMerge && transactionQueue_.TryMergeAsync( + std::move(item.Func), + std::move(item.StatusPtr))) continue; bool shouldPropagate = false; - TurnT turn( nextTurnId(), item.Flags ); - // Blocks until turn is at the front of the queue - Engine::EnterTurnQueue(turn); + typename TransactionQueueT::QueueEntry tr( item.Flags ); + transactionQueue_.EnterQueue(tr); + + TurnT turn( nextTurnId(), item.Flags ); // Phase 1 - Input admission ThreadLocalInputState<>::IsTransactionActive = true; @@ -474,7 +697,7 @@ class InputManager : item.Func(); // Merged inputs that arrived while this item was queued - Engine::ApplyMergedInputs(turn); + tr.RunMergedInputs(); // Save data, because item might be re-used for next input savedStatusPtr = std::move(item.StatusPtr); @@ -514,18 +737,18 @@ class InputManager : ThreadLocalInputState<>::IsTransactionActive = false; // Phase 2 - Apply input node changes - for (auto* p : transaction_.ChangedInputs) + for (auto* p : changedInputs_) if (p->ApplyInput(&turn)) shouldPropagate = true; - transaction_.ChangedInputs.clear(); + changedInputs_.clear(); // Phase 3 - Propagate changes if (shouldPropagate) Engine::Propagate(turn); - Engine::ExitTurnQueue(turn); + transactionQueue_.ExitQueue(tr); - processContinuationData(savedFlags); + processContinuations(savedFlags); // Decrement transaction status counts of processed items if (savedStatusPtr != nullptr) @@ -537,27 +760,29 @@ class InputManager : } } - void processContinuationData(TurnFlagsT flags) + void processContinuations(TurnFlagsT flags) { // No merging for continuations flags &= ~allow_merging; - continuation_.template DetachQueuedObservers(); + continuationManager_.template DetachQueuedObservers(); - while (continuation_.HasNext()) + while (continuationManager_.HasNext()) { - continuation_.RunNext(); - continuation_.template DetachQueuedObservers(); + continuationManager_.ProcessNext(); + continuationManager_.template DetachQueuedObservers(); } } + TransactionQueueT transactionQueue_; + ContinuationManagerT continuationManager_; + AsyncQueueT asyncQueue_; std::thread asyncWorker_; std::atomic nextTurnId_{ 0 }; - TransactionData transaction_; - ContinuationT continuation_; + std::vector changedInputs_; }; template diff --git a/include/react/detail/graph/ContinuationNodes.h b/include/react/detail/graph/ContinuationNodes.h index 4e3749de..1b165370 100644 --- a/include/react/detail/graph/ContinuationNodes.h +++ b/include/react/detail/graph/ContinuationNodes.h @@ -98,7 +98,7 @@ class SignalContinuationNode : public ContinuationNode } ); - DomainSpecificInputManager::Instance().Continuation() + DomainSpecificInputManager::Instance() .StoreContinuation( DomainSpecificInputManager::Instance(), std::move(cont)); @@ -171,7 +171,7 @@ class EventContinuationNode : public ContinuationNode TransactionFuncT cont2 = [] { return; }; - DomainSpecificInputManager::Instance().Continuation() + DomainSpecificInputManager::Instance() .StoreContinuation( DomainSpecificInputManager::Instance(), std::move(cont)); @@ -289,7 +289,7 @@ class SyncedContinuationNode : public ContinuationNode } }; - DomainSpecificInputManager::Instance().Continuation() + DomainSpecificInputManager::Instance() .StoreContinuation( DomainSpecificInputManager::Instance(), std::move(cont)); diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index 2bc4b02b..2def3890 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -109,7 +109,7 @@ class SignalObserverNode : }// ~timer if (shouldDetach) - DomainSpecificInputManager::Instance().Continuation() + DomainSpecificInputManager::Instance() .QueueObserverForDetach(*this); REACT_LOG(D::Log().template Append( @@ -195,7 +195,7 @@ class EventObserverNode : }// ~timer if (shouldDetach) - DomainSpecificInputManager::Instance().Continuation() + DomainSpecificInputManager::Instance() .QueueObserverForDetach(*this); REACT_LOG(D::Log().template Append( @@ -308,7 +308,7 @@ class SyncedObserverNode : } if (shouldDetach) - DomainSpecificInputManager::Instance().Continuation() + DomainSpecificInputManager::Instance() .QueueObserverForDetach(*this); REACT_LOG(D::Log().template Append( diff --git a/include/react/engine/PulsecountEngine.h b/include/react/engine/PulsecountEngine.h index 5ff3b126..4bf2537b 100644 --- a/include/react/engine/PulsecountEngine.h +++ b/include/react/engine/PulsecountEngine.h @@ -37,7 +37,7 @@ using tbb::task_list; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Turn /////////////////////////////////////////////////////////////////////////////////////////////////// -struct Turn : public TurnBase +class Turn : public TurnBase { public: Turn(TurnIdT id, TurnFlagsT flags); @@ -98,8 +98,7 @@ class Node : public IReactiveNode /////////////////////////////////////////////////////////////////////////////////////////////////// /// EngineBase /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EngineBase : public IReactiveEngine +class EngineBase : public IReactiveEngine { public: using NodeShiftMutexT = Node::ShiftMutexT; @@ -108,14 +107,14 @@ class EngineBase : public IReactiveEngine void OnNodeAttach(Node& node, Node& parent); void OnNodeDetach(Node& node, Node& parent); - void OnInputChange(Node& node, TTurn& turn); - void Propagate(TTurn& turn); + void OnInputChange(Node& node, Turn& turn); + void Propagate(Turn& turn); - void OnNodePulse(Node& node, TTurn& turn); - void OnNodeIdlePulse(Node& node, TTurn& turn); + void OnNodePulse(Node& node, Turn& turn); + void OnNodeIdlePulse(Node& node, Turn& turn); - void OnDynamicNodeAttach(Node& node, Node& parent, TTurn& turn); - void OnDynamicNodeDetach(Node& node, Node& parent, TTurn& turn); + void OnDynamicNodeAttach(Node& node, Node& parent, Turn& turn); + void OnDynamicNodeDetach(Node& node, Node& parent, Turn& turn); private: NodeVectT changedInputs_; @@ -123,42 +122,25 @@ class EngineBase : public IReactiveEngine task_list spawnList_; }; -class BasicEngine : public EngineBase {}; -class QueuingEngine : public DefaultQueuingEngine {}; - } // ~namespace pulsecount /****************************************/ REACT_IMPL_END /***************************************/ /*****************************************/ REACT_BEGIN /*****************************************/ -struct parallel; -struct parallel_concurrent; - -template +template class PulsecountEngine; -template <> class PulsecountEngine : - public REACT_IMPL::pulsecount::BasicEngine {}; - -template <> class PulsecountEngine : - public REACT_IMPL::pulsecount::QueuingEngine {}; +template <> +class PulsecountEngine : + public REACT_IMPL::pulsecount::EngineBase +{}; /******************************************/ REACT_END /******************************************/ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -template struct NodeUpdateTimerEnabled; -template <> struct NodeUpdateTimerEnabled> : std::true_type {}; -template <> struct NodeUpdateTimerEnabled> : - std::true_type {}; - -template struct IsParallelEngine; -template <> struct IsParallelEngine> : std::true_type {}; -template <> struct IsParallelEngine> : std::true_type {}; - -template struct IsConcurrentEngine; -template <> struct IsConcurrentEngine> : std::false_type {}; -template <> struct IsConcurrentEngine> : std::true_type {}; +template <> +struct NodeUpdateTimerEnabled> : std::true_type {}; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/engine/SubtreeEngine.h b/include/react/engine/SubtreeEngine.h index 216aac91..97d74af4 100644 --- a/include/react/engine/SubtreeEngine.h +++ b/include/react/engine/SubtreeEngine.h @@ -37,7 +37,7 @@ using tbb::spin_rw_mutex; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Turn /////////////////////////////////////////////////////////////////////////////////////////////////// -class Turn : public TurnBase +class Turn : public TurnBase { public: Turn(TurnIdT id, TurnFlagsT flags); @@ -133,8 +133,7 @@ struct GetLevelFunctor /////////////////////////////////////////////////////////////////////////////////////////////////// /// EngineBase /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EngineBase : public IReactiveEngine +class EngineBase : public IReactiveEngine { public: using TopoQueueT = TopoQueue>; @@ -143,21 +142,21 @@ class EngineBase : public IReactiveEngine void OnNodeAttach(Node& node, Node& parent); void OnNodeDetach(Node& node, Node& parent); - void OnInputChange(Node& node, TTurn& turn); - void Propagate(TTurn& turn); + void OnInputChange(Node& node, Turn& turn); + void Propagate(Turn& turn); - void OnNodePulse(Node& node, TTurn& turn); - void OnNodeIdlePulse(Node& node, TTurn& turn); + void OnNodePulse(Node& node, Turn& turn); + void OnNodeIdlePulse(Node& node, Turn& turn); - void OnDynamicNodeAttach(Node& node, Node& parent, TTurn& turn); - void OnDynamicNodeDetach(Node& node, Node& parent, TTurn& turn); + void OnDynamicNodeAttach(Node& node, Node& parent, Turn& turn); + void OnDynamicNodeDetach(Node& node, Node& parent, Turn& turn); private: - void applyAsyncDynamicAttach(Node& node, Node& parent, TTurn& turn); - void applyAsyncDynamicDetach(Node& node, Node& parent, TTurn& turn); + void applyAsyncDynamicAttach(Node& node, Node& parent, Turn& turn); + void applyAsyncDynamicDetach(Node& node, Node& parent, Turn& turn); void invalidateSuccessors(Node& node); - void processChildren(Node& node, TTurn& turn); + void processChildren(Node& node, Turn& turn); void markSubtree(Node& root); @@ -170,40 +169,25 @@ class EngineBase : public IReactiveEngine bool isInPhase2_ = false; }; -class BasicEngine : public EngineBase {}; -class QueuingEngine : public DefaultQueuingEngine {}; - } // ~namespace subtree /****************************************/ REACT_IMPL_END /***************************************/ /*****************************************/ REACT_BEGIN /*****************************************/ -struct parallel; -struct parallel_concurrent; - -template +template class SubtreeEngine; -template <> class SubtreeEngine : - public REACT_IMPL::subtree::BasicEngine {}; - -template <> class SubtreeEngine : - public REACT_IMPL::subtree::QueuingEngine {}; +template <> +class SubtreeEngine : + public REACT_IMPL::subtree::EngineBase +{}; /******************************************/ REACT_END /******************************************/ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -template struct NodeUpdateTimerEnabled; -template <> struct NodeUpdateTimerEnabled> : std::true_type {}; -template <> struct NodeUpdateTimerEnabled> : std::true_type {}; - -template struct IsParallelEngine; -template <> struct IsParallelEngine> : std::true_type {}; -template <> struct IsParallelEngine> : std::true_type {}; - -template struct IsConcurrentEngine; -template <> struct IsConcurrentEngine> : std::true_type {}; +template <> +struct NodeUpdateTimerEnabled> : std::true_type {}; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/engine/ToposortEngine.h b/include/react/engine/ToposortEngine.h index 50510ca8..dfefde60 100644 --- a/include/react/engine/ToposortEngine.h +++ b/include/react/engine/ToposortEngine.h @@ -26,9 +26,6 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -template -class TurnBase; - namespace toposort { using std::atomic; @@ -84,19 +81,19 @@ struct DynRequestData /////////////////////////////////////////////////////////////////////////////////////////////////// /// ExclusiveSeqTurn /////////////////////////////////////////////////////////////////////////////////////////////////// -class ExclusiveSeqTurn : public REACT_IMPL::TurnBase +class SeqTurn : public TurnBase { public: - ExclusiveSeqTurn(TurnIdT id, TurnFlagsT flags); + SeqTurn(TurnIdT id, TurnFlagsT flags); }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// ExclusiveParTurn /////////////////////////////////////////////////////////////////////////////////////////////////// -class ExclusiveParTurn : public REACT_IMPL::TurnBase +class ParTurn : public TurnBase { public: - ExclusiveParTurn(TurnIdT id, TurnFlagsT flags); + ParTurn(TurnIdT id, TurnFlagsT flags); }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -134,21 +131,20 @@ class EngineBase : public IReactiveEngine /////////////////////////////////////////////////////////////////////////////////////////////////// /// SeqEngineBase /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class SeqEngineBase : public EngineBase +class SeqEngineBase : public EngineBase { public: using TopoQueueT = TopoQueue>; - void Propagate(TTurn& turn); + void Propagate(SeqTurn& turn); - void OnDynamicNodeAttach(SeqNode& node, SeqNode& parent, TTurn& turn); - void OnDynamicNodeDetach(SeqNode& node, SeqNode& parent, TTurn& turn); + void OnDynamicNodeAttach(SeqNode& node, SeqNode& parent, SeqTurn& turn); + void OnDynamicNodeDetach(SeqNode& node, SeqNode& parent, SeqTurn& turn); private: void invalidateSuccessors(SeqNode& node); - virtual void processChildren(SeqNode& node, TTurn& turn) override; + virtual void processChildren(SeqNode& node, SeqTurn& turn) override; TopoQueueT scheduledNodes_; }; @@ -156,8 +152,7 @@ class SeqEngineBase : public EngineBase /////////////////////////////////////////////////////////////////////////////////////////////////// /// ParEngineBase /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ParEngineBase : public EngineBase +class ParEngineBase : public EngineBase { public: using DynRequestVectT = concurrent_vector; @@ -169,73 +164,46 @@ class ParEngineBase : public EngineBase GetWeightFunctor >; - void Propagate(TTurn& turn); + void Propagate(ParTurn& turn); - void OnDynamicNodeAttach(ParNode& node, ParNode& parent, TTurn& turn); - void OnDynamicNodeDetach(ParNode& node, ParNode& parent, TTurn& turn); + void OnDynamicNodeAttach(ParNode& node, ParNode& parent, ParTurn& turn); + void OnDynamicNodeDetach(ParNode& node, ParNode& parent, ParTurn& turn); private: - void applyDynamicAttach(ParNode& node, ParNode& parent, TTurn& turn); - void applyDynamicDetach(ParNode& node, ParNode& parent, TTurn& turn); + void applyDynamicAttach(ParNode& node, ParNode& parent, ParTurn& turn); + void applyDynamicDetach(ParNode& node, ParNode& parent, ParTurn& turn); void invalidateSuccessors(ParNode& node); - virtual void processChildren(ParNode& node, TTurn& turn) override; + virtual void processChildren(ParNode& node, ParTurn& turn) override; TopoQueueT topoQueue_; DynRequestVectT dynRequests_; }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Concrete engines -/////////////////////////////////////////////////////////////////////////////////////////////////// -class BasicSeqEngine : public SeqEngineBase {}; -class QueuingSeqEngine : public DefaultQueuingEngine {}; - -class BasicParEngine : public ParEngineBase {}; -class QueuingParEngine : public DefaultQueuingEngine {}; - } // ~namespace toposort /****************************************/ REACT_IMPL_END /***************************************/ /*****************************************/ REACT_BEGIN /*****************************************/ -struct sequential; -struct sequential_concurrent; -struct parallel; -struct parallel_concurrent; - -template +template class ToposortEngine; -template <> class ToposortEngine : - public REACT_IMPL::toposort::BasicSeqEngine {}; - -template <> class ToposortEngine : - public REACT_IMPL::toposort::QueuingSeqEngine {}; - -template <> class ToposortEngine : - public REACT_IMPL::toposort::BasicParEngine {}; +template <> class ToposortEngine : + public REACT_IMPL::toposort::SeqEngineBase +{}; -template <> class ToposortEngine : - public REACT_IMPL::toposort::QueuingParEngine {}; +template <> class ToposortEngine : + public REACT_IMPL::toposort::ParEngineBase +{}; /******************************************/ REACT_END /******************************************/ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -template struct NodeUpdateTimerEnabled; -template <> struct NodeUpdateTimerEnabled> : std::true_type {}; -template <> struct NodeUpdateTimerEnabled> : std::true_type {}; - -template struct IsParallelEngine; -template <> struct IsParallelEngine> : std::true_type {}; -template <> struct IsParallelEngine> : std::true_type {}; - -template struct IsConcurrentEngine; -template <> struct IsConcurrentEngine> : std::true_type {}; -template <> struct IsConcurrentEngine> : std::true_type {}; +template <> +struct NodeUpdateTimerEnabled> : std::true_type {}; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/src/engine/PulsecountEngine.cpp b/src/engine/PulsecountEngine.cpp index 5059b870..291d7a31 100644 --- a/src/engine/PulsecountEngine.cpp +++ b/src/engine/PulsecountEngine.cpp @@ -79,19 +79,18 @@ class MarkerTask: public task /////////////////////////////////////////////////////////////////////////////////////////////////// /// UpdaterTask /////////////////////////////////////////////////////////////////////////////////////////////////// -template class UpdaterTask: public task { public: using BufferT = NodeBuffer; template - UpdaterTask(TTurn& turn, TInput srcBegin, TInput srcEnd) : + UpdaterTask(Turn& turn, TInput srcBegin, TInput srcEnd) : turn_( turn ), nodes_( srcBegin, srcEnd ) {} - UpdaterTask(TTurn& turn, Node* node) : + UpdaterTask(Turn& turn, Node* node) : turn_( turn ), nodes_( node ) {} @@ -103,7 +102,7 @@ class UpdaterTask: public task task* execute() { - int splitCount = 0; + uint splitCount = 0; while (!nodes_.IsEmpty()) { @@ -172,7 +171,7 @@ class UpdaterTask: public task } private: - TTurn& turn_; + Turn& turn_; BufferT nodes_; }; @@ -181,27 +180,24 @@ class UpdaterTask: public task /// Turn /////////////////////////////////////////////////////////////////////////////////////////////////// Turn::Turn(TurnIdT id, TurnFlagsT flags) : - TurnBase(id, flags) + TurnBase( id, flags ) {} /////////////////////////////////////////////////////////////////////////////////////////////////// /// PulsecountEngine /////////////////////////////////////////////////////////////////////////////////////////////////// -template -void EngineBase::OnNodeAttach(Node& node, Node& parent) +void EngineBase::OnNodeAttach(Node& node, Node& parent) { parent.Successors.Add(node); } -template -void EngineBase::OnNodeDetach(Node& node, Node& parent) +void EngineBase::OnNodeDetach(Node& node, Node& parent) { parent.Successors.Remove(node); } -template -void EngineBase::OnInputChange(Node& node, TTurn& turn) +void EngineBase::OnInputChange(Node& node, Turn& turn) { changedInputs_.push_back(&node); node.State = ENodeState::changed; @@ -232,34 +228,30 @@ void spawnTasks spawnList.clear(); } -template -void EngineBase::Propagate(TTurn& turn) +void EngineBase::Propagate(Turn& turn) { const uint initialTaskCount = (changedInputs_.size() - 1) / chunk_size + 1; spawnTasks(rootTask_, spawnList_, initialTaskCount, changedInputs_.begin(), changedInputs_.end()); - spawnTasks>(rootTask_, spawnList_, initialTaskCount, + spawnTasks(rootTask_, spawnList_, initialTaskCount, changedInputs_.begin(), changedInputs_.end(), turn); changedInputs_.clear(); } -template -void EngineBase::OnNodePulse(Node& node, TTurn& turn) +void EngineBase::OnNodePulse(Node& node, Turn& turn) { node.State = ENodeState::changed; } -template -void EngineBase::OnNodeIdlePulse(Node& node, TTurn& turn) +void EngineBase::OnNodeIdlePulse(Node& node, Turn& turn) { node.State = ENodeState::unchanged; } -template -void EngineBase::OnDynamicNodeAttach(Node& node, Node& parent, TTurn& turn) +void EngineBase::OnDynamicNodeAttach(Node& node, Node& parent, Turn& turn) {// parent.ShiftMutex (write) NodeShiftMutexT::scoped_lock lock(parent.ShiftMutex, true); @@ -278,17 +270,12 @@ void EngineBase::OnDynamicNodeAttach(Node& node, Node& parent, TTurn& tur } }// ~parent.ShiftMutex (write) -template -void EngineBase::OnDynamicNodeDetach(Node& node, Node& parent, TTurn& turn) +void EngineBase::OnDynamicNodeDetach(Node& node, Node& parent, Turn& turn) {// parent.ShiftMutex (write) NodeShiftMutexT::scoped_lock lock(parent.ShiftMutex, true); parent.Successors.Remove(node); }// ~parent.ShiftMutex (write) -// Explicit instantiation -template class EngineBase; -template class EngineBase>; - } // ~namespace pulsecount /****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file diff --git a/src/engine/SubtreeEngine.cpp b/src/engine/SubtreeEngine.cpp index 096de5ba..c04f5edf 100644 --- a/src/engine/SubtreeEngine.cpp +++ b/src/engine/SubtreeEngine.cpp @@ -24,15 +24,14 @@ static const uint dfs_threshold = 3; /// Turn /////////////////////////////////////////////////////////////////////////////////////////////////// Turn::Turn(TurnIdT id, TurnFlagsT flags) : - TurnBase(id, flags) + TurnBase( id, flags ) {} /////////////////////////////////////////////////////////////////////////////////////////////////// /// PulsecountEngine /////////////////////////////////////////////////////////////////////////////////////////////////// -template -void EngineBase::OnNodeAttach(Node& node, Node& parent) +void EngineBase::OnNodeAttach(Node& node, Node& parent) { parent.Successors.Add(node); @@ -40,14 +39,12 @@ void EngineBase::OnNodeAttach(Node& node, Node& parent) node.Level = parent.Level + 1; } -template -void EngineBase::OnNodeDetach(Node& node, Node& parent) +void EngineBase::OnNodeDetach(Node& node, Node& parent) { parent.Successors.Remove(node); } -template -void EngineBase::OnInputChange(Node& node, TTurn& turn) +void EngineBase::OnInputChange(Node& node, Turn& turn) { processChildren(node, turn); } @@ -55,13 +52,12 @@ void EngineBase::OnInputChange(Node& node, TTurn& turn) /////////////////////////////////////////////////////////////////////////////////////////////////// /// UpdaterTask /////////////////////////////////////////////////////////////////////////////////////////////////// -template class UpdaterTask: public task { public: using BufferT = NodeBuffer; - UpdaterTask(TTurn& turn, Node* node) : + UpdaterTask(Turn& turn, Node* node) : turn_( turn ), nodes_( node ) {} @@ -152,12 +148,14 @@ class UpdaterTask: public task } private: - TTurn& turn_; + Turn& turn_; BufferT nodes_; }; -template -void EngineBase::Propagate(TTurn& turn) +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EngineBase +/////////////////////////////////////////////////////////////////////////////////////////////////// +void EngineBase::Propagate(Turn& turn) { // Phase 1 while (scheduledNodes_.FetchNext()) @@ -192,7 +190,7 @@ void EngineBase::Propagate(TTurn& turn) } spawnList_.push_back(*new(rootTask_.allocate_child()) - UpdaterTask(turn, node)); + UpdaterTask(turn, node)); node->ClearRootFlag(); } @@ -205,8 +203,7 @@ void EngineBase::Propagate(TTurn& turn) isInPhase2_ = false; } -template -void EngineBase::OnNodePulse(Node& node, TTurn& turn) +void EngineBase::OnNodePulse(Node& node, Turn& turn) { if (isInPhase2_) node.SetChangedFlag(); @@ -214,15 +211,13 @@ void EngineBase::OnNodePulse(Node& node, TTurn& turn) processChildren(node, turn); } -template -void EngineBase::OnNodeIdlePulse(Node& node, TTurn& turn) +void EngineBase::OnNodeIdlePulse(Node& node, Turn& turn) { if (isInPhase2_) node.ClearChangedFlag(); } -template -void EngineBase::OnDynamicNodeAttach(Node& node, Node& parent, TTurn& turn) +void EngineBase::OnDynamicNodeAttach(Node& node, Node& parent, Turn& turn) { if (isInPhase2_) { @@ -240,8 +235,7 @@ void EngineBase::OnDynamicNodeAttach(Node& node, Node& parent, TTurn& tur } } -template -void EngineBase::OnDynamicNodeDetach(Node& node, Node& parent, TTurn& turn) +void EngineBase::OnDynamicNodeDetach(Node& node, Node& parent, Turn& turn) { if (isInPhase2_) applyAsyncDynamicDetach(node, parent, turn); @@ -249,8 +243,7 @@ void EngineBase::OnDynamicNodeDetach(Node& node, Node& parent, TTurn& tur OnNodeDetach(node, parent); } -template -void EngineBase::applyAsyncDynamicAttach(Node& node, Node& parent, TTurn& turn) +void EngineBase::applyAsyncDynamicAttach(Node& node, Node& parent, Turn& turn) {// parent.ShiftMutex (write) NodeShiftMutexT::scoped_lock lock(parent.ShiftMutex, true); @@ -274,16 +267,14 @@ void EngineBase::applyAsyncDynamicAttach(Node& node, Node& parent, TTurn& } }// ~parent.ShiftMutex (write) -template -void EngineBase::applyAsyncDynamicDetach(Node& node, Node& parent, TTurn& turn) +void EngineBase::applyAsyncDynamicDetach(Node& node, Node& parent, Turn& turn) {// parent.ShiftMutex (write) NodeShiftMutexT::scoped_lock lock(parent.ShiftMutex, true); parent.Successors.Remove(node); }// ~parent.ShiftMutex (write) -template -void EngineBase::processChildren(Node& node, TTurn& turn) +void EngineBase::processChildren(Node& node, Turn& turn) { // Add children to queue for (auto* succ : node.Successors) @@ -319,8 +310,7 @@ void EngineBase::processChildren(Node& node, TTurn& turn) } } -template -void EngineBase::markSubtree(Node& root) +void EngineBase::markSubtree(Node& root) { root.SetMarkedFlag(); root.WaitCount = 0; @@ -338,8 +328,7 @@ void EngineBase::markSubtree(Node& root) } } -template -void EngineBase::invalidateSuccessors(Node& node) +void EngineBase::invalidateSuccessors(Node& node) { for (auto* succ : node.Successors) { @@ -348,9 +337,5 @@ void EngineBase::invalidateSuccessors(Node& node) } } -// Explicit instantiation -template class EngineBase; -template class EngineBase>; - } // ~namespace subtree /****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file diff --git a/src/engine/ToposortEngine.cpp b/src/engine/ToposortEngine.cpp index 2db5971c..ae96728a 100644 --- a/src/engine/ToposortEngine.cpp +++ b/src/engine/ToposortEngine.cpp @@ -11,17 +11,17 @@ namespace toposort { /////////////////////////////////////////////////////////////////////////////////////////////////// -/// ExclusiveSeqTurn +/// SeqTurn /////////////////////////////////////////////////////////////////////////////////////////////////// -ExclusiveSeqTurn::ExclusiveSeqTurn(TurnIdT id, TurnFlagsT flags) : - TurnBase(id, flags) +SeqTurn::SeqTurn(TurnIdT id, TurnFlagsT flags) : + TurnBase( id, flags ) {} /////////////////////////////////////////////////////////////////////////////////////////////////// -/// ExclusiveParTurn +/// ParTurn /////////////////////////////////////////////////////////////////////////////////////////////////// -ExclusiveParTurn::ExclusiveParTurn(TurnIdT id, TurnFlagsT flags) : - TurnBase(id, flags) +ParTurn::ParTurn(TurnIdT id, TurnFlagsT flags) : + TurnBase( id, flags ) {} /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -55,16 +55,13 @@ void EngineBase::OnNodePulse(TNode& node, TTurn& turn) } // Explicit instantiation -template class EngineBase; -template class EngineBase; -template class EngineBase>; -template class EngineBase>; +template class EngineBase; +template class EngineBase; /////////////////////////////////////////////////////////////////////////////////////////////////// /// SeqEngineBase /////////////////////////////////////////////////////////////////////////////////////////////////// -template -void SeqEngineBase::Propagate(TTurn& turn) +void SeqEngineBase::Propagate(SeqTurn& turn) { while (scheduledNodes_.FetchNext()) { @@ -84,8 +81,7 @@ void SeqEngineBase::Propagate(TTurn& turn) } } -template -void SeqEngineBase::OnDynamicNodeAttach(SeqNode& node, SeqNode& parent, TTurn& turn) +void SeqEngineBase::OnDynamicNodeAttach(SeqNode& node, SeqNode& parent, SeqTurn& turn) { this->OnNodeAttach(node, parent); @@ -96,14 +92,12 @@ void SeqEngineBase::OnDynamicNodeAttach(SeqNode& node, SeqNode& parent, T scheduledNodes_.Push(&node); } -template -void SeqEngineBase::OnDynamicNodeDetach(SeqNode& node, SeqNode& parent, TTurn& turn) +void SeqEngineBase::OnDynamicNodeDetach(SeqNode& node, SeqNode& parent, SeqTurn& turn) { this->OnNodeDetach(node, parent); } -template -void SeqEngineBase::processChildren(SeqNode& node, TTurn& turn) +void SeqEngineBase::processChildren(SeqNode& node, SeqTurn& turn) { // Add children to queue for (auto* succ : node.Successors) @@ -116,8 +110,7 @@ void SeqEngineBase::processChildren(SeqNode& node, TTurn& turn) } } -template -void SeqEngineBase::invalidateSuccessors(SeqNode& node) +void SeqEngineBase::invalidateSuccessors(SeqNode& node) { for (auto* succ : node.Successors) { @@ -126,15 +119,10 @@ void SeqEngineBase::invalidateSuccessors(SeqNode& node) } } -// Explicit instantiation -template class SeqEngineBase; -template class SeqEngineBase>; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// ParEngineBase /////////////////////////////////////////////////////////////////////////////////////////////////// -template -void ParEngineBase::Propagate(TTurn& turn) +void ParEngineBase::Propagate(ParTurn& turn) { while (topoQueue_.FetchNext()) { @@ -180,22 +168,19 @@ void ParEngineBase::Propagate(TTurn& turn) } } -template -void ParEngineBase::OnDynamicNodeAttach(ParNode& node, ParNode& parent, TTurn& turn) +void ParEngineBase::OnDynamicNodeAttach(ParNode& node, ParNode& parent, ParTurn& turn) { DynRequestData data{ true, &node, &parent }; dynRequests_.push_back(data); } -template -void ParEngineBase::OnDynamicNodeDetach(ParNode& node, ParNode& parent, TTurn& turn) +void ParEngineBase::OnDynamicNodeDetach(ParNode& node, ParNode& parent, ParTurn& turn) { DynRequestData data{ false, &node, &parent }; dynRequests_.push_back(data); } -template -void ParEngineBase::applyDynamicAttach(ParNode& node, ParNode& parent, TTurn& turn) +void ParEngineBase::applyDynamicAttach(ParNode& node, ParNode& parent, ParTurn& turn) { this->OnNodeAttach(node, parent); @@ -206,14 +191,12 @@ void ParEngineBase::applyDynamicAttach(ParNode& node, ParNode& parent, TT topoQueue_.Push(&node); } -template -void ParEngineBase::applyDynamicDetach(ParNode& node, ParNode& parent, TTurn& turn) +void ParEngineBase::applyDynamicDetach(ParNode& node, ParNode& parent, ParTurn& turn) { this->OnNodeDetach(node, parent); } -template -void ParEngineBase::processChildren(ParNode& node, TTurn& turn) +void ParEngineBase::processChildren(ParNode& node, ParTurn& turn) { // Add children to queue for (auto* succ : node.Successors) @@ -221,8 +204,7 @@ void ParEngineBase::processChildren(ParNode& node, TTurn& turn) topoQueue_.Push(succ); } -template -void ParEngineBase::invalidateSuccessors(ParNode& node) +void ParEngineBase::invalidateSuccessors(ParNode& node) { for (auto* succ : node.Successors) {// succ->InvalidateMutex @@ -233,9 +215,5 @@ void ParEngineBase::invalidateSuccessors(ParNode& node) }// ~succ->InvalidateMutex } -// Explicit instantiation -template class ParEngineBase; -template class ParEngineBase>; - } // ~namespace toposort /****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file From 1d825a5c63d5318055a376230e586b37bc325cdd Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 22 Jun 2014 17:31:44 +0200 Subject: [PATCH 163/266] Updated domain definitions in examples and benchmarks. --- benchmarks/src/Main.cpp | 24 +++--- examples/src/BasicAlgorithms.cpp | 12 +-- examples/src/BasicComposition.cpp | 6 +- examples/src/BasicEvents.cpp | 10 +-- examples/src/BasicObservers.cpp | 6 +- examples/src/BasicReactors.cpp | 2 +- examples/src/BasicSignals.cpp | 10 +-- examples/src/Main.cpp | 119 ++++++++++++++++++++++++++++-- 8 files changed, 149 insertions(+), 40 deletions(-) diff --git a/benchmarks/src/Main.cpp b/benchmarks/src/Main.cpp index 0722b13e..eb47f33f 100644 --- a/benchmarks/src/Main.cpp +++ b/benchmarks/src/Main.cpp @@ -4,7 +4,7 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#define REACT_ENABLE_LOGGING +//#define REACT_ENABLE_LOGGING #include "tbb/tick_count.h" #include "tbb/tbbmalloc_proxy.h" @@ -32,10 +32,10 @@ namespace { using namespace react; -REACTIVE_DOMAIN(ToposortSTDomain, ToposortEngine) -REACTIVE_DOMAIN(ToposortDomain, ToposortEngine) -REACTIVE_DOMAIN(PulsecountDomain, PulsecountEngine) -REACTIVE_DOMAIN(SubtreeDomain, SubtreeEngine) +REACTIVE_DOMAIN(ToposortSTDomain, sequential, ToposortEngine) +REACTIVE_DOMAIN(ToposortDomain, parallel, ToposortEngine) +REACTIVE_DOMAIN(PulsecountDomain, parallel, PulsecountEngine) +REACTIVE_DOMAIN(SubtreeDomain, parallel, SubtreeEngine) void runBenchmarkGrid(std::ostream& out) { @@ -216,15 +216,15 @@ void debugBenchmarks() void profileBenchmark() { - //RUN_BENCHMARK(std::cout, 1, Benchmark_Grid, BenchmarkParams_Grid(100, 10000), - // PulsecountDomain); + RUN_BENCHMARK(std::cout, 1, Benchmark_Grid, BenchmarkParams_Grid(30, 10000), + ToposortSTDomain); //ToposortSTDomain, ToposortDomain, PulsecountDomain, SubtreeDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_Grid, BenchmarkParams_Grid(30, 10000), - // SourceSetDomain); + //SourceSetDomain); - RUN_BENCHMARK(std::cout, 3, Benchmark_Random, BenchmarkParams_Random(20, 11, 100, 0, 1, 40, 40, false, 41556, 21624), - SubtreeDomain); + //RUN_BENCHMARK(std::cout, 3, Benchmark_Random, BenchmarkParams_Random(20, 11, 100, 0, 1, 40, 40, false, 41556, 21624), + //SubtreeDomain); //ToposortSTDomain, ToposortDomain, PulsecountDomain, SubtreeDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 15, 10000), @@ -236,6 +236,6 @@ void profileBenchmark() int main() { //runBenchmarks(); - debugBenchmarks(); - //profileBenchmark(); + //debugBenchmarks(); + profileBenchmark(); } \ No newline at end of file diff --git a/examples/src/BasicAlgorithms.cpp b/examples/src/BasicAlgorithms.cpp index a01c8c2d..1222c02a 100644 --- a/examples/src/BasicAlgorithms.cpp +++ b/examples/src/BasicAlgorithms.cpp @@ -23,7 +23,7 @@ namespace example1 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) class Sensor { @@ -62,7 +62,7 @@ namespace example2 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) class Employee { @@ -100,7 +100,7 @@ namespace example3 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) class Counter { @@ -142,7 +142,7 @@ namespace example4 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) class Sensor { @@ -181,7 +181,7 @@ namespace example5 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) enum ECmd { increment, decrement, reset }; @@ -244,7 +244,7 @@ namespace example6 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) class Sensor { diff --git a/examples/src/BasicComposition.cpp b/examples/src/BasicComposition.cpp index 66e2e61a..5d0a8ad4 100644 --- a/examples/src/BasicComposition.cpp +++ b/examples/src/BasicComposition.cpp @@ -21,7 +21,7 @@ namespace example1 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) class Shape { @@ -63,7 +63,7 @@ namespace example2 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) class Company { @@ -121,7 +121,7 @@ namespace example3 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) class Company { diff --git a/examples/src/BasicEvents.cpp b/examples/src/BasicEvents.cpp index 1de14039..02c3f450 100644 --- a/examples/src/BasicEvents.cpp +++ b/examples/src/BasicEvents.cpp @@ -23,7 +23,7 @@ namespace example1 // Defines a domain. // Each domain represents a separate dependency graph, managed by a dedicated propagation engine. // Reactives of different domains can not be combined. - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) // Define type aliases for the given domain in this namespace. // Now we can use EventSourceT instead of D::EventSourceT. @@ -90,7 +90,7 @@ namespace example2 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) USING_REACTIVE_DOMAIN(D) // An event stream that merges both sources @@ -152,7 +152,7 @@ namespace example3 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) USING_REACTIVE_DOMAIN(D) EventSourceT numbers = MakeEventSource(); @@ -183,7 +183,7 @@ namespace example4 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) USING_REACTIVE_DOMAIN(D) // Data types @@ -225,7 +225,7 @@ namespace example5 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) USING_REACTIVE_DOMAIN(D) EventSourceT src = MakeEventSource(); diff --git a/examples/src/BasicObservers.cpp b/examples/src/BasicObservers.cpp index 56e5adf4..ba5b3420 100644 --- a/examples/src/BasicObservers.cpp +++ b/examples/src/BasicObservers.cpp @@ -21,7 +21,7 @@ namespace example1 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) USING_REACTIVE_DOMAIN(D) auto x = MakeVar(1); @@ -103,7 +103,7 @@ namespace example2 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) USING_REACTIVE_DOMAIN(D) EventSourceT<> trigger = MakeEventSource(); @@ -134,7 +134,7 @@ namespace example3 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) USING_REACTIVE_DOMAIN(D) EventSourceT<> trigger = MakeEventSource(); diff --git a/examples/src/BasicReactors.cpp b/examples/src/BasicReactors.cpp index 990ea589..3562adad 100644 --- a/examples/src/BasicReactors.cpp +++ b/examples/src/BasicReactors.cpp @@ -20,7 +20,7 @@ namespace example1 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) USING_REACTIVE_DOMAIN(D) using PointT = pair; diff --git a/examples/src/BasicSignals.cpp b/examples/src/BasicSignals.cpp index dfb02f23..78af0975 100644 --- a/examples/src/BasicSignals.cpp +++ b/examples/src/BasicSignals.cpp @@ -26,7 +26,7 @@ namespace example1 // Defines a domain. // Each domain represents a separate dependency graph, managed by a dedicated propagation engine. // Reactives of different domains can not be combined. - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) // Define type aliases for the given domain in this namespace. // Now we can use VarSignalT instead of D::VarSignalT. @@ -102,7 +102,7 @@ namespace example2 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) USING_REACTIVE_DOMAIN(D) VarSignalT x = MakeVar(1); @@ -133,7 +133,7 @@ namespace example3 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) USING_REACTIVE_DOMAIN(D) VarSignalT a = MakeVar(1); @@ -171,7 +171,7 @@ namespace example4 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) USING_REACTIVE_DOMAIN(D) VarSignalT> data = MakeVar(vector{ }); @@ -205,7 +205,7 @@ namespace example5 using namespace std; using namespace react; - REACTIVE_DOMAIN(D) + REACTIVE_DOMAIN(D, sequential) USING_REACTIVE_DOMAIN(D) // Helpers diff --git a/examples/src/Main.cpp b/examples/src/Main.cpp index acad8788..e061a6fb 100644 --- a/examples/src/Main.cpp +++ b/examples/src/Main.cpp @@ -11,13 +11,17 @@ #include "react/Event.h" #include "react/Algorithm.h" +#include "tbb/tick_count.h" + +#include + using namespace std; using namespace react; // Defines a domain. // Each domain represents a separate dependency graph, managed by a dedicated propagation engine. // Reactives of different domains can not be combined. -REACTIVE_DOMAIN(D, ToposortEngine) +REACTIVE_DOMAIN(D, sequential) void SignalExample3() { @@ -42,8 +46,8 @@ void SignalExample3() void SignalExample4() { - REACTIVE_DOMAIN(L, ToposortEngine) - REACTIVE_DOMAIN(R, ToposortEngine) + REACTIVE_DOMAIN(L, sequential_concurrent, ToposortEngine) + REACTIVE_DOMAIN(R, sequential_concurrent, ToposortEngine) cout << "Signal Example 4" << endl; @@ -63,14 +67,15 @@ void SignalExample4() }); srcL <<= 1; + printf("End\n"); cout << endl; } void SignalExample5() { - REACTIVE_DOMAIN(L, ToposortEngine) - REACTIVE_DOMAIN(R, ToposortEngine) + REACTIVE_DOMAIN(L, sequential_concurrent, ToposortEngine) + REACTIVE_DOMAIN(R, sequential_concurrent, ToposortEngine) cout << "Signal Example 5" << endl; @@ -97,14 +102,118 @@ void SignalExample5() }); srcL <<= 1; + printf("End\n"); cout << endl; } +void testme() +{ + std::vector results; + + auto f_0 = [] (int a) -> int + { + int k = 0; + for (int i = 0; i<10000; i++) + k += i; + return a + k; + }; + + auto f_n = [] (int a, int b) -> int + { + int k = 0; + for (int i=0; i<10000; i++) + k += i; + return a + b + k; + }; + + auto n1 = MakeVar(0); + auto n2 = n1 ->* f_0; + auto n3 = ((n2, n1) ->* f_n) ->* f_0; + auto n4 = n3 ->* f_0; + auto n5 = ((((n4, n3) ->* f_n), n1) ->* f_n) ->* f_0; + auto n6 = n5 ->* f_0; + auto n7 = ((n6, n5) ->* f_n) ->* f_0; + auto n8 = n7 ->* f_0; + auto n9 = ((((((n8, n7) ->* f_n), n5) ->* f_n), n1) ->* f_n) ->* f_0; + auto n10 = n9 ->* f_0; + auto n11 = ((n10, n9) ->* f_n) ->* f_0; + auto n12 = n11 ->* f_0; + auto n13 = ((((n12, n11) ->* f_n), n9) ->* f_n) ->* f_0; + auto n14 = n13 ->* f_0; + auto n15 = ((n14, n13) ->* f_n) ->* f_0; + auto n16 = n15 ->* f_0; + auto n17 = ((((((n16, n15) ->* f_n), n13) ->* f_n), n9) ->* f_n) ->* f_0; + + auto src = MakeEventSource(); + + atomic c( 0 ); + + Observe(src, [&] (int v){ + c++; + }); + + auto x0 = tbb::tick_count::now(); + + TransactionStatus st; + + for (int i=0; i<10000; i++) + { + D::AsyncTransaction(st, [&,i] { + n1 <<= 1+i; + }); + } + + for (int i=0; i<10000; i++) + { + D::AsyncTransaction(st, [&,i] { + n1 <<= 20000+i; + }); + } + + for (int i=0; i<10000; i++) + { + D::AsyncTransaction(st, [&,i] { + n1 <<= 100000+i; + }); + } + + st.Wait(); + + //std::thread t3([&] { + // for (int i=0; i<10000; i++) + // n1 <<= 1+i; + //}); + + //std::thread t2([&] { + // for (int i=0; i<10000; i++) + // n1 <<= 20000+i; + //}); + + //std::thread t1([&] { + // for (int i=0; i<10000; i++) + // n1 <<= 100000+i; + //}); + + //t3.join(); + //t2.join(); + //t1.join(); + //std::chrono::milliseconds dura( 10000 ); + //std::this_thread::sleep_for( dura ); + + auto x1 = tbb::tick_count::now(); + + double d = (x1 - x0).seconds(); + printf("Time %g\n", d); + printf("Updates %d\n", c.load()); + printf("n1 %d\n", n1()); +} + int main() { SignalExample3(); SignalExample4(); + testme(); #ifdef REACT_ENABLE_LOGGING std::ofstream logfile; From 47ec985d9824c881eb06b17885b9413d3ca02cb2 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 22 Jun 2014 17:32:07 +0200 Subject: [PATCH 164/266] Updated domain definitions in tests. --- project/msvc/CppReactTest.vcxproj | 1 + project/msvc/CppReactTest.vcxproj.filters | 3 +++ tests/src/EventStreamTest.cpp | 14 +++++++++---- tests/src/EventStreamTest.h | 7 +++++-- tests/src/EventStreamTestQ.cpp | 14 +++++++++---- tests/src/MoveTest.cpp | 5 ++++- tests/src/MoveTest.h | 7 +++++-- tests/src/ObserverTest.cpp | 14 +++++++++---- tests/src/ObserverTest.h | 7 +++++-- tests/src/ObserverTestQ.cpp | 14 +++++++++---- tests/src/OperationsTest.cpp | 14 +++++++++---- tests/src/OperationsTest.h | 7 +++++-- tests/src/OperationsTestQ.cpp | 14 +++++++++---- tests/src/SignalTest.cpp | 14 +++++++++---- tests/src/SignalTest.h | 7 +++++-- tests/src/SignalTestQ.cpp | 14 +++++++++---- tests/src/TestUtil.h | 25 +++++++++++++++++++++++ tests/src/TransactionTest.cpp | 14 +++++++++---- tests/src/TransactionTest.h | 11 ++++++---- 19 files changed, 155 insertions(+), 51 deletions(-) create mode 100644 tests/src/TestUtil.h diff --git a/project/msvc/CppReactTest.vcxproj b/project/msvc/CppReactTest.vcxproj index 21a8d9e1..68e94238 100644 --- a/project/msvc/CppReactTest.vcxproj +++ b/project/msvc/CppReactTest.vcxproj @@ -109,6 +109,7 @@ + diff --git a/project/msvc/CppReactTest.vcxproj.filters b/project/msvc/CppReactTest.vcxproj.filters index d3522f4a..91914d7c 100644 --- a/project/msvc/CppReactTest.vcxproj.filters +++ b/project/msvc/CppReactTest.vcxproj.filters @@ -65,5 +65,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/tests/src/EventStreamTest.cpp b/tests/src/EventStreamTest.cpp index ade5f6ae..3714b371 100644 --- a/tests/src/EventStreamTest.cpp +++ b/tests/src/EventStreamTest.cpp @@ -5,6 +5,7 @@ // http://www.boost.org/LICENSE_1_0.txt) #include "EventStreamTest.h" +#include "TestUtil.h" #include "react/engine/PulsecountEngine.h" #include "react/engine/ToposortEngine.h" @@ -15,9 +16,14 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, EventStreamTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, EventStreamTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, EventStreamTest, PulsecountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(Subtree, EventStreamTest, SubtreeEngine); +using P1 = DomainParams; +using P2 = DomainParams; +using P3 = DomainParams; +using P4 = DomainParams; + +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, EventStreamTest, P1); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, EventStreamTest, P2); +INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, EventStreamTest, P3); +INSTANTIATE_TYPED_TEST_CASE_P(Subtree, EventStreamTest, P4); } // ~namespace \ No newline at end of file diff --git a/tests/src/EventStreamTest.h b/tests/src/EventStreamTest.h index 71941328..8a70cfb2 100644 --- a/tests/src/EventStreamTest.h +++ b/tests/src/EventStreamTest.h @@ -22,11 +22,14 @@ using namespace react; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventStreamTest fixture /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class EventStreamTest : public testing::Test { public: - REACTIVE_DOMAIN(MyDomain, TEngine) + template + class MyEngine : public TParams::template EngineT {}; + + REACTIVE_DOMAIN(MyDomain, TParams::mode, MyEngine) }; TYPED_TEST_CASE_P(EventStreamTest); diff --git a/tests/src/EventStreamTestQ.cpp b/tests/src/EventStreamTestQ.cpp index 328fe40e..767a3dad 100644 --- a/tests/src/EventStreamTestQ.cpp +++ b/tests/src/EventStreamTestQ.cpp @@ -5,6 +5,7 @@ // http://www.boost.org/LICENSE_1_0.txt) #include "EventStreamTest.h" +#include "TestUtil.h" #include "react/engine/PulsecountEngine.h" #include "react/engine/ToposortEngine.h" @@ -15,9 +16,14 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, EventStreamTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, EventStreamTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, EventStreamTest, PulsecountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, EventStreamTest, SubtreeEngine); +using P1 = DomainParams; +using P2 = DomainParams; +using P3 = DomainParams; +using P4 = DomainParams; + +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, EventStreamTest, P1); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, EventStreamTest, P2); +INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, EventStreamTest, P3); +INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, EventStreamTest, P4); } // ~namespace \ No newline at end of file diff --git a/tests/src/MoveTest.cpp b/tests/src/MoveTest.cpp index 73c17b70..81f09f31 100644 --- a/tests/src/MoveTest.cpp +++ b/tests/src/MoveTest.cpp @@ -5,6 +5,7 @@ // http://www.boost.org/LICENSE_1_0.txt) #include "MoveTest.h" +#include "TestUtil.h" #include "react/engine/ToposortEngine.h" @@ -13,6 +14,8 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, MoveTest, ToposortEngine); +using P1 = DomainParams; + +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, MoveTest, P1); } // ~namespace \ No newline at end of file diff --git a/tests/src/MoveTest.h b/tests/src/MoveTest.h index e6d91c3e..46e1d39d 100644 --- a/tests/src/MoveTest.h +++ b/tests/src/MoveTest.h @@ -19,11 +19,14 @@ using namespace react; /////////////////////////////////////////////////////////////////////////////////////////////////// /// MoveTest fixture /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class MoveTest : public testing::Test { public: - REACTIVE_DOMAIN(MyDomain, TEngine) + template + class MyEngine : public TParams::template EngineT {}; + + REACTIVE_DOMAIN(MyDomain, TParams::mode, MyEngine) struct Stats { diff --git a/tests/src/ObserverTest.cpp b/tests/src/ObserverTest.cpp index 4c068f00..87cb46eb 100644 --- a/tests/src/ObserverTest.cpp +++ b/tests/src/ObserverTest.cpp @@ -5,6 +5,7 @@ // http://www.boost.org/LICENSE_1_0.txt) #include "ObserverTest.h" +#include "TestUtil.h" #include "react/engine/PulsecountEngine.h" #include "react/engine/ToposortEngine.h" @@ -15,9 +16,14 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, ObserverTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, ObserverTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, ObserverTest, PulsecountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(Subtree, ObserverTest, SubtreeEngine); +using P1 = DomainParams; +using P2 = DomainParams; +using P3 = DomainParams; +using P4 = DomainParams; + +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, ObserverTest, P1); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, ObserverTest, P2); +INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, ObserverTest, P3); +INSTANTIATE_TYPED_TEST_CASE_P(Subtree, ObserverTest, P4); } // ~namespace \ No newline at end of file diff --git a/tests/src/ObserverTest.h b/tests/src/ObserverTest.h index 1cdbb955..cc07f44c 100644 --- a/tests/src/ObserverTest.h +++ b/tests/src/ObserverTest.h @@ -23,11 +23,14 @@ using namespace react; /////////////////////////////////////////////////////////////////////////////////////////////////// /// ObserverTest fixture /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class ObserverTest : public testing::Test { public: - REACTIVE_DOMAIN(MyDomain, TEngine) + template + class MyEngine : public TParams::template EngineT {}; + + REACTIVE_DOMAIN(MyDomain, TParams::mode, MyEngine) }; TYPED_TEST_CASE_P(ObserverTest); diff --git a/tests/src/ObserverTestQ.cpp b/tests/src/ObserverTestQ.cpp index c77b6fda..68449272 100644 --- a/tests/src/ObserverTestQ.cpp +++ b/tests/src/ObserverTestQ.cpp @@ -5,6 +5,7 @@ // http://www.boost.org/LICENSE_1_0.txt) #include "ObserverTest.h" +#include "TestUtil.h" #include "react/engine/PulsecountEngine.h" #include "react/engine/ToposortEngine.h" @@ -15,9 +16,14 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, ObserverTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, ObserverTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, ObserverTest, PulsecountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, ObserverTest, SubtreeEngine); +using P1 = DomainParams; +using P2 = DomainParams; +using P3 = DomainParams; +using P4 = DomainParams; + +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, ObserverTest, P1); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, ObserverTest, P2); +INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, ObserverTest, P3); +INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, ObserverTest, P4); } // ~namespace \ No newline at end of file diff --git a/tests/src/OperationsTest.cpp b/tests/src/OperationsTest.cpp index d1a89f52..72257431 100644 --- a/tests/src/OperationsTest.cpp +++ b/tests/src/OperationsTest.cpp @@ -5,6 +5,7 @@ // http://www.boost.org/LICENSE_1_0.txt) #include "OperationsTest.h" +#include "TestUtil.h" #include "react/engine/PulsecountEngine.h" #include "react/engine/ToposortEngine.h" @@ -15,9 +16,14 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, OperationsTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, OperationsTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, OperationsTest, PulsecountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(Subtree, OperationsTest, SubtreeEngine); +using P1 = DomainParams; +using P2 = DomainParams; +using P3 = DomainParams; +using P4 = DomainParams; + +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, OperationsTest, P1); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, OperationsTest, P2); +INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, OperationsTest, P3); +INSTANTIATE_TYPED_TEST_CASE_P(Subtree, OperationsTest, P4); } // ~namespace \ No newline at end of file diff --git a/tests/src/OperationsTest.h b/tests/src/OperationsTest.h index 60a8e98d..6490a3f9 100644 --- a/tests/src/OperationsTest.h +++ b/tests/src/OperationsTest.h @@ -33,11 +33,14 @@ struct Decrementer { T operator()(Token, T v) const { return v-1; } }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventStreamTest fixture /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class OperationsTest : public testing::Test { public: - REACTIVE_DOMAIN(MyDomain, TEngine) + template + class MyEngine : public TParams::template EngineT {}; + + REACTIVE_DOMAIN(MyDomain, TParams::mode, MyEngine) }; TYPED_TEST_CASE_P(OperationsTest); diff --git a/tests/src/OperationsTestQ.cpp b/tests/src/OperationsTestQ.cpp index cda8cb7e..5da10bfa 100644 --- a/tests/src/OperationsTestQ.cpp +++ b/tests/src/OperationsTestQ.cpp @@ -5,6 +5,7 @@ // http://www.boost.org/LICENSE_1_0.txt) #include "OperationsTest.h" +#include "TestUtil.h" #include "react/engine/PulsecountEngine.h" #include "react/engine/ToposortEngine.h" @@ -15,9 +16,14 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, OperationsTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, OperationsTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, OperationsTest, PulsecountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, OperationsTest, SubtreeEngine); +using P1 = DomainParams; +using P2 = DomainParams; +using P3 = DomainParams; +using P4 = DomainParams; + +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, OperationsTest, P1); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, OperationsTest, P2); +INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, OperationsTest, P3); +INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, OperationsTest, P4); } // ~namespace \ No newline at end of file diff --git a/tests/src/SignalTest.cpp b/tests/src/SignalTest.cpp index fbe9bbe4..d19239bd 100644 --- a/tests/src/SignalTest.cpp +++ b/tests/src/SignalTest.cpp @@ -5,6 +5,7 @@ // http://www.boost.org/LICENSE_1_0.txt) #include "SignalTest.h" +#include "TestUtil.h" #include "react/engine/PulsecountEngine.h" #include "react/engine/ToposortEngine.h" @@ -15,9 +16,14 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, SignalTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, SignalTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, SignalTest, PulsecountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(Subtree, SignalTest, SubtreeEngine); +using P1 = DomainParams; +using P2 = DomainParams; +using P3 = DomainParams; +using P4 = DomainParams; + +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, SignalTest, P1); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, SignalTest, P2); +INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, SignalTest, P3); +INSTANTIATE_TYPED_TEST_CASE_P(Subtree, SignalTest, P4); } // ~namespace \ No newline at end of file diff --git a/tests/src/SignalTest.h b/tests/src/SignalTest.h index f15c7735..1dfbcb8a 100644 --- a/tests/src/SignalTest.h +++ b/tests/src/SignalTest.h @@ -22,11 +22,14 @@ using namespace react; /////////////////////////////////////////////////////////////////////////////////////////////////// /// SignalTest fixture /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class SignalTest : public testing::Test { public: - REACTIVE_DOMAIN(MyDomain, TEngine) + template + class MyEngine : public TParams::template EngineT {}; + + REACTIVE_DOMAIN(MyDomain, TParams::mode, MyEngine) }; TYPED_TEST_CASE_P(SignalTest); diff --git a/tests/src/SignalTestQ.cpp b/tests/src/SignalTestQ.cpp index b383a98b..6e93c730 100644 --- a/tests/src/SignalTestQ.cpp +++ b/tests/src/SignalTestQ.cpp @@ -5,6 +5,7 @@ // http://www.boost.org/LICENSE_1_0.txt) #include "SignalTest.h" +#include "TestUtil.h" #include "react/engine/PulsecountEngine.h" #include "react/engine/ToposortEngine.h" @@ -15,9 +16,14 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, SignalTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, SignalTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, SignalTest, PulsecountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, SignalTest, SubtreeEngine); +using P1 = DomainParams; +using P2 = DomainParams; +using P3 = DomainParams; +using P4 = DomainParams; + +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, SignalTest, P1); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, SignalTest, P2); +INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, SignalTest, P3); +INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, SignalTest, P4); } // ~namespace \ No newline at end of file diff --git a/tests/src/TestUtil.h b/tests/src/TestUtil.h new file mode 100644 index 00000000..dd00efb8 --- /dev/null +++ b/tests/src/TestUtil.h @@ -0,0 +1,25 @@ + +// 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) + +#pragma once + +#ifndef REACT_TESTS_TESTUTIL_H_INCLUDED +#define REACT_TESTS_TESTUTIL_H_INCLUDED + +template +< + EDomainMode m, + template class TTEngine +> +struct DomainParams +{ + static const EDomainMode mode = m; + + template + using EngineT = TTEngine; +}; + +#endif // REACT_TESTS_TESTUTIL_H_INCLUDED \ No newline at end of file diff --git a/tests/src/TransactionTest.cpp b/tests/src/TransactionTest.cpp index b5cfd7ec..61a51e11 100644 --- a/tests/src/TransactionTest.cpp +++ b/tests/src/TransactionTest.cpp @@ -5,6 +5,7 @@ // http://www.boost.org/LICENSE_1_0.txt) #include "TransactionTest.h" +#include "TestUtil.h" #include "react/engine/PulsecountEngine.h" #include "react/engine/ToposortEngine.h" @@ -15,9 +16,14 @@ namespace { using namespace react; -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, TransactionTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, TransactionTest, ToposortEngine); -INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, TransactionTest, PulsecountEngine); -INSTANTIATE_TYPED_TEST_CASE_P(Subtree, TransactionTest, SubtreeEngine); +using P1 = DomainParams; +using P2 = DomainParams; +using P3 = DomainParams; +using P4 = DomainParams; + +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, TransactionTest, P1); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, TransactionTest, P2); +INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, TransactionTest, P3); +INSTANTIATE_TYPED_TEST_CASE_P(Subtree, TransactionTest, P4); } // ~namespace \ No newline at end of file diff --git a/tests/src/TransactionTest.h b/tests/src/TransactionTest.h index 4452edb3..af74f3b0 100644 --- a/tests/src/TransactionTest.h +++ b/tests/src/TransactionTest.h @@ -26,11 +26,14 @@ using namespace react; /////////////////////////////////////////////////////////////////////////////////////////////////// /// SignalTest fixture /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class TransactionTest : public testing::Test { public: - REACTIVE_DOMAIN(MyDomain, TEngine) + template + class MyEngine : public TParams::template EngineT {}; + + REACTIVE_DOMAIN(MyDomain, TParams::mode, MyEngine) }; TYPED_TEST_CASE_P(TransactionTest); @@ -431,7 +434,7 @@ TYPED_TEST_P(TransactionTest, Continuation2) { using L = typename Continuation2::MyDomain; - REACTIVE_DOMAIN(R) + REACTIVE_DOMAIN(R, sequential) std::vector results; @@ -464,7 +467,7 @@ TYPED_TEST_P(TransactionTest, Continuation3) { using L = typename Continuation3::MyDomain; - REACTIVE_DOMAIN(R) + REACTIVE_DOMAIN(R, sequential) std::vector results; From 40122886b338353296023069ec0ec65bb3de86e7 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 25 Jun 2014 02:27:49 +0200 Subject: [PATCH 165/266] Updated readme. --- README.md | 64 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 8c4dbe37..befafaa0 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,27 @@ # Introduction -Cpp.React is an experimental [Reactive Programming](http://en.wikipedia.org/wiki/Reactive_programming) library for C++11. +Cpp.React is reactive programming library for C++11. +Generally speaking, it provides abstractions to handle change propagation and data processing for a push-based event model. +It is an alternative to implementing imperative change propagation manually through callback functions and side-effects. -It provides abstractions to simplify the implementation of reactive behaviour. +The two core features of the library are -To react to changing data, reactive variables are declared in terms of functions that calcuate their values, rather than manipulating them directly. -A runtime engine will automatically handle the change propagation by re-calculating data if its dependencies have been modified. +* **signals**, reactive variables that are automatically re-calculated when their dependencies change, and +* **event streams** as composable first class objects. -To react to events, event streams are provided as composable first class objects with value semantics. +Signals specifically deal with aspects of time-varying state, whereas event streams facilitate generic processing. +A set of operations and algorithms can be used to combine signals and events. +Together, these abstractions enable intuitive modeling of dataflow systems in a declarative manner. -For seamless integration with existing code, _reactive domains_ encapsulate a reactive sub-system, with an input interface to trigger changes imperatively, and an output interface to apply side effects. +In this case, declarative means that the programer defines how values are calculated through functional composition of other values. +Applying these calcuations is implicitely handled by a so-called propagation engine as changes are propagated through the dataflow graph. -As an alternative to implementing change propagation or callback-based approaches manually, using Cpp.React results in less boilerplate code and better scalability due to the guarentees of avoiding unnecessary recalculations and glitches. -Furthermore, since functional purity is maintained in each reactive domain, the propagation engine can safely support implicit parallelism for the updating process. -Behind the scenes, task-based programming and dynamic chunking based on collected timing data are employed to optimize parallel utilization. +Since the engine has the "whole picture", it can schedule updates efficiently, so that: + +* No value is re-calculated or processed unnecessarily; +* intermediate results are cached to avoid redundant calculations; +* no glitches will occur (i.e. no inconsistent sets of input values). + +It can even implicitly parallelize calculations, while automatically taking care of potential data-races and effective utilization of available parallel hardware. # Documentation The documentation is still incomplete, but it already contains plenty of useful information and examples. @@ -42,8 +51,6 @@ make For more details, see [Build instructions](https://github.com/schlangster/cpp.react/wiki/Build-instructions). ### Dependencies -Cpp.React uses several external dependencies, but only one of them is mandatory: - * [Intel TBB 4.2](https://www.threadingbuildingblocks.org/) (required) * [Google test framework](https://code.google.com/p/googletest/) (optional, to compile the unit tests) * [Boost 1.55.0 C++ Libraries](http://www.boost.org/) (optional, to include Reactor.h, which requires `boost::coroutine`) @@ -59,7 +66,11 @@ They can be combined to expressions to create new signals, which are automatical using namespace std; using namespace react; -REACTIVE_DOMAIN(D) +// Define a reactive domain that uses single-threaded, sequential updating +REACTIVE_DOMAIN(D, sequential) + +// Define aliases for types of the given domain, +// e.g. using VarSignalT = VarSignal USING_REACTIVE_DOMAIN(D) // Two reactive variables that can be manipulated imperatively @@ -67,11 +78,14 @@ VarSignalT width = MakeVar(1); VarSignalT height = MakeVar(2); // A signal that depends on width and height and multiplies their values -SignalT area = MakeSignal(With(width, height), +SignalT area = MakeSignal( + With(width, height), [] (int w, int h) { return w * h; }); - +``` +``` +// Signal values can be accessed imperativly at any time, but only VarSignals can be directly manipulated. cout << "area: " << area.Value() << endl; // => area: 2 // Width changed, so area is re-calculated automatically @@ -80,22 +94,19 @@ width.Set(10); cout << "area: " << area.Value() << endl; // => area: 20 ``` -For expressions that use operators only, `MakeSignal` can be omitted completely: +Overloaded operators for signal types allow to omit `MakeSignal` in this case for a more concise syntax: ```C++ -// Lift as reactive expression +// Lift as reactive expression - equivalent to previous example SignalT area = width * height; ``` ## Events and Observers -Event streams represent flows of discrete values. -They are first-class objects and can be merged, filtered, transformed or composed to more complex types: - ```C++ using namespace std; using namespace react; -REACTIVE_DOMAIN(D) +REACTIVE_DOMAIN(D, sequential) USING_REACTIVE_DOMAIN(D) // Two event sources @@ -109,15 +120,14 @@ EventsT merged = leftClicked | rightClicked; auto obs = Observe(merged, [] (Token) { cout << "clicked!" << endl; }); - -// ... - +``` +``` rightClicked.Emit(); // => clicked! ``` -## Implicit parallelism +## Parallelism and concurrency -The change propagation is handled implicitly by a _propagation engine_. +The change propagation is handled implicitly by a propagation engine. Depending on the selected engine, updates can be parallelized: ```C++ @@ -126,7 +136,7 @@ using namespace react; // Sequential updating { - REACTIVE_DOMAIN(D, ToposortEngine) + REACTIVE_DOMAIN(D, sequential) auto a = MakeVar(1); auto b = MakeVar(2); @@ -139,7 +149,7 @@ using namespace react; // Parallel updating { - REACTIVE_DOMAIN(D, ToposortEngine) + REACTIVE_DOMAIN(D, parallel) auto in = MakeVar(0); From 8bfd19867f272f3612099c029c85a3e2d378e349 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 25 Jun 2014 02:31:17 +0200 Subject: [PATCH 166/266] Updated readme. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index befafaa0..0f9cca6b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # Introduction Cpp.React is reactive programming library for C++11. Generally speaking, it provides abstractions to handle change propagation and data processing for a push-based event model. -It is an alternative to implementing imperative change propagation manually through callback functions and side-effects. The two core features of the library are @@ -23,6 +22,8 @@ Since the engine has the "whole picture", it can schedule updates efficiently, s It can even implicitly parallelize calculations, while automatically taking care of potential data-races and effective utilization of available parallel hardware. +This makes using Cpp.React an alternative to implementing imperative change propagation manually through callback functions and side-effects. + # Documentation The documentation is still incomplete, but it already contains plenty of useful information and examples. It can be found in the [wiki](https://github.com/schlangster/cpp.react/wiki). From 04bf0eba59760b03e0538e152ab1d946d0f32691 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 25 Jun 2014 02:33:13 +0200 Subject: [PATCH 167/266] Updated readme. --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0f9cca6b..33403ab2 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,8 @@ SignalT area = MakeSignal( }); ``` ``` -// Signal values can be accessed imperativly at any time, but only VarSignals can be directly manipulated. +// Signal values can be accessed imperativly at any time, +// but only VarSignals can be directly manipulated. cout << "area: " << area.Value() << endl; // => area: 2 // Width changed, so area is re-calculated automatically @@ -103,6 +104,8 @@ SignalT area = width * height; ## Events and Observers +Event streams represent flows of discrete values. They are first-class objects and can be merged, filtered, transformed or composed to more complex types: + ```C++ using namespace std; using namespace react; From 5a2bb612af5a66de5e84ae7eb1f4ef871ad364ca Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 29 Jun 2014 16:35:49 +0200 Subject: [PATCH 168/266] Added ReactorT::Context::Get. --- include/react/Reactor.h | 6 ++ include/react/detail/graph/ReactorNodes.h | 70 ++++++++++++++++------- 2 files changed, 54 insertions(+), 22 deletions(-) diff --git a/include/react/Reactor.h b/include/react/Reactor.h index ceec738d..eac2f93e 100644 --- a/include/react/Reactor.h +++ b/include/react/Reactor.h @@ -50,6 +50,12 @@ class Reactor node_.RepeatUntil(evn.NodePtr(), std::forward(func)); } + template + const S& Get(const Signal& sig) + { + return node_.Get(sig.NodePtr()); + } + private: NodeT& node_; }; diff --git a/include/react/detail/graph/ReactorNodes.h b/include/react/detail/graph/ReactorNodes.h index 3b300380..9a259bae 100644 --- a/include/react/detail/graph/ReactorNodes.h +++ b/include/react/detail/graph/ReactorNodes.h @@ -78,10 +78,7 @@ class ReactorNode : // First blocking event is not initiated by Tick() but after loop creation. NodeBase* p = mainLoop_.get(); - - assert(p != nullptr); - - Engine::OnNodeAttach(*this, *p); + doAttach(p); ++depCount_; } @@ -101,8 +98,7 @@ class ReactorNode : if (p != nullptr) { - Engine::OnDynamicNodeAttach(*this, *p, *turnPtr_); - ++depCount_; + doDynAttach(p); } else { @@ -126,10 +122,7 @@ class ReactorNode : while (! checkEvent(events)) (*curOutPtr_)(nullptr); - assert(turnPtr_ != nullptr); - - Engine::OnDynamicNodeDetach(*this, *events, *turnPtr_); - --depCount_; + doDynDetach(events.get()); auto index = offsets_[reinterpret_cast(events.get())]++; return events->Events()[index]; @@ -147,8 +140,7 @@ class ReactorNode : { // Non-dynamic attach in case first loop until is encountered before the loop was // suspended for the first time. - Engine::OnNodeAttach(*this, *eventsPtr); - ++depCount_; + doAttach(eventsPtr.get()); } // Don't enter loop if event already present @@ -190,18 +182,20 @@ class ReactorNode : --depCount_; } - //template - //S& Get(const std::shared_ptr>& sig) - //{ - // // Attach to target signal node - // (*curOutPtr_)(sig.get()); + template + const S& Get(const std::shared_ptr>& sigPtr) + { + // Do dynamic attach followed by attach if Get() happens during a turn. + if (turnPtr_ != nullptr) + { + // Request attach + (*curOutPtr_)(sigPtr.get()); - // Engine::OnDynamicNodeDetach(*this, *events, *turnPtr_); - // --depCount_; + doDynDetach(sigPtr.get()); + } - // auto index = offsets_[reinterpret_cast(events.get())]++; - // return events->Events()[index]; - //} + return sigPtr->ValueRef(); + } private: template @@ -216,6 +210,38 @@ class ReactorNode : return offsets_[index] < events->Events().size(); } + void doAttach(NodeBase* nodePtr) + { + assert(nodePtr != nullptr); + assert(turnPtr_ == nullptr); + Engine::OnNodeAttach(*this, *nodePtr); + ++depCount_; + } + + void doDetach(NodeBase* nodePtr) + { + assert(nodePtr != nullptr); + assert(turnPtr_ == nullptr); + Engine::OnNodeDetach(*this, *nodePtr); + --depCount_; + } + + void doDynAttach(NodeBase* nodePtr) + { + assert(nodePtr != nullptr); + assert(turnPtr_ != nullptr); + Engine::OnDynamicNodeAttach(*this, *nodePtr, *turnPtr_); + ++depCount_; + } + + void doDynDetach(NodeBase* nodePtr) + { + assert(nodePtr != nullptr); + assert(turnPtr_ != nullptr); + Engine::OnDynamicNodeDetach(*this, *nodePtr, *turnPtr_); + --depCount_; + } + std::function func_; PullT mainLoop_; From e929f025abcd34617a8e26336872911546852014 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 8 Jul 2014 12:35:32 +0200 Subject: [PATCH 169/266] DoTransaction is now a free function. --- benchmarks/src/BenchmarkRandom.h | 2 +- examples/src/BasicAlgorithms.cpp | 2 +- examples/src/BasicComposition.cpp | 2 +- examples/src/BasicEvents.cpp | 2 +- examples/src/BasicReactors.cpp | 74 +++++++++++++++++ examples/src/BasicSignals.cpp | 2 +- examples/src/Main.cpp | 12 ++- include/react/Domain.h | 127 +++++++++++++++++------------- tests/src/EventStreamTest.h | 4 +- tests/src/OperationsTest.h | 4 +- tests/src/SignalTest.h | 65 +++------------ tests/src/TransactionTest.h | 79 +++++++++++++++---- 12 files changed, 235 insertions(+), 140 deletions(-) diff --git a/benchmarks/src/BenchmarkRandom.h b/benchmarks/src/BenchmarkRandom.h index a8e82fe5..743a7363 100644 --- a/benchmarks/src/BenchmarkRandom.h +++ b/benchmarks/src/BenchmarkRandom.h @@ -304,7 +304,7 @@ struct Benchmark_Random : public BenchmarkBase auto t0 = tbb::tick_count::now(); for (int i=0; i([&] { for (int j=0; j([&] { mySensor.Samples << 30 << 31 << 31 << 32; }); // output: 32 diff --git a/examples/src/BasicComposition.cpp b/examples/src/BasicComposition.cpp index 5d0a8ad4..da6075f5 100644 --- a/examples/src/BasicComposition.cpp +++ b/examples/src/BasicComposition.cpp @@ -46,7 +46,7 @@ namespace example1 cout << "Size changed to " << newValue << endl; }); - D::DoTransaction([&] { + DoTransaction([&] { myShape.Width <<= 4; myShape.Height <<= 4; }); // output: Size changed to 16 diff --git a/examples/src/BasicEvents.cpp b/examples/src/BasicEvents.cpp index 02c3f450..656a141b 100644 --- a/examples/src/BasicEvents.cpp +++ b/examples/src/BasicEvents.cpp @@ -238,7 +238,7 @@ namespace example5 cout << v << endl; }); // output: 1, 2, 3, 4 - D::DoTransaction([] { + DoTransaction([] { src << 1 << 2 << 3; src << 4; }); diff --git a/examples/src/BasicReactors.cpp b/examples/src/BasicReactors.cpp index 3562adad..1e30cc1c 100644 --- a/examples/src/BasicReactors.cpp +++ b/examples/src/BasicReactors.cpp @@ -9,6 +9,7 @@ #include #include "react/Domain.h" +#include "react/Signal.h" #include "react/Event.h" #include "react/Reactor.h" @@ -76,12 +77,85 @@ namespace example1 } } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 2 - Creating reactive loops +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example2 +{ + using namespace std; + using namespace react; + + REACTIVE_DOMAIN(D, sequential) + USING_REACTIVE_DOMAIN(D) + + using PointT = pair; + using PathT = vector; + + vector paths; + + EventSourceT mouseDown = MakeEventSource(); + EventSourceT mouseUp = MakeEventSource(); + EventSourceT mouseMove = MakeEventSource(); + + VarSignalT counter = MakeVar(103); + + ReactorT loop + { + [&] (ReactorT::Context ctx) + { + PathT points; + + points.emplace_back(ctx.Await(mouseDown)); + + auto count = ctx.Get(counter); + + ctx.RepeatUntil(mouseUp, [&] { + points.emplace_back(ctx.Await(mouseMove)); + }); + + points.emplace_back(ctx.Await(mouseUp)); + + paths.push_back(points); + } + }; + + void Run() + { + cout << "Example 2 - Creating reactive loops" << endl; + + mouseDown << PointT( 1,1 ); + mouseMove << PointT( 2,2 ) << PointT( 3,3 ) << PointT( 4,4 ); + mouseUp << PointT( 5,5 ); + + counter <<= 42; + + mouseMove << PointT( 999,999 ); + + counter <<= 80; + + mouseDown << PointT( 10,10 ); + mouseMove << PointT( 20,20 ); + mouseUp << PointT( 30,30 ); + + for (const auto& path : paths) + { + cout << "Path: "; + for (const auto& point : path) + cout << "(" << point.first << "," << point.second << ") "; + cout << endl; + } + + cout << endl; + } +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Run examples /////////////////////////////////////////////////////////////////////////////////////////////////// int main() { example1::Run(); + example2::Run(); return 0; } \ No newline at end of file diff --git a/examples/src/BasicSignals.cpp b/examples/src/BasicSignals.cpp index 78af0975..03707949 100644 --- a/examples/src/BasicSignals.cpp +++ b/examples/src/BasicSignals.cpp @@ -154,7 +154,7 @@ namespace example3 a <<= 2; // output: z changed to 6 b <<= 2; // output: z changed to 8 - D::DoTransaction([] { + DoTransaction([] { a <<= 4; b <<= 4; }); // output: z changed to 16 diff --git a/examples/src/Main.cpp b/examples/src/Main.cpp index e061a6fb..1a8f1ee3 100644 --- a/examples/src/Main.cpp +++ b/examples/src/Main.cpp @@ -21,10 +21,12 @@ using namespace react; // Defines a domain. // Each domain represents a separate dependency graph, managed by a dedicated propagation engine. // Reactives of different domains can not be combined. -REACTIVE_DOMAIN(D, sequential) + void SignalExample3() { + REACTIVE_DOMAIN(D, sequential_concurrent) + cout << "Signal Example 3" << endl; auto src = MakeVar(0); @@ -109,6 +111,8 @@ void SignalExample5() void testme() { + REACTIVE_DOMAIN(D, sequential_concurrent) + std::vector results; auto f_0 = [] (int a) -> int @@ -159,21 +163,21 @@ void testme() for (int i=0; i<10000; i++) { - D::AsyncTransaction(st, [&,i] { + AsyncTransaction(st, [&,i] { n1 <<= 1+i; }); } for (int i=0; i<10000; i++) { - D::AsyncTransaction(st, [&,i] { + AsyncTransaction(st, [&,i] { n1 <<= 20000+i; }); } for (int i=0; i<10000; i++) { - D::AsyncTransaction(st, [&,i] { + AsyncTransaction(st, [&,i] { n1 <<= 100000+i; }); } diff --git a/include/react/Domain.h b/include/react/Domain.h index 13c7bfd0..8128d984 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -127,8 +127,11 @@ class TransactionStatus private: std::shared_ptr state_; - template - friend class DomainBase; + template + friend void AsyncTransaction(TransactionStatus& status, F&& func); + + template + friend void AsyncTransaction(TurnFlagsT flags, TransactionStatus& status, F&& func); }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -178,59 +181,6 @@ class DomainBase using ReactorT = Reactor; - /////////////////////////////////////////////////////////////////////////////////////////////// - /// DoTransaction - /////////////////////////////////////////////////////////////////////////////////////////////// - template - static void DoTransaction(F&& func) - { - using REACT_IMPL::DomainSpecificInputManager; - DomainSpecificInputManager::Instance().DoTransaction(0, std::forward(func)); - } - - template - static void DoTransaction(TurnFlagsT flags, F&& func) - { - using REACT_IMPL::DomainSpecificInputManager; - DomainSpecificInputManager::Instance().DoTransaction(flags, std::forward(func)); - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - /// AsyncTransaction - /////////////////////////////////////////////////////////////////////////////////////////////// - template - static void AsyncTransaction(F&& func) - { - using REACT_IMPL::DomainSpecificInputManager; - DomainSpecificInputManager::Instance() - .AsyncTransaction(0, nullptr, std::forward(func)); - } - - template - static void AsyncTransaction(TurnFlagsT flags, F&& func) - { - using REACT_IMPL::DomainSpecificInputManager; - DomainSpecificInputManager::Instance() - .AsyncTransaction(flags, nullptr, std::forward(func)); - } - - template - static void AsyncTransaction(TransactionStatus& status, F&& func) - { - using REACT_IMPL::DomainSpecificInputManager; - - DomainSpecificInputManager::Instance() - .AsyncTransaction(0, status.state_, std::forward(func)); - } - - template - static void AsyncTransaction(TurnFlagsT flags, TransactionStatus& status, F&& func) - { - using REACT_IMPL::DomainSpecificInputManager; - DomainSpecificInputManager::Instance() - .AsyncTransaction(flags, status.state_, std::forward(func)); - } - #ifdef REACT_ENABLE_LOGGING /////////////////////////////////////////////////////////////////////////////////////////////// /// Log @@ -294,6 +244,8 @@ template auto MakeContinuation(const Signal& trigger, FIn&& func) -> Continuation { + static_assert(DOut::is_concurrent, "MakeContinuation requires concurrent target domain."); + using REACT_IMPL::SignalContinuationNode; using F = typename std::decay::type; @@ -315,6 +267,8 @@ template auto MakeContinuation(const Events& trigger, FIn&& func) -> Continuation { + static_assert(DOut::is_concurrent, "MakeContinuation requires concurrent target domain."); + using REACT_IMPL::EventContinuationNode; using F = typename std::decay::type; @@ -338,6 +292,8 @@ auto MakeContinuation(const Events& trigger, const SignalPack& depPack, FIn&& func) -> Continuation { + static_assert(DOut::is_concurrent, "MakeContinuation requires concurrent target domain."); + using REACT_IMPL::SyncedContinuationNode; using F = typename std::decay::type; @@ -366,6 +322,67 @@ auto MakeContinuation(const Events& trigger, depPack.Data); } +/////////////////////////////////////////////////////////////////////////////////////////////// +/// DoTransaction +/////////////////////////////////////////////////////////////////////////////////////////////// +template +void DoTransaction(F&& func) +{ + using REACT_IMPL::DomainSpecificInputManager; + DomainSpecificInputManager::Instance().DoTransaction(0, std::forward(func)); +} + +template +void DoTransaction(TurnFlagsT flags, F&& func) +{ + using REACT_IMPL::DomainSpecificInputManager; + DomainSpecificInputManager::Instance().DoTransaction(flags, std::forward(func)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// AsyncTransaction +/////////////////////////////////////////////////////////////////////////////////////////////// +template +void AsyncTransaction(F&& func) +{ + static_assert(D::is_concurrent, "AsyncTransaction requires concurrent domain."); + + using REACT_IMPL::DomainSpecificInputManager; + DomainSpecificInputManager::Instance() + .AsyncTransaction(0, nullptr, std::forward(func)); +} + +template +void AsyncTransaction(TurnFlagsT flags, F&& func) +{ + static_assert(D::is_concurrent, "AsyncTransaction requires concurrent domain."); + + using REACT_IMPL::DomainSpecificInputManager; + DomainSpecificInputManager::Instance() + .AsyncTransaction(flags, nullptr, std::forward(func)); +} + +template +void AsyncTransaction(TransactionStatus& status, F&& func) +{ + static_assert(D::is_concurrent, "AsyncTransaction requires concurrent domain."); + + using REACT_IMPL::DomainSpecificInputManager; + + DomainSpecificInputManager::Instance() + .AsyncTransaction(0, status.state_, std::forward(func)); +} + +template +void AsyncTransaction(TurnFlagsT flags, TransactionStatus& status, F&& func) +{ + static_assert(D::is_concurrent, "AsyncTransaction requires concurrent domain."); + + using REACT_IMPL::DomainSpecificInputManager; + DomainSpecificInputManager::Instance() + .AsyncTransaction(flags, status.state_, std::forward(func)); +} + /******************************************/ REACT_END /******************************************/ /***************************************/ REACT_IMPL_BEGIN /**************************************/ diff --git a/tests/src/EventStreamTest.h b/tests/src/EventStreamTest.h index 8a70cfb2..55a67124 100644 --- a/tests/src/EventStreamTest.h +++ b/tests/src/EventStreamTest.h @@ -111,7 +111,7 @@ TYPED_TEST_P(EventStreamTest, EventMerge1) results.push_back(v); }); - D::DoTransaction([&] { + DoTransaction([&] { a1 << 10; a2 << 20; a3 << 30; @@ -147,7 +147,7 @@ TYPED_TEST_P(EventStreamTest, EventMerge2) std::string s2("two"); std::string s3("three"); - D::DoTransaction([&] { + DoTransaction([&] { a1 << s1; a2 << s2; a3 << s3; diff --git a/tests/src/OperationsTest.h b/tests/src/OperationsTest.h index 6490a3f9..87c54f35 100644 --- a/tests/src/OperationsTest.h +++ b/tests/src/OperationsTest.h @@ -93,7 +93,7 @@ TYPED_TEST_P(OperationsTest, Iterate2) ASSERT_EQ(v, 5050); }); - D::DoTransaction([&] { + DoTransaction([&] { for (auto i=1; i<=100; i++) numSrc << i; }); @@ -660,7 +660,7 @@ TYPED_TEST_P(OperationsTest, SyncedEventTransform1) in1 << string("Hello Worlt") << string("Hello World"); - D::DoTransaction([&] { + DoTransaction([&] { in2 << string("Hello Vorld"); first.Set(string("Alice")); last.Set(string("Anderson")); diff --git a/tests/src/SignalTest.h b/tests/src/SignalTest.h index 1dfbcb8a..8d7a34e2 100644 --- a/tests/src/SignalTest.h +++ b/tests/src/SignalTest.h @@ -226,7 +226,7 @@ TYPED_TEST_P(SignalTest, Signals3) ASSERT_EQ(result(),6); - D::DoTransaction([&] { + DoTransaction([&] { a1 <<= 2; a2 <<= 2; }); @@ -421,7 +421,7 @@ TYPED_TEST_P(SignalTest, Flatten2) ASSERT_EQ(result(), 100 + 300); ASSERT_EQ(observeCount, 2); - D::DoTransaction([&] { + DoTransaction([&] { a0 <<= 5000; a1 <<= 6000; }); @@ -462,7 +462,7 @@ TYPED_TEST_P(SignalTest, Flatten3) ASSERT_EQ(result(), 10 + 30); ASSERT_EQ(observeCount, 0); - D::DoTransaction([&] { + DoTransaction([&] { inner1 <<= 1000; a0 <<= 200000; a1 <<= 50000; @@ -472,7 +472,7 @@ TYPED_TEST_P(SignalTest, Flatten3) ASSERT_EQ(result(), 50000 + 200000); ASSERT_EQ(observeCount, 1); - D::DoTransaction([&] { + DoTransaction([&] { a0 <<= 667; a1 <<= 776; }); @@ -480,7 +480,7 @@ TYPED_TEST_P(SignalTest, Flatten3) ASSERT_EQ(result(), 776 + 667); ASSERT_EQ(observeCount, 2); - D::DoTransaction([&] { + DoTransaction([&] { inner1 <<= 999; a0 <<= 888; }); @@ -516,7 +516,7 @@ TYPED_TEST_P(SignalTest, Flatten4) results.push_back(v); }); - D::DoTransaction([&] { + DoTransaction([&] { a3 <<= 400; outer <<= inner2; }); @@ -596,7 +596,7 @@ TYPED_TEST_P(SignalTest, Modify2) obsCount++; }); - D::DoTransaction([&] { + DoTransaction([&] { v.Modify([] (vector& v) { v.push_back(30); }); @@ -625,53 +625,6 @@ TYPED_TEST_P(SignalTest, Modify3) auto vect = MakeVar(vector{}); - int count = 0; - - // Terrible, but lets test it - auto cont = MakeContinuation(vect, [&] (const vector& v) - { - if (count == 0) - { - ASSERT_EQ(v[0], 30); - - vect.Modify([] (vector& v) { - v.push_back(50); - }); - } - else if (count == 1) - { - ASSERT_EQ(v[1], 50); - - vect.Modify([] (vector& v) { - v.push_back(70); - }); - } - else - { - ASSERT_EQ(v[2], 70); - } - - count++; - }); - - vect.Modify([] (vector& v) { - v.push_back(30); - }); - - ASSERT_EQ(count, 3); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Modify4 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(SignalTest, Modify4) -{ - using D = typename Modify4::MyDomain; - - using std::vector; - - auto vect = MakeVar(vector{}); - int obsCount = 0; Observe(vect, [&] (const vector& v) { @@ -683,7 +636,7 @@ TYPED_TEST_P(SignalTest, Modify4) }); // Also terrible - D::DoTransaction([&] { + DoTransaction([&] { vect.Set(vector{ 30, 50 }); @@ -704,7 +657,7 @@ REGISTER_TYPED_TEST_CASE_P FunctionBind1, FunctionBind2, Flatten1, Flatten2, Flatten3, Flatten4, Member1, - Modify1, Modify2, Modify3, Modify4 + Modify1, Modify2, Modify3 ); diff --git a/tests/src/TransactionTest.h b/tests/src/TransactionTest.h index af74f3b0..74fc7b6c 100644 --- a/tests/src/TransactionTest.h +++ b/tests/src/TransactionTest.h @@ -271,25 +271,25 @@ TYPED_TEST_P(TransactionTest, Merging1) // Todo: improve this as it'll fail occasionally shouldSpin = true; std::thread t1([&] { - D::DoTransaction(allow_merging, [&] { + DoTransaction(allow_merging, [&] { n1 <<= 2; }); }); std::this_thread::sleep_for(std::chrono::milliseconds(2000)); std::thread t2([&] { - D::DoTransaction(allow_merging, [&] { + DoTransaction(allow_merging, [&] { n1 <<= 3; }); }); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::thread t3([&] { - D::DoTransaction(allow_merging, [&] { + DoTransaction(allow_merging, [&] { n1 <<= 4; }); }); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::thread t4([&] { - D::DoTransaction(allow_merging, [&] { + DoTransaction(allow_merging, [&] { n1 <<= 5; }); @@ -325,15 +325,15 @@ TYPED_TEST_P(TransactionTest, Async1) TransactionStatus st; - D::AsyncTransaction(st, [&] { + AsyncTransaction(st, [&] { in <<= 10; }); - D::AsyncTransaction(st, [&] { + AsyncTransaction(st, [&] { in <<= 20; }); - D::AsyncTransaction(st, [&] { + AsyncTransaction(st, [&] { in <<= 30; }); @@ -363,7 +363,7 @@ TYPED_TEST_P(TransactionTest, AsyncMerging1) TransactionStatus st; - D::AsyncTransaction(allow_merging, st, [&] { + AsyncTransaction(allow_merging, st, [&] { std::this_thread::sleep_for(std::chrono::milliseconds(1000)); in <<= 10; }); @@ -372,25 +372,25 @@ TYPED_TEST_P(TransactionTest, AsyncMerging1) std::this_thread::sleep_for(std::chrono::milliseconds(10)); // These two can still be pulled in after the first input function is done - D::AsyncTransaction(allow_merging, st, [&] { + AsyncTransaction(allow_merging, st, [&] { in <<= 20; }); - D::AsyncTransaction(allow_merging, st, [&] { + AsyncTransaction(allow_merging, st, [&] { in <<= 30; }); // Can't be merged - D::AsyncTransaction(st, [&] { + AsyncTransaction(st, [&] { in <<= 40; }); // These two should be merged again - D::AsyncTransaction(allow_merging, st, [&] { + AsyncTransaction(allow_merging, st, [&] { in <<= 50; }); - D::AsyncTransaction(allow_merging, st, [&] { + AsyncTransaction(allow_merging, st, [&] { in <<= 60; }); @@ -434,7 +434,7 @@ TYPED_TEST_P(TransactionTest, Continuation2) { using L = typename Continuation2::MyDomain; - REACTIVE_DOMAIN(R, sequential) + REACTIVE_DOMAIN(R, sequential_concurrent) std::vector results; @@ -467,7 +467,7 @@ TYPED_TEST_P(TransactionTest, Continuation3) { using L = typename Continuation3::MyDomain; - REACTIVE_DOMAIN(R, sequential) + REACTIVE_DOMAIN(R, sequential_concurrent) std::vector results; @@ -506,6 +506,52 @@ TYPED_TEST_P(TransactionTest, Continuation3) ASSERT_TRUE(results[i] == i+1); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Continuation4 test +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(TransactionTest, Continuation4) +{ + using D = typename Continuation4::MyDomain; + + using std::vector; + + auto vect = MakeVar(vector{}); + + int count = 0; + + auto cont = MakeContinuation(vect, [&] (const vector& v) + { + if (count == 0) + { + ASSERT_EQ(v[0], 30); + + vect.Modify([] (vector& v) { + v.push_back(50); + }); + } + else if (count == 1) + { + ASSERT_EQ(v[1], 50); + + vect.Modify([] (vector& v) { + v.push_back(70); + }); + } + else + { + ASSERT_EQ(v[2], 70); + } + + count++; + }); + + vect.Modify([] (vector& v) { + v.push_back(30); + }); + + ASSERT_EQ(count, 3); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// REGISTER_TYPED_TEST_CASE_P ( @@ -518,7 +564,8 @@ REGISTER_TYPED_TEST_CASE_P AsyncMerging1, Continuation1, Continuation2, - Continuation3 + Continuation3, + Continuation4 ); } // ~namespace From 41977b192a6b58c5d40a64fcccaf42903c03d1de Mon Sep 17 00:00:00 2001 From: schlangster Date: Sat, 12 Jul 2014 01:43:35 +0200 Subject: [PATCH 170/266] Updated readme. --- README.md | 56 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 33403ab2..04d1b3fb 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,50 @@ # Introduction -Cpp.React is reactive programming library for C++11. -Generally speaking, it provides abstractions to handle change propagation and data processing for a push-based event model. -The two core features of the library are +# ![C++React](http://schlangster.github.io/cpp.react//media/logo_banner3.png) + +C++React is reactive programming library for C++11. + +Generally speaking, it provides abstractions to handle change propagation and data processing for a push-based event model. +A more practical description is that it enables coordinated, multi-layered - and potentially parallel - execution of callbacks. +All this happens implicitly, based on declarative definitions, with guarantees regarding -* **signals**, reactive variables that are automatically re-calculated when their dependencies change, and -* **event streams** as composable first class objects. +- update-minimality - nothing is re-calculated or processed unnecessarily; +- glitch-freedom - no transiently inconsistent data sets; +- thread-safety - no data races for parallel execution. -Signals specifically deal with aspects of time-varying state, whereas event streams facilitate generic processing. -A set of operations and algorithms can be used to combine signals and events. -Together, these abstractions enable intuitive modeling of dataflow systems in a declarative manner. +The core abstractions of the library are -In this case, declarative means that the programer defines how values are calculated through functional composition of other values. -Applying these calcuations is implicitely handled by a so-called propagation engine as changes are propagated through the dataflow graph. +- **signals**, reactive variables that are automatically re-calculated when their dependencies change, and +- **event streams** as composable first class objects. -Since the engine has the "whole picture", it can schedule updates efficiently, so that: +Signals specifically deal with aspects of time-varying state, whereas event streams facilitate event processing in general. -* No value is re-calculated or processed unnecessarily; -* intermediate results are cached to avoid redundant calculations; -* no glitches will occur (i.e. no inconsistent sets of input values). +Additional features include -It can even implicitly parallelize calculations, while automatically taking care of potential data-races and effective utilization of available parallel hardware. +- a publish/subscribe mechanism for callbacks with side-effects; +- a set of operations and algorithms to combine signals and events; +- a domain model to encapsulate multiple reactive systems; +- transactions to group related events, supporting both synchronous and asynchrounous execution. -This makes using Cpp.React an alternative to implementing imperative change propagation manually through callback functions and side-effects. # Documentation -The documentation is still incomplete, but it already contains plenty of useful information and examples. -It can be found in the [wiki](https://github.com/schlangster/cpp.react/wiki). + +If you're interested in learning about C++React, [have a look at its documentation](http://schlangster.github.io/cpp.react/). + +It's still incomplete, but it already contains plenty of useful information and examples. + # Development -Cpp.React is a work-in-progress and should not be considered release quality yet. -The project has been actively developed for about 6 months and has seen a fair share of testing and tuning during that time, so it's in a usable state. + +This library is still a work-in-progress and should not be considered release quality yet. + +Don't let this statement stop you from giving it a try though! +It's in a usable state and has already seen a fair share of testing and tuning during it's development so far. + ### Compiling -Cpp.React has been tested with the following compilers: + +The following compilers are supported at the moment: * Visual Studio 2013.2 * GCC 4.8.2 @@ -51,11 +62,14 @@ make For more details, see [Build instructions](https://github.com/schlangster/cpp.react/wiki/Build-instructions). + ### Dependencies + * [Intel TBB 4.2](https://www.threadingbuildingblocks.org/) (required) * [Google test framework](https://code.google.com/p/googletest/) (optional, to compile the unit tests) * [Boost 1.55.0 C++ Libraries](http://www.boost.org/) (optional, to include Reactor.h, which requires `boost::coroutine`) + # Features by example ## Signals From 98b17484d571e497b8a5e3e928f08ac857856d73 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sat, 12 Jul 2014 01:44:12 +0200 Subject: [PATCH 171/266] Updated readme. --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 04d1b3fb..40d68141 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -# Introduction - # ![C++React](http://schlangster.github.io/cpp.react//media/logo_banner3.png) C++React is reactive programming library for C++11. From c785b019e460140ec771565fa174d8e9e7235469 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sat, 12 Jul 2014 01:45:38 +0200 Subject: [PATCH 172/266] Updated readme. --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 40d68141..fcf3a086 100644 --- a/README.md +++ b/README.md @@ -25,14 +25,14 @@ Additional features include - transactions to group related events, supporting both synchronous and asynchrounous execution. -# Documentation +## Documentation If you're interested in learning about C++React, [have a look at its documentation](http://schlangster.github.io/cpp.react/). It's still incomplete, but it already contains plenty of useful information and examples. -# Development +## Development This library is still a work-in-progress and should not be considered release quality yet. @@ -68,9 +68,9 @@ For more details, see [Build instructions](https://github.com/schlangster/cpp.re * [Boost 1.55.0 C++ Libraries](http://www.boost.org/) (optional, to include Reactor.h, which requires `boost::coroutine`) -# Features by example +## Features by example -## Signals +### Signals Signals are self-updating reactive variables. They can be combined to expressions to create new signals, which are automatically re-calculated whenever one of their data dependencies changes. @@ -114,7 +114,7 @@ Overloaded operators for signal types allow to omit `MakeSignal` in this case fo SignalT area = width * height; ``` -## Events and Observers +### Event streams and Observers Event streams represent flows of discrete values. They are first-class objects and can be merged, filtered, transformed or composed to more complex types: @@ -141,7 +141,7 @@ auto obs = Observe(merged, [] (Token) { rightClicked.Emit(); // => clicked! ``` -## Parallelism and concurrency +### Parallelism and concurrency The change propagation is handled implicitly by a propagation engine. Depending on the selected engine, updates can be parallelized: @@ -188,13 +188,13 @@ using namespace react; } ``` -## More examples +### More examples * [Examples](https://github.com/schlangster/cpp.react/tree/master/examples/src) * [Test cases](https://github.com/schlangster/cpp.react/tree/master/tests/src) * [Benchmarks](https://github.com/schlangster/cpp.react/blob/master/benchmarks/src/BenchmarkLifeSim.h) -# Acknowledgements +## Acknowledgements The API of Cpp.React has been inspired by the following two research papers: From a68feb6e8a026f2afb767fcb6cbe0625b95af223 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sat, 12 Jul 2014 01:47:50 +0200 Subject: [PATCH 173/266] Updated readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fcf3a086..f2020d3e 100644 --- a/README.md +++ b/README.md @@ -196,7 +196,7 @@ using namespace react; ## Acknowledgements -The API of Cpp.React has been inspired by the following two research papers: +The API of C++React has been inspired by the following two research papers: * [Deprecating the Observer Pattern with Scala.React](http://infoscience.epfl.ch/record/176887/files/DeprecatingObservers2012.pdf) * [REScala: Bridging Between Object-oriented and Functional Style in Reactive Applications](http://www.stg.tu-darmstadt.de/media/st/research/rescala_folder/REScala-Bridging-The-Gap-Between-Object-Oriented-And-Functional-Style-In-Reactive-Applications.pdf) From f4d022fd9e600275adc46527981f5261457b50a5 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sat, 12 Jul 2014 02:29:34 +0200 Subject: [PATCH 174/266] Updated readme. --- README.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f2020d3e..36439dbe 100644 --- a/README.md +++ b/README.md @@ -34,10 +34,9 @@ It's still incomplete, but it already contains plenty of useful information and ## Development -This library is still a work-in-progress and should not be considered release quality yet. - -Don't let this statement stop you from giving it a try though! -It's in a usable state and has already seen a fair share of testing and tuning during it's development so far. +This library is a work-in-progress and should not be considered release quality yet. +Don't let this statement stop you from giving it a try though. +It's in a usable state and has already received a fair amount of testing and tuning. ### Compiling @@ -98,12 +97,12 @@ SignalT area = MakeSignal( }); ``` ``` -// Signal values can be accessed imperativly at any time, -// but only VarSignals can be directly manipulated. +// Signal values can be accessed imperativly cout << "area: " << area.Value() << endl; // => area: 2 -// Width changed, so area is re-calculated automatically +// VarSignals can be manipulated imperatively width.Set(10); +// Width changed, so area is re-calculated automatically cout << "area: " << area.Value() << endl; // => area: 20 ``` @@ -143,8 +142,8 @@ rightClicked.Emit(); // => clicked! ### Parallelism and concurrency -The change propagation is handled implicitly by a propagation engine. -Depending on the selected engine, updates can be parallelized: +The change propagation is handled implicitly. +Depending on the selected concurrency policy, updates can be parallelized: ```C++ using namespace react; @@ -192,7 +191,6 @@ using namespace react; * [Examples](https://github.com/schlangster/cpp.react/tree/master/examples/src) * [Test cases](https://github.com/schlangster/cpp.react/tree/master/tests/src) -* [Benchmarks](https://github.com/schlangster/cpp.react/blob/master/benchmarks/src/BenchmarkLifeSim.h) ## Acknowledgements From c897cbdd3f7de03337e46b3e9bc8fd37b9080de6 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 22 Jul 2014 01:46:09 +0200 Subject: [PATCH 175/266] Re-implemented how continuations are handled on transaction level. --- include/react/Domain.h | 86 +++- include/react/common/Concurrency.h | 208 +++++++++ include/react/common/RefCounting.h | 112 +++++ include/react/detail/ReactiveInput.h | 440 ++++++++++-------- .../react/detail/graph/ContinuationNodes.h | 25 +- project/msvc/CppReact.vcxproj | 1 + project/msvc/CppReact.vcxproj.filters | 3 + 7 files changed, 663 insertions(+), 212 deletions(-) create mode 100644 include/react/common/RefCounting.h diff --git a/include/react/Domain.h b/include/react/Domain.h index 8128d984..2b84d200 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -97,35 +97,36 @@ using REACT_IMPL::EPropagationMode; /////////////////////////////////////////////////////////////////////////////////////////////////// class TransactionStatus { - using StateT = REACT_IMPL::AsyncState; + using StateT = REACT_IMPL::SharedWaitingState; + using PtrT = REACT_IMPL::WaitingStatePtrT; public: TransactionStatus() : - state_( std::make_shared() ) + statePtr_( StateT::Create() ) {} TransactionStatus(const TransactionStatus&) = default; TransactionStatus(TransactionStatus&& other) : - state_( std::move(other.state_) ) + statePtr_( std::move(other.statePtr_) ) {} TransactionStatus& operator=(const TransactionStatus&) = default; TransactionStatus& operator=(TransactionStatus&& other) { - state_ = std::move(other.state_); + statePtr_ = std::move(other.statePtr_); return *this; } inline void Wait() { - assert(state_.get() != nullptr); - state_->Wait(); + assert(statePtr_.Get() != nullptr); + statePtr_->Wait(); } private: - std::shared_ptr state_; + PtrT statePtr_; template friend void AsyncTransaction(TransactionStatus& status, F&& func); @@ -241,17 +242,31 @@ template typename S, typename FIn > -auto MakeContinuation(const Signal& trigger, FIn&& func) +auto MakeContinuation(TurnFlagsT flags, const Signal& trigger, FIn&& func) -> Continuation { - static_assert(DOut::is_concurrent, "MakeContinuation requires concurrent target domain."); + static_assert(DOut::is_concurrent, + "MakeContinuation requires support for concurrent input to target domain."); using REACT_IMPL::SignalContinuationNode; using F = typename std::decay::type; return Continuation( std::make_shared>( - trigger.NodePtr(), std::forward(func))); + flags, trigger.NodePtr(), std::forward(func))); +} + +template +< + typename D, + typename DOut = D, + typename S, + typename FIn +> +auto MakeContinuation(const Signal& trigger, FIn&& func) + -> Continuation +{ + return MakeContinuation(0, trigger, std::forward(func)); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -264,17 +279,31 @@ template typename E, typename FIn > -auto MakeContinuation(const Events& trigger, FIn&& func) +auto MakeContinuation(TurnFlagsT flags, const Events& trigger, FIn&& func) -> Continuation { - static_assert(DOut::is_concurrent, "MakeContinuation requires concurrent target domain."); + static_assert(DOut::is_concurrent, + "MakeContinuation requires support for concurrent input to target domain."); using REACT_IMPL::EventContinuationNode; using F = typename std::decay::type; return Continuation( std::make_shared>( - trigger.NodePtr(), std::forward(func))); + flags, trigger.NodePtr(), std::forward(func))); +} + +template +< + typename D, + typename DOut = D, + typename E, + typename FIn +> +auto MakeContinuation(const Events& trigger, FIn&& func) + -> Continuation +{ + return MakeContinuation(0, trigger, std::forward(func)); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -288,18 +317,20 @@ template typename FIn, typename ... TDepValues > -auto MakeContinuation(const Events& trigger, +auto MakeContinuation(TurnFlagsT flags, const Events& trigger, const SignalPack& depPack, FIn&& func) -> Continuation { - static_assert(DOut::is_concurrent, "MakeContinuation requires concurrent target domain."); + static_assert(DOut::is_concurrent, + "MakeContinuation requires support for concurrent input to target domain."); using REACT_IMPL::SyncedContinuationNode; using F = typename std::decay::type; struct NodeBuilder_ { - NodeBuilder_(const Events& trigger, FIn&& func) : + NodeBuilder_(TurnFlagsT flags, const Events& trigger, FIn&& func) : + MyFlags( flags ), MyTrigger( trigger ), MyFunc( std::forward(func) ) {} @@ -309,19 +340,36 @@ auto MakeContinuation(const Events& trigger, { return Continuation( std::make_shared>( + MyFlags, MyTrigger.NodePtr(), std::forward(MyFunc), deps.NodePtr() ...)); } + TurnFlagsT MyFlags; const Events& MyTrigger; FIn MyFunc; }; return REACT_IMPL::apply( - NodeBuilder_( trigger, std::forward(func) ), + NodeBuilder_( flags, trigger, std::forward(func) ), depPack.Data); } +template +< + typename D, + typename DOut = D, + typename E, + typename FIn, + typename ... TDepValues +> +auto MakeContinuation(const Events& trigger, + const SignalPack& depPack, FIn&& func) + -> Continuation +{ + return MakeContinuation(0, trigger, depPack, std::forward(func)); +} + /////////////////////////////////////////////////////////////////////////////////////////////// /// DoTransaction /////////////////////////////////////////////////////////////////////////////////////////////// @@ -370,7 +418,7 @@ void AsyncTransaction(TransactionStatus& status, F&& func) using REACT_IMPL::DomainSpecificInputManager; DomainSpecificInputManager::Instance() - .AsyncTransaction(0, status.state_, std::forward(func)); + .AsyncTransaction(0, status.statePtr_, std::forward(func)); } template @@ -380,7 +428,7 @@ void AsyncTransaction(TurnFlagsT flags, TransactionStatus& status, F&& func) using REACT_IMPL::DomainSpecificInputManager; DomainSpecificInputManager::Instance() - .AsyncTransaction(flags, status.state_, std::forward(func)); + .AsyncTransaction(flags, status.statePtr_, std::forward(func)); } /******************************************/ REACT_END /******************************************/ diff --git a/include/react/common/Concurrency.h b/include/react/common/Concurrency.h index cd9efc73..e312be20 100644 --- a/include/react/common/Concurrency.h +++ b/include/react/common/Concurrency.h @@ -14,8 +14,216 @@ #include #include +#include "react/common/RefCounting.h" + /***************************************/ REACT_IMPL_BEGIN /**************************************/ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// IWaitingState +/////////////////////////////////////////////////////////////////////////////////////////////////// +class IWaitingState +{ +public: + virtual inline ~IWaitingState() {} + + virtual void Wait() = 0; + + virtual void IncWaitCount() = 0; + virtual void DecWaitCount() = 0; + +protected: + virtual bool IsRefCounted() const = 0; + virtual void IncRefCount() = 0; + virtual void DecRefCount() = 0; + + friend class IntrusiveRefCountingPtr; +}; + +using WaitingStatePtrT = IntrusiveRefCountingPtr; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// WaitingStateBase +/////////////////////////////////////////////////////////////////////////////////////////////////// +class WaitingStateBase : public IWaitingState +{ +public: + virtual inline void Wait() override + { + std::unique_lock lock(mutex_); + condition_.wait(lock, [this] { return !isWaiting_; }); + } + + virtual inline void IncWaitCount() override + { + auto oldVal = waitCount_.fetch_add(1, std::memory_order_relaxed); + + if (oldVal == 0) + {// mutex_ + std::lock_guard scopedLock(mutex_); + isWaiting_ = true; + }// ~mutex_ + } + + virtual inline void DecWaitCount() override + { + auto oldVal = waitCount_.fetch_sub(1, std::memory_order_relaxed); + + if (oldVal == 1) + {// mutex_ + std::lock_guard scopedLock(mutex_); + isWaiting_ = false; + condition_.notify_all(); + }// ~mutex_ + } + + inline bool IsWaiting() + {// mutex_ + std::lock_guard scopedLock(mutex_); + return isWaiting_; + }// ~mutex_ + + template + auto Run(F&& func) -> decltype(func(false)) + {// mutex_ + std::lock_guard scopedLock(mutex_); + return func(blocked_); + }// ~mutex_ + + template + bool RunIfWaiting(F&& func) + {// mutex_ + std::lock_guard scopedLock(mutex_); + + if (!isWaiting_) + return false; + + func(); + return true; + }// ~mutex_ + + template + bool RunIfNotWaiting(F&& func) + {// mutex_ + std::lock_guard scopedLock(mutex_); + + if (isWaiting_) + return false; + + func(); + return true; + }// ~mutex_ + +private: + std::atomic waitCount_{ 0 }; + std::condition_variable condition_; + std::mutex mutex_; + bool isWaiting_ = false; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// UniqueWaitingState +/////////////////////////////////////////////////////////////////////////////////////////////////// +class UniqueWaitingState : public WaitingStateBase +{ +protected: + // Do nothing + virtual inline bool IsRefCounted() const override { return false; } + virtual inline void IncRefCount() override {} + virtual inline void DecRefCount() override {} +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SharedWaitingState +/////////////////////////////////////////////////////////////////////////////////////////////////// +class SharedWaitingState : public WaitingStateBase +{ +private: + SharedWaitingState() = default; + +public: + static inline WaitingStatePtrT Create() + { + return WaitingStatePtrT(new SharedWaitingState()); + } + + +protected: + virtual inline bool IsRefCounted() const override { return true; } + + virtual inline void IncRefCount() override + { + refCount_.fetch_add(1, std::memory_order_relaxed); + } + + virtual inline void DecRefCount() override + { + auto oldVal = refCount_.fetch_sub(1, std::memory_order_relaxed); + + if (oldVal == 1) + delete this; + } + +private: + std::atomic refCount_{ 0 }; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SharedWaitingStateCollection +/////////////////////////////////////////////////////////////////////////////////////////////////// +class SharedWaitingStateCollection : public IWaitingState +{ +private: + SharedWaitingStateCollection(std::vector&& others) : + others_( std::move(others) ) + {} + +public: + static inline WaitingStatePtrT Create(std::vector&& others) + { + return WaitingStatePtrT(new SharedWaitingStateCollection(std::move(others))); + } + + virtual inline bool IsRefCounted() const override { return true; } + + virtual inline void Wait() override + { + for (const auto& e : others_) + e->Wait(); + } + + virtual inline void IncWaitCount() override + { + for (const auto& e : others_) + e->IncWaitCount(); + } + + virtual inline void DecWaitCount() override + { + for (const auto& e : others_) + e->DecWaitCount(); + } + + virtual inline void IncRefCount() override + { + refCount_.fetch_add(1, std::memory_order_relaxed); + } + + virtual inline void DecRefCount() override + { + auto oldVal = refCount_.fetch_sub(1, std::memory_order_relaxed); + + if (oldVal == 1) + delete this; + } + +private: + std::atomic refCount_{ 0 }; + std::vector others_; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// BlockingCondition +/////////////////////////////////////////////////////////////////////////////////////////////////// class BlockingCondition { public: diff --git a/include/react/common/RefCounting.h b/include/react/common/RefCounting.h new file mode 100644 index 00000000..3a84dbb4 --- /dev/null +++ b/include/react/common/RefCounting.h @@ -0,0 +1,112 @@ + +// 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_COMMON_REF_COUNTING_H_INCLUDED +#define REACT_COMMON_REF_COUNTING_H_INCLUDED + +#pragma once + +#include "react/detail/Defs.h" + +#include +#include + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +// A non-exception-safe pointer wrapper with conditional intrusive ref counting. +template +class IntrusiveRefCountingPtr +{ + enum + { + tag_ref_counted = 0x1 + }; + +public: + IntrusiveRefCountingPtr() : + ptrData_( reinterpret_cast(nullptr) ) + {} + + IntrusiveRefCountingPtr(T* ptr) : + ptrData_( reinterpret_cast(ptr) ) + { + if (ptr != nullptr && ptr->IsRefCounted()) + { + ptr->IncRefCount(); + ptrData_ |= tag_ref_counted; + } + } + + IntrusiveRefCountingPtr(const IntrusiveRefCountingPtr& other) : + ptrData_( other.ptrData_ ) + { + if (isValidRefCountedPtr()) + Get()->IncRefCount(); + } + + IntrusiveRefCountingPtr(IntrusiveRefCountingPtr&& other) : + ptrData_( other.ptrData_ ) + { + other.ptrData_ = reinterpret_cast(nullptr); + } + + ~IntrusiveRefCountingPtr() + { + if (isValidRefCountedPtr()) + Get()->DecRefCount(); + } + + IntrusiveRefCountingPtr& operator=(const IntrusiveRefCountingPtr& other) + { + if (this != &other) + { + if (other.isValidRefCountedPtr()) + other.Get()->IncRefCount(); + + if (isValidRefCountedPtr()) + Get()->DecRefCount(); + + ptrData_ = other.ptrData_; + } + + return *this; + } + + IntrusiveRefCountingPtr& operator=(IntrusiveRefCountingPtr&& other) + { + if (this != &other) + { + if (isValidRefCountedPtr()) + Get()->DecRefCount(); + + ptrData_ = other.ptrData_; + other.ptrData_ = reinterpret_cast(nullptr); + } + + return *this; + } + + T* Get() const { return reinterpret_cast(ptrData_ & ~tag_ref_counted); } + + T& operator*() const { return *Get(); } + T* operator->() const { return Get(); } + + bool operator==(const IntrusiveRefCountingPtr& other) const { return Get() == other.Get(); } + bool operator!=(const IntrusiveRefCountingPtr& other) const { return !(*this == other); } + +private: + bool isValidRefCountedPtr() const + { + return ptrData_ != reinterpret_cast(nullptr) + && (ptrData_ & tag_ref_counted) != 0; + } + + uintptr_t ptrData_; +}; + +/****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_COMMON_REF_COUNTING_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index 916e64fb..fa1c0152 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "tbb/concurrent_queue.h" @@ -42,7 +43,6 @@ class IObserver; /// Common types & constants /////////////////////////////////////////////////////////////////////////////////////////////////// using TurnIdT = uint; -using TurnFlagsT = uint; using TransactionFuncT = std::function; enum ETurnFlags @@ -50,6 +50,8 @@ enum ETurnFlags allow_merging = 1 << 0 }; +using TurnFlagsT = std::underlying_type::type; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// EInputMode /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -64,7 +66,7 @@ enum EInputMode /////////////////////////////////////////////////////////////////////////////////////////////////// struct IContinuationTarget { - virtual void AsyncContinuation(TransactionFuncT&&) = 0; + virtual void AsyncContinuation(TurnFlagsT, const WaitingStatePtrT&, TransactionFuncT&&) = 0; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -87,10 +89,10 @@ template class ContinuationManager { public: - void StoreContinuation(IContinuationTarget& target, TransactionFuncT&& cont); + void StoreContinuation(IContinuationTarget& target, TurnFlagsT flags, TransactionFuncT&& cont); - bool HasNext() const; - void ProcessNext(); + bool HasContinuations() const; + void StartContinuations(const WaitingStatePtrT& waitingStatePtr); void QueueObserverForDetach(IObserver& obs); @@ -102,27 +104,39 @@ class ContinuationManager template <> class ContinuationManager { - using ContDataT = std::pair; - using DataQueueT = std::deque; - using ObsVectT = std::vector; + struct Data_ + { + Data_(IContinuationTarget* target, TurnFlagsT flags, TransactionFuncT&& func) : + Target( target ), + Flags( flags ), + Func( func ) + {} + + IContinuationTarget* Target; + TurnFlagsT Flags; + TransactionFuncT Func; + }; + + using DataVectT = std::vector; + using ObsVectT = std::vector; public: - void StoreContinuation(IContinuationTarget& target, TransactionFuncT&& cont) + void StoreContinuation(IContinuationTarget& target, TurnFlagsT flags, TransactionFuncT&& cont) { - storedContinuations_.emplace_back(&target, std::move(cont)); + storedContinuations_.emplace_back(&target, flags, std::move(cont)); } - bool HasNext() const + bool HasContinuations() const { return !storedContinuations_.empty(); } - void ProcessNext() + void StartContinuations(const WaitingStatePtrT& waitingStatePtr) { - ContDataT t = std::move(storedContinuations_.front()); - storedContinuations_.pop_front(); + for (auto& t : storedContinuations_) + t.Target->AsyncContinuation(t.Flags, waitingStatePtr, std::move(t.Func)); - t.first->AsyncContinuation(std::move(t.second)); + storedContinuations_.clear(); } // Todo: Move this somewhere else @@ -142,7 +156,7 @@ class ContinuationManager } private: - DataQueueT storedContinuations_; + DataVectT storedContinuations_; ObsVectT detachedObservers_; }; @@ -150,36 +164,42 @@ class ContinuationManager template <> class ContinuationManager { - using ContDataT = std::pair; - using DataQueueT = std::deque; - using ObsVectT = std::vector; + struct Data_ + { + Data_(IContinuationTarget* target, TurnFlagsT flags, TransactionFuncT&& func) : + Target( target ), + Flags( flags ), + Func( func ) + {} + + IContinuationTarget* Target; + TurnFlagsT Flags; + TransactionFuncT Func; + }; + + using DataVecT = std::vector; + using ObsVectT = std::vector; public: - void StoreContinuation(IContinuationTarget& target, TransactionFuncT&& cont) + void StoreContinuation(IContinuationTarget& target, TurnFlagsT flags, TransactionFuncT&& cont) { - storedContinuations_.local().emplace_back(&target, std::move(cont)); + storedContinuations_.local().emplace_back(&target, flags, std::move(cont)); ++contCount_; } - bool HasNext() const + bool HasContinuations() const { return contCount_ != 0; } - void ProcessNext() + void StartContinuations(const WaitingStatePtrT& waitingStatePtr) { for (auto& v : storedContinuations_) - { - if (v.size() == 0) - continue; + for (auto& t : v) + t.Target->AsyncContinuation(t.Flags, waitingStatePtr, std::move(t.Func)); - ContDataT t = std::move(v.front()); - v.pop_front(); - contCount_--; - - t.first->AsyncContinuation(std::move(t.second)); - break; - } + storedContinuations_.clear(); + contCount_ = 0; } void QueueObserverForDetach(IObserver& obs) @@ -201,56 +221,12 @@ class ContinuationManager } private: - tbb::enumerable_thread_specific storedContinuations_; + tbb::enumerable_thread_specific storedContinuations_; tbb::enumerable_thread_specific detachedObservers_; size_t contCount_ = 0; }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// AsyncState -/////////////////////////////////////////////////////////////////////////////////////////////////// -class AsyncState -{ -public: - inline void Wait() - { - std::unique_lock lock(mutex_); - condition_.wait(lock, [this] { return !isWaiting_; }); - } - - inline void IncWaitCount() - { - auto oldVal = waitCount_.fetch_add(1, std::memory_order_relaxed); - - if (oldVal == 0) - {// mutex_ - std::lock_guard scopedLock(mutex_); - isWaiting_ = true; - }// ~mutex_ - } - - inline void DecWaitCount() - { - auto oldVal = waitCount_.fetch_sub(1, std::memory_order_relaxed); - - if (oldVal == 1) - {// mutex_ - std::lock_guard scopedLock(mutex_); - isWaiting_ = false; - condition_.notify_all(); - }// ~mutex_ - } - -private: - std::atomic waitCount_{ 0 }; - std::condition_variable condition_; - std::mutex mutex_; - bool isWaiting_ = false; -}; - -using AsyncStatePtrT = std::shared_ptr; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// TransactionQueue /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -263,14 +239,22 @@ class TransactionQueue { public: explicit QueueEntry(TurnFlagsT flags); + void RunMergedInputs(); + + size_t GetWaitingStatePtrCount() const; + void MoveWaitingStatePtrs(std::vector& out); + + void Release(); + + bool IsAsync() const; }; template bool TryMergeSync(F&& inputFunc); template - bool TryMergeAsync(F&& inputFunc, AsyncStatePtrT&& statusPtr); + bool TryMergeAsync(F&& inputFunc, WaitingStatePtrT&& waitingStatePtr); void EnterQueue(QueueEntry& turn); void ExitQueue(QueueEntry& turn); @@ -284,15 +268,23 @@ class TransactionQueue class QueueEntry { public: - explicit QueueEntry(TurnFlagsT flags) {} - void RunMergedInputs() {} + explicit QueueEntry(TurnFlagsT flags) {} + + void RunMergedInputs() {} + + size_t GetWaitingStatePtrCount() const { return 0; } + void MoveWaitingStatePtrs(std::vector& out) {} + + void Release() {} + + bool IsAsync() const { return false; } }; template bool TryMergeSync(F&& inputFunc) { return false; } template - bool TryMergeAsync(F&& inputFunc, AsyncStatePtrT&& statusPtr) { return false; } + bool TryMergeAsync(F&& inputFunc, WaitingStatePtrT&& waitingStatePtr) { return false; } void EnterQueue(QueueEntry& turn) {} void ExitQueue(QueueEntry& turn) {} @@ -313,12 +305,13 @@ class TransactionQueue void Append(QueueEntry& tr) { successor_ = &tr; - tr.blockCondition_.Block(); + tr.waitingState_.IncWaitCount(); + ++waitingStatePtrCount_; } void WaitForUnblock() { - blockCondition_.WaitForUnblock(); + waitingState_.Wait(); } void RunMergedInputs() const @@ -327,39 +320,67 @@ class TransactionQueue e.InputFunc(); } - void UnblockSuccessors() + size_t GetWaitingStatePtrCount() const { - // Release merged + return waitingStatePtrCount_; + } + + void MoveWaitingStatePtrs(std::vector& out) + { + if (waitingStatePtrCount_ == 0) + return; + for (const auto& e : merged_) - { - // Note: Since a merged input is either sync or async, - // either cond or status will be null + if (e.WaitingStatePtr != nullptr) + out.push_back(std::move(e.WaitingStatePtr)); - // Sync - if (e.Cond != nullptr) - e.Cond->Unblock(); + if (successor_) + { + out.push_back(WaitingStatePtrT( &successor_->waitingState_ )); - // Async - else if (e.StatusPtr != nullptr) - e.StatusPtr->DecWaitCount(); + // Ownership of successors waiting state has been transfered, + // we no longer need it + successor_ = nullptr; } + waitingStatePtrCount_ = 0; + } + + void Release() + { + if (waitingStatePtrCount_ == 0) + return; + + // Release merged + for (const auto& e : merged_) + if (e.WaitingStatePtr != nullptr) + e.WaitingStatePtr->DecWaitCount(); + + // Waiting state ptrs may point to stack of waiting threads. + // After decwaitcount, they may start running and terminate. + // Thus, clear merged ASAP because it now contains invalid ptrs. + merged_.clear(); + // Release next thread in queue if (successor_) - successor_->blockCondition_.Unblock(); + successor_->waitingState_.DecWaitCount(); } - template - bool TryMerge(F&& inputFunc, BlockingCondition* caller, AsyncStatePtrT&& statusPtr) + template + bool TryMerge(F&& inputFunc, P&& waitingStatePtr) { if (!isMergeable_) return false; - // Only merge if target is still blocked - bool merged = blockCondition_.RunIfBlocked([&] { - if (caller) - caller->Block(); - merged_.emplace_back(std::forward(inputFunc), caller, std::move(statusPtr)); + // Only merge if target is still waiting + bool merged = waitingState_.RunIfWaiting([&] { + if (waitingStatePtr != nullptr) + { + waitingStatePtr->IncWaitCount(); + ++waitingStatePtrCount_; + } + + merged_.emplace_back(std::forward(inputFunc), std::forward

(waitingStatePtr)); }); return merged; @@ -368,28 +389,23 @@ class TransactionQueue private: struct MergedData { - template - MergedData(F&& func, BlockingCondition* cond, AsyncStatePtrT&& statusPtr) : + template + MergedData(F&& func, P&& waitingStatePtr) : InputFunc( std::forward(func) ), - Cond( cond ), - StatusPtr( std::move(statusPtr) ) + WaitingStatePtr( std::forward

(waitingStatePtr)); - }); - - return merged; - } - - private: - struct MergedData - { - template - MergedData(F&& func, P&& waitingStatePtr) : - InputFunc( std::forward(func) ), - WaitingStatePtr( std::forward

(waitingStatePtr) ) - {} - - std::function InputFunc; - WaitingStatePtrT WaitingStatePtr; - }; - - using MergedDataVectT = std::vector; - - bool isMergeable_; - QueueEntry* successor_ = nullptr; - MergedDataVectT merged_; - UniqueWaitingState waitingState_; - size_t waitingStatePtrCount_ = 0; - }; - - template - bool TryMergeSync(F&& inputFunc) - { - bool merged = false; - - UniqueWaitingState st; - WaitingStatePtrT p( &st ); - - {// seqMutex_ - SeqMutexT::scoped_lock lock(seqMutex_); - - if (tail_) - merged = tail_->TryMerge(std::forward(inputFunc), p); - }// ~seqMutex_ - - if (merged) - p->Wait(); - - return merged; - } - - template - bool TryMergeAsync(F&& inputFunc, WaitingStatePtrT&& waitingStatePtr) - { - bool merged = false; - - {// seqMutex_ - SeqMutexT::scoped_lock lock(seqMutex_); - - if (tail_) - merged = tail_->TryMerge(std::forward(inputFunc), std::move(waitingStatePtr)); - }// ~seqMutex_ - - return merged; - } - - void EnterQueue(QueueEntry& tr) - { - {// seqMutex_ - SeqMutexT::scoped_lock lock(seqMutex_); - - if (tail_) - tail_->Append(tr); - - tail_ = &tr; - }// ~seqMutex_ - - tr.WaitForUnblock(); - } - - void ExitQueue(QueueEntry& tr) - { - {// seqMutex_ - SeqMutexT::scoped_lock lock(seqMutex_); - - if (tail_ == &tr) - tail_ = nullptr; - }// ~seqMutex_ - - tr.Release(); - } - -private: - using SeqMutexT = tbb::queuing_mutex; - - SeqMutexT seqMutex_; - QueueEntry* tail_ = nullptr; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// AsyncWorker -/////////////////////////////////////////////////////////////////////////////////////////////////// -struct AsyncItem -{ - TransactionFlagsT Flags; - WaitingStatePtrT WaitingStatePtr; - TransactionFuncT Func; -}; - -// Interface -template -class AsyncWorker -{ -public: - AsyncWorker(InputManager& mgr); - - void PushItem(AsyncItem&& item); - - void PopItem(AsyncItem& item); - bool TryPopItem(AsyncItem& item); - - bool IncrementItemCount(size_t n); - bool DecrementItemCount(size_t n); - - void Start(); -}; - -// Disabled -template -struct AsyncWorker -{ -public: - AsyncWorker(InputManager& mgr) - {} - - void PushItem(AsyncItem&& item) { assert(false); } - - void PopItem(AsyncItem& item) { assert(false); } - bool TryPopItem(AsyncItem& item) { assert(false); return false; } - - bool IncrementItemCount(size_t n) { assert(false); return false; } - bool DecrementItemCount(size_t n) { assert(false); return false; } - - void Start() { assert(false); } -}; - -// Enabled -template -struct AsyncWorker -{ - using DataT = tbb::concurrent_bounded_queue; - - class WorkerTask : public tbb::task - { - public: - WorkerTask(InputManager& mgr) : - mgr_( mgr ) - {} - - tbb::task* execute() - { - mgr_.processAsyncQueue(); - return nullptr; - } - - private: - InputManager& mgr_; - }; - -public: - AsyncWorker(InputManager& mgr) : - mgr_( mgr ) - {} - - void PushItem(AsyncItem&& item) - { - data_.push(std::move(item)); - } - - void PopItem(AsyncItem& item) - { - data_.pop(item); - } - - bool TryPopItem(AsyncItem& item) - { - return data_.try_pop(item); - } - - bool IncrementItemCount(size_t n) - { - return count_.fetch_add(n, std::memory_order_relaxed) == 0; - } - - bool DecrementItemCount(size_t n) - { - return count_.fetch_sub(n, std::memory_order_relaxed) == n; - } - - void Start() - { - tbb::task::enqueue(*new(tbb::task::allocate_root()) WorkerTask(mgr_)); - } - -private: - DataT data_; - std::atomic count_{ 0 }; - - InputManager& mgr_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// InputManager -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class InputManager : - public IContinuationTarget -{ -private: - // Select between thread-safe and non thread-safe implementations - using TransactionQueueT = TransactionQueue; - using QueueEntryT = typename TransactionQueueT::QueueEntry; - - using ContinuationManagerT = ContinuationManager; - using AsyncWorkerT = AsyncWorker; - - template - friend class AsyncWorker; - -public: - using TurnT = typename D::TurnT; - using Engine = typename D::Engine; - - InputManager() : - asyncWorker_(*this) - {} - - template - void DoTransaction(TransactionFlagsT flags, F&& func) - { - // Attempt to add input to another turn. - // If successful, blocks until other turn is done and returns. - bool canMerge = (flags & allow_merging) != 0; - if (canMerge && transactionQueue_.TryMergeSync(std::forward(func))) - return; - - bool shouldPropagate = false; - - QueueEntryT tr( flags ); - - transactionQueue_.EnterQueue(tr); - - // Phase 1 - Input admission - ThreadLocalInputState<>::IsTransactionActive = true; - - TurnT turn( nextTurnId(), flags ); - Engine::OnTurnAdmissionStart(turn); - func(); - tr.RunMergedInputs(); - Engine::OnTurnAdmissionEnd(turn); - - ThreadLocalInputState<>::IsTransactionActive = false; - - // Phase 2 - Apply input node changes - for (auto* p : changedInputs_) - if (p->ApplyInput(&turn)) - shouldPropagate = true; - changedInputs_.clear(); - - // Phase 3 - Propagate changes - if (shouldPropagate) - Engine::Propagate(turn); - - finalizeSyncTransaction(tr); - } - - template - void AsyncTransaction(TransactionFlagsT flags, const WaitingStatePtrT& waitingStatePtr, F&& func) - { - if (waitingStatePtr != nullptr) - waitingStatePtr->IncWaitCount(); - - asyncWorker_.PushItem(AsyncItem{ flags, waitingStatePtr, std::forward(func) }); - - if (asyncWorker_.IncrementItemCount(1)) - asyncWorker_.Start(); - } - - template - void AddInput(R& r, V&& v) - { - if (ThreadLocalInputState<>::IsTransactionActive) - { - addTransactionInput(r, std::forward(v)); - } - else - { - addSimpleInput(r, std::forward(v)); - } - } - - template - void ModifyInput(R& r, const F& func) - { - if (ThreadLocalInputState<>::IsTransactionActive) - { - modifyTransactionInput(r, func); - } - else - { - modifySimpleInput(r, func); - } - } - - //IContinuationTarget - virtual void AsyncContinuation(TransactionFlagsT flags, const WaitingStatePtrT& waitingStatePtr, TransactionFuncT&& cont) override - { - AsyncTransaction(flags, waitingStatePtr, std::move(cont)); - } - //~IContinuationTarget - - void StoreContinuation(IContinuationTarget& target, TransactionFlagsT flags, - TransactionFuncT&& cont) - { - continuationManager_.StoreContinuation(target, flags, std::move(cont)); - } - - void QueueObserverForDetach(IObserver& obs) - { - continuationManager_.QueueObserverForDetach(obs); - } - -private: - TurnIdT nextTurnId() - { - auto curId = nextTurnId_.fetch_add(1, std::memory_order_relaxed); - - if (curId == (std::numeric_limits::max)()) - nextTurnId_.fetch_sub((std::numeric_limits::max)()); - - return curId; - } - - // Create a turn with a single input - template - void addSimpleInput(R& r, V&& v) - { - QueueEntryT tr( 0 ); - - transactionQueue_.EnterQueue(tr); - - TurnT turn( nextTurnId(), 0 ); - Engine::OnTurnAdmissionStart(turn); - r.AddInput(std::forward(v)); - tr.RunMergedInputs(); - Engine::OnTurnAdmissionEnd(turn); - - if (r.ApplyInput(&turn)) - Engine::Propagate(turn); - - finalizeSyncTransaction(tr); - } - - template - void modifySimpleInput(R& r, const F& func) - { - QueueEntryT tr( 0 ); - - transactionQueue_.EnterQueue(tr); - - TurnT turn( nextTurnId(), 0 ); - Engine::OnTurnAdmissionStart(turn); - r.ModifyInput(func); - Engine::OnTurnAdmissionEnd(turn); - - // Return value, will always be true - r.ApplyInput(&turn); - - Engine::Propagate(turn); - - finalizeSyncTransaction(tr); - } - - void finalizeSyncTransaction(QueueEntryT& tr) - { - continuationManager_.template DetachQueuedObservers(); - - if (continuationManager_.HasContinuations()) - { - UniqueWaitingState st; - WaitingStatePtrT p( &st ); - - continuationManager_.StartContinuations(p); - - transactionQueue_.ExitQueue(tr); - - p->Wait(); - } - else - { - transactionQueue_.ExitQueue(tr); - } - } - - // This input is part of an active transaction - template - void addTransactionInput(R& r, V&& v) - { - r.AddInput(std::forward(v)); - changedInputs_.push_back(&r); - } - - template - void modifyTransactionInput(R& r, const F& func) - { - r.ModifyInput(func); - changedInputs_.push_back(&r); - } - - void processAsyncQueue() - { - AsyncItem item; - - std::vector waitingStatePtrs; - - bool skipPop = false; - - while (true) - { - size_t popCount = 0; - - if (!skipPop) - { - // Blocks if queue is empty. - // This should never happen, - // and if (maybe due to some memory access internals), only briefly - asyncWorker_.PopItem(item); - popCount++; - } - else - { - skipPop = false; - } - - // First try to merge to an existing synchronous item in the queue - bool canMerge = (item.Flags & allow_merging) != 0; - if (canMerge && transactionQueue_.TryMergeAsync( - std::move(item.Func), - std::move(item.WaitingStatePtr))) - continue; - - bool shouldPropagate = false; - - QueueEntryT tr( item.Flags ); - - // Blocks until turn is at the front of the queue - transactionQueue_.EnterQueue(tr); - - TurnT turn( nextTurnId(), item.Flags ); - - // Phase 1 - Input admission - ThreadLocalInputState<>::IsTransactionActive = true; - - Engine::OnTurnAdmissionStart(turn); - - // Input of current item - item.Func(); - - // Merged sync inputs that arrived while this item was queued - tr.RunMergedInputs(); - - // Save data, item might be re-used for next input - if (item.WaitingStatePtr != nullptr) - waitingStatePtrs.push_back(std::move(item.WaitingStatePtr)); - - // If the current item supports merging, try to add more mergeable inputs - // to this turn - if (canMerge) - { - uint extraCount = 0; - // Todo: Make configurable - while (extraCount < 1024 && asyncWorker_.TryPopItem(item)) - { - ++popCount; - - bool canMergeNext = (item.Flags & allow_merging) != 0; - if (canMergeNext) - { - item.Func(); - - if (item.WaitingStatePtr != nullptr) - waitingStatePtrs.push_back(std::move(item.WaitingStatePtr)); - - ++extraCount; - } - else - { - // We already popped an item we could not merge - // Process it in the next iteration - skipPop = true; - - // Break at first item that cannot be merged. - // We only allow merging of continuous ranges. - break; - } - } - } - - Engine::OnTurnAdmissionEnd(turn); - - ThreadLocalInputState<>::IsTransactionActive = false; - - // Phase 2 - Apply input node changes - for (auto* p : changedInputs_) - if (p->ApplyInput(&turn)) - shouldPropagate = true; - changedInputs_.clear(); - - // Phase 3 - Propagate changes - if (shouldPropagate) - Engine::Propagate(turn); - - continuationManager_.template DetachQueuedObservers(); - - // Has continuations? If so, status ptrs have to be passed on to - // continuation transactions - if (continuationManager_.HasContinuations()) - { - // Merge all waiting state ptrs for this transaction into a single vector - tr.MoveWaitingStatePtrs(waitingStatePtrs); - - // More than 1 waiting state -> create collection from vector - if (waitingStatePtrs.size() > 1) - { - WaitingStatePtrT p - ( - SharedWaitingStateCollection::Create(std::move(waitingStatePtrs)) - ); - - continuationManager_.StartContinuations(p); - - transactionQueue_.ExitQueue(tr); - p->DecWaitCount(); - } - // Exactly one status ptr -> pass it on directly - else if (waitingStatePtrs.size() == 1) - { - WaitingStatePtrT p( std::move(waitingStatePtrs[0]) ); - - continuationManager_.StartContinuations(p); - - transactionQueue_.ExitQueue(tr); - p->DecWaitCount(); - } - // No status ptrs - else - { - continuationManager_.StartContinuations(nullptr); - } - } - else - { - transactionQueue_.ExitQueue(tr); - - for (auto& p : waitingStatePtrs) - p->DecWaitCount(); - } - - waitingStatePtrs.clear(); - - // Stop this task if the number of items has just been decremented zero. - // A new task will be started by the thread that increments the item count from zero. - if (asyncWorker_.DecrementItemCount(popCount)) - break; - } - } - - TransactionQueueT transactionQueue_; - ContinuationManagerT continuationManager_; - AsyncWorkerT asyncWorker_; - - std::atomic nextTurnId_{ 0 }; - - std::vector changedInputs_; -}; - -template -class DomainSpecificInputManager -{ -public: - DomainSpecificInputManager() = delete; - - static InputManager& Instance() - { - static InputManager instance; - return instance; - } -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_DETAIL_REACTIVEINPUT_H_INCLUDED - -#endif \ No newline at end of file diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/algorithm_nodes.h similarity index 68% rename from include/react/detail/graph/AlgorithmNodes.h rename to include/react/detail/algorithm_nodes.h index fdb925e1..94cdde7e 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/algorithm_nodes.h @@ -1,83 +1,24 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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 +#ifndef REACT_DETAIL_ALGORITHM_NODES_H_INCLUDED +#define REACT_DETAIL_ALGORITHM_NODES_H_INCLUDED #pragma once -#include "react/detail/Defs.h" +#include "react/detail/defs.h" #include #include -#include "EventNodes.h" -#include "SignalNodes.h" +#include "event_nodes.h" +#include "signal_nodes.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 /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -101,12 +42,10 @@ class IterateNode : public SignalNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { S newValue = func_(EventRange( GetInternals(evnt_).Events() ), this->Value()); - GetInternals(evnt_).DecrementPendingSuccessorCount(); - if (! (newValue == this->Value())) { this->Value() = std::move(newValue); @@ -118,12 +57,6 @@ class IterateNode : public SignalNode } } - virtual const char* GetNodeType() const override - { return "Iterate"; } - - virtual int GetDependencyCount() const override - { return 1; } - private: F func_; Event evnt_; @@ -152,22 +85,14 @@ class IterateByRefNode : public SignalNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { func_(EventRange( GetInternals(evnt_).Events() ), this->Value()); - GetInternals(evnt_).DecrementPendingSuccessorCount(); - // Always assume change return UpdateResult::changed; } - virtual const char* GetNodeType() const override - { return "IterateByRefNode"; } - - virtual int GetDependencyCount() const override - { return 1; } - protected: F func_; Event evnt_; @@ -194,26 +119,24 @@ class SyncedIterateNode : public SignalNode ~SyncedIterateNode() { - apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(syncs).GetNodeId())); }, syncHolder_); + std::apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(syncs).GetNodeId())); }, syncHolder_); this->DetachFromMe(GetInternals(evnt_).GetNodeId()); this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { // Updates might be triggered even if only sync nodes changed. Ignore those. if (GetInternals(evnt_).Events().empty()) return UpdateResult::unchanged; - S newValue = apply( + S newValue = std::apply( [this] (const auto& ... syncs) { return func_(EventRange( GetInternals(evnt_).Events() ), this->Value(), GetInternals(syncs).Value() ...); }, syncHolder_); - GetInternals(evnt_).DecrementPendingSuccessorCount(); - if (! (newValue == this->Value())) { this->Value() = std::move(newValue); @@ -225,12 +148,6 @@ class SyncedIterateNode : public SignalNode } } - virtual const char* GetNodeType() const override - { return "SyncedIterate"; } - - virtual int GetDependencyCount() const override - { return 1 + sizeof...(TSyncs); } - private: F func_; Event evnt_; @@ -259,35 +176,27 @@ class SyncedIterateByRefNode : public SignalNode ~SyncedIterateByRefNode() { - apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(syncs).GetNodeId())); }, syncHolder_); + std::apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(syncs).GetNodeId())); }, syncHolder_); this->DetachFromMe(GetInternals(evnt_).GetNodeId()); this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { // Updates might be triggered even if only sync nodes changed. Ignore those. if (GetInternals(evnt_).Events().empty()) return UpdateResult::unchanged; - apply( + std::apply( [this] (const auto& ... args) { func_(EventRange( GetInternals(evnt_).Events() ), this->Value(), GetInternals(args).Value() ...); }, syncHolder_); - GetInternals(evnt_).DecrementPendingSuccessorCount(); - return UpdateResult::changed; } - virtual const char* GetNodeType() const override - { return "SyncedIterateByRef"; } - - virtual int GetDependencyCount() const override - { return 1 + sizeof...(TSyncs); } - private: F func_; Event events_; @@ -317,10 +226,7 @@ class HoldNode : public SignalNode this->UnregisterMe(); } - virtual const char* GetNodeType() const override - { return "HoldNode"; } - - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { bool changed = false; @@ -333,8 +239,6 @@ class HoldNode : public SignalNode changed = true; this->Value() = newValue; } - - GetInternals(evnt_).DecrementPendingSuccessorCount(); } if (changed) @@ -343,9 +247,6 @@ class HoldNode : public SignalNode return UpdateResult::unchanged; } - virtual int GetDependencyCount() const override - { return 1; } - private: Event evnt_; }; @@ -374,7 +275,7 @@ class SnapshotNode : public SignalNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { bool changed = false; @@ -387,8 +288,6 @@ class SnapshotNode : public SignalNode changed = true; this->Value() = newValue; } - - GetInternals(trigger_).DecrementPendingSuccessorCount(); } if (changed) @@ -397,12 +296,6 @@ class SnapshotNode : public SignalNode return UpdateResult::unchanged; } - virtual const char* GetNodeType() const override - { return "Snapshot"; } - - virtual int GetDependencyCount() const override - { return 2; } - private: Signal target_; Event trigger_; @@ -429,21 +322,13 @@ class MonitorNode : public EventStreamNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { this->Events().push_back(GetInternals(target_).Value()); - this->SetPendingSuccessorCount(successorCount); - return UpdateResult::changed; } - virtual const char* GetNodeType() const override - { return "Monitor"; } - - virtual int GetDependencyCount() const override - { return 1; } - private: Signal target_; }; @@ -472,16 +357,13 @@ class PulseNode : public EventStreamNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { for (size_t i = 0; i < GetInternals(trigger_).Events().size(); i++) this->Events().push_back(GetInternals(target_).Value()); - GetInternals(trigger_).DecrementPendingSuccessorCount(); - if (! this->Events().empty()) { - this->SetPendingSuccessorCount(successorCount); return UpdateResult::changed; } else @@ -490,12 +372,6 @@ class PulseNode : public EventStreamNode } } - virtual const char* GetNodeType() const override - { return "Pulse"; } - - virtual int GetDependencyCount() const override - { return 2; } - private: Signal target_; Event trigger_; @@ -503,4 +379,4 @@ class PulseNode : public EventStreamNode /****************************************/ REACT_IMPL_END /***************************************/ -#endif // REACT_DETAIL_GRAPH_ALGORITHMNODES_H_INCLUDED \ No newline at end of file +#endif // REACT_DETAIL_ALGORITHM_NODES_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/event_nodes.h similarity index 66% rename from include/react/detail/graph/EventNodes.h rename to include/react/detail/event_nodes.h index e216b4b5..1a2e1cdf 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/event_nodes.h @@ -1,15 +1,15 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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_EVENTNODES_H_INCLUDED -#define REACT_DETAIL_GRAPH_EVENTNODES_H_INCLUDED +#ifndef REACT_DETAIL_EVENT_NODES_H_INCLUDED +#define REACT_DETAIL_EVENT_NODES_H_INCLUDED #pragma once -#include "react/detail/Defs.h" +#include "react/detail/defs.h" #include #include @@ -19,7 +19,7 @@ //#include "tbb/spin_mutex.h" -#include "GraphBase.h" +#include "node_base.h" #include "react/common/Types.h" /*****************************************/ REACT_BEGIN /*****************************************/ @@ -28,39 +28,10 @@ /// Iterators for event processing /////////////////////////////////////////////////////////////////////////////////////////////////// template -class EventRange -{ -public: - using const_iterator = typename std::vector::const_iterator; - using size_type = typename std::vector::size_type; - - EventRange() = delete; - - EventRange(const EventRange&) = default; - EventRange& operator=(const EventRange&) = default; - - explicit EventRange(const std::vector& data) : - data_( data ) - { } - - const_iterator begin() const - { return data_.begin(); } - - const_iterator end() const - { return data_.end(); } - - size_type GetSize() const - { return data_.size(); } - - bool IsEmpty() const - { return data_.empty(); } - -private: - const std::vector& data_; -}; +using EventValueList = std::vector; template -using EventSink = std::back_insert_iterator>; +using EventValueSink = std::back_insert_iterator>; /******************************************/ REACT_END /******************************************/ @@ -79,8 +50,6 @@ template class EventStreamNode : public NodeBase { public: - using StorageType = std::vector; - EventStreamNode(EventStreamNode&&) = default; EventStreamNode& operator=(EventStreamNode&&) = default; @@ -88,53 +57,27 @@ class EventStreamNode : public NodeBase EventStreamNode& operator=(const EventStreamNode&) = delete; explicit EventStreamNode(const Group& group) : - NodeBase( group ) - { } + EventStreamNode::NodeBase( group ) + { } - StorageType& Events() + EventValueList& Events() { return events_; } - const StorageType& Events() const + const EventValueList& Events() const { return events_; } - - void SetPendingSuccessorCount(size_t count) - { - if (count == 0) - { - // If there are no successors, buffer is cleared immediately. - events_.clear(); - } - else - { - // Otherwise, the last finished successor clears it. - pendingSuccessorCount_ = count; - } - } - - void DecrementPendingSuccessorCount() - { - // Not all predecessors of a node might be visited during a turn. - // In this case, the count is zero and the call to this function should be ignored. - if (pendingSuccessorCount_ == 0) - return; - - // Last successor to arrive clears the buffer. - if (pendingSuccessorCount_-- == 1) - events_.clear(); - } + virtual void Clear() noexcept override + { events_.clear(); } private: - StorageType events_; - - std::atomic pendingSuccessorCount_ = 0; + EventValueList events_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventSourceNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EventSourceNode : public EventStreamNode +template +class EventSourceNode : public EventStreamNode { public: EventSourceNode(const Group& group) : @@ -148,23 +91,12 @@ class EventSourceNode : public EventStreamNode this->UnregisterMe(); } - virtual const char* GetNodeType() const override - { return "EventSource"; } - - virtual int GetDependencyCount() const override - { return 0; } - - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { if (! this->Events().empty()) - { - this->SetPendingSuccessorCount(successorCount); return UpdateResult::changed; - } else - { return UpdateResult::unchanged; - } } template @@ -175,13 +107,13 @@ class EventSourceNode : public EventStreamNode /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventMergeNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EventMergeNode : public EventStreamNode +template +class EventMergeNode : public EventStreamNode { public: - EventMergeNode(const Group& group, const Event& ... deps) : + EventMergeNode(const Group& group, const Event& ... deps) : EventMergeNode::EventStreamNode( group ), - depHolder_( deps ... ) + inputs_( deps ... ) { this->RegisterMe(); REACT_EXPAND_PACK(this->AttachToMe(GetInternals(deps).GetNodeId())); @@ -189,43 +121,31 @@ class EventMergeNode : public EventStreamNode ~EventMergeNode() { - apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(deps).GetNodeId())); }, depHolder_); + std::apply([this] (const auto& ... deps) + { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(deps).GetNodeId())); }, depHolder_); this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { - apply([this] (auto& ... deps) { REACT_EXPAND_PACK(MergeFromDep(deps)); }, depHolder_); + std::apply([this] (auto& ... deps) + { REACT_EXPAND_PACK(MergeFromDep(deps)); }, depHolder_); if (! this->Events().empty()) - { - this->SetPendingSuccessorCount(successorCount); return UpdateResult::changed; - } else - { return UpdateResult::unchanged; - } } - virtual const char* GetNodeType() const override - { return "EventMerge"; } - - virtual int GetDependencyCount() const override - { return sizeof...(TIns); } - private: template - void MergeFromDep(Event& dep) + void MergeFromInput(Event& dep) { - auto& depInternals = GetInternals(dep); - + EventInternals& depInternals = GetInternals(dep); this->Events().insert(this->Events().end(), depInternals.Events().begin(), depInternals.Events().end()); - - depInternals.DecrementPendingSuccessorCount(); } - std::tuple ...> depHolder_; + std::tuple ...> inputs_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -253,31 +173,18 @@ class EventSlotNode : public EventStreamNode GetGraphPtr()->UnregisterNode(inputNodeId_); } - virtual const char* GetNodeType() const override - { return "EventSlot"; } - - virtual int GetDependencyCount() const override - { return 2; } - - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { for (auto& e : inputs_) { const auto& events = GetInternals(e).Events(); this->Events().insert(this->Events().end(), events.begin(), events.end()); - - GetInternals(e).DecrementPendingSuccessorCount(); } if (! this->Events().empty()) - { - this->SetPendingSuccessorCount(successorCount); return UpdateResult::changed; - } else - { return UpdateResult::unchanged; - } } void AddInput(const Event& input) @@ -312,15 +219,9 @@ class EventSlotNode : public EventStreamNode { return inputNodeId_; } private: - struct VirtualInputNode : public IReactiveNode + struct VirtualInputNode : public IReactNode { - virtual const char* GetNodeType() const override - { return "EventSlotInput"; } - - virtual int GetDependencyCount() const override - { return 0; } - - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { return UpdateResult::changed; } }; @@ -353,29 +254,16 @@ class EventProcessingNode : public EventStreamNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { func_(EventRange( GetInternals(dep_).Events() ), std::back_inserter(this->Events())); - GetInternals(dep_).DecrementPendingSuccessorCount(); - if (! this->Events().empty()) - { - this->SetPendingSuccessorCount(successorCount); return UpdateResult::changed; - } else - { return UpdateResult::unchanged; - } } - virtual const char* GetNodeType() const override - { return "EventProcessing"; } - - virtual int GetDependencyCount() const override - { return 1; } - private: F func_; @@ -403,43 +291,30 @@ class SyncedEventProcessingNode : public EventStreamNode ~SyncedEventProcessingNode() { - apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(syncs->GetNodeId())); }, syncHolder_); + std::apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(syncs->GetNodeId())); }, syncHolder_); this->DetachFromMe(dep_->GetNodeId()); this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { // Updates might be triggered even if only sync nodes changed. Ignore those. if (dep_->Events().empty()) return UpdateResult::unchanged; - apply( + std::apply( [this] (const auto& ... syncs) { func_(EventRange( this->dep_->Events() ), std::back_inserter(this->Events()), syncs->Value() ...); }, syncHolder_); - dep_->DecrementPendingSuccessorCount(); - if (! this->Events().empty()) - { - this->SetPendingSuccessorCount(successorCount); return UpdateResult::changed; - } else - { return UpdateResult::unchanged; - } } - virtual const char* GetNodeType() const override - { return "SyncedEventProcessing"; } - - virtual int GetDependencyCount() const override - { return 1 + sizeof...(TSyncs); } - private: F func_; @@ -465,21 +340,21 @@ class EventJoinNode : public EventStreamNode> ~EventJoinNode() { - apply([this] (const auto& ... slots) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(slots.source).GetNodeId())); }, slots_); + std::apply([this] (const auto& ... slots) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(slots.source).GetNodeId())); }, slots_); this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { // Move events into buffers. - apply([this, turnId] (Slot& ... slots) { REACT_EXPAND_PACK(FetchBuffer(turnId, slots)); }, slots_); + std::apply([this, turnId] (Slot& ... slots) { REACT_EXPAND_PACK(FetchBuffer(turnId, slots)); }, slots_); while (true) { bool isReady = true; // All slots ready? - apply( + std::apply( [this, &isReady] (Slot& ... slots) { // Todo: combine return values instead @@ -491,7 +366,7 @@ class EventJoinNode : public EventStreamNode> break; // Pop values from buffers and emit tuple. - apply( + std::apply( [this] (Slot& ... slots) { this->Events().emplace_back(slots.buffer.front() ...); @@ -511,12 +386,6 @@ class EventJoinNode : public EventStreamNode> } } - virtual const char* GetNodeType() const override - { return "EventJoin"; } - - virtual int GetDependencyCount() const override - { return sizeof...(Ts); } - private: template struct Slot @@ -571,23 +440,14 @@ class EventLinkNode : public EventStreamNode void SetWeakSelfPtr(const std::weak_ptr& self) { linkOutput_.parent = self; } - virtual const char* GetNodeType() const override - { return "EventLink"; } + virtual UpdateResult Update(TurnId turnId) noexcept override + { return UpdateResult::changed; } - virtual int GetDependencyCount() const override - { return 1; } - - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override - { - this->SetPendingSuccessorCount(successorCount); - return UpdateResult::changed; - } - - void SetEvents(EventLinkNode::EventStreamNode::StorageType&& events) + void SetEvents(EventValueList&& events) { this->Events() = std::move(events); } private: - struct VirtualOutputNode : public ILinkOutputNode + struct VirtualOutputNode : public IReactNode { VirtualOutputNode(const Event& depIn) : parent( ), @@ -596,23 +456,17 @@ class EventLinkNode : public EventStreamNode { auto& srcGraphPtr = GetInternals(srcGroup).GetGraphPtr(); nodeId = srcGraphPtr->RegisterNode(this, NodeCategory::linkoutput); - srcGraphPtr->OnNodeAttach(nodeId, GetInternals(dep).GetNodeId()); + srcGraphPtr->AttachNode(nodeId, GetInternals(dep).GetNodeId()); } ~VirtualOutputNode() { auto& srcGraphPtr = GetInternals(srcGroup).GetGraphPtr(); - srcGraphPtr->OnNodeDetach(nodeId, GetInternals(dep).GetNodeId()); + srcGraphPtr->DetachNode(nodeId, GetInternals(dep).GetNodeId()); srcGraphPtr->UnregisterNode(nodeId); } - virtual const char* GetNodeType() const override - { return "EventLinkOutput"; } - - virtual int GetDependencyCount() const override - { return 1; } - - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { return UpdateResult::changed; } virtual void CollectOutput(LinkOutputMap& output) override @@ -645,6 +499,42 @@ class EventLinkNode : public EventStreamNode VirtualOutputNode linkOutput_; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventInternals +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class EventInternals +{ +public: + EventInternals(const EventInternals&) = default; + EventInternals& operator=(const EventInternals&) = default; + + EventInternals(EventInternals&&) = default; + EventInternals& operator=(EventInternals&&) = default; + + explicit EventInternals(std::shared_ptr>&& nodePtr) : + nodePtr_( std::move(nodePtr) ) + { } + + auto GetNodePtr() -> std::shared_ptr>& + { return nodePtr_; } + + auto GetNodePtr() const -> const std::shared_ptr>& + { return nodePtr_; } + + NodeId GetNodeId() const + { return nodePtr_->GetNodeId(); } + + EventValueList& Events() + { return nodePtr_->Events(); } + + const EventValueList& Events() const + { return nodePtr_->Events(); } + +private: + std::shared_ptr> nodePtr_; +}; + /****************************************/ REACT_IMPL_END /***************************************/ -#endif // REACT_DETAIL_GRAPH_EVENTNODES_H_INCLUDED \ No newline at end of file +#endif // REACT_DETAIL_EVENT_NODES_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/PropagationMT.h b/include/react/detail/graph/PropagationMT.h deleted file mode 100644 index e69de29b..00000000 diff --git a/include/react/detail/graph/PropagationST.h b/include/react/detail/graph/PropagationST.h deleted file mode 100644 index 2fc0d048..00000000 --- a/include/react/detail/graph/PropagationST.h +++ /dev/null @@ -1,500 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_PROPAGATIONST_H_INCLUDED -#define REACT_DETAIL_GRAPH_PROPAGATIONST_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "react/common/Containers.h" -#include "react/common/Types.h" -#include "react/detail/IReactiveGraph.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -template -class PtrCache -{ -public: - template - std::shared_ptr LookupOrCreate(const K& key, F&& createFunc) - { - std::lock_guard scopedLock(mutex_); - - auto it = map1_.find(key); - - if (it != map1_.end()) - { - if (auto ptr = it->second.lock()) - { - return std::static_pointer_cast(ptr); - } - } - - std::shared_ptr v = createFunc(); - auto res = map1_.insert({ key, std::weak_ptr{ v } }); - map2_[v.get()] = res.first; - return v; - } - - template - void Erase(V* ptr) - { - std::lock_guard scopedLock(mutex_); - - auto it = map2_.find((void*)ptr); - - if (it != map2_.end()) - { - map1_.erase(it->second); - map2_.erase(it); - } - } - -private: - std::mutex mutex_; - - using Map1Type = std::map>; - using Map2Type = std::unordered_map; - - Map1Type map1_; - Map2Type map2_; -}; - -class ReactiveGraph; - -class TransactionQueue -{ -public: - TransactionQueue(ReactiveGraph& graph) : - graph_( graph ) - { } - - TransactionQueue(const TransactionQueue&) = delete; - TransactionQueue& operator=(const TransactionQueue&) = delete; - - TransactionQueue(TransactionQueue&&) = default; - TransactionQueue& operator =(TransactionQueue&&) = default; - - template - void Push(TransactionFlags flags, F&& transaction) - { - transactions_.push(StoredTransaction{ flags, std::forward(transaction) }); - - if (count_.fetch_add(1, std::memory_order_relaxed) == 0) - tbb::task::enqueue(*new(tbb::task::allocate_root()) WorkerTask(*this)); - } - -private: - struct StoredTransaction - { - TransactionFlags flags; - std::function callback; - }; - - class WorkerTask : public tbb::task - { - public: - WorkerTask(TransactionQueue& parent) : - parent_( parent ) - { } - - tbb::task* execute() - { - parent_.ProcessQueue(); - return nullptr; - } - - private: - TransactionQueue& parent_; - }; - - void ProcessQueue(); - - size_t ProcessNextBatch(); - - tbb::concurrent_queue transactions_; - - std::atomic count_{ 0 }; - - ReactiveGraph& graph_; -}; - -class ReactiveGraph -{ -public: - using LinkCacheType = PtrCache>; - - NodeId RegisterNode(IReactiveNode* nodePtr, NodeCategory category); - void UnregisterNode(NodeId nodeId); - - void OnNodeAttach(NodeId node, NodeId parentId); - void OnNodeDetach(NodeId node, NodeId parentId); - - template - void AddInput(NodeId nodeId, F&& inputCallback); - - template - void DoTransaction(F&& transactionCallback); - - template - void EnqueueTransaction(TransactionFlags flags, F&& transactionCallback); - - LinkCacheType& GetLinkCache() - { return linkCache_; } - -private: - struct NodeData - { - NodeData() = default; - - NodeData(const NodeData&) = default; - NodeData& operator=(const NodeData&) = default; - - NodeData(IReactiveNode* nodePtrIn, NodeCategory categoryIn) : - nodePtr( nodePtrIn ), - category(categoryIn) - { } - - NodeCategory category = NodeCategory::normal; - - int level = 0; - int newLevel = 0 ; - bool queued = false; - - IReactiveNode* nodePtr = nullptr; - - std::vector successors; - }; - - class TopoQueue - { - public: - void Push(NodeId nodeId, int level) - { queueData_.emplace_back(nodeId, level); } - - bool FetchNext(); - - const std::vector& Next() const - { return nextData_; } - - bool IsEmpty() const - { return queueData_.empty(); } - - private: - using Entry = std::pair; - - std::vector queueData_; - std::vector nextData_; - - int minLevel_ = (std::numeric_limits::max)(); - }; - - void Propagate(); - void UpdateLinkNodes(); - - void ScheduleSuccessors(NodeData & node); - void InvalidateSuccessors(NodeData & node); - -private: - TransactionQueue transactionQueue_{ *this }; - - IndexedStorage nodeData_; - - TopoQueue scheduledNodes_; - std::vector changedInputs_; - LinkOutputMap scheduledLinkOutputs_; - - LinkCacheType linkCache_; - - bool isTransactionActive_ = false; -}; - -NodeId ReactiveGraph::RegisterNode(IReactiveNode* nodePtr, NodeCategory category) -{ - return nodeData_.Insert(NodeData{ nodePtr, category }); -} - -void ReactiveGraph::UnregisterNode(NodeId nodeId) -{ - nodeData_.Erase(nodeId); -} - -void ReactiveGraph::OnNodeAttach(NodeId nodeId, NodeId parentId) -{ - auto& node = nodeData_[nodeId]; - auto& parent = nodeData_[parentId]; - - parent.successors.push_back(nodeId); - - if (node.level <= parent.level) - node.level = parent.level + 1; -} - -void ReactiveGraph::OnNodeDetach(NodeId nodeId, NodeId parentId) -{ - auto& parent = nodeData_[parentId]; - auto& successors = parent.successors; - - successors.erase(std::find(successors.begin(), successors.end(), nodeId)); -} - -template -void ReactiveGraph::AddInput(NodeId nodeId, F&& inputCallback) -{ - auto& node = nodeData_[nodeId]; - auto* nodePtr = node.nodePtr; - - // This writes to the input buffer of the respective node. - inputCallback(); - - if (isTransactionActive_) - { - // If a transaction is active, don't propagate immediately. - // Instead, record the node and wait for more inputs. - changedInputs_.push_back(nodeId); - } - else - { - size_t successorCount = node.successors.size(); - - // Update the node. This applies the input buffer to the node value and checks if it changed. - if (nodePtr->Update(0, successorCount) == UpdateResult::changed) - { - // Propagate changes through the graph - ScheduleSuccessors(node); - - if (! scheduledNodes_.IsEmpty()) - Propagate(); - - if (! scheduledLinkOutputs_.empty()) - UpdateLinkNodes(); - } - } -} - -template -void ReactiveGraph::DoTransaction(F&& transactionCallback) -{ - // Transaction callback may add multiple inputs. - isTransactionActive_ = true; - std::forward(transactionCallback)(); - isTransactionActive_ = false; - - // Apply all buffered inputs. - for (NodeId nodeId : changedInputs_) - { - auto& node = nodeData_[nodeId]; - auto* nodePtr = node.nodePtr; - - size_t successorCount = node.successors.size(); - - if (nodePtr->Update(0, successorCount) == UpdateResult::changed) - { - if (node.category == NodeCategory::dyninput) - InvalidateSuccessors(node); - - ScheduleSuccessors(node); - } - } - - changedInputs_.clear(); - - // Propagate changes through the graph. - if (! scheduledNodes_.IsEmpty()) - Propagate(); - - if (! scheduledLinkOutputs_.empty()) - UpdateLinkNodes(); -} - -template -void ReactiveGraph::EnqueueTransaction(TransactionFlags flags, F&& transactionCallback) -{ - transactionQueue_.Push(flags, std::forward(transactionCallback)); -} - -void ReactiveGraph::Propagate() -{ - while (scheduledNodes_.FetchNext()) - { - for (NodeId nodeId : scheduledNodes_.Next()) - { - auto& node = nodeData_[nodeId]; - auto* nodePtr = node.nodePtr; - - if (node.level < node.newLevel) - { - // Re-schedule this node - node.level = node.newLevel; - InvalidateSuccessors(node); - scheduledNodes_.Push(nodeId, node.level); - continue; - } - - // Special handling for link output nodes. They have no successors and they don't have to be updated. - if (node.category == NodeCategory::linkoutput) - { - static_cast(node.nodePtr)->CollectOutput(scheduledLinkOutputs_); - continue; - } - - size_t successorCount = node.successors.size(); - - if (nodePtr->Update(0u, successorCount) == UpdateResult::changed) - { - ScheduleSuccessors(node); - } - - node.queued = false; - } - } -} - -void ReactiveGraph::UpdateLinkNodes() -{ - for (auto& e : scheduledLinkOutputs_) - { - e.first->EnqueueTransaction(TransactionFlags::none, - [inputs = std::move(e.second)] - { - for (auto& callback : inputs) - callback(); - }); - } - - scheduledLinkOutputs_.clear(); -} - -void ReactiveGraph::ScheduleSuccessors(NodeData& node) -{ - for (NodeId succId : node.successors) - { - auto& succ = nodeData_[succId]; - - if (!succ.queued) - { - succ.queued = true; - scheduledNodes_.Push(succId, succ.level); - } - } -} - -void ReactiveGraph::InvalidateSuccessors(NodeData& node) -{ - for (NodeId succId : node.successors) - { - auto& succ = nodeData_[succId]; - - if (succ.newLevel <= node.level) - succ.newLevel = node.level + 1; - } -} - -bool ReactiveGraph::TopoQueue::FetchNext() -{ - // Throw away previous values - nextData_.clear(); - - // Find min level of nodes in queue data - minLevel_ = (std::numeric_limits::max)(); - for (const auto& e : queueData_) - if (minLevel_ > e.second) - minLevel_ = e.second; - - // Swap entries with min level to the end - auto p = std::partition(queueData_.begin(), queueData_.end(), [t = minLevel_] (const Entry& e) { return t != e.second; }); - - // Move min level values to next data - nextData_.reserve(std::distance(p, queueData_.end())); - - for (auto it = p; it != queueData_.end(); ++it) - nextData_.push_back(it->first); - - // Truncate moved entries - queueData_.resize(std::distance(queueData_.begin(), p)); - - return !nextData_.empty(); -} - -void TransactionQueue::ProcessQueue() -{ - for (;;) - { - size_t popCount = ProcessNextBatch(); - if (count_.fetch_sub(popCount) == popCount) - return; - } -} - -size_t TransactionQueue::ProcessNextBatch() -{ - StoredTransaction curTransaction; - size_t popCount = 0; - bool canMerge = false; - bool skipPop = false; - bool isDone = false; - - // Outer loop. One transaction per iteration. - for (;;) - { - if (!skipPop) - { - if (!transactions_.try_pop(curTransaction)) - return popCount; - - canMerge = IsBitmaskSet(curTransaction.flags, TransactionFlags::allow_merging); - ++popCount; - } - else - { - skipPop = false; - } - - graph_.DoTransaction([&] - { - curTransaction.callback(); - - if (canMerge) - { - // Inner loop. Mergeable transactions are merged - for (;;) - { - if (transactions_.try_pop(curTransaction)) - return; - - canMerge = IsBitmaskSet(curTransaction.flags, TransactionFlags::allow_merging); - ++popCount; - - if (!canMerge) - { - skipPop = true; - return; - } - - curTransaction.callback(); - } - } - }); - } -} - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_DETAIL_GRAPH_PROPAGATIONST_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph_impl.h b/include/react/detail/graph_impl.h new file mode 100644 index 00000000..133ffbe6 --- /dev/null +++ b/include/react/detail/graph_impl.h @@ -0,0 +1,224 @@ + +// Copyright Sebastian Jeckel 2017. +// 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_PROPAGATION_H_INCLUDED +#define REACT_DETAIL_PROPAGATION_H_INCLUDED + +#pragma once + +#include "react/detail/defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "react/common/ptrcache.h" +#include "react/common/slotmap.h" +#include "react/common/syncpoint.h" +#include "react/detail/graph_interface.h" + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +class ReactGraph; + +class TransactionQueue +{ +public: + TransactionQueue(ReactGraph& graph) : + graph_( graph ) + { } + + TransactionQueue(const TransactionQueue&) = delete; + TransactionQueue& operator=(const TransactionQueue&) = delete; + + TransactionQueue(TransactionQueue&&) = default; + TransactionQueue& operator =(TransactionQueue&&) = default; + + template + void Push(F&& func, SyncPoint::Dependency dep, TransactionFlags flags) + { + transactions_.push(StoredTransaction{ std::forward(transaction), std::move(dep), flags }); + + if (count_.fetch_add(1, std::memory_order_release) == 0) + tbb::task::enqueue(*new(tbb::task::allocate_root()) WorkerTask(*this)); + } + +private: + struct StoredTransaction + { + std::function func; + SyncPoint::DependencyList deps; + TransactionFlags flags; + }; + + class WorkerTask : public tbb::task + { + public: + WorkerTask(TransactionQueue& parent) : + parent_( parent ) + { } + + tbb::task* execute() + { + parent_.ProcessQueue(); + return nullptr; + } + + private: + TransactionQueue& parent_; + }; + + void ProcessQueue(); + + size_t ProcessNextBatch(); + + tbb::concurrent_queue transactions_; + + std::atomic count_{ 0 }; + + ReactGraph& graph_; +}; + +class ReactGraph +{ +public: + using LinkCache = WeakPtrCache; + + NodeId RegisterNode(IReactNode* nodePtr, NodeCategory category); + void UnregisterNode(NodeId nodeId); + + void AttachNode(NodeId node, NodeId parentId); + void DetachNode(NodeId node, NodeId parentId); + + template + void PushInput(NodeId nodeId, F&& inputCallback); + + void PushDependency(SyncPoint::Dependency dep); + + template + void DoTransaction(F&& transactionCallback); + + template + void EnqueueTransaction(F&& func, SyncPoint::DependencyList&& deps, TransactionFlags flags); + + LinkCache& GetLinkCache() + { return linkCache_; } + +private: + struct NodeData + { + NodeData() = default; + + NodeData(const NodeData&) = default; + NodeData& operator=(const NodeData&) = default; + + NodeData(IReactNode* nodePtrIn, NodeCategory categoryIn) : + nodePtr( nodePtrIn ), + category(categoryIn) + { } + + NodeCategory category = NodeCategory::normal; + + int level = 0; + int newLevel = 0 ; + bool queued = false; + + IReactNode* nodePtr = nullptr; + + std::vector successors; + }; + + class TopoQueue + { + public: + void Push(NodeId nodeId, int level) + { queueData_.emplace_back(nodeId, level); } + + bool FetchNext(); + + const std::vector& Next() const + { return nextData_; } + + bool IsEmpty() const + { return queueData_.empty(); } + + private: + using Entry = std::pair; + + std::vector queueData_; + std::vector nextData_; + + int minLevel_ = (std::numeric_limits::max)(); + }; + + void Propagate(); + void UpdateLinkNodes(); + + void ScheduleSuccessors(NodeData & node); + void RecalculateSuccessorLevels(NodeData & node); + +private: + TransactionQueue transactionQueue_{ *this }; + + SlotMap nodeData_; + + TopoQueue scheduledNodes_; + + std::vector changedInputs_; + std::vector changedNodes_; + + std::vector curDependencies_; + + LinkOutputMap scheduledLinkOutputs_; + + LinkCache linkCache_; + + bool isTransactionActive_ = false; +}; + +template +void ReactGraph::PushInput(NodeId nodeId, F&& inputCallback) +{ + auto& node = nodeData_[nodeId]; + auto* nodePtr = node.nodePtr; + + // This writes to the input buffer of the respective node. + std::forward(inputCallback)(); + + changedInputs_.push_back(nodeId); + + if (!isTransactionActive_) + Propagate(); +} + +template +void ReactGraph::DoTransaction(F&& transactionCallback) +{ + // Transaction callback may add multiple inputs. + isTransactionActive_ = true; + std::forward(transactionCallback)(); + isTransactionActive_ = false; + + Propagate(); +} + +template +void ReactGraph::EnqueueTransaction(F&& func, SyncPoint::DependencyList&& deps, TransactionFlags flags) +{ + transactionQueue_.Push(std::forward(func), std::move(deps), flags); +} + +/****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_PROPAGATION_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/IReactiveGraph.h b/include/react/detail/graph_interface.h similarity index 63% rename from include/react/detail/IReactiveGraph.h rename to include/react/detail/graph_interface.h index 85f3d253..6fa51f89 100644 --- a/include/react/detail/IReactiveGraph.h +++ b/include/react/detail/graph_interface.h @@ -1,24 +1,24 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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_IREACTIVEENGINE_H_INCLUDED -#define REACT_DETAIL_IREACTIVEENGINE_H_INCLUDED +#ifndef REACT_DETAIL_GRAPH_INTERFACE_H_INCLUDED +#define REACT_DETAIL_GRAPH_INTERFACE_H_INCLUDED #pragma once -#include "react/detail/Defs.h" +#include "react/detail/defs.h" #include #include #include #include -#include "react/API.h" -#include "react/common/Types.h" -#include "react/common/Util.h" +#include "react/api.h" +#include "react/common/types.h" +#include "react/common/utility.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -48,29 +48,29 @@ enum class NodeCategory linkoutput }; -class ReactiveGraph; +class ReactGraph; +struct IReactNode; + +using LinkOutputMap = std::unordered_map>>; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// IReactiveNode +/// IReactNode /////////////////////////////////////////////////////////////////////////////////////////////////// -struct IReactiveNode +struct IReactNode { - virtual ~IReactiveNode() = default; - - virtual const char* GetNodeType() const = 0; + virtual ~IReactNode() = default; - virtual UpdateResult Update(TurnId turnId, size_t successorCount) = 0; + virtual UpdateResult Update(TurnId turnId) noexcept = 0; + + virtual void Clear() noexcept + { } - virtual int GetDependencyCount() const = 0; + virtual void CollectOutput(LinkOutputMap& output) + { } }; -using LinkOutputMap = std::unordered_map>>; -struct ILinkOutputNode : public IReactiveNode -{ - virtual void CollectOutput(LinkOutputMap& output) = 0; -}; /****************************************/ REACT_IMPL_END /***************************************/ -#endif // REACT_DETAIL_IREACTIVEENGINE_H_INCLUDED \ No newline at end of file +#endif // REACT_DETAIL_GRAPH_INTERFACE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/node_base.h similarity index 65% rename from include/react/detail/graph/GraphBase.h rename to include/react/detail/node_base.h index 65f8d1f7..6345d431 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/node_base.h @@ -1,37 +1,47 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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_GRAPHBASE_H_INCLUDED -#define REACT_DETAIL_GRAPH_GRAPHBASE_H_INCLUDED +#ifndef REACT_DETAIL_NODE_BASE_H_INCLUDED +#define REACT_DETAIL_NODE_BASE_H_INCLUDED #pragma once -#include "react/detail/Defs.h" +#include "react/detail/defs.h" #include #include #include -#include "react/common/Types.h" -#include "react/common/Util.h" -#include "react/detail/IReactiveGraph.h" +#include "react/common/types.h" +#include "react/common/utility.h" +#include "react/detail/graph_interface.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ -class ReactiveGraph; +class ReactGraph; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// CreateWrappedNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +static RET CreateWrappedNode(ARGS&& ... args) +{ + auto node = std::make_shared(std::forward(args) ...); + return RET(std::move(node)); +} /////////////////////////////////////////////////////////////////////////////////////////////////// /// NodeBase /////////////////////////////////////////////////////////////////////////////////////////////////// -class NodeBase : public IReactiveNode +class NodeBase : public IReactNode { public: NodeBase(const Group& group) : group_( group ) - { } + { } NodeBase(const NodeBase&) = delete; NodeBase& operator=(const NodeBase&) = delete; @@ -65,10 +75,10 @@ class NodeBase : public IReactiveNode { return group_; } protected: - auto GetGraphPtr() const -> const std::shared_ptr& + auto GetGraphPtr() const -> const std::shared_ptr& { return GetInternals(group_).GetGraphPtr(); } - auto GetGraphPtr() -> std::shared_ptr& + auto GetGraphPtr() -> std::shared_ptr& { return GetInternals(group_).GetGraphPtr(); } void RegisterMe(NodeCategory category = NodeCategory::normal) @@ -78,10 +88,10 @@ class NodeBase : public IReactiveNode { GetGraphPtr()->UnregisterNode(nodeId_); } void AttachToMe(NodeId otherNodeId) - { GetGraphPtr()->OnNodeAttach(nodeId_, otherNodeId); } + { GetGraphPtr()->AttachNode(nodeId_, otherNodeId); } void DetachFromMe(NodeId otherNodeId) - { GetGraphPtr()->OnNodeDetach(nodeId_, otherNodeId); } + { GetGraphPtr()->DetachNode(nodeId_, otherNodeId); } private: NodeId nodeId_; @@ -91,4 +101,4 @@ class NodeBase : public IReactiveNode /****************************************/ REACT_IMPL_END /***************************************/ -#endif // REACT_DETAIL_GRAPH_GRAPHBASE_H_INCLUDED \ No newline at end of file +#endif // REACT_DETAIL_NODE_BASE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/observer_nodes.h similarity index 69% rename from include/react/detail/graph/ObserverNodes.h rename to include/react/detail/observer_nodes.h index 695c0792..35c160b1 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/observer_nodes.h @@ -1,23 +1,21 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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_OBSERVERNODES_H_INCLUDED -#define REACT_DETAIL_GRAPH_OBSERVERNODES_H_INCLUDED +#ifndef REACT_DETAIL_OBSERVER_NODES_H_INCLUDED +#define REACT_DETAIL_OBSERVER_NODES_H_INCLUDED #pragma once -#include "react/detail/Defs.h" -#include "react/API.h" +#include "react/detail/defs.h" +#include "react/api.h" #include #include -#include "GraphBase.h" - -#include "react/detail/ReactiveInput.h" +#include "node_base.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -36,9 +34,9 @@ class EventStreamNode; class ObserverNode : public NodeBase { public: - ObserverNode(const Group& group) : + explicit ObserverNode(const Group& group) : ObserverNode::NodeBase( group ) - { } + { } }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -60,19 +58,14 @@ class SignalObserverNode : public ObserverNode ~SignalObserverNode() { - apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(deps).GetNodeId())); }, depHolder_); + std::apply([this] (const auto& ... deps) + { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(deps).GetNodeId())); }, depHolder_); this->UnregisterMe(); } - virtual const char* GetNodeType() const override - { return "SignalObserver"; } - - virtual int GetDependencyCount() const override - { return sizeof...(TDeps); } - - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { - apply([this] (const auto& ... deps) { this->func_(GetInternals(deps).Value() ...); }, depHolder_); + std::apply([this] (const auto& ... deps) { this->func_(GetInternals(deps).Value() ...); }, depHolder_); return UpdateResult::unchanged; } @@ -105,16 +98,9 @@ class EventObserverNode : public ObserverNode this->UnregisterMe(); } - virtual const char* GetNodeType() const override - { return "EventObserver"; } - - virtual int GetDependencyCount() const override - { return 1; } - - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { - func_(EventRange( GetInternals(subject_).Events() )); - GetInternals(subject_).DecrementPendingSuccessorCount(); + func_(GetInternals(subject_).Events()); return UpdateResult::unchanged; } @@ -145,26 +131,20 @@ class SyncedEventObserverNode : public ObserverNode ~SyncedEventObserverNode() { - apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(syncs).GetNodeId())); }, syncHolder_); + std::apply([this] (const auto& ... syncs) + { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(syncs).GetNodeId())); }, syncHolder_); this->DetachFromMe(GetInternals(subject_).GetNodeId()); this->UnregisterMe(); } - virtual const char* GetNodeType() const override - { return "SyncedEventObserver"; } - - virtual int GetDependencyCount() const override - { return 1 + sizeof...(TSyncs); } - - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { // Updates might be triggered even if only sync nodes changed. Ignore those. if (GetInternals(this->subject_).Events().empty()) return UpdateResult::unchanged; - apply([this] (const auto& ... syncs) { func_(EventRange( GetInternals(this->subject_).Events() ), GetInternals(syncs).Value() ...); }, syncHolder_); - - GetInternals(subject_).DecrementPendingSuccessorCount(); + std::apply([this] (const auto& ... syncs) + { func_(EventRange( GetInternals(this->subject_).Events() ), GetInternals(syncs).Value() ...); }, syncHolder_); return UpdateResult::unchanged; } @@ -179,4 +159,4 @@ class SyncedEventObserverNode : public ObserverNode /****************************************/ REACT_IMPL_END /***************************************/ -#endif // REACT_DETAIL_GRAPH_OBSERVERNODES_H_INCLUDED \ No newline at end of file +#endif // REACT_DETAIL_OBSERVER_NODES_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/signal_nodes.h similarity index 74% rename from include/react/detail/graph/SignalNodes.h rename to include/react/detail/signal_nodes.h index 268faf7c..f97dc908 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/signal_nodes.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) @@ -9,14 +9,14 @@ #ifndef REACT_DETAIL_GRAPH_SIGNALNODES_H_INCLUDED #define REACT_DETAIL_GRAPH_SIGNALNODES_H_INCLUDED -#include "react/detail/Defs.h" +#include "react/detail/defs.h" #include #include #include #include -#include "GraphBase.h" +#include "node_base.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -42,13 +42,13 @@ class SignalNode : public NodeBase explicit SignalNode(const Group& group) : SignalNode::NodeBase( group ), value_( ) - { } + { } template SignalNode(const Group& group, T&& value) : SignalNode::NodeBase( group ), value_( std::forward(value) ) - { } + { } S& Value() { return value_; } @@ -87,13 +87,7 @@ class VarSignalNode : public SignalNode this->UnregisterMe(); } - virtual const char* GetNodeType() const override - { return "VarSignal"; } - - virtual int GetDependencyCount() const override - { return 0; } - - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { if (isInputAdded_) { @@ -149,7 +143,7 @@ class VarSignalNode : public SignalNode // in ApplyInput else { - func(newValue_); + func(newValue_); } } @@ -178,21 +172,17 @@ class SignalFuncNode : public SignalNode ~SignalFuncNode() { - apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(deps).GetNodePtr()->GetNodeId())); }, depHolder_); + std::apply([this] (const auto& ... deps) + { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(deps).GetNodePtr()->GetNodeId())); }, depHolder_); this->UnregisterMe(); } - virtual const char* GetNodeType() const override - { return "SignalFunc"; } - - virtual int GetDependencyCount() const override - { return sizeof...(TDeps); } - - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { bool changed = false; - S newValue = apply([this] (const auto& ... deps) { return this->func_(GetInternals(deps).Value() ...); }, depHolder_); + S newValue = std::apply([this] (const auto& ... deps) + { return this->func_(GetInternals(deps).Value() ...); }, depHolder_); if (! (this->Value() == newValue)) { @@ -208,7 +198,6 @@ class SignalFuncNode : public SignalNode private: F func_; - std::tuple ...> depHolder_; }; @@ -224,8 +213,8 @@ class SignalSlotNode : public SignalNode input_( dep ) { inputNodeId_ = GetGraphPtr()->RegisterNode(&slotInput_, NodeCategory::dyninput); + this->RegisterMe(); - this->AttachToMe(inputNodeId_); this->AttachToMe(GetInternals(dep).GetNodeId()); } @@ -234,18 +223,12 @@ class SignalSlotNode : public SignalNode { this->DetachFromMe(GetInternals(input_).GetNodeId()); this->DetachFromMe(inputNodeId_); - this->UnregisterMe(); + GetGraphPtr()->UnregisterNode(inputNodeId_); } - virtual const char* GetNodeType() const override - { return "SignalSlot"; } - - virtual int GetDependencyCount() const override - { return 2; } - - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { if (! (this->Value() == GetInternals(input_).Value())) { @@ -273,20 +256,13 @@ class SignalSlotNode : public SignalNode { return inputNodeId_; } private: - struct VirtualInputNode : public IReactiveNode + struct VirtualInputNode : public IReactNode { - virtual const char* GetNodeType() const override - { return "SignalSlotInput"; } - - virtual int GetDependencyCount() const override - { return 0; } - - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { return UpdateResult::changed; } }; - Signal input_; - + Signal input_; NodeId inputNodeId_; VirtualInputNode slotInput_; @@ -301,13 +277,24 @@ class SignalLinkNode : public SignalNode public: SignalLinkNode(const Group& group, const Signal& dep) : SignalLinkNode::SignalNode( group, GetInternals(dep).Value() ), - linkOutput_( dep ) + dep_ ( dep ) { this->RegisterMe(NodeCategory::input); + + auto& srcGraphPtr = GetInternals(srcGroup).GetGraphPtr(); + outputNodeId_ = srcGraphPtr->RegisterNode(this, NodeCategory::linkoutput); + + srcGraphPtr->AttachNode(outputNodeId_, GetInternals(dep).GetNodeId()); } ~SignalLinkNode() { + this->DetachFromMe(GetInternals(dep_).GetNodeId()); + + auto& srcGraphPtr = GetInternals(srcGroup).GetGraphPtr(); + srcGraphPtr->DetachNode(outputNodeId_, GetInternals(dep_).GetNodeId()); + srcGraphPtr->UnregisterNode(outputNodeId_); + auto& linkCache = GetGraphPtr()->GetLinkCache(); linkCache.Erase(this); @@ -317,46 +304,16 @@ class SignalLinkNode : public SignalNode void SetWeakSelfPtr(const std::weak_ptr& self) { linkOutput_.parent = self; } - virtual const char* GetNodeType() const override - { return "SignalLink"; } - - virtual int GetDependencyCount() const override - { return 1; } - - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { return UpdateResult::changed; } - template - void SetValue(T&& newValue) - { this->Value() = std::forward(newValue); } + void SetValue(S&& newValue) + { this->Value() = std::move(newValue); } private: - struct VirtualOutputNode : public ILinkOutputNode + struct VirtualOutputNode : public IReactNode { - VirtualOutputNode(const Signal& depIn) : - parent( ), - dep( depIn ), - srcGroup( depIn.GetGroup() ) - { - auto& srcGraphPtr = GetInternals(srcGroup).GetGraphPtr(); - nodeId = srcGraphPtr->RegisterNode(this, NodeCategory::linkoutput); - srcGraphPtr->OnNodeAttach(nodeId, GetInternals(dep).GetNodeId()); - } - - ~VirtualOutputNode() - { - auto& srcGraphPtr = GetInternals(srcGroup).GetGraphPtr(); - srcGraphPtr->OnNodeDetach(nodeId, GetInternals(dep).GetNodeId()); - srcGraphPtr->UnregisterNode(nodeId); - } - - virtual const char* GetNodeType() const override - { return "SignalLinkOutput"; } - - virtual int GetDependencyCount() const override - { return 1; } - - virtual UpdateResult Update(TurnId turnId, size_t successorCount) override + virtual UpdateResult Update(TurnId turnId) noexcept override { return UpdateResult::changed; } virtual void CollectOutput(LinkOutputMap& output) override @@ -365,7 +322,7 @@ class SignalLinkNode : public SignalNode { auto* rawPtr = p->GetGraphPtr().get(); output[rawPtr].push_back( - [storedParent = std::move(p), storedValue = GetInternals(dep).Value()] + [storedParent = std::move(p), storedValue = GetInternals(p->dep_).Value()] () mutable -> void { NodeId nodeId = storedParent->GetNodeId(); auto& graphPtr = storedParent->GetGraphPtr(); @@ -380,15 +337,51 @@ class SignalLinkNode : public SignalNode } std::weak_ptr parent; - - NodeId nodeId; - Signal dep; - Group srcGroup; }; + Signal dep_; + Group srcGroup; + NodeId outputNodeId_; VirtualOutputNode linkOutput_; }; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SignalInternals +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class SignalInternals +{ +public: + SignalInternals(const SignalInternals&) = default; + SignalInternals& operator=(const SignalInternals&) = default; + + SignalInternals(SignalInternals&&) = default; + SignalInternals& operator=(SignalInternals&&) = default; + + explicit SignalInternals(std::shared_ptr>&& nodePtr) : + nodePtr_( std::move(nodePtr) ) + { } + + auto GetNodePtr() -> std::shared_ptr>& + { return nodePtr_; } + + auto GetNodePtr() const -> const std::shared_ptr>& + { return nodePtr_; } + + NodeId GetNodeId() const + { return nodePtr_->GetNodeId(); } + + S& Value() + { return nodePtr_->Value(); } + + const S& Value() const + { return nodePtr_->Value(); } + +private: + std::shared_ptr> nodePtr_; +}; + /****************************************/ REACT_IMPL_END /***************************************/ #endif // REACT_DETAIL_GRAPH_SIGNALNODES_H_INCLUDED \ No newline at end of file diff --git a/project/msvc/CppReact.sln b/project/msvc/CppReact.sln index bd00b26a..5f987e52 100644 --- a/project/msvc/CppReact.sln +++ b/project/msvc/CppReact.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2005 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CppReact", "CppReact.vcxproj", "{5E56AAB9-4E33-4B9E-A315-E85CEDB75CF1}" EndProject @@ -31,6 +31,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example_BasicSignals", "Exa EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example_BasicSynchronization", "Example_BasicSynchronization.vcxproj", "{269329F8-A9E1-41AC-9C37-3A82A082A62C}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest-md", "..\..\..\googletest\googletest\msvc\2010\gtest-md.vcxproj", "{C8F6C172-56F2-4E76-B5FA-C3B423B31BE8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest_main-md", "..\..\..\googletest\googletest\msvc\2010\gtest_main-md.vcxproj", "{3AF54C8A-10BF-4332-9147-F68ED9862033}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -119,6 +123,26 @@ Global {269329F8-A9E1-41AC-9C37-3A82A082A62C}.Release|Win32.Build.0 = Release|Win32 {269329F8-A9E1-41AC-9C37-3A82A082A62C}.Release|x64.ActiveCfg = Release|x64 {269329F8-A9E1-41AC-9C37-3A82A082A62C}.Release|x64.Build.0 = Release|x64 + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE8}.Debug|Win32.ActiveCfg = Debug|Win32 + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE8}.Debug|Win32.Build.0 = Debug|Win32 + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE8}.Debug|x64.ActiveCfg = Debug|x64 + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE8}.Debug|x64.Build.0 = Debug|x64 + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE8}.Release|Win32.ActiveCfg = Release|Win32 + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE8}.Release|Win32.Build.0 = Release|Win32 + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE8}.Release|x64.ActiveCfg = Release|x64 + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE8}.Release|x64.Build.0 = Release|x64 + {3AF54C8A-10BF-4332-9147-F68ED9862033}.Debug|Win32.ActiveCfg = Debug|Win32 + {3AF54C8A-10BF-4332-9147-F68ED9862033}.Debug|Win32.Build.0 = Debug|Win32 + {3AF54C8A-10BF-4332-9147-F68ED9862033}.Debug|Win32.Deploy.0 = Debug|Win32 + {3AF54C8A-10BF-4332-9147-F68ED9862033}.Debug|x64.ActiveCfg = Debug|x64 + {3AF54C8A-10BF-4332-9147-F68ED9862033}.Debug|x64.Build.0 = Debug|x64 + {3AF54C8A-10BF-4332-9147-F68ED9862033}.Debug|x64.Deploy.0 = Debug|x64 + {3AF54C8A-10BF-4332-9147-F68ED9862033}.Release|Win32.ActiveCfg = Release|Win32 + {3AF54C8A-10BF-4332-9147-F68ED9862033}.Release|Win32.Build.0 = Release|Win32 + {3AF54C8A-10BF-4332-9147-F68ED9862033}.Release|Win32.Deploy.0 = Release|Win32 + {3AF54C8A-10BF-4332-9147-F68ED9862033}.Release|x64.ActiveCfg = Release|x64 + {3AF54C8A-10BF-4332-9147-F68ED9862033}.Release|x64.Build.0 = Release|x64 + {3AF54C8A-10BF-4332-9147-F68ED9862033}.Release|x64.Deploy.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -134,5 +158,10 @@ Global {CC66BFA0-D609-46E0-9FD1-F9CC902410B1} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} {230C9137-CCD0-47E2-8F1F-2E1DD19984A1} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} {269329F8-A9E1-41AC-9C37-3A82A082A62C} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE8} = {3F97AA87-0A03-4428-94C1-C9B4007C2C80} + {3AF54C8A-10BF-4332-9147-F68ED9862033} = {3F97AA87-0A03-4428-94C1-C9B4007C2C80} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C336763D-6F55-40BD-B6E1-9CAE12FF54E0} EndGlobalSection EndGlobal diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index 6b961f03..68b88817 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -21,31 +21,32 @@ {5E56AAB9-4E33-4B9E-A315-E85CEDB75CF1} CppReact + 8.1 StaticLibrary true - v140 + v141 MultiByte StaticLibrary true - v140 + v141 MultiByte StaticLibrary false - v140 + v141 true MultiByte StaticLibrary false - v140 + v141 true MultiByte @@ -105,6 +106,7 @@ $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) true %(PreprocessorDefinitions) + stdcpp17 true @@ -143,6 +145,7 @@ $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) true %(PreprocessorDefinitions) + stdcpp17 true @@ -154,32 +157,27 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index c78bde77..d87af090 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -16,88 +16,70 @@ {82fd9b9e-0b63-40f7-b245-b5eb0271b0d2} - - {c7adc39d-d19e-4fe2-b945-332515717bc8} - {11a75126-8bfd-4693-be4b-4e06ab73450c} {3f444875-ab1a-4bbd-99eb-7018c930098a} + + {45678bfc-0ce5-4e47-a365-54a72d8ecb6d} + - - Header Files\common - - - Header Files\common + + Header Files - - Header Files\common + + Header Files - - Header Files\common + + Header Files - - Header Files\common + + Header Files - - Header Files\common + + Header Files - + Header Files\detail - - Header Files\detail\graph - - - Header Files\detail\graph - - + Header Files\detail - - Header Files\detail\graph - - - Header Files\detail\graph - - - Header Files\detail\graph - - - Header Files\common + + Header Files\detail - - Header Files\common + + Header Files\detail - + Header Files\detail - - Header Files\detail\graph + + Header Files\detail - - Header Files\detail\graph + + Header Files\detail - + Header Files - - Header Files + + Header Files\detail - - Header Files + + Header Files\common - - Header Files + + Header Files\common - - Header Files + + Header Files\common - - Header Files + + Header Files\common @@ -110,5 +92,8 @@ Source Files\engine + + Source Files\detail + \ No newline at end of file diff --git a/project/msvc/CppReactBenchmark.vcxproj b/project/msvc/CppReactBenchmark.vcxproj index a57e4b20..d6cd06b4 100644 --- a/project/msvc/CppReactBenchmark.vcxproj +++ b/project/msvc/CppReactBenchmark.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -26,26 +26,26 @@ Application true - v140 + v141 MultiByte Application true - v140 + v141 MultiByte Application false - v140 + v141 true MultiByte Application false - v140 + v141 true MultiByte diff --git a/project/msvc/CppReactExample.vcxproj b/project/msvc/CppReactExample.vcxproj index 34a714e6..65945095 100644 --- a/project/msvc/CppReactExample.vcxproj +++ b/project/msvc/CppReactExample.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -27,26 +27,26 @@ Application true - v140 + v141 MultiByte Application true - v140 + v141 MultiByte Application false - v140 + v141 true MultiByte Application false - v140 + v141 true MultiByte diff --git a/project/msvc/CppReactTest.vcxproj b/project/msvc/CppReactTest.vcxproj index 5c2878a4..eccef6a3 100644 --- a/project/msvc/CppReactTest.vcxproj +++ b/project/msvc/CppReactTest.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -26,26 +26,26 @@ Application true - v140 + v141 MultiByte Application true - v140 + v141 MultiByte Application false - v140 + v141 true MultiByte Application false - v140 + v141 true MultiByte @@ -93,26 +93,19 @@ true - $(GTestDir)\msvc\gtest-md\Debug;%(AdditionalLibraryDirectories) gtestd.lib;gtest_main-mdd.lib;%(AdditionalDependencies) Console - Level3 - Disabled true $(SolutionDir)..\..\include;$(GTestDir)\include;%(AdditionalIncludeDirectories) - _VARIADIC_MAX=10;%(PreprocessorDefinitions) true 4503;%(DisableSpecificWarnings) - /bigobj %(AdditionalOptions) true - $(GTestDir)\msvc\gtest-md\Debug;%(AdditionalLibraryDirectories) - gtestd.lib;gtest_main-mdd.lib;%(AdditionalDependencies) Console @@ -141,12 +134,10 @@ Level3 - MaxSpeed true true true $(SolutionDir)..\..\include;$(GTestDir)\include;%(AdditionalIncludeDirectories) - _VARIADIC_MAX=10;%(PreprocessorDefinitions) true 4503;%(DisableSpecificWarnings) /bigobj %(AdditionalOptions) @@ -155,40 +146,31 @@ true true true - $(GTestDir)\msvc\gtest-md\Release;%(AdditionalLibraryDirectories) - gtest.lib;gtest_main-md.lib;%(AdditionalDependencies) Console false + + {c8f6c172-56f2-4e76-b5fa-c3b423b31be8} + + + {3af54c8a-10bf-4332-9147-f68ed9862033} + {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} - - + + - - - - - - - - - - - - - diff --git a/project/msvc/CppReactTest.vcxproj.filters b/project/msvc/CppReactTest.vcxproj.filters index ae74e006..a6f9919a 100644 --- a/project/msvc/CppReactTest.vcxproj.filters +++ b/project/msvc/CppReactTest.vcxproj.filters @@ -15,64 +15,29 @@ - - Source Files - - - Source Files - Source Files Source Files - - Source Files - Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - \ No newline at end of file diff --git a/project/msvc/Example_BasicAlgorithms.vcxproj b/project/msvc/Example_BasicAlgorithms.vcxproj index f77233e6..f376172b 100644 --- a/project/msvc/Example_BasicAlgorithms.vcxproj +++ b/project/msvc/Example_BasicAlgorithms.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -26,26 +26,26 @@ Application true - v140 + v141 MultiByte Application true - v140 + v141 MultiByte Application false - v140 + v141 true MultiByte Application false - v140 + v141 true MultiByte diff --git a/project/msvc/Example_BasicComposition.vcxproj b/project/msvc/Example_BasicComposition.vcxproj index 43101fb2..17164ed5 100644 --- a/project/msvc/Example_BasicComposition.vcxproj +++ b/project/msvc/Example_BasicComposition.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -26,26 +26,26 @@ Application true - v140 + v141 MultiByte Application true - v140 + v141 MultiByte Application false - v140 + v141 true MultiByte Application false - v140 + v141 true MultiByte diff --git a/project/msvc/Example_BasicEvents.vcxproj b/project/msvc/Example_BasicEvents.vcxproj index 07370926..c6d6256a 100644 --- a/project/msvc/Example_BasicEvents.vcxproj +++ b/project/msvc/Example_BasicEvents.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -26,26 +26,26 @@ Application true - v140 + v141 MultiByte Application true - v140 + v141 MultiByte Application false - v140 + v141 true MultiByte Application false - v140 + v141 true MultiByte diff --git a/project/msvc/Example_BasicObservers.vcxproj b/project/msvc/Example_BasicObservers.vcxproj index 7e27da42..ff78e91c 100644 --- a/project/msvc/Example_BasicObservers.vcxproj +++ b/project/msvc/Example_BasicObservers.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -26,26 +26,26 @@ Application true - v140 + v141 MultiByte Application true - v140 + v141 MultiByte Application false - v140 + v141 true MultiByte Application false - v140 + v141 true MultiByte diff --git a/project/msvc/Example_BasicSignals.vcxproj b/project/msvc/Example_BasicSignals.vcxproj index 556b2ad0..4e8cb03d 100644 --- a/project/msvc/Example_BasicSignals.vcxproj +++ b/project/msvc/Example_BasicSignals.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -26,26 +26,26 @@ Application true - v140 + v141 MultiByte Application true - v140 + v141 MultiByte Application false - v140 + v141 true MultiByte Application false - v140 + v141 true MultiByte diff --git a/project/msvc/Example_BasicSynchronization.vcxproj b/project/msvc/Example_BasicSynchronization.vcxproj index faa0ccfb..2de78be0 100644 --- a/project/msvc/Example_BasicSynchronization.vcxproj +++ b/project/msvc/Example_BasicSynchronization.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -26,26 +26,26 @@ Application true - v140 + v141 MultiByte Application true - v140 + v141 MultiByte Application false - v140 + v141 true MultiByte Application false - v140 + v141 true MultiByte diff --git a/src/detail/graph_impl.cpp b/src/detail/graph_impl.cpp new file mode 100644 index 00000000..8dc4be9a --- /dev/null +++ b/src/detail/graph_impl.cpp @@ -0,0 +1,248 @@ +// Copyright Sebastian Jeckel 2017. +// 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) + +#include "react/detail/defs.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "react/detail/graph_impl.h" + +#include "react/common/types.h" +#include "react/common/expected.h" +#include "react/detail/graph_interface.h" + + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +NodeId ReactGraph::RegisterNode(IReactNode* nodePtr, NodeCategory category) +{ + return nodeData_.Insert(NodeData{ nodePtr, category }); +} + +void ReactGraph::UnregisterNode(NodeId nodeId) +{ + nodeData_.Erase(nodeId); +} + +void ReactGraph::AttachNode(NodeId nodeId, NodeId parentId) +{ + auto& node = nodeData_[nodeId]; + auto& parent = nodeData_[parentId]; + + parent.successors.push_back(nodeId); + + if (node.level <= parent.level) + node.level = parent.level + 1; +} + +void ReactGraph::DetachNode(NodeId nodeId, NodeId parentId) +{ + auto& parent = nodeData_[parentId]; + auto& successors = parent.successors; + + successors.erase(std::find(successors.begin(), successors.end(), nodeId)); +} + +void ReactGraph::PushDependency(SyncPoint::Dependency dep) +{ + curDependencies_.push_back(std::move(dep)); +} + +void ReactGraph::Propagate() +{ + // Fill update queue with successors of changed inputs. + for (NodeId nodeId : changedInputs_) + { + auto& node = nodeData_[nodeId]; + auto* nodePtr = node.nodePtr; + + if (nodePtr->Update(0u) == UpdateResult::changed) + { + changedNodes_.push_back(nodePtr); + ScheduleSuccessors(node); + } + } + + // Propagate changes. + while (scheduledNodes_.FetchNext()) + { + for (NodeId nodeId : scheduledNodes_.Next()) + { + auto& node = nodeData_[nodeId]; + auto* nodePtr = node.nodePtr; + + if (node.level < node.newLevel) + { + // Re-schedule this node + node.level = node.newLevel; + RecalculateSuccessorLevels(node); + scheduledNodes_.Push(nodeId, node.level); + continue; + } + + // Special handling for link output nodes. They have no successors and they don't have to be updated. + if (node.category == NodeCategory::linkoutput) + { + node.nodePtr->CollectOutput(scheduledLinkOutputs_); + continue; + } + + if (nodePtr->Update(0u) == UpdateResult::changed) + { + changedNodes_.push_back(nodePtr); + ScheduleSuccessors(node); + } + + node.queued = false; + } + } + + if (!scheduledLinkOutputs_.empty()) + UpdateLinkNodes(); + + // Cleanup buffers in changed nodes. + for (IReactNode* nodePtr : changedNodes_) + nodePtr->Clear(); + changedNodes_.clear(); +} + +void ReactGraph::UpdateLinkNodes() +{ + for (auto& e : scheduledLinkOutputs_) + { + e.first->EnqueueTransaction(TransactionFlags::none, + [inputs = std::move(e.second)] + { + for (auto& callback : inputs) + callback(); + }); + } + + scheduledLinkOutputs_.clear(); +} + +void ReactGraph::ScheduleSuccessors(NodeData& node) +{ + for (NodeId succId : node.successors) + { + auto& succ = nodeData_[succId]; + + if (!succ.queued) + { + succ.queued = true; + scheduledNodes_.Push(succId, succ.level); + } + } +} + +void ReactGraph::RecalculateSuccessorLevels(NodeData& node) +{ + for (NodeId succId : node.successors) + { + auto& succ = nodeData_[succId]; + + if (succ.newLevel <= node.level) + succ.newLevel = node.level + 1; + } +} + +bool ReactGraph::TopoQueue::FetchNext() +{ + // Throw away previous values + nextData_.clear(); + + // Find min level of nodes in queue data + minLevel_ = (std::numeric_limits::max)(); + for (const auto& e : queueData_) + if (minLevel_ > e.second) + minLevel_ = e.second; + + // Swap entries with min level to the end + auto p = std::partition(queueData_.begin(), queueData_.end(), [t = minLevel_] (const Entry& e) { return t != e.second; }); + + // Move min level values to next data + nextData_.reserve(std::distance(p, queueData_.end())); + + for (auto it = p; it != queueData_.end(); ++it) + nextData_.push_back(it->first); + + // Truncate moved entries + queueData_.resize(std::distance(queueData_.begin(), p)); + + return !nextData_.empty(); +} + +void TransactionQueue::ProcessQueue() +{ + for (;;) + { + size_t popCount = ProcessNextBatch(); + if (count_.fetch_sub(popCount) == popCount) + return; + } +} + +size_t TransactionQueue::ProcessNextBatch() +{ + StoredTransaction curTransaction; + size_t popCount = 0; + bool canMerge = false; + bool skipPop = false; + bool isDone = false; + + // Outer loop. One transaction per iteration. + for (;;) + { + if (!skipPop) + { + if (!transactions_.try_pop(curTransaction)) + return popCount; + + canMerge = IsBitmaskSet(curTransaction.flags, TransactionFlags::allow_merging); + ++popCount; + } + else + { + skipPop = false; + } + + graph_.DoTransaction([&] + { + curTransaction.func(); + + if (canMerge) + { + // Inner loop. Mergeable transactions are merged + for (;;) + { + if (transactions_.try_pop(curTransaction)) + return; + + canMerge = IsBitmaskSet(curTransaction.flags, TransactionFlags::allow_merging); + ++popCount; + + if (!canMerge) + { + skipPop = true; + return; + } + + curTransaction.func(); + } + } + }); + } +} + +/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file diff --git a/src/engine/PulsecountEngine.cpp b/src/engine/PulsecountEngine.cpp index 0be62ba6..c5318fd6 100644 --- a/src/engine/PulsecountEngine.cpp +++ b/src/engine/PulsecountEngine.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) @@ -189,12 +189,12 @@ Turn::Turn(TurnIdT id, TransactionFlagsT flags) : /// PulsecountEngine /////////////////////////////////////////////////////////////////////////////////////////////////// -void EngineBase::OnNodeAttach(Node& node, Node& parent) +void EngineBase::AttachNode(Node& node, Node& parent) { parent.Successors.Add(node); } -void EngineBase::OnNodeDetach(Node& node, Node& parent) +void EngineBase::DetachNode(Node& node, Node& parent) { parent.Successors.Remove(node); } diff --git a/src/engine/SubtreeEngine.cpp b/src/engine/SubtreeEngine.cpp index 8584fcc2..69fb3278 100644 --- a/src/engine/SubtreeEngine.cpp +++ b/src/engine/SubtreeEngine.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) @@ -33,7 +33,7 @@ Turn::Turn(TurnIdT id, TransactionFlagsT flags) : /// PulsecountEngine /////////////////////////////////////////////////////////////////////////////////////////////////// -void EngineBase::OnNodeAttach(Node& node, Node& parent) +void EngineBase::AttachNode(Node& node, Node& parent) { parent.Successors.Add(node); @@ -41,7 +41,7 @@ void EngineBase::OnNodeAttach(Node& node, Node& parent) node.Level = parent.Level + 1; } -void EngineBase::OnNodeDetach(Node& node, Node& parent) +void EngineBase::DetachNode(Node& node, Node& parent) { parent.Successors.Remove(node); } @@ -230,7 +230,7 @@ void EngineBase::OnDynamicNodeAttach(Node& node, Node& parent, Turn& turn) } else { - OnNodeAttach(node, parent); + AttachNode(node, parent); invalidateSuccessors(node); @@ -245,7 +245,7 @@ void EngineBase::OnDynamicNodeDetach(Node& node, Node& parent, Turn& turn) if (isInPhase2_) applyAsyncDynamicDetach(node, parent, turn); else - OnNodeDetach(node, parent); + DetachNode(node, parent); } void EngineBase::applyAsyncDynamicAttach(Node& node, Node& parent, Turn& turn) diff --git a/src/engine/ToposortEngine.cpp b/src/engine/ToposortEngine.cpp index b8db5f67..1029775f 100644 --- a/src/engine/ToposortEngine.cpp +++ b/src/engine/ToposortEngine.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) @@ -30,7 +30,7 @@ ParTurn::ParTurn(TurnIdT id, TransactionFlagsT flags) : /// EngineBase /////////////////////////////////////////////////////////////////////////////////////////////////// template -void EngineBase::OnNodeAttach(TNode& node, TNode& parent) +void EngineBase::AttachNode(TNode& node, TNode& parent) { parent.Successors.Add(node); @@ -39,7 +39,7 @@ void EngineBase::OnNodeAttach(TNode& node, TNode& parent) } template -void EngineBase::OnNodeDetach(TNode& node, TNode& parent) +void EngineBase::DetachNode(TNode& node, TNode& parent) { parent.Successors.Remove(node); } @@ -85,7 +85,7 @@ void SeqEngineBase::Propagate(SeqTurn& turn) void SeqEngineBase::OnDynamicNodeAttach(SeqNode& node, SeqNode& parent, SeqTurn& turn) { - this->OnNodeAttach(node, parent); + this->AttachNode(node, parent); invalidateSuccessors(node); @@ -96,7 +96,7 @@ void SeqEngineBase::OnDynamicNodeAttach(SeqNode& node, SeqNode& parent, SeqTurn& void SeqEngineBase::OnDynamicNodeDetach(SeqNode& node, SeqNode& parent, SeqTurn& turn) { - this->OnNodeDetach(node, parent); + this->DetachNode(node, parent); } void SeqEngineBase::processChildren(SeqNode& node, SeqTurn& turn) @@ -184,7 +184,7 @@ void ParEngineBase::OnDynamicNodeDetach(ParNode& node, ParNode& parent, ParTurn& void ParEngineBase::applyDynamicAttach(ParNode& node, ParNode& parent, ParTurn& turn) { - this->OnNodeAttach(node, parent); + this->AttachNode(node, parent); invalidateSuccessors(node); @@ -195,7 +195,7 @@ void ParEngineBase::applyDynamicAttach(ParNode& node, ParNode& parent, ParTurn& void ParEngineBase::applyDynamicDetach(ParNode& node, ParNode& parent, ParTurn& turn) { - this->OnNodeDetach(node, parent); + this->DetachNode(node, parent); } void ParEngineBase::processChildren(ParNode& node, ParTurn& turn) diff --git a/src/logging/EventLog.cpp b/src/logging/EventLog.cpp deleted file mode 100644 index dcd8f627..00000000 --- a/src/logging/EventLog.cpp +++ /dev/null @@ -1,84 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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) - -#include "react/logging/EventLog.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -using std::chrono::duration_cast; -using std::chrono::microseconds; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventLog -/////////////////////////////////////////////////////////////////////////////////////////////////// -EventLog::Entry::Entry() : - time_{ std::chrono::system_clock::now() }, - data_{ nullptr } -{ -} - -EventLog::Entry::Entry(const Entry& other) : - time_{ other.time_ }, - data_{ other.data_} -{ -} - -EventLog::Entry::Entry(IEventRecord* ptr) : - time_{ std::chrono::system_clock::now() }, - data_{ ptr } -{ -} - -void EventLog::Entry::Serialize(std::ostream& out, const TimestampT& startTime) const -{ - out << EventId() << " : " << duration_cast(Time() - startTime).count() << std::endl; - data_->Serialize(out); -} - -EventLog::Entry& EventLog::Entry::operator=(Entry& rhs) -{ - time_ = rhs.time_, - data_ = std::move(rhs.data_); - return *this; -} - -bool EventLog::Entry::Equals(const Entry& other) const -{ - if (EventId() != other.EventId()) - return false; - // Todo - return false; -} - -EventLog::EventLog() : - startTime_(std::chrono::system_clock::now()) -{ -} - -EventLog::~EventLog() -{ - Clear(); -} - -void EventLog::Print() -{ - Write(std::cout); -} - -void EventLog::Write(std::ostream& out) -{ - for (auto& e : entries_) - e.Serialize(out, startTime_); -} - -void EventLog::Clear() -{ - for (auto& e : entries_) - e.Release(); - entries_.clear(); -} - -/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file diff --git a/src/logging/EventRecords.cpp b/src/logging/EventRecords.cpp deleted file mode 100644 index 3145c43a..00000000 --- a/src/logging/EventRecords.cpp +++ /dev/null @@ -1,210 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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) - -#include -#include - -#include "react/logging/EventRecords.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// NodeCreateEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -NodeCreateEvent::NodeCreateEvent(ObjectId nodeId, const char* type) : - nodeId_( nodeId ), - type_( type ) -{} - -void NodeCreateEvent::Serialize(std::ostream& out) const -{ - out << "> Node = " << nodeId_ << std::endl; - out << "> Type = " << type_ << std::endl; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// NodeDestroyEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -NodeDestroyEvent::NodeDestroyEvent(ObjectId nodeId) : - nodeId_( nodeId ) -{} - -void NodeDestroyEvent::Serialize(std::ostream& out) const -{ - out << "> Node = " << nodeId_ << std::endl; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// NodeAttachEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -NodeAttachEvent::NodeAttachEvent(ObjectId nodeId, ObjectId parentId) : - nodeId_( nodeId ), - parentId_{ parentId } -{} - -void NodeAttachEvent::Serialize(std::ostream& out) const -{ - out << "> Node = " << nodeId_ << std::endl; - out << "> Parent = " << parentId_ << std::endl; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// NodeDetachEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -NodeDetachEvent::NodeDetachEvent(ObjectId nodeId, ObjectId parentId) : - nodeId_( nodeId ), - parentId_{ parentId } -{} - -void NodeDetachEvent::Serialize(std::ostream& out) const -{ - out << "> Node = " << nodeId_ << std::endl; - out << "> Parent = " << parentId_ << std::endl; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// InputNodeAdmissionEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -InputNodeAdmissionEvent::InputNodeAdmissionEvent(ObjectId nodeId, int transactionId) : - nodeId_( nodeId ), - transactionId_( transactionId ) -{} - -void InputNodeAdmissionEvent::Serialize(std::ostream& out) const -{ - out << "> Node = " << nodeId_ << std::endl; - out << "> Transaction = " << transactionId_ << std::endl; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// NodePulseEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -NodePulseEvent::NodePulseEvent(ObjectId nodeId, int transactionId) : - nodeId_( nodeId ), - transactionId_( transactionId ) -{} - -void NodePulseEvent::Serialize(std::ostream& out) const -{ - out << "> Node = " << nodeId_ << std::endl; - out << "> Transaction = " << transactionId_ << std::endl; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// NodeIdlePulseEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -NodeIdlePulseEvent::NodeIdlePulseEvent(ObjectId nodeId, int transactionId) : - nodeId_( nodeId ), - transactionId_( transactionId ) -{} - -void NodeIdlePulseEvent::Serialize(std::ostream& out) const -{ - out << "> Node = " << nodeId_ << std::endl; - out << "> Transaction = " << transactionId_ << std::endl; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DynamicNodeAttachEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -DynamicNodeAttachEvent::DynamicNodeAttachEvent(ObjectId nodeId, ObjectId parentId, int transactionId) : - nodeId_( nodeId ), - parentId_{ parentId }, - transactionId_( transactionId ) -{} - -void DynamicNodeAttachEvent::Serialize(std::ostream& out) const -{ - out << "> Node = " << nodeId_ << std::endl; - out << "> Parent = " << parentId_ << std::endl; - out << "> Transaction = " << transactionId_ << std::endl; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DynamicNodeDetachEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -DynamicNodeDetachEvent::DynamicNodeDetachEvent(ObjectId nodeId, ObjectId parentId, int transactionId) : - nodeId_( nodeId ), - parentId_{ parentId }, - transactionId_( transactionId ) -{} - -void DynamicNodeDetachEvent::Serialize(std::ostream& out) const -{ - out << "> Node = " << nodeId_ << std::endl; - out << "> Parent = " << parentId_ << std::endl; - out << "> Transaction = " << transactionId_ << std::endl; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// NodeEvaluateBeginEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -NodeEvaluateBeginEvent::NodeEvaluateBeginEvent(ObjectId nodeId, int transactionId) : - nodeId_( nodeId ), - transactionId_( transactionId ), - threadId_( std::this_thread::get_id() ) -{} - -void NodeEvaluateBeginEvent::Serialize(std::ostream& out) const -{ - out << "> Node = " << nodeId_ << std::endl; - out << "> Transaction = " << transactionId_ << std::endl; - out << "> Thread = " << threadId_ << std::endl; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// NodeEvaluateEndEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -NodeEvaluateEndEvent::NodeEvaluateEndEvent(ObjectId nodeId, int transactionId) : - nodeId_( nodeId ), - transactionId_( transactionId ), - threadId_{ std::this_thread::get_id() } -{} - -void NodeEvaluateEndEvent::Serialize(std::ostream& out) const -{ - out << "> Node = " << nodeId_ << std::endl; - out << "> Transaction = " << transactionId_ << std::endl; - out << "> Thread = " << threadId_ << std::endl; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// TransactionBeginEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -TransactionBeginEvent::TransactionBeginEvent(int transactionId) : - transactionId_( transactionId ) -{} - -void TransactionBeginEvent::Serialize(std::ostream& out) const -{ - out << "> Transaction = " << transactionId_ << std::endl; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// TransactionEndEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -TransactionEndEvent::TransactionEndEvent(int transactionId) : - transactionId_( transactionId ) -{} - -void TransactionEndEvent::Serialize(std::ostream& out) const -{ - out << "> Transaction = " << transactionId_ << std::endl; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// UserBreakpointEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -UserBreakpointEvent::UserBreakpointEvent(const char* name) : - name_( name ) -{} - -void UserBreakpointEvent::Serialize(std::ostream& out) const -{ - out << "> Name = " << name_ << std::endl; -} - -/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file diff --git a/tests/src/EventStreamTest.cpp b/tests/src/EventStreamTest.cpp deleted file mode 100644 index 6801c5e9..00000000 --- a/tests/src/EventStreamTest.cpp +++ /dev/null @@ -1,29 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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) - -#include "EventStreamTest.h" -#include "TestUtil.h" - -#include "react/engine/PulsecountEngine.h" -#include "react/engine/ToposortEngine.h" -#include "react/engine/SubtreeEngine.h" - -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace { - -using namespace react; - -using P1 = DomainParams; -using P2 = DomainParams; -using P3 = DomainParams; -using P4 = DomainParams; - -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, EventStreamTest, P1); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, EventStreamTest, P2); -INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, EventStreamTest, P3); -INSTANTIATE_TYPED_TEST_CASE_P(Subtree, EventStreamTest, P4); - -} // ~namespace \ No newline at end of file diff --git a/tests/src/EventStreamTest.h b/tests/src/EventStreamTest.h index e33653a0..db12a8ea 100644 --- a/tests/src/EventStreamTest.h +++ b/tests/src/EventStreamTest.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/tests/src/EventStreamTestQ.cpp b/tests/src/EventStreamTestQ.cpp index d61330d1..26d07507 100644 --- a/tests/src/EventStreamTestQ.cpp +++ b/tests/src/EventStreamTestQ.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/tests/src/MoveTest.cpp b/tests/src/MoveTest.cpp index c5963745..6054092a 100644 --- a/tests/src/MoveTest.cpp +++ b/tests/src/MoveTest.cpp @@ -1,21 +1,12 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) -#include "MoveTest.h" -#include "TestUtil.h" - -#include "react/engine/ToposortEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// namespace { -using namespace react; - -using P1 = DomainParams; - -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, MoveTest, P1); } // ~namespace \ No newline at end of file diff --git a/tests/src/MoveTest.h b/tests/src/MoveTest.h index 41eb5810..0804423a 100644 --- a/tests/src/MoveTest.h +++ b/tests/src/MoveTest.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/tests/src/ObserverTest.cpp b/tests/src/ObserverTest.cpp index ae331812..6054092a 100644 --- a/tests/src/ObserverTest.cpp +++ b/tests/src/ObserverTest.cpp @@ -1,29 +1,12 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) -#include "ObserverTest.h" -#include "TestUtil.h" - -#include "react/engine/PulsecountEngine.h" -#include "react/engine/ToposortEngine.h" -#include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// namespace { -using namespace react; - -using P1 = DomainParams; -using P2 = DomainParams; -using P3 = DomainParams; -using P4 = DomainParams; - -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, ObserverTest, P1); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, ObserverTest, P2); -INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, ObserverTest, P3); -INSTANTIATE_TYPED_TEST_CASE_P(Subtree, ObserverTest, P4); } // ~namespace \ No newline at end of file diff --git a/tests/src/ObserverTest.h b/tests/src/ObserverTest.h index af5ac834..559c0686 100644 --- a/tests/src/ObserverTest.h +++ b/tests/src/ObserverTest.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/tests/src/ObserverTestQ.cpp b/tests/src/ObserverTestQ.cpp index 1e36c088..4dfe55f6 100644 --- a/tests/src/ObserverTestQ.cpp +++ b/tests/src/ObserverTestQ.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/tests/src/OperationsTest.cpp b/tests/src/OperationsTest.cpp index cc3b576e..6054092a 100644 --- a/tests/src/OperationsTest.cpp +++ b/tests/src/OperationsTest.cpp @@ -1,29 +1,12 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) -#include "OperationsTest.h" -#include "TestUtil.h" - -#include "react/engine/PulsecountEngine.h" -#include "react/engine/ToposortEngine.h" -#include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// namespace { -using namespace react; - -using P1 = DomainParams; -using P2 = DomainParams; -using P3 = DomainParams; -using P4 = DomainParams; - -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, OperationsTest, P1); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, OperationsTest, P2); -INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, OperationsTest, P3); -INSTANTIATE_TYPED_TEST_CASE_P(Subtree, OperationsTest, P4); } // ~namespace \ No newline at end of file diff --git a/tests/src/OperationsTest.h b/tests/src/OperationsTest.h index 1af27694..c7fb6d3f 100644 --- a/tests/src/OperationsTest.h +++ b/tests/src/OperationsTest.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/tests/src/OperationsTestQ.cpp b/tests/src/OperationsTestQ.cpp index 83e8a7ab..576c0752 100644 --- a/tests/src/OperationsTestQ.cpp +++ b/tests/src/OperationsTestQ.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/tests/src/ParallelizationTest.cpp b/tests/src/ParallelizationTest.cpp index 9ef6d156..6054092a 100644 --- a/tests/src/ParallelizationTest.cpp +++ b/tests/src/ParallelizationTest.cpp @@ -1,29 +1,12 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) -#include "ParallelizationTest.h" -#include "TestUtil.h" - -#include "react/engine/PulsecountEngine.h" -#include "react/engine/ToposortEngine.h" -#include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// namespace { -using namespace react; - -using P1 = DomainParams; -using P2 = DomainParams; -using P3 = DomainParams; -using P4 = DomainParams; - -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, ParallelizationTest, P1); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, ParallelizationTest, P2); -INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, ParallelizationTest, P3); -INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, ParallelizationTest, P4); } // ~namespace \ No newline at end of file diff --git a/tests/src/ParallelizationTest.h b/tests/src/ParallelizationTest.h index 43a99f05..145c730c 100644 --- a/tests/src/ParallelizationTest.h +++ b/tests/src/ParallelizationTest.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/tests/src/SignalTest.cpp b/tests/src/SignalTest.cpp index 44a68828..a34b7f98 100644 --- a/tests/src/SignalTest.cpp +++ b/tests/src/SignalTest.cpp @@ -1,29 +1,11 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) -#include "SignalTest.h" -#include "TestUtil.h" - -#include "react/engine/PulsecountEngine.h" -#include "react/engine/ToposortEngine.h" -#include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// namespace { -using namespace react; - -using P1 = DomainParams; -using P2 = DomainParams; -using P3 = DomainParams; -using P4 = DomainParams; - -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, SignalTest, P1); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, SignalTest, P2); -INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, SignalTest, P3); -INSTANTIATE_TYPED_TEST_CASE_P(Subtree, SignalTest, P4); - } // ~namespace \ No newline at end of file diff --git a/tests/src/SignalTest.h b/tests/src/SignalTest.h index b20eabe8..69ae81de 100644 --- a/tests/src/SignalTest.h +++ b/tests/src/SignalTest.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/tests/src/SignalTestQ.cpp b/tests/src/SignalTestQ.cpp index b6bacde1..d91875c7 100644 --- a/tests/src/SignalTestQ.cpp +++ b/tests/src/SignalTestQ.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/tests/src/TestUtil.h b/tests/src/TestUtil.h index a6cc5306..0d7b9cb6 100644 --- a/tests/src/TestUtil.h +++ b/tests/src/TestUtil.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/tests/src/TransactionTest.cpp b/tests/src/TransactionTest.cpp index 487f7f5b..6054092a 100644 --- a/tests/src/TransactionTest.cpp +++ b/tests/src/TransactionTest.cpp @@ -1,29 +1,12 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) -#include "TransactionTest.h" -#include "TestUtil.h" - -#include "react/engine/PulsecountEngine.h" -#include "react/engine/ToposortEngine.h" -#include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// namespace { -using namespace react; - -using P1 = DomainParams; -using P2 = DomainParams; -using P3 = DomainParams; -using P4 = DomainParams; - -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposort, TransactionTest, P1); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposort, TransactionTest, P2); -INSTANTIATE_TYPED_TEST_CASE_P(Pulsecount, TransactionTest, P3); -INSTANTIATE_TYPED_TEST_CASE_P(Subtree, TransactionTest, P4); } // ~namespace \ No newline at end of file diff --git a/tests/src/TransactionTest.h b/tests/src/TransactionTest.h index a332de90..0501a81c 100644 --- a/tests/src/TransactionTest.h +++ b/tests/src/TransactionTest.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/tests/src/common_tests.cpp b/tests/src/common_tests.cpp new file mode 100644 index 00000000..0bd5b6dc --- /dev/null +++ b/tests/src/common_tests.cpp @@ -0,0 +1,132 @@ + +// Copyright Sebastian Jeckel 2017. +// 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) + +#include "gtest/gtest.h" + +#include "react/common/syncpoint.h" + +#include +#include +#include + +using namespace react; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +TEST(SyncPointTest, SingleWait) +{ + SyncPoint sp; + + SyncPoint::Dependency dep(sp); + + int output = 0; + + std::thread t1([storedDep = std::move(dep), &output] () + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + output = 1; + }); + + + sp.Wait(); + + EXPECT_EQ(1, output); + + t1.join(); +} + +TEST(SyncPointTest, MultiWait) +{ + SyncPoint sp; + + SyncPoint::Dependency dep1(sp); + SyncPoint::Dependency dep2(sp); + SyncPoint::Dependency dep3(sp); + + int output1 = 0; + int output2 = 0; + int output3 = 0; + + std::thread t1([storedDep = std::move(dep1), &output1] () + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + output1 = 1; + }); + + std::thread t2([storedDep = std::move(dep2), &output2] () + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + output2 = 2; + }); + + std::thread t3([storedDep = std::move(dep3), &output3] () + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + output3 = 3; + }); + + + sp.Wait(); + + EXPECT_EQ(1, output1); + EXPECT_EQ(2, output2); + EXPECT_EQ(3, output3); + + t1.join(); + t2.join(); + t3.join(); +} + +TEST(SyncPointTest, MultiWaitFor) +{ + SyncPoint sp; + + SyncPoint::Dependency dep1(sp); + SyncPoint::Dependency dep2(sp); + SyncPoint::Dependency dep3(sp); + + int output1 = 0; + int output2 = 0; + int output3 = 0; + + std::thread t1([storedDep = std::move(dep1), &output1] () + { + std::this_thread::sleep_for(std::chrono::seconds(3)); + output1 = 1; + }); + + std::thread t2([storedDep = std::move(dep2), &output2] () + { + std::this_thread::sleep_for(std::chrono::seconds(3)); + output2 = 2; + }); + + std::thread t3([storedDep = std::move(dep3), &output3] () + { + std::this_thread::sleep_for(std::chrono::seconds(3)); + output3 = 3; + }); + + + bool done = sp.WaitFor(std::chrono::milliseconds(1)); + + EXPECT_FALSE(done); + + EXPECT_EQ(0, output1); + EXPECT_EQ(0, output2); + EXPECT_EQ(0, output3); + + done = sp.WaitFor(std::chrono::seconds(10)); + + EXPECT_TRUE(done); + + EXPECT_EQ(1, output1); + EXPECT_EQ(2, output2); + EXPECT_EQ(3, output3); + + t1.join(); + t2.join(); + t3.join(); +} \ No newline at end of file diff --git a/tests/src/event_tests.cpp b/tests/src/event_tests.cpp new file mode 100644 index 00000000..4204a466 --- /dev/null +++ b/tests/src/event_tests.cpp @@ -0,0 +1,219 @@ + +// Copyright Sebastian Jeckel 2017. +// 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) + +#include "gtest/gtest.h" + +#include "react/event.h" +#include "react/observer.h" + +#include +#include + +using namespace react; + +TEST(EventTest, Construction) +{ + Group g; + + // Event source + { + EventSource t1( g ); + EventSource t2( t1 ); + EventSource t3( std::move(t1) ); + + EventSource ref1( t2 ); + Event ref2( t3 ); + + EXPECT_TRUE(ref1 == ref2); + } + + // Event slot + { + EventSlot t1( g ); + EventSlot t2( t1 ); + EventSlot t3( std::move(t1) ); + + EventSlot ref1( t2 ); + Event ref2( t3 ); + + EXPECT_TRUE(ref1 == ref2); + } + + // Event link + { + EventSlot s1( g ); + + EventLink t1( g, s1 ); + EventLink t2( t1 ); + EventLink t3( std::move(t1) ); + + EventLink ref1( t2 ); + Event ref2( t3 ); + + EXPECT_TRUE(ref1 == ref2); + } +} + +TEST(EventTest, BasicOutput) +{ + Group g; + + EventSource evt( g ); + + int output = 0; + + Observer obs([&] (const auto& events) + { + for (int e : events) + output += e; + }, evt); + + EXPECT_EQ(0, output); + + evt.Emit(1); + EXPECT_EQ(1, output); + + evt.Emit(2); + EXPECT_EQ(3, output); +} + +TEST(EventTest, EventSlots) +{ + Group g; + + EventSource evt1( g ); + EventSource evt2( g ); + + EventSlot slot( g ); + + int output = 0; + int turns = 0; + + Observer obs([&] (const auto& events) + { + ++turns; + + for (int e : events) + output += e; + }, slot); + + EXPECT_EQ(0, output); + EXPECT_EQ(0, turns); + + slot.Add(evt1); + slot.Add(evt2); + + evt1.Emit(5); + evt2.Emit(2); + + EXPECT_EQ(7, output); + EXPECT_EQ(2, turns); + + output = 0; + + slot.Remove(evt1); + + evt1.Emit(5); + evt2.Emit(2); + + EXPECT_EQ(2, output); + EXPECT_EQ(3, turns); + + output = 0; + + slot.Remove(evt2); + + evt1.Emit(5); + evt2.Emit(2); + + EXPECT_EQ(0, output); + EXPECT_EQ(3, turns); + + output = 0; + + slot.Add(evt1); + slot.Add(evt1); + + evt1.Emit(5); + evt2.Emit(2); + + EXPECT_EQ(5, output); + EXPECT_EQ(4, turns); +} + +TEST(EventTest, EventTransactions) +{ + Group g; + + EventSource evt( g ); + + int output = 0; + int turns = 0; + + Observer obs([&] (const auto& events) + { + ++turns; + for (int e : events) + output += e; + }, evt); + + EXPECT_EQ(0, output); + + g.DoTransaction([&] + { + evt.Emit(1); + evt.Emit(1); + evt.Emit(1); + evt.Emit(1); + }); + + EXPECT_EQ(4, output); + EXPECT_EQ(1, turns); +} + +TEST(EventTest, ExplicitEventLinks) +{ + Group g1; + Group g2; + Group g3; + + EventSource evt1( g1 ); + EventSource evt2( g2 ); + EventSource evt3( g3 ); + + EventSlot slot( g1 ); + + // Same group + slot.Add(evt1); + + // Explicit link + EventLink lnk2( g1, evt2 ); + slot.Add(lnk2); + + // Implicit link + slot.Add(evt3); + + int output = 0; + int turns = 0; + + EXPECT_EQ(0, output); + + Observer obs([&] (const auto& events) + { + ++turns; + for (int e : events) + output += e; + }, slot); + + evt1.Emit(1); + evt2.Emit(1); + evt3.Emit(1); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + EXPECT_EQ(3, output); + EXPECT_EQ(3, turns); +} \ No newline at end of file From 63488198260b1f9c75ff8c883f93fe843459a7a6 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 24 Oct 2017 22:33:52 +0200 Subject: [PATCH 244/266] Renamed Signal to State. General progress. --- include/react/API.h | 46 +-- include/react/Algorithm.h | 78 +++--- include/react/Event.h | 42 +-- include/react/Observer.h | 61 +--- include/react/common/slotmap.h | 2 +- include/react/common/syncpoint.h | 103 ++++--- include/react/common/utility.h | 28 ++ include/react/detail/algorithm_nodes.h | 54 ++-- include/react/detail/event_nodes.h | 80 +++--- include/react/detail/graph_impl.h | 37 ++- include/react/detail/observer_nodes.h | 66 ++++- .../detail/{signal_nodes.h => state_nodes.h} | 121 ++++---- include/react/{Signal.h => state.h} | 162 +++++------ project/msvc/CppReact.vcxproj | 6 +- project/msvc/CppReact.vcxproj.filters | 12 +- project/msvc/CppReactTest.vcxproj | 4 +- project/msvc/CppReactTest.vcxproj.filters | 10 +- src/detail/graph_impl.cpp | 49 +++- tests/src/SignalTest.cpp | 11 - tests/src/TransactionTest.cpp | 12 - tests/src/common_tests.cpp | 31 ++- tests/src/event_tests.cpp | 6 +- tests/src/state_tests.cpp | 262 ++++++++++++++++++ tests/src/transaction_tests.cpp | 220 +++++++++++++++ 24 files changed, 1031 insertions(+), 472 deletions(-) rename include/react/detail/{signal_nodes.h => state_nodes.h} (75%) rename include/react/{Signal.h => state.h} (51%) delete mode 100644 tests/src/SignalTest.cpp delete mode 100644 tests/src/TransactionTest.cpp create mode 100644 tests/src/state_tests.cpp create mode 100644 tests/src/transaction_tests.cpp diff --git a/include/react/API.h b/include/react/API.h index 3e20d5bb..89ace35c 100644 --- a/include/react/API.h +++ b/include/react/API.h @@ -37,23 +37,23 @@ REACT_DEFINE_BITMASK_OPERATORS(TransactionFlags) /// API types /////////////////////////////////////////////////////////////////////////////////////////////////// -// Groups +// Group class Group; -// Signals +// State template -class Signal; +class State; template -class VarSignal; +class StateVar; template -class SignalSlot; +class StateSlot; template -class SignalLink; +class StateLink; -// Events +// Event enum class Token; template @@ -65,39 +65,9 @@ class EventSource; template class EventSlot; -// Observers +// Observer class Observer; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Traits -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct IsSignal { static const bool value = false; }; - -template -struct IsSignal> { static const bool value = true; }; - -template -struct IsSignal> { static const bool value = true; }; - -template -struct IsEvent { static const bool value = false; }; - -template -struct IsEvent> { static const bool value = true; }; - -template -struct IsEvent> { static const bool value = true; }; - -template -struct AsNonInputNode { using type = T; }; - -template -struct AsNonInputNode> { using type = Signal; }; - -template -struct AsNonInputNode> { using type = Event; }; - /******************************************/ REACT_END /******************************************/ #endif // REACT_TYPETRAITS_H_INCLUDED \ No newline at end of file diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index e149b491..9c1b2023 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) @@ -18,52 +18,52 @@ #include "react/API.h" #include "react/detail/algorithm_nodes.h" +#include "react/state.h" #include "react/event.h" -#include "react/signal.h" /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Hold the most recent event in a signal +/// Hold the most recent event in a state /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Hold(const Group& group, T&& initialValue, const Event& evnt) -> Signal +auto Hold(const Group& group, T&& initialValue, const Event& evnt) -> State { using REACT_IMPL::HoldNode; using REACT_IMPL::SameGroupOrLink; using REACT_IMPL::CreateWrappedNode; - return CreateWrappedNode, HoldNode>( + return CreateWrappedNode, HoldNode>( group, std::forward(initialValue), SameGroupOrLink(group, evnt)); } template -auto Hold(T&& initialValue, const Event& evnt) -> Signal +auto Hold(T&& initialValue, const Event& evnt) -> State { return Hold(evnt.GetGroup(), std::forward(initialValue), evnt); } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Emits value changes of target signal. +/// Emits value changes of target state. /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Monitor(const Group& group, const Signal& signal) -> Event +auto Monitor(const Group& group, const State& state) -> Event { using REACT_IMPL::MonitorNode; using REACT_IMPL::SameGroupOrLink; using REACT_IMPL::CreateWrappedNode; return CreateWrappedNode, MonitorNode>( - group, SameGroupOrLink(group, signal)); + group, SameGroupOrLink(group, state)); } template -auto Monitor(const Signal& signal) -> Event - { return Monitor(signal.GetGroup(), signal); } +auto Monitor(const State& state) -> Event + { return Monitor(state.GetGroup(), state); } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Iterate - Iteratively combines signal value with values from event stream (aka Fold) +/// Iterate - Iteratively combines state value with values from event stream (aka Fold) /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Iterate(const Group& group, T&& initialValue, F&& func, const Event& evnt) -> Signal +auto Iterate(const Group& group, T&& initialValue, F&& func, const Event& evnt) -> State { using REACT_IMPL::IterateNode; using REACT_IMPL::IsCallableWith; @@ -72,12 +72,12 @@ auto Iterate(const Group& group, T&& initialValue, F&& func, const Event& evn using FuncType = typename std::decay::type; - return CreateWrappedNode, IterateNode>( + return CreateWrappedNode, IterateNode>( group, std::forward(initialValue), std::forward(func), SameGroupOrLink(group, evnt)); } template -auto IterateByRef(const Group& group, T&& initialValue, F&& func, const Event& evnt) -> Signal +auto IterateByRef(const Group& group, T&& initialValue, F&& func, const Event& evnt) -> State { using REACT_IMPL::IterateByRefNode; using REACT_IMPL::IsCallableWith; @@ -86,23 +86,23 @@ auto IterateByRef(const Group& group, T&& initialValue, F&& func, const Event using FuncType = typename std::decay::type; - return CreateWrappedNode, IterateByRefNode>( + return CreateWrappedNode, IterateByRefNode>( group, std::forward(initialValue), std::forward(func), SameGroupOrLink(group, evnt)); } template -auto Iterate(T&& initialValue, F&& func, const Event& evnt) -> Signal +auto Iterate(T&& initialValue, F&& func, const Event& evnt) -> State { return Iterate(evnt.GetGroup(), std::forward(initialValue), std::forward(func), evnt); } template -auto IterateByRef(T&& initialValue, F&& func, const Event& evnt) -> Signal +auto IterateByRef(T&& initialValue, F&& func, const Event& evnt) -> State { return IterateByRef(evnt.GetGroup(), std::forward(initialValue), std::forward(func), evnt); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Iterate - Synced /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Iterate(const Group& group, T&& initialValue, F&& func, const Event& evnt, const Signal& ... signals) -> Signal +auto Iterate(const Group& group, T&& initialValue, F&& func, const Event& evnt, const State& ... states) -> State { using REACT_IMPL::SyncedIterateNode; using REACT_IMPL::IsCallableWith; @@ -111,12 +111,12 @@ auto Iterate(const Group& group, T&& initialValue, F&& func, const Event& evn using FuncType = typename std::decay::type; - return CreateWrappedNode, SyncedIterateNode>( - group, std::forward(initialValue), std::forward(func), SameGroupOrLink(group, evnt), SameGroupOrLink(group, signals) ...); + return CreateWrappedNode, SyncedIterateNode>( + group, std::forward(initialValue), std::forward(func), SameGroupOrLink(group, evnt), SameGroupOrLink(group, states) ...); } template -auto IterateByRef(const Group& group, T&& initialValue, F&& func, const Event& evnt, const Signal& ... signals) -> Signal +auto IterateByRef(const Group& group, T&& initialValue, F&& func, const Event& evnt, const State& ... states) -> State { using REACT_IMPL::SyncedIterateByRefNode; using REACT_IMPL::IsCallableWith; @@ -125,53 +125,53 @@ auto IterateByRef(const Group& group, T&& initialValue, F&& func, const Event using FuncType = typename std::decay::type; - return CreateWrappedNode, SyncedIterateByRefNode>( - group, std::forward(initialValue), std::forward(func), SameGroupOrLink(group, evnt), SameGroupOrLink(group, signals) ...); + return CreateWrappedNode, SyncedIterateByRefNode>( + group, std::forward(initialValue), std::forward(func), SameGroupOrLink(group, evnt), SameGroupOrLink(group, states) ...); } template -auto Iterate(T&& initialValue, F&& func, const Event& evnt, const Signal& ... signals) -> Signal - { return Iterate(evnt.GetGroup(), std::forward(initialValue), std::forward(func), evnt, signals ...); } +auto Iterate(T&& initialValue, F&& func, const Event& evnt, const State& ... states) -> State + { return Iterate(evnt.GetGroup(), std::forward(initialValue), std::forward(func), evnt, states ...); } template -auto IterateByRef(T&& initialValue, F&& func, const Event& evnt, const Signal& ... signals) -> Signal - { return IterateByRef(evnt.GetGroup(), std::forward(initialValue), std::forward(func), evnt, signals ...); } +auto IterateByRef(T&& initialValue, F&& func, const Event& evnt, const State& ... states) -> State + { return IterateByRef(evnt.GetGroup(), std::forward(initialValue), std::forward(func), evnt, states ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Snapshot - Sets signal value to value of other signal when event is received +/// Snapshot - Sets state value to value of other state when event is received /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Snapshot(const Group& group, const Signal& signal, const Event& evnt) -> Signal +auto Snapshot(const Group& group, const State& state, const Event& evnt) -> State { using REACT_IMPL::SnapshotNode; using REACT_IMPL::SameGroupOrLink; using REACT_IMPL::CreateWrappedNode; - return CreateWrappedNode, SnapshotNode>( - group, SameGroupOrLink(group, signal), SameGroupOrLink(group, evnt)); + return CreateWrappedNode, SnapshotNode>( + group, SameGroupOrLink(group, state), SameGroupOrLink(group, evnt)); } template -auto Snapshot(const Signal& signal, const Event& evnt) -> Signal - { return Snapshot(signal.GetGroup(), signal, evnt); } +auto Snapshot(const State& state, const Event& evnt) -> State + { return Snapshot(state.GetGroup(), state, evnt); } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Pulse - Emits value of target signal when event is received +/// Pulse - Emits value of target state when event is received /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Pulse(const Group& group, const Signal& signal, const Event& evnt) -> Event +auto Pulse(const Group& group, const State& state, const Event& evnt) -> Event { using REACT_IMPL::PulseNode; using REACT_IMPL::SameGroupOrLink; using REACT_IMPL::CreateWrappedNode; return CreateWrappedNode, PulseNode>( - group, SameGroupOrLink(group, signal), SameGroupOrLink(group, evnt)); + group, SameGroupOrLink(group, state), SameGroupOrLink(group, evnt)); } template -auto Pulse(const Signal& signal, const Event& evnt) -> Event - { return Pulse(signal.GetGroup(), signal, evnt); } +auto Pulse(const State& state, const Event& evnt) -> Event + { return Pulse(state.GetGroup(), state, evnt); } /******************************************/ REACT_END /******************************************/ diff --git a/include/react/Event.h b/include/react/Event.h index ff4a0a17..903d3dc6 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -51,14 +51,14 @@ class Event : protected REACT_IMPL::EventInternals // Construct with explicit group template - Event(const Group& group, F&& func, const Event& dep, const Signal& ... signals) : - Event::Event( CreateSyncedProcessingNode(group, std::forward(func), dep, signals ...) ) + Event(const Group& group, F&& func, const Event& dep, const State& ... states) : + Event::Event( CreateSyncedProcessingNode(group, std::forward(func), dep, states ...) ) { } // Construct with implicit group template - Event(F&& func, const Event& dep, const Signal& ... signals) : - Event::Event( CreateSyncedProcessingNode(dep.GetGroup(), std::forward(func), dep, signals ...) ) + Event(F&& func, const Event& dep, const State& ... states) : + Event::Event( CreateSyncedProcessingNode(dep.GetGroup(), std::forward(func), dep, states ...) ) { } auto Tokenize() const -> decltype(auto) @@ -84,7 +84,7 @@ class Event : protected REACT_IMPL::EventInternals protected: // Private node ctor - explicit Event(std::shared_ptr>&& nodePtr) : + explicit Event(std::shared_ptr>&& nodePtr) : Event::EventInternals( std::move(nodePtr) ) { } @@ -99,7 +99,7 @@ class Event : protected REACT_IMPL::EventInternals } template - auto CreateSyncedProcessingNode(const Group& group, F&& func, const Event& dep, const Signal& ... syncs) -> decltype(auto) + auto CreateSyncedProcessingNode(const Group& group, F&& func, const Event& dep, const State& ... syncs) -> decltype(auto) { using REACT_IMPL::SyncedEventProcessingNode; using REACT_IMPL::SameGroupOrLink; @@ -166,7 +166,7 @@ class EventSource : public Event NodeId nodeId = castedPtr->GetNodeId(); auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); - graphPtr->AddInput(nodeId, [castedPtr, &value] { castedPtr->EmitValue(std::forward(value)); }); + graphPtr->PushInput(nodeId, [castedPtr, &value] { castedPtr->EmitValue(std::forward(value)); }); } }; @@ -215,7 +215,7 @@ class EventSlot : public Event NodeId nodeId = castedPtr->GetInputNodeId(); auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); - graphPtr->AddInput(nodeId, [this, castedPtr, &input] { castedPtr->AddInput(SameGroupOrLink(GetGroup(), input)); }); + graphPtr->PushInput(nodeId, [this, castedPtr, &input] { castedPtr->AddInput(SameGroupOrLink(GetGroup(), input)); }); } void RemoveInput(const Event& input) @@ -228,7 +228,7 @@ class EventSlot : public Event NodeId nodeId = castedPtr->GetInputNodeId(); auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); - graphPtr->AddInput(nodeId, [this, castedPtr, &input] { castedPtr->RemoveInput(SameGroupOrLink(GetGroup(), input)); }); + graphPtr->PushInput(nodeId, [this, castedPtr, &input] { castedPtr->RemoveInput(SameGroupOrLink(GetGroup(), input)); }); } void RemoveAllInputs() @@ -241,7 +241,7 @@ class EventSlot : public Event NodeId nodeId = castedPtr->GetInputNodeId(); auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); - graphPtr->AddInput(nodeId, [castedPtr] { castedPtr->RemoveAllInputs(); }); + graphPtr->PushInput(nodeId, [castedPtr] { castedPtr->RemoveAllInputs(); }); } }; @@ -309,8 +309,8 @@ auto Merge(const Event& dep1, const Event& ... deps) -> decltype(auto) template auto Filter(const Group& group, F&& pred, const Event& dep) -> Event { - auto filterFunc = [capturedPred = std::forward(pred)] (EventRange inRange, EventSink out) - { std::copy_if(inRange.begin(), inRange.end(), out, capturedPred); }; + auto filterFunc = [capturedPred = std::forward(pred)] (const EventValueList& evts, EventValueSink out) + { std::copy_if(evts.begin(), evts.end(), out, capturedPred); }; return Event(group, std::move(filterFunc), dep); } @@ -320,20 +320,20 @@ auto Filter(F&& pred, const Event& dep) -> Event { return Filter(dep.GetGroup(), std::forward(pred), dep); } template -auto Filter(const Group& group, F&& pred, const Event& dep, const Signal& ... signals) -> Event +auto Filter(const Group& group, F&& pred, const Event& dep, const State& ... states) -> Event { - auto filterFunc = [capturedPred = std::forward(pred)] (EventRange inRange, EventSink out, const Us& ... values) + auto filterFunc = [capturedPred = std::forward(pred)] (const EventValueList& evts, EventValueSink out, const Us& ... values) { - for (const auto& v : inRange) + for (const auto& v : evts) if (capturedPred(v, values ...)) *out++ = v; }; - return Event(group, std::move(filterFunc), dep, signals ...); + return Event(group, std::move(filterFunc), dep, State ...); } template -auto Filter(F&& pred, const Event& dep, const Signal& ... signals) -> Event +auto Filter(F&& pred, const Event& dep, const State& ... states) -> Event { return Filter(dep.GetGroup(), std::forward(pred), dep); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -353,7 +353,7 @@ auto Transform(F&& op, const Event& dep) -> Event { return Transform(dep.GetGroup(), std::forward(op), dep); } template -auto Transform(const Group& group, F&& op, const Event& dep, const Signal& ... signals) -> Event +auto Transform(const Group& group, F&& op, const Event& dep, const State& ... states) -> Event { auto transformFunc = [capturedOp = std::forward(pred)] (EventRange inRange, EventSink out, const Vs& ... values) { @@ -361,12 +361,12 @@ auto Transform(const Group& group, F&& op, const Event& dep, const Signal *out++ = capturedPred(v, values ...); }; - return Event(group, std::move(transformFunc), dep, signals ...); + return Event(group, std::move(transformFunc), dep, states ...); } template -auto Transform(F&& op, const Event& dep, const Signal& ... signals) -> Event - { return Transform(dep.GetGroup(), std::forward(op), dep, signals ...); } +auto Transform(F&& op, const Event& dep, const State& ... states) -> Event + { return Transform(dep.GetGroup(), std::forward(op), dep, states ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Join diff --git a/include/react/Observer.h b/include/react/Observer.h index 1f28136d..3f228ffd 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -18,39 +18,6 @@ #include "react/detail/observer_nodes.h" -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -class ObserverInternals -{ -public: - ObserverInternals(const ObserverInternals&) = default; - ObserverInternals& operator=(const ObserverInternals&) = default; - - ObserverInternals(ObserverInternals&&) = default; - ObserverInternals& operator=(ObserverInternals&&) = default; - - explicit ObserverInternals(std::shared_ptr&& nodePtr) : - nodePtr_( std::move(nodePtr) ) - { } - - auto GetNodePtr() -> std::shared_ptr& - { return nodePtr_; } - - auto GetNodePtr() const -> const std::shared_ptr& - { return nodePtr_; } - - NodeId GetNodeId() const - { return nodePtr_->GetNodeId(); } - -protected: - ObserverInternals() = default; - -private: - std::shared_ptr nodePtr_; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -68,16 +35,16 @@ class Observer : protected REACT_IMPL::ObserverInternals Observer(Observer&&) = default; Observer& operator=(Observer&&) = default; - // Construct signal observer with explicit group + // Construct state observer with explicit group template - Observer(const Group& group, F&& func, const Signal& subject1, const Signal& ... subjects) : - Observer::Observer( CreateSignalObserverNode(group, std::forward(func), subject1, subjects ...)) + Observer(const Group& group, F&& func, const State& subject1, const State& ... subjects) : + Observer::Observer( CreateStateObserverNode(group, std::forward(func), subject1, subjects ...)) { } - // Construct signal observer with implicit group + // Construct state observer with implicit group template - Observer(F&& func, const Signal& subject1, const Signal& ... subjects) : - Observer::Observer( CreateSignalObserverNode(subject1.GetGroup(), std::forward(func), subject1, subjects ...)) + Observer(F&& func, const State& subject1, const State& ... subjects) : + Observer::Observer( CreateStateObserverNode(subject1.GetGroup(), std::forward(func), subject1, subjects ...)) { } // Construct event observer with explicit group @@ -94,14 +61,14 @@ class Observer : protected REACT_IMPL::ObserverInternals // Constructed synced event observer with explicit group template - Observer(const Group& group, F&& func, const Event& subject, const Signal& ... signals) : - Observer::Observer( CreateSyncedEventObserverNode(group, std::forward(func), subject, signals ...)) + Observer(const Group& group, F&& func, const Event& subject, const State& ... states) : + Observer::Observer( CreateSyncedEventObserverNode(group, std::forward(func), subject, states ...)) { } // Constructed synced event observer with implicit group template - Observer(F&& func, const Event& subject, const Signal& ... signals) : - Observer::Observer( CreateSyncedEventObserverNode(subject.GetGroup(), std::forward(func), subject, signals ...)) + Observer(F&& func, const Event& subject, const State& ... states) : + Observer::Observer( CreateSyncedEventObserverNode(subject.GetGroup(), std::forward(func), subject, states ...)) { } public: //Internal @@ -112,10 +79,10 @@ class Observer : protected REACT_IMPL::ObserverInternals protected: template - auto CreateSignalObserverNode(const Group& group, F&& func, const Signal& dep1, const Signal& ... deps) -> decltype(auto) + auto CreateStateObserverNode(const Group& group, F&& func, const State& dep1, const State& ... deps) -> decltype(auto) { - using REACT_IMPL::SignalObserverNode; - return std::make_shared::type, T1, Ts ...>>( + using REACT_IMPL::StateObserverNode; + return std::make_shared::type, T1, Ts ...>>( group, std::forward(func), SameGroupOrLink(group, dep1), SameGroupOrLink(group, deps) ...); } @@ -128,7 +95,7 @@ class Observer : protected REACT_IMPL::ObserverInternals } template - auto CreateSyncedEventObserverNode(const Group& group, F&& func, const Event& dep, const Signal& ... syncs) -> decltype(auto) + auto CreateSyncedEventObserverNode(const Group& group, F&& func, const Event& dep, const State& ... syncs) -> decltype(auto) { using REACT_IMPL::SyncedEventObserverNode; return std::make_shared::type, T, Us ...>>( diff --git a/include/react/common/slotmap.h b/include/react/common/slotmap.h index c0282ec0..b5b51e21 100644 --- a/include/react/common/slotmap.h +++ b/include/react/common/slotmap.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/include/react/common/syncpoint.h b/include/react/common/syncpoint.h index 7ca5586b..b4fc73f8 100644 --- a/include/react/common/syncpoint.h +++ b/include/react/common/syncpoint.h @@ -11,8 +11,10 @@ #include "react/detail/defs.h" +#include #include #include +#include #include @@ -23,20 +25,23 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// class SyncPoint { -private: +public: class Dependency; - class ISyncPointState +private: + + + class ISyncTarget { public: - virtual ~ISyncPointState() = default; + virtual ~ISyncTarget() = default; virtual void IncrementWaitCount() = 0; virtual void DecrementWaitCount() = 0; }; - class SyncPointState : public ISyncPointState + class SyncPointState : public ISyncTarget { public: virtual void IncrementWaitCount() override @@ -81,14 +86,27 @@ class SyncPoint int waitCount_ = 0; }; - class SyncPointStateCollection : public ISyncPointState + class SyncTargetCollection : public ISyncTarget { - private: - std::vector + public: + virtual void IncrementWaitCount() override + { + for (const auto& e : targets) + e->IncrementWaitCount(); + } + + virtual void DecrementWaitCount() override + { + for (const auto& e : targets) + e->DecrementWaitCount(); + } + + std::vector> targets; }; public: - SyncPoint() : state_( std::make_shared() ) + SyncPoint() : + state_( std::make_shared() ) { } SyncPoint(const SyncPoint&) = default; @@ -123,67 +141,90 @@ class SyncPoint // Construct from single sync point. explicit Dependency(const SyncPoint& sp) : - state_( sp.state_ ) + target_( sp.state_ ) { - state_->IncrementWaitCount(); + target_->IncrementWaitCount(); } // Construct from vector of other dependencies. - explicit Dependency(std::vector&& others) : - state_( sp.state_ ) + template + Dependency(TBegin first, TEnd last) { - state_->IncrementWaitCount(); + auto count = std::distance(first, last); + + if (count == 1) + { + target_ = first->target_; + if (target_) + target_->IncrementWaitCount(); + } + else if (count > 1) + { + auto collection = std::make_shared(); + collection->targets.reserve(count); + + for (; !(first == last); ++first) + if (first->target_) // There's no point in propagating released/empty dependencies. + collection->targets.push_back(first->target_); + + collection->IncrementWaitCount(); + + target_ = std::move(collection); + } } Dependency(const Dependency& other) : - state_( other.state_ ) + target_( other.target_ ) { - state_->IncrementWaitCount(); + if (target_) + target_->IncrementWaitCount(); } Dependency& operator=(const Dependency& other) { - if (other.state_) - state_->IncrementWaitCount(); + if (other.target_) + other.target_->IncrementWaitCount(); - if (state_) - state_->DecrementWaitCount(); + if (target_) + target_->DecrementWaitCount(); - state_ = other.state_; + target_ = other.target_; + return *this; } Dependency(Dependency&& other) : - state_( std::move(other.state_) ) + target_( std::move(other.target_) ) { } Dependency& operator=(Dependency&& other) { - if (state_) - state_->DecrementWaitCount(); + if (target_) + target_->DecrementWaitCount(); - state_ = std::move(other.state_); + target_ = std::move(other.target_); + return *this; } ~Dependency() { - if (state_) - state_->DecrementWaitCount(); + if (target_) + target_->DecrementWaitCount(); } void Release() { - if (state_) + if (target_) { - state_->DecrementWaitCount(); - state_.reset(); + target_->DecrementWaitCount(); + target_ = nullptr; } } bool IsReleased() const - { return state_ == nullptr; } + { return target_ == nullptr; } private: - std::shared_ptr state_; + std::shared_ptr target_; }; private: diff --git a/include/react/common/utility.h b/include/react/common/utility.h index d1ce975d..8152ca3f 100644 --- a/include/react/common/utility.h +++ b/include/react/common/utility.h @@ -17,6 +17,34 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ +template +struct Apply +{ + template + static auto apply(F&& f, T&& t, A&& ... a) -> decltype(auto) + { + return Apply::apply(std::forward(f), std::forward(t), std::get( + std::forward(t)), std::forward(a)...); + } +}; + +template<> +struct Apply<0> +{ + template + static auto apply(F&& f, T&&, A&& ... a) -> decltype(auto) + { + return std::forward(f)(std::forward(a) ...); + } +}; + +template +inline auto apply(F&& f, T&& t) -> decltype(auto) +{ + return Apply::type>::value>::apply( + std::forward(f), std::forward(t)); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Helper to enable calling a function on each element of an argument pack. /// We can't do f(args) ...; because ... expands with a comma. diff --git a/include/react/detail/algorithm_nodes.h b/include/react/detail/algorithm_nodes.h index 94cdde7e..b0a7d9da 100644 --- a/include/react/detail/algorithm_nodes.h +++ b/include/react/detail/algorithm_nodes.h @@ -14,8 +14,8 @@ #include #include +#include "state_nodes.h" #include "event_nodes.h" -#include "signal_nodes.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -23,12 +23,12 @@ /// IterateNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class IterateNode : public SignalNode +class IterateNode : public StateNode { public: template IterateNode(const Group& group, T&& init, FIn&& func, const Event& evnt) : - IterateNode::SignalNode( group, std::forward(init) ), + IterateNode::StateNode( group, std::forward(init) ), func_( std::forward(func) ), evnt_( evnt ) { @@ -66,12 +66,12 @@ class IterateNode : public SignalNode /// IterateByRefNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class IterateByRefNode : public SignalNode +class IterateByRefNode : public StateNode { public: template IterateByRefNode(const Group& group, T&& init, FIn&& func, const Event& evnt) : - IterateByRefNode::SignalNode( group, std::forward(init) ), + IterateByRefNode::StateNode( group, std::forward(init) ), func_( std::forward(func) ), evnt_( evnt ) { @@ -102,12 +102,12 @@ class IterateByRefNode : public SignalNode /// SyncedIterateNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class SyncedIterateNode : public SignalNode +class SyncedIterateNode : public StateNode { public: template - SyncedIterateNode(const Group& group, T&& init, FIn&& func, const Event& evnt, const Signal& ... syncs) : - SyncedIterateNode::SignalNode( group, std::forward(init) ), + SyncedIterateNode(const Group& group, T&& init, FIn&& func, const Event& evnt, const State& ... syncs) : + SyncedIterateNode::StateNode( group, std::forward(init) ), func_( std::forward(func) ), evnt_( evnt ), syncHolder_( syncs ... ) @@ -119,7 +119,7 @@ class SyncedIterateNode : public SignalNode ~SyncedIterateNode() { - std::apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(syncs).GetNodeId())); }, syncHolder_); + apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(syncs).GetNodeId())); }, syncHolder_); this->DetachFromMe(GetInternals(evnt_).GetNodeId()); this->UnregisterMe(); } @@ -130,7 +130,7 @@ class SyncedIterateNode : public SignalNode if (GetInternals(evnt_).Events().empty()) return UpdateResult::unchanged; - S newValue = std::apply( + S newValue = apply( [this] (const auto& ... syncs) { return func_(EventRange( GetInternals(evnt_).Events() ), this->Value(), GetInternals(syncs).Value() ...); @@ -152,19 +152,19 @@ class SyncedIterateNode : public SignalNode F func_; Event evnt_; - std::tuple ...> syncHolder_; + std::tuple ...> syncHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// SyncedIterateByRefNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class SyncedIterateByRefNode : public SignalNode +class SyncedIterateByRefNode : public StateNode { public: template - SyncedIterateByRefNode(const Group& group, T&& init, FIn&& func, const Event& evnt, const Signal& ... syncs) : - SyncedIterateByRefNode::SignalNode( group, std::forward(init) ), + SyncedIterateByRefNode(const Group& group, T&& init, FIn&& func, const Event& evnt, const State& ... syncs) : + SyncedIterateByRefNode::StateNode( group, std::forward(init) ), func_( std::forward(func) ), evnt_( evnt ), syncHolder_( syncs ... ) @@ -176,7 +176,7 @@ class SyncedIterateByRefNode : public SignalNode ~SyncedIterateByRefNode() { - std::apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(syncs).GetNodeId())); }, syncHolder_); + apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(syncs).GetNodeId())); }, syncHolder_); this->DetachFromMe(GetInternals(evnt_).GetNodeId()); this->UnregisterMe(); } @@ -187,7 +187,7 @@ class SyncedIterateByRefNode : public SignalNode if (GetInternals(evnt_).Events().empty()) return UpdateResult::unchanged; - std::apply( + apply( [this] (const auto& ... args) { func_(EventRange( GetInternals(evnt_).Events() ), this->Value(), GetInternals(args).Value() ...); @@ -201,19 +201,19 @@ class SyncedIterateByRefNode : public SignalNode F func_; Event events_; - std::tuple ...> syncHolder_; + std::tuple ...> syncHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// HoldNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class HoldNode : public SignalNode +class HoldNode : public StateNode { public: template HoldNode(const Group& group, T&& init, const Event& evnt) : - HoldNode::SignalNode( group, std::forward(init) ), + HoldNode::StateNode( group, std::forward(init) ), evnt_( evnt ) { this->RegisterMe(); @@ -255,11 +255,11 @@ class HoldNode : public SignalNode /// SnapshotNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class SnapshotNode : public SignalNode +class SnapshotNode : public StateNode { public: - SnapshotNode(const Group& group, const Signal& target, const Event& trigger) : - SnapshotNode::SignalNode( group, GetInternals(target).Value() ), + SnapshotNode(const Group& group, const State& target, const Event& trigger) : + SnapshotNode::StateNode( group, GetInternals(target).Value() ), target_( target ), trigger_( trigger ) { @@ -297,7 +297,7 @@ class SnapshotNode : public SignalNode } private: - Signal target_; + State target_; Event trigger_; }; @@ -308,7 +308,7 @@ template class MonitorNode : public EventStreamNode { public: - MonitorNode(const Group& group, const Signal& target) : + MonitorNode(const Group& group, const State& target) : MonitorNode::EventStreamNode( group ), target_( target ) { @@ -330,7 +330,7 @@ class MonitorNode : public EventStreamNode } private: - Signal target_; + State target_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -340,7 +340,7 @@ template class PulseNode : public EventStreamNode { public: - PulseNode(const Group& group, const Signal& target, const Event& trigger) : + PulseNode(const Group& group, const State& target, const Event& trigger) : PulseNode::EventStreamNode( group ), target_( target ), trigger_( trigger ) @@ -373,7 +373,7 @@ class PulseNode : public EventStreamNode } private: - Signal target_; + State target_; Event trigger_; }; diff --git a/include/react/detail/event_nodes.h b/include/react/detail/event_nodes.h index 1a2e1cdf..f422a903 100644 --- a/include/react/detail/event_nodes.h +++ b/include/react/detail/event_nodes.h @@ -20,7 +20,7 @@ //#include "tbb/spin_mutex.h" #include "node_base.h" -#include "react/common/Types.h" +#include "react/common/utility.h" /*****************************************/ REACT_BEGIN /*****************************************/ @@ -41,23 +41,23 @@ using EventValueSink = std::back_insert_iterator>; /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// template -class SignalNode; +class StateNode; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventStreamNode +/// EventNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class EventStreamNode : public NodeBase +class EventNode : public NodeBase { public: - EventStreamNode(EventStreamNode&&) = default; - EventStreamNode& operator=(EventStreamNode&&) = default; + EventNode(EventNode&&) = default; + EventNode& operator=(EventNode&&) = default; - EventStreamNode(const EventStreamNode&) = delete; - EventStreamNode& operator=(const EventStreamNode&) = delete; + EventNode(const EventNode&) = delete; + EventNode& operator=(const EventNode&) = delete; - explicit EventStreamNode(const Group& group) : - EventStreamNode::NodeBase( group ) + explicit EventNode(const Group& group) : + EventNode::NodeBase( group ) { } EventValueList& Events() @@ -77,11 +77,11 @@ class EventStreamNode : public NodeBase /// EventSourceNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class EventSourceNode : public EventStreamNode +class EventSourceNode : public EventNode { public: EventSourceNode(const Group& group) : - EventSourceNode::EventStreamNode( group ) + EventSourceNode::EventNode( group ) { this->RegisterMe(NodeCategory::input); } @@ -108,11 +108,11 @@ class EventSourceNode : public EventStreamNode /// EventMergeNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class EventMergeNode : public EventStreamNode +class EventMergeNode : public EventNode { public: EventMergeNode(const Group& group, const Event& ... deps) : - EventMergeNode::EventStreamNode( group ), + EventMergeNode::EventNode( group ), inputs_( deps ... ) { this->RegisterMe(); @@ -121,14 +121,14 @@ class EventMergeNode : public EventStreamNode ~EventMergeNode() { - std::apply([this] (const auto& ... deps) + apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(deps).GetNodeId())); }, depHolder_); this->UnregisterMe(); } virtual UpdateResult Update(TurnId turnId) noexcept override { - std::apply([this] (auto& ... deps) + apply([this] (auto& ... deps) { REACT_EXPAND_PACK(MergeFromDep(deps)); }, depHolder_); if (! this->Events().empty()) @@ -152,11 +152,11 @@ class EventMergeNode : public EventStreamNode /// EventSlotNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class EventSlotNode : public EventStreamNode +class EventSlotNode : public EventNode { public: EventSlotNode(const Group& group) : - EventSlotNode::EventStreamNode( group ) + EventSlotNode::EventNode( group ) { inputNodeId_ = GetGraphPtr()->RegisterNode(&slotInput_, NodeCategory::dyninput); this->RegisterMe(); @@ -235,12 +235,12 @@ class EventSlotNode : public EventStreamNode /// EventProcessingNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class EventProcessingNode : public EventStreamNode +class EventProcessingNode : public EventNode { public: template EventProcessingNode(const Group& group, FIn&& func, const Event& dep) : - EventProcessingNode::EventStreamNode( group ), + EventProcessingNode::EventNode( group ), func_( std::forward(func) ), dep_( dep ) { @@ -256,7 +256,7 @@ class EventProcessingNode : public EventStreamNode virtual UpdateResult Update(TurnId turnId) noexcept override { - func_(EventRange( GetInternals(dep_).Events() ), std::back_inserter(this->Events())); + func_(GetInternals(dep_).Events(), std::back_inserter(this->Events())); if (! this->Events().empty()) return UpdateResult::changed; @@ -274,12 +274,12 @@ class EventProcessingNode : public EventStreamNode /// SyncedEventProcessingNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class SyncedEventProcessingNode : public EventStreamNode +class SyncedEventProcessingNode : public EventNode { public: template - SyncedEventProcessingNode(const Group& group, FIn&& func, const Event& dep, const Signal& ... syncs) : - SyncedEventProcessingNode::EventStreamNode( group ), + SyncedEventProcessingNode(const Group& group, FIn&& func, const Event& dep, const State& ... syncs) : + SyncedEventProcessingNode::EventNode( group ), func_( std::forward(func) ), dep_( dep ), syncHolder_( syncs ... ) @@ -291,7 +291,7 @@ class SyncedEventProcessingNode : public EventStreamNode ~SyncedEventProcessingNode() { - std::apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(syncs->GetNodeId())); }, syncHolder_); + apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(syncs->GetNodeId())); }, syncHolder_); this->DetachFromMe(dep_->GetNodeId()); this->UnregisterMe(); } @@ -302,7 +302,7 @@ class SyncedEventProcessingNode : public EventStreamNode if (dep_->Events().empty()) return UpdateResult::unchanged; - std::apply( + apply( [this] (const auto& ... syncs) { func_(EventRange( this->dep_->Events() ), std::back_inserter(this->Events()), syncs->Value() ...); @@ -320,18 +320,18 @@ class SyncedEventProcessingNode : public EventStreamNode Event dep_; - std::tuple ...> syncHolder_; + std::tuple ...> syncHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventJoinNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class EventJoinNode : public EventStreamNode> +class EventJoinNode : public EventNode> { public: EventJoinNode(const Group& group, const Event& ... deps) : - EventJoinNode::EventStreamNode( group ), + EventJoinNode::EventNode( group ), slots_( deps ... ) { this->RegisterMe(); @@ -340,21 +340,21 @@ class EventJoinNode : public EventStreamNode> ~EventJoinNode() { - std::apply([this] (const auto& ... slots) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(slots.source).GetNodeId())); }, slots_); + apply([this] (const auto& ... slots) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(slots.source).GetNodeId())); }, slots_); this->UnregisterMe(); } virtual UpdateResult Update(TurnId turnId) noexcept override { // Move events into buffers. - std::apply([this, turnId] (Slot& ... slots) { REACT_EXPAND_PACK(FetchBuffer(turnId, slots)); }, slots_); + apply([this, turnId] (Slot& ... slots) { REACT_EXPAND_PACK(FetchBuffer(turnId, slots)); }, slots_); while (true) { bool isReady = true; // All slots ready? - std::apply( + apply( [this, &isReady] (Slot& ... slots) { // Todo: combine return values instead @@ -366,7 +366,7 @@ class EventJoinNode : public EventStreamNode> break; // Pop values from buffers and emit tuple. - std::apply( + apply( [this] (Slot& ... slots) { this->Events().emplace_back(slots.buffer.front() ...); @@ -419,11 +419,11 @@ class EventJoinNode : public EventStreamNode> /// EventLinkNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class EventLinkNode : public EventStreamNode +class EventLinkNode : public EventNode { public: EventLinkNode(const Group& group, const Event& dep) : - EventLinkNode::EventStreamNode( group ), + EventLinkNode::EventNode( group ), linkOutput_( dep ) { this->RegisterMe(NodeCategory::input); @@ -480,7 +480,7 @@ class EventLinkNode : public EventStreamNode NodeId nodeId = storedParent->GetNodeId(); auto& graphPtr = storedParent->GetGraphPtr(); - graphPtr->AddInput(nodeId, + graphPtr->PushInput(nodeId, [&storedParent, &storedEvents] { storedParent->SetEvents(std::move(storedEvents)); @@ -512,14 +512,14 @@ class EventInternals EventInternals(EventInternals&&) = default; EventInternals& operator=(EventInternals&&) = default; - explicit EventInternals(std::shared_ptr>&& nodePtr) : + explicit EventInternals(std::shared_ptr>&& nodePtr) : nodePtr_( std::move(nodePtr) ) { } - auto GetNodePtr() -> std::shared_ptr>& + auto GetNodePtr() -> std::shared_ptr>& { return nodePtr_; } - auto GetNodePtr() const -> const std::shared_ptr>& + auto GetNodePtr() const -> const std::shared_ptr>& { return nodePtr_; } NodeId GetNodeId() const @@ -532,7 +532,7 @@ class EventInternals { return nodePtr_->Events(); } private: - std::shared_ptr> nodePtr_; + std::shared_ptr> nodePtr_; }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/graph_impl.h b/include/react/detail/graph_impl.h index 133ffbe6..42c04789 100644 --- a/include/react/detail/graph_impl.h +++ b/include/react/detail/graph_impl.h @@ -18,7 +18,6 @@ #include #include #include -#include #include #include @@ -39,16 +38,10 @@ class TransactionQueue graph_( graph ) { } - TransactionQueue(const TransactionQueue&) = delete; - TransactionQueue& operator=(const TransactionQueue&) = delete; - - TransactionQueue(TransactionQueue&&) = default; - TransactionQueue& operator =(TransactionQueue&&) = default; - template void Push(F&& func, SyncPoint::Dependency dep, TransactionFlags flags) { - transactions_.push(StoredTransaction{ std::forward(transaction), std::move(dep), flags }); + transactions_.push(StoredTransaction{ std::forward(func), std::move(dep), flags }); if (count_.fetch_add(1, std::memory_order_release) == 0) tbb::task::enqueue(*new(tbb::task::allocate_root()) WorkerTask(*this)); @@ -57,9 +50,9 @@ class TransactionQueue private: struct StoredTransaction { - std::function func; - SyncPoint::DependencyList deps; - TransactionFlags flags; + std::function func; + SyncPoint::Dependency dep; + TransactionFlags flags; }; class WorkerTask : public tbb::task @@ -67,7 +60,7 @@ class TransactionQueue public: WorkerTask(TransactionQueue& parent) : parent_( parent ) - { } + { } tbb::task* execute() { @@ -104,13 +97,15 @@ class ReactGraph template void PushInput(NodeId nodeId, F&& inputCallback); - void PushDependency(SyncPoint::Dependency dep); + void AddSyncPointDependency(SyncPoint::Dependency dep, bool syncLinked); + + void AllowLinkedTransactionMerging(bool allowMerging); template void DoTransaction(F&& transactionCallback); template - void EnqueueTransaction(F&& func, SyncPoint::DependencyList&& deps, TransactionFlags flags); + void EnqueueTransaction(F&& func, SyncPoint::Dependency dep, TransactionFlags flags); LinkCache& GetLinkCache() { return linkCache_; } @@ -173,18 +168,20 @@ class ReactGraph SlotMap nodeData_; - TopoQueue scheduledNodes_; + TopoQueue scheduledNodes_; std::vector changedInputs_; std::vector changedNodes_; - std::vector curDependencies_; + LinkOutputMap scheduledLinkOutputs_; - LinkOutputMap scheduledLinkOutputs_; + std::vector localDependencies_; + std::vector linkDependencies_; - LinkCache linkCache_; + LinkCache linkCache_; bool isTransactionActive_ = false; + bool allowLinkedTransactionMerging_ = false; }; template @@ -214,9 +211,9 @@ void ReactGraph::DoTransaction(F&& transactionCallback) } template -void ReactGraph::EnqueueTransaction(F&& func, SyncPoint::DependencyList&& deps, TransactionFlags flags) +void ReactGraph::EnqueueTransaction(F&& func, SyncPoint::Dependency dep, TransactionFlags flags) { - transactionQueue_.Push(std::forward(func), std::move(deps), flags); + transactionQueue_.Push(std::forward(func), std::move(dep), flags); } /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/observer_nodes.h b/include/react/detail/observer_nodes.h index 35c160b1..9634c89c 100644 --- a/include/react/detail/observer_nodes.h +++ b/include/react/detail/observer_nodes.h @@ -11,9 +11,11 @@ #include "react/detail/defs.h" #include "react/api.h" +#include "react/common/utility.h" #include #include +#include #include "node_base.h" @@ -23,13 +25,13 @@ /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// template -class SignalNode; +class StateNode; template class EventStreamNode; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalObserverNode +/// StateObserverNode /////////////////////////////////////////////////////////////////////////////////////////////////// class ObserverNode : public NodeBase { @@ -40,39 +42,43 @@ class ObserverNode : public NodeBase }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalObserverNode +/// StateObserverNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class SignalObserverNode : public ObserverNode +class StateObserverNode : public ObserverNode { public: template - SignalObserverNode(const Group& group, FIn&& func, const Signal& ... deps) : - SignalObserverNode::ObserverNode( group ), + StateObserverNode(const Group& group, FIn&& func, const State& ... deps) : + StateObserverNode::ObserverNode( group ), func_( std::forward(func) ), depHolder_( deps ... ) { this->RegisterMe(NodeCategory::output); REACT_EXPAND_PACK(this->AttachToMe(GetInternals(deps).GetNodeId())); + + apply([this] (const auto& ... deps) + { this->func_(GetInternals(deps).Value() ...); }, depHolder_); } - ~SignalObserverNode() + ~StateObserverNode() { - std::apply([this] (const auto& ... deps) + apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(deps).GetNodeId())); }, depHolder_); this->UnregisterMe(); } virtual UpdateResult Update(TurnId turnId) noexcept override { - std::apply([this] (const auto& ... deps) { this->func_(GetInternals(deps).Value() ...); }, depHolder_); + apply([this] (const auto& ... deps) + { this->func_(GetInternals(deps).Value() ...); }, depHolder_); return UpdateResult::unchanged; } private: F func_; - std::tuple ...> depHolder_; + std::tuple ...> depHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -118,7 +124,7 @@ class SyncedEventObserverNode : public ObserverNode { public: template - SyncedEventObserverNode(const Group& group, FIn&& func, const Event& subject, const Signal& ... syncs) : + SyncedEventObserverNode(const Group& group, FIn&& func, const Event& subject, const State& ... syncs) : SyncedEventObserverNode::ObserverNode( group ), func_( std::forward(func) ), subject_( subject ), @@ -131,7 +137,7 @@ class SyncedEventObserverNode : public ObserverNode ~SyncedEventObserverNode() { - std::apply([this] (const auto& ... syncs) + apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(syncs).GetNodeId())); }, syncHolder_); this->DetachFromMe(GetInternals(subject_).GetNodeId()); this->UnregisterMe(); @@ -143,7 +149,7 @@ class SyncedEventObserverNode : public ObserverNode if (GetInternals(this->subject_).Events().empty()) return UpdateResult::unchanged; - std::apply([this] (const auto& ... syncs) + apply([this] (const auto& ... syncs) { func_(EventRange( GetInternals(this->subject_).Events() ), GetInternals(syncs).Value() ...); }, syncHolder_); return UpdateResult::unchanged; @@ -154,7 +160,39 @@ class SyncedEventObserverNode : public ObserverNode Event subject_; - std::tuple ...> syncHolder_; + std::tuple ...> syncHolder_; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ObserverInternals +/////////////////////////////////////////////////////////////////////////////////////////////////// +class ObserverInternals +{ +public: + ObserverInternals(const ObserverInternals&) = default; + ObserverInternals& operator=(const ObserverInternals&) = default; + + ObserverInternals(ObserverInternals&&) = default; + ObserverInternals& operator=(ObserverInternals&&) = default; + + explicit ObserverInternals(std::shared_ptr&& nodePtr) : + nodePtr_( std::move(nodePtr) ) + { } + + auto GetNodePtr() -> std::shared_ptr& + { return nodePtr_; } + + auto GetNodePtr() const -> const std::shared_ptr& + { return nodePtr_; } + + NodeId GetNodeId() const + { return nodePtr_->GetNodeId(); } + +protected: + ObserverInternals() = default; + +private: + std::shared_ptr nodePtr_; }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/signal_nodes.h b/include/react/detail/state_nodes.h similarity index 75% rename from include/react/detail/signal_nodes.h rename to include/react/detail/state_nodes.h index f97dc908..655ab542 100644 --- a/include/react/detail/signal_nodes.h +++ b/include/react/detail/state_nodes.h @@ -6,8 +6,8 @@ #pragma once -#ifndef REACT_DETAIL_GRAPH_SIGNALNODES_H_INCLUDED -#define REACT_DETAIL_GRAPH_SIGNALNODES_H_INCLUDED +#ifndef REACT_DETAIL_GRAPH_STATENODES_H_INCLUDED +#define REACT_DETAIL_GRAPH_STATENODES_H_INCLUDED #include "react/detail/defs.h" @@ -27,26 +27,26 @@ template bool Equals(const L& lhs, const R& rhs); /////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalNode +/// StateNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class SignalNode : public NodeBase +class StateNode : public NodeBase { public: - SignalNode(SignalNode&&) = default; - SignalNode& operator=(SignalNode&&) = default; + StateNode(StateNode&&) = default; + StateNode& operator=(StateNode&&) = default; - SignalNode(const SignalNode&) = delete; - SignalNode& operator=(const SignalNode&) = delete; + StateNode(const StateNode&) = delete; + StateNode& operator=(const StateNode&) = delete; - explicit SignalNode(const Group& group) : - SignalNode::NodeBase( group ), + explicit StateNode(const Group& group) : + StateNode::NodeBase( group ), value_( ) { } template - SignalNode(const Group& group, T&& value) : - SignalNode::NodeBase( group ), + StateNode(const Group& group, T&& value) : + StateNode::NodeBase( group ), value_( std::forward(value) ) { } @@ -64,25 +64,25 @@ class SignalNode : public NodeBase /// VarNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class VarSignalNode : public SignalNode +class StateVarNode : public StateNode { public: - explicit VarSignalNode(const Group& group) : - VarSignalNode::SignalNode( group ), + explicit StateVarNode(const Group& group) : + StateVarNode::StateNode( group ), newValue_( ) { this->RegisterMe(NodeCategory::input); } template - VarSignalNode(const Group& group, T&& value) : - VarSignalNode::SignalNode( group, std::forward(value) ), + StateVarNode(const Group& group, T&& value) : + StateVarNode::StateNode( group, std::forward(value) ), newValue_( value ) { this->RegisterMe(); } - ~VarSignalNode() + ~StateVarNode() { this->UnregisterMe(); } @@ -127,7 +127,6 @@ class VarSignalNode : public SignalNode isInputModified_ = false; } - // This is signal-specific template void ModifyValue(F&& func) { @@ -154,15 +153,15 @@ class VarSignalNode : public SignalNode }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalOpNode +/// StateOpNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class SignalFuncNode : public SignalNode +class StateFuncNode : public StateNode { public: template - SignalFuncNode(const Group& group, FIn&& func, const Signal& ... deps) : - SignalFuncNode::SignalNode( group, func(GetInternals(deps).Value() ...) ), + StateFuncNode(const Group& group, FIn&& func, const State& ... deps) : + StateFuncNode::StateNode( group, func(GetInternals(deps).Value() ...) ), func_( std::forward(func) ), depHolder_( deps ... ) { @@ -170,9 +169,9 @@ class SignalFuncNode : public SignalNode REACT_EXPAND_PACK(this->AttachToMe(GetInternals(deps).GetNodeId())); } - ~SignalFuncNode() + ~StateFuncNode() { - std::apply([this] (const auto& ... deps) + apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(deps).GetNodePtr()->GetNodeId())); }, depHolder_); this->UnregisterMe(); } @@ -181,7 +180,7 @@ class SignalFuncNode : public SignalNode { bool changed = false; - S newValue = std::apply([this] (const auto& ... deps) + S newValue = apply([this] (const auto& ... deps) { return this->func_(GetInternals(deps).Value() ...); }, depHolder_); if (! (this->Value() == newValue)) @@ -198,18 +197,18 @@ class SignalFuncNode : public SignalNode private: F func_; - std::tuple ...> depHolder_; + std::tuple ...> depHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalSlotNode +/// StateSlotNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class SignalSlotNode : public SignalNode +class StateSlotNode : public StateNode { public: - SignalSlotNode(const Group& group, const Signal& dep) : - SignalSlotNode::SignalNode( group, GetInternals(dep).Value() ), + StateSlotNode(const Group& group, const State& dep) : + StateSlotNode::StateNode( group, GetInternals(dep).Value() ), input_( dep ) { inputNodeId_ = GetGraphPtr()->RegisterNode(&slotInput_, NodeCategory::dyninput); @@ -219,7 +218,7 @@ class SignalSlotNode : public SignalNode this->AttachToMe(GetInternals(dep).GetNodeId()); } - ~SignalSlotNode() + ~StateSlotNode() { this->DetachFromMe(GetInternals(input_).GetNodeId()); this->DetachFromMe(inputNodeId_); @@ -241,7 +240,7 @@ class SignalSlotNode : public SignalNode } } - void SetInput(const Signal& newInput) + void SetInput(const State& newInput) { if (newInput == input_) return; @@ -262,36 +261,35 @@ class SignalSlotNode : public SignalNode { return UpdateResult::changed; } }; - Signal input_; + State input_; NodeId inputNodeId_; VirtualInputNode slotInput_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalLinkNode +/// StateLinkNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class SignalLinkNode : public SignalNode +class StateLinkNode : public StateNode { public: - SignalLinkNode(const Group& group, const Signal& dep) : - SignalLinkNode::SignalNode( group, GetInternals(dep).Value() ), - dep_ ( dep ) + StateLinkNode(const Group& group, const State& dep) : + StateLinkNode::StateNode( group, GetInternals(dep).Value() ), + dep_ ( dep ), + srcGroup_( dep.GetGroup() ) { this->RegisterMe(NodeCategory::input); - auto& srcGraphPtr = GetInternals(srcGroup).GetGraphPtr(); - outputNodeId_ = srcGraphPtr->RegisterNode(this, NodeCategory::linkoutput); + auto& srcGraphPtr = GetInternals(srcGroup_).GetGraphPtr(); + outputNodeId_ = srcGraphPtr->RegisterNode(&linkOutput_, NodeCategory::linkoutput); srcGraphPtr->AttachNode(outputNodeId_, GetInternals(dep).GetNodeId()); } - ~SignalLinkNode() + ~StateLinkNode() { - this->DetachFromMe(GetInternals(dep_).GetNodeId()); - - auto& srcGraphPtr = GetInternals(srcGroup).GetGraphPtr(); + auto& srcGraphPtr = GetInternals(srcGroup_).GetGraphPtr(); srcGraphPtr->DetachNode(outputNodeId_, GetInternals(dep_).GetNodeId()); srcGraphPtr->UnregisterNode(outputNodeId_); @@ -301,7 +299,7 @@ class SignalLinkNode : public SignalNode this->UnregisterMe(); } - void SetWeakSelfPtr(const std::weak_ptr& self) + void SetWeakSelfPtr(const std::weak_ptr& self) { linkOutput_.parent = self; } virtual UpdateResult Update(TurnId turnId) noexcept override @@ -327,7 +325,7 @@ class SignalLinkNode : public SignalNode NodeId nodeId = storedParent->GetNodeId(); auto& graphPtr = storedParent->GetGraphPtr(); - graphPtr->AddInput(nodeId, + graphPtr->PushInput(nodeId, [&storedParent, &storedValue] { storedParent->SetValue(std::move(storedValue)); @@ -336,37 +334,38 @@ class SignalLinkNode : public SignalNode } } - std::weak_ptr parent; + std::weak_ptr parent; }; - Signal dep_; - Group srcGroup; + State dep_; + Group srcGroup_; NodeId outputNodeId_; + VirtualOutputNode linkOutput_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalInternals +/// StateInternals /////////////////////////////////////////////////////////////////////////////////////////////////// template -class SignalInternals +class StateInternals { public: - SignalInternals(const SignalInternals&) = default; - SignalInternals& operator=(const SignalInternals&) = default; + StateInternals(const StateInternals&) = default; + StateInternals& operator=(const StateInternals&) = default; - SignalInternals(SignalInternals&&) = default; - SignalInternals& operator=(SignalInternals&&) = default; + StateInternals(StateInternals&&) = default; + StateInternals& operator=(StateInternals&&) = default; - explicit SignalInternals(std::shared_ptr>&& nodePtr) : + explicit StateInternals(std::shared_ptr>&& nodePtr) : nodePtr_( std::move(nodePtr) ) { } - auto GetNodePtr() -> std::shared_ptr>& + auto GetNodePtr() -> std::shared_ptr>& { return nodePtr_; } - auto GetNodePtr() const -> const std::shared_ptr>& + auto GetNodePtr() const -> const std::shared_ptr>& { return nodePtr_; } NodeId GetNodeId() const @@ -379,9 +378,9 @@ class SignalInternals { return nodePtr_->Value(); } private: - std::shared_ptr> nodePtr_; + std::shared_ptr> nodePtr_; }; /****************************************/ REACT_IMPL_END /***************************************/ -#endif // REACT_DETAIL_GRAPH_SIGNALNODES_H_INCLUDED \ No newline at end of file +#endif // REACT_DETAIL_GRAPH_STATENODES_H_INCLUDED \ No newline at end of file diff --git a/include/react/Signal.h b/include/react/state.h similarity index 51% rename from include/react/Signal.h rename to include/react/state.h index c7e11e8f..95d80b6c 100644 --- a/include/react/Signal.h +++ b/include/react/state.h @@ -4,8 +4,8 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#ifndef REACT_SIGNAL_H_INCLUDED -#define REACT_SIGNAL_H_INCLUDED +#ifndef REACT_STATE_H_INCLUDED +#define REACT_STATE_H_INCLUDED #pragma once @@ -19,34 +19,34 @@ #include #include -#include "react/detail/signal_nodes.h" +#include "react/detail/state_nodes.h" /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Signal +/// State /////////////////////////////////////////////////////////////////////////////////////////////////// template -class Signal : protected REACT_IMPL::SignalInternals +class State : protected REACT_IMPL::StateInternals { public: - Signal(const Signal&) = default; - Signal& operator=(const Signal&) = default; + State(const State&) = default; + State& operator=(const State&) = default; - Signal(Signal&&) = default; - Signal& operator=(Signal&&) = default; + State(State&&) = default; + State& operator=(State&&) = default; // Construct with explicit group template - explicit Signal(const Group& group, F&& func, const Signal& dep1, const Signal& ... deps) : - Signal::SignalInternals( CreateFuncNode(group, std::forward(func), dep1, deps ...) ) + explicit State(const Group& group, F&& func, const State& dep1, const State& ... deps) : + State::StateInternals( CreateFuncNode(group, std::forward(func), dep1, deps ...) ) { } // Construct with implicit group template - explicit Signal(F&& func, const Signal& dep1, const Signal& ... deps) : - Signal::SignalInternals( CreateFuncNode(dep1.GetGroup(), std::forward(func), dep1, deps ...) ) + explicit State(F&& func, const State& dep1, const State& ... deps) : + State::StateInternals( CreateFuncNode(dep1.GetGroup(), std::forward(func), dep1, deps ...) ) { } auto GetGroup() const -> const Group& @@ -55,31 +55,31 @@ class Signal : protected REACT_IMPL::SignalInternals auto GetGroup() -> Group& { return this->GetNodePtr()->GetGroup(); } - friend bool operator==(const Signal& a, const Signal& b) + friend bool operator==(const State& a, const State& b) { return a.GetNodePtr() == b.GetNodePtr(); } - friend bool operator!=(const Signal& a, const Signal& b) + friend bool operator!=(const State& a, const State& b) { return !(a == b); } - friend auto GetInternals(Signal& s) -> REACT_IMPL::SignalInternals& + friend auto GetInternals(State& s) -> REACT_IMPL::StateInternals& { return s; } - friend auto GetInternals(const Signal& s) -> const REACT_IMPL::SignalInternals& + friend auto GetInternals(const State& s) -> const REACT_IMPL::StateInternals& { return s; } protected: - explicit Signal(std::shared_ptr>&& nodePtr) : - Signal::SignalInternals( std::move(nodePtr) ) + explicit State(std::shared_ptr>&& nodePtr) : + State::StateInternals( std::move(nodePtr) ) { } private: template - auto CreateFuncNode(const Group& group, F&& func, const Signal& dep1, const Signal& ... deps) -> decltype(auto) + auto CreateFuncNode(const Group& group, F&& func, const State& dep1, const State& ... deps) -> decltype(auto) { - using REACT_IMPL::SignalFuncNode; + using REACT_IMPL::StateFuncNode; using REACT_IMPL::SameGroupOrLink; - return std::make_shared::type, T1, Ts ...>>( + return std::make_shared::type, T1, Ts ...>>( group, std::forward(func), SameGroupOrLink(group, dep1), SameGroupOrLink(group, deps) ...); } @@ -88,27 +88,27 @@ class Signal : protected REACT_IMPL::SignalInternals }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// VarSignal +/// StateVar /////////////////////////////////////////////////////////////////////////////////////////////////// template -class VarSignal : public Signal +class StateVar : public State { public: - VarSignal(const VarSignal&) = default; - VarSignal& operator=(const VarSignal&) = default; + StateVar(const StateVar&) = default; + StateVar& operator=(const StateVar&) = default; - VarSignal(VarSignal&&) = default; - VarSignal& operator=(VarSignal&&) = default; + StateVar(StateVar&&) = default; + StateVar& operator=(StateVar&&) = default; // Construct with group + default - explicit VarSignal(const Group& group) : - VarSignal::Signal( CreateVarNode(group) ) + explicit StateVar(const Group& group) : + StateVar::State( CreateVarNode(group) ) { } // Construct with group + value template - VarSignal(const Group& group, T&& value) : - VarSignal::Signal( CreateVarNode(group, std::forward(value)) ) + StateVar(const Group& group, T&& value) : + StateVar::State( CreateVarNode(group, std::forward(value)) ) { } void Set(const S& newValue) @@ -121,140 +121,140 @@ class VarSignal : public Signal void Modify(const F& func) { ModifyValue(func); } - friend bool operator==(const VarSignal& a, VarSignal& b) + friend bool operator==(const StateVar& a, StateVar& b) { return a.GetNodePtr() == b.GetNodePtr(); } - friend bool operator!=(const VarSignal& a, VarSignal& b) + friend bool operator!=(const StateVar& a, StateVar& b) { return !(a == b); } protected: - explicit VarSignal(std::shared_ptr>&& nodePtr) : - VarSignal::Signal( std::move(nodePtr) ) + explicit StateVar(std::shared_ptr>&& nodePtr) : + StateVar::State( std::move(nodePtr) ) { } private: static auto CreateVarNode(const Group& group) -> decltype(auto) { - using REACT_IMPL::VarSignalNode; - return std::make_shared>(group); + using REACT_IMPL::StateVarNode; + return std::make_shared>(group); } template static auto CreateVarNode(const Group& group, T&& value) -> decltype(auto) { - using REACT_IMPL::VarSignalNode; - return std::make_shared>(group, std::forward(value)); + using REACT_IMPL::StateVarNode; + return std::make_shared>(group, std::forward(value)); } template void SetValue(T&& newValue) { using REACT_IMPL::NodeId; - using VarNodeType = REACT_IMPL::VarSignalNode; + using VarNodeType = REACT_IMPL::StateVarNode; VarNodeType* castedPtr = static_cast(this->GetNodePtr().get()); NodeId nodeId = castedPtr->GetNodeId(); auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); - graphPtr->AddInput(nodeId, [castedPtr, &newValue] { castedPtr->SetValue(std::forward(newValue)); }); + graphPtr->PushInput(nodeId, [castedPtr, &newValue] { castedPtr->SetValue(std::forward(newValue)); }); } template void ModifyValue(const F& func) { using REACT_IMPL::NodeId; - using VarNodeType = REACT_IMPL::VarSignalNode; + using VarNodeType = REACT_IMPL::StateVarNode; VarNodeType* castedPtr = static_cast(this->GetNodePtr().get()); NodeId nodeId = castedPtr->GetNodeId(); auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); - graphPtr->AddInput(nodeId, [castedPtr, &func] { castedPtr->ModifyValue(func); }); + graphPtr->PushInput(nodeId, [castedPtr, &func] { castedPtr->ModifyValue(func); }); } }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalSlotBase +/// StateSlotBase /////////////////////////////////////////////////////////////////////////////////////////////////// template -class SignalSlot : public Signal +class StateSlot : public State { public: - SignalSlot(const SignalSlot&) = default; - SignalSlot& operator=(const SignalSlot&) = default; + StateSlot(const StateSlot&) = default; + StateSlot& operator=(const StateSlot&) = default; - SignalSlot(SignalSlot&&) = default; - SignalSlot& operator=(SignalSlot&&) = default; + StateSlot(StateSlot&&) = default; + StateSlot& operator=(StateSlot&&) = default; // Construct with explicit group - SignalSlot(const Group& group, const Signal& input) : - SignalSlot::Signal( CreateSlotNode(group, input) ) + StateSlot(const Group& group, const State& input) : + StateSlot::State( CreateSlotNode(group, input) ) { } // Construct with implicit group - explicit SignalSlot(const Signal& input) : - SignalSlot::Signal( CreateSlotNode(input.GetGroup(), input) ) + explicit StateSlot(const State& input) : + StateSlot::State( CreateSlotNode(input.GetGroup(), input) ) { } - void Set(const Signal& newInput) + void Set(const State& newInput) { SetInput(newInput); } - void operator<<=(const Signal& newInput) + void operator<<=(const State& newInput) { SetInput(newInput); } protected: - explicit SignalSlot(std::shared_ptr>&& nodePtr) : - SignalSlot::Signal( std::move(nodePtr) ) + explicit StateSlot(std::shared_ptr>&& nodePtr) : + StateSlot::State( std::move(nodePtr) ) { } private: - static auto CreateSlotNode(const Group& group, const Signal& input) -> decltype(auto) + static auto CreateSlotNode(const Group& group, const State& input) -> decltype(auto) { - using REACT_IMPL::SignalSlotNode; + using REACT_IMPL::StateSlotNode; using REACT_IMPL::SameGroupOrLink; - return std::make_shared>(group, SameGroupOrLink(group, input)); + return std::make_shared>(group, SameGroupOrLink(group, input)); } - void SetInput(const Signal& newInput) + void SetInput(const State& newInput) { using REACT_IMPL::NodeId; - using REACT_IMPL::SignalSlotNode; + using REACT_IMPL::StateSlotNode; using REACT_IMPL::SameGroupOrLink; - auto* castedPtr = static_cast*>(this->GetNodePtr().get()); + auto* castedPtr = static_cast*>(this->GetNodePtr().get()); NodeId nodeId = castedPtr->GetInputNodeId(); auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); - graphPtr->AddInput(nodeId, [this, castedPtr, &newInput] { castedPtr->SetInput(SameGroupOrLink(GetGroup(), newInput)); }); + graphPtr->PushInput(nodeId, [this, castedPtr, &newInput] { castedPtr->SetInput(SameGroupOrLink(GetGroup(), newInput)); }); } }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalLink +/// StateLink /////////////////////////////////////////////////////////////////////////////////////////////////// template -class SignalLink : public Signal +class StateLink : public State { public: - SignalLink(const SignalLink&) = default; - SignalLink& operator=(const SignalLink&) = default; + StateLink(const StateLink&) = default; + StateLink& operator=(const StateLink&) = default; - SignalLink(SignalLink&&) = default; - SignalLink& operator=(SignalLink&&) = default; + StateLink(StateLink&&) = default; + StateLink& operator=(StateLink&&) = default; // Construct with group - SignalLink(const Group& group, const Signal& input) : - SignalLink::Signal( GetOrCreateLinkNode(group, input) ) + StateLink(const Group& group, const State& input) : + StateLink::State( GetOrCreateLinkNode(group, input) ) { } protected: - static auto GetOrCreateLinkNode(const Group& group, const Signal& input) -> decltype(auto) + static auto GetOrCreateLinkNode(const Group& group, const State& input) -> decltype(auto) { - using REACT_IMPL::SignalLinkNode; + using REACT_IMPL::StateLinkNode; using REACT_IMPL::IReactNode; using REACT_IMPL::ReactGraph; @@ -266,12 +266,12 @@ class SignalLink : public Signal k, [&] { - auto nodePtr = std::make_shared>(group, input); - nodePtr->SetWeakSelfPtr(std::weak_ptr>{ nodePtr }); + auto nodePtr = std::make_shared>(group, input); + nodePtr->SetWeakSelfPtr(std::weak_ptr>{ nodePtr }); return std::static_pointer_cast(nodePtr); }); - return std::static_pointer_cast>(nodePtr); + return std::static_pointer_cast>(nodePtr); } }; @@ -280,14 +280,14 @@ class SignalLink : public Signal /***************************************/ REACT_IMPL_BEGIN /**************************************/ template -static Signal SameGroupOrLink(const Group& targetGroup, const Signal& dep) +static State SameGroupOrLink(const Group& targetGroup, const State& dep) { if (dep.GetGroup() == targetGroup) return dep; else - return SignalLink( targetGroup, dep ); + return StateLink( targetGroup, dep ); } /****************************************/ REACT_IMPL_END /***************************************/ -#endif // REACT_SIGNAL_H_INCLUDED \ No newline at end of file +#endif // REACT_STATE_H_INCLUDED \ No newline at end of file diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index 68b88817..1867d880 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -106,7 +106,6 @@ $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) true %(PreprocessorDefinitions) - stdcpp17 true @@ -145,7 +144,6 @@ $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) true %(PreprocessorDefinitions) - stdcpp17 true @@ -170,11 +168,11 @@ - + - + diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index d87af090..040e8acc 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -39,9 +39,6 @@ Header Files - - Header Files - Header Files\detail @@ -60,9 +57,6 @@ Header Files\detail - - Header Files\detail - Header Files @@ -81,6 +75,12 @@ Header Files\common + + Header Files\detail + + + Header Files + diff --git a/project/msvc/CppReactTest.vcxproj b/project/msvc/CppReactTest.vcxproj index eccef6a3..7516767f 100644 --- a/project/msvc/CppReactTest.vcxproj +++ b/project/msvc/CppReactTest.vcxproj @@ -168,8 +168,8 @@ - - + + diff --git a/project/msvc/CppReactTest.vcxproj.filters b/project/msvc/CppReactTest.vcxproj.filters index a6f9919a..6004eeed 100644 --- a/project/msvc/CppReactTest.vcxproj.filters +++ b/project/msvc/CppReactTest.vcxproj.filters @@ -24,19 +24,19 @@ Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files diff --git a/src/detail/graph_impl.cpp b/src/detail/graph_impl.cpp index 8dc4be9a..c6ceb6be 100644 --- a/src/detail/graph_impl.cpp +++ b/src/detail/graph_impl.cpp @@ -54,9 +54,17 @@ void ReactGraph::DetachNode(NodeId nodeId, NodeId parentId) successors.erase(std::find(successors.begin(), successors.end(), nodeId)); } -void ReactGraph::PushDependency(SyncPoint::Dependency dep) +void ReactGraph::AddSyncPointDependency(SyncPoint::Dependency dep, bool syncLinked) { - curDependencies_.push_back(std::move(dep)); + if (syncLinked) + linkDependencies_.push_back(std::move(dep)); + else + localDependencies_.push_back(std::move(dep)); +} + +void ReactGraph::AllowLinkedTransactionMerging(bool allowMerging) +{ + allowLinkedTransactionMerging_ = true; } void ReactGraph::Propagate() @@ -115,21 +123,35 @@ void ReactGraph::Propagate() for (IReactNode* nodePtr : changedNodes_) nodePtr->Clear(); changedNodes_.clear(); + + // Clean link state. + scheduledLinkOutputs_.clear(); + localDependencies_.clear(); + linkDependencies_.clear(); + allowLinkedTransactionMerging_ = false; } void ReactGraph::UpdateLinkNodes() { + TransactionFlags flags = TransactionFlags::none; + + if (! linkDependencies_.empty()) + flags |= TransactionFlags::sync_linked; + + if (allowLinkedTransactionMerging_) + flags |= TransactionFlags::allow_merging; + + SyncPoint::Dependency dep{ begin(linkDependencies_), end(linkDependencies_) }; + for (auto& e : scheduledLinkOutputs_) { - e.first->EnqueueTransaction(TransactionFlags::none, + e.first->EnqueueTransaction( [inputs = std::move(e.second)] { for (auto& callback : inputs) callback(); - }); + }, dep, flags); } - - scheduledLinkOutputs_.clear(); } void ReactGraph::ScheduleSuccessors(NodeData& node) @@ -197,7 +219,10 @@ size_t TransactionQueue::ProcessNextBatch() { StoredTransaction curTransaction; size_t popCount = 0; + bool canMerge = false; + bool syncLinked = false; + bool skipPop = false; bool isDone = false; @@ -210,6 +235,8 @@ size_t TransactionQueue::ProcessNextBatch() return popCount; canMerge = IsBitmaskSet(curTransaction.flags, TransactionFlags::allow_merging); + syncLinked = IsBitmaskSet(curTransaction.flags, TransactionFlags::sync_linked); + ++popCount; } else @@ -220,16 +247,21 @@ size_t TransactionQueue::ProcessNextBatch() graph_.DoTransaction([&] { curTransaction.func(); + graph_.AddSyncPointDependency(std::move(curTransaction.dep), syncLinked); if (canMerge) { - // Inner loop. Mergeable transactions are merged + graph_.AllowLinkedTransactionMerging(true); + + // Pull in additional mergeable transactions. for (;;) { - if (transactions_.try_pop(curTransaction)) + if (!transactions_.try_pop(curTransaction)) return; canMerge = IsBitmaskSet(curTransaction.flags, TransactionFlags::allow_merging); + syncLinked = IsBitmaskSet(curTransaction.flags, TransactionFlags::sync_linked); + ++popCount; if (!canMerge) @@ -239,6 +271,7 @@ size_t TransactionQueue::ProcessNextBatch() } curTransaction.func(); + graph_.AddSyncPointDependency(std::move(curTransaction.dep), syncLinked); } } }); diff --git a/tests/src/SignalTest.cpp b/tests/src/SignalTest.cpp deleted file mode 100644 index a34b7f98..00000000 --- a/tests/src/SignalTest.cpp +++ /dev/null @@ -1,11 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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) - - -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace { - -} // ~namespace \ No newline at end of file diff --git a/tests/src/TransactionTest.cpp b/tests/src/TransactionTest.cpp deleted file mode 100644 index 6054092a..00000000 --- a/tests/src/TransactionTest.cpp +++ /dev/null @@ -1,12 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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) - - -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace { - - -} // ~namespace \ No newline at end of file diff --git a/tests/src/common_tests.cpp b/tests/src/common_tests.cpp index 0bd5b6dc..8a78457a 100644 --- a/tests/src/common_tests.cpp +++ b/tests/src/common_tests.cpp @@ -15,6 +15,36 @@ using namespace react; /////////////////////////////////////////////////////////////////////////////////////////////////// +TEST(SyncPointTest, DependencyCreation) +{ + SyncPoint sp; + + { + SyncPoint::Dependency dep1(sp); + SyncPoint::Dependency dep2(sp); + SyncPoint::Dependency dep3(sp); + + std::vector deps1 = { dep1, dep2, dep3 }; + std::vector deps2 = { dep1 }; + + SyncPoint::Dependency dep4( begin(deps1), end(deps1) ); + + + SyncPoint::Dependency dep5; + + dep5 = std::move(dep4); + + SyncPoint::Dependency dep6; + dep6 = dep5; + SyncPoint::Dependency dep7( dep6 ); + + SyncPoint::Dependency dep8( begin(deps2), end(deps2) ); + } + + bool done = sp.WaitFor(std::chrono::milliseconds(1)); + EXPECT_EQ(true, done); +} + TEST(SyncPointTest, SingleWait) { SyncPoint sp; @@ -29,7 +59,6 @@ TEST(SyncPointTest, SingleWait) output = 1; }); - sp.Wait(); EXPECT_EQ(1, output); diff --git a/tests/src/event_tests.cpp b/tests/src/event_tests.cpp index 4204a466..31565b87 100644 --- a/tests/src/event_tests.cpp +++ b/tests/src/event_tests.cpp @@ -80,7 +80,7 @@ TEST(EventTest, BasicOutput) EXPECT_EQ(3, output); } -TEST(EventTest, EventSlots) +TEST(EventTest, Slots) { Group g; @@ -144,7 +144,7 @@ TEST(EventTest, EventSlots) EXPECT_EQ(4, turns); } -TEST(EventTest, EventTransactions) +TEST(EventTest, Transactions) { Group g; @@ -174,7 +174,7 @@ TEST(EventTest, EventTransactions) EXPECT_EQ(1, turns); } -TEST(EventTest, ExplicitEventLinks) +TEST(EventTest, Links) { Group g1; Group g2; diff --git a/tests/src/state_tests.cpp b/tests/src/state_tests.cpp new file mode 100644 index 00000000..7cca28db --- /dev/null +++ b/tests/src/state_tests.cpp @@ -0,0 +1,262 @@ + +// Copyright Sebastian Jeckel 2017. +// 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) + +#include "gtest/gtest.h" + +#include "react/state.h" +#include "react/observer.h" + +#include +#include + +using namespace react; + +TEST(StateTest, Construction) +{ + Group g; + + // State variable + { + StateVar t1( g, 0 ); + StateVar t2( t1 ); + StateVar t3( std::move(t1) ); + + StateVar ref1( t2 ); + State ref2( t3 ); + + EXPECT_TRUE(ref1 == ref2); + } + + // State slot + { + StateVar t0( g, 0 ); + + StateSlot t1( g, t0 ); + StateSlot t2( t1 ); + StateSlot t3( std::move(t1) ); + + StateSlot ref1( t2 ); + State ref2( t3 ); + + EXPECT_TRUE(ref1 == ref2); + } + + // State link + { + StateVar t0( g, 0 ); + + StateSlot s1( g, t0 ); + + StateLink t1( g, s1 ); + StateLink t2( t1 ); + StateLink t3( std::move(t1) ); + + StateLink ref1( t2 ); + State ref2( t3 ); + + EXPECT_TRUE(ref1 == ref2); + } +} + +TEST(StateTest, BasicOutput) +{ + Group g; + + StateVar st( g ); + + int output = 0; + + Observer obs([&] (const auto& v) + { + output += v; + }, st); + + EXPECT_EQ(0, output); + + st.Set(1); + EXPECT_EQ(1, output); + + st.Set(2); + EXPECT_EQ(3, output); +} + +TEST(StateTest, Slots) +{ + Group g; + + StateVar st1( g ); + StateVar st2( g ); + + StateSlot slot( g, st1 ); + + int output = 0; + int turns = 0; + + Observer obs([&] (const auto& v) + { + ++turns; + output += v; + }, slot); + + EXPECT_EQ(0, output); + EXPECT_EQ(1, turns); + + slot.Set(st1); + st1.Set(5); + st2.Set(2); + + EXPECT_EQ(5, output); + EXPECT_EQ(2, turns); + + output = 0; + + slot.Set(st2); + st1.Set(5); + st2.Set(2); + + EXPECT_EQ(2, output); + EXPECT_EQ(3, turns); +} + +TEST(StateTest, Transactions) +{ + Group g; + + StateVar st( g, 1 ); + + int output = 0; + int turns = 0; + + Observer obs([&] (const auto& v) + { + ++turns; + output += v; + }, st); + + EXPECT_EQ(1, output); + + g.DoTransaction([&] + { + st.Set(1); + st.Set(2); + st.Set(3); + st.Set(4); + }); + + EXPECT_EQ(5, output); + EXPECT_EQ(2, turns); +} + +TEST(StateTest, Links) +{ + Group g1; + Group g2; + Group g3; + + StateVar st1( g1, 1 ); + StateVar st2( g2, 2 ); + StateVar st3( g3, 3 ); + + StateSlot slot( g1, st1 ); + + int output = 0; + int turns = 0; + + Observer obs([&] (const auto& v) + { + ++turns; + output = v; + }, slot); + + EXPECT_EQ(1, turns); + st1.Set(10); + EXPECT_EQ(10, output); + EXPECT_EQ(2, turns); + + // Explicit link + StateLink lnk2( g1, st2 ); + slot.Set(lnk2); + std::this_thread::sleep_for(std::chrono::seconds(1)); + EXPECT_EQ(2, output); + EXPECT_EQ(3, turns); + + st2.Set(20); + std::this_thread::sleep_for(std::chrono::seconds(1)); + EXPECT_EQ(20, output); + EXPECT_EQ(4, turns); + + // Implicit link + slot.Set(st3); + std::this_thread::sleep_for(std::chrono::seconds(1)); + EXPECT_EQ(3, output); + EXPECT_EQ(5, turns); + + st3.Set(30); + std::this_thread::sleep_for(std::chrono::seconds(1)); + EXPECT_EQ(30, output); + EXPECT_EQ(6, turns); + + std::this_thread::sleep_for(std::chrono::seconds(1)); +} + +template +static T Sum2(T a, T b) +{ + return a + b; +} + +template +static T Sum3(T a, T b, T c) +{ + return a + b + c; +} + +TEST(StateTest, StateCombination) +{ + Group g; + + StateVar a( g, 0 ); + StateVar b( g, 0 ); + StateVar c( g, 0 ); + + State s1(Sum2, a, b); + + State x(Sum2, s1, c); + State y(Sum3, a, b, c); + + int output1 = 0; + int output2 = 0; + int turns1 = 0; + int turns2 = 0; + + Observer obs1([&] (int v) + { + ++turns1; + output1 = v; + }, x); + + EXPECT_EQ(0, output1); + EXPECT_EQ(1, turns1); + + Observer obs2([&] (int v) + { + ++turns2; + output2 = v; + }, y); + + EXPECT_EQ(0, output2); + EXPECT_EQ(1, turns2); + + a.Set(1); + b.Set(1); + c.Set(1); + + EXPECT_EQ(3, output1); + EXPECT_EQ(4, turns1); + + EXPECT_EQ(3, output2); + EXPECT_EQ(4, turns2); +} \ No newline at end of file diff --git a/tests/src/transaction_tests.cpp b/tests/src/transaction_tests.cpp new file mode 100644 index 00000000..33812c75 --- /dev/null +++ b/tests/src/transaction_tests.cpp @@ -0,0 +1,220 @@ + +// Copyright Sebastian Jeckel 2017. +// 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) + +#include "gtest/gtest.h" + +#include "react/event.h" +#include "react/observer.h" + +#include +#include + +using namespace react; + +TEST(TransactionTests, Merging) +{ + Group g; + + EventSource evt( g ); + + int output = 0; + int turns = 0; + + Observer obs([&] (const auto& events) + { + ++turns; + for (int e : events) + output += e; + }, evt); + + // This transaction blocks the queue for one second. + g.EnqueueTransaction([&] + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + }); + + SyncPoint sp; + + // Enqueue 3 more transaction while the queue is blocked. + // They should be merged together as a result. + g.EnqueueTransaction([&] + { + evt.Emit(1); + evt.Emit(2); + }, sp, TransactionFlags::allow_merging); + + g.EnqueueTransaction([&] + { + evt.Emit(3); + evt.Emit(4); + }, sp, TransactionFlags::allow_merging); + + g.EnqueueTransaction([&] + { + evt.Emit(5); + evt.Emit(6); + }, sp, TransactionFlags::allow_merging); + + bool done = sp.WaitFor(std::chrono::seconds(3)); + EXPECT_EQ(true, done); + + // They have been merged, there should only be a single turn. + EXPECT_EQ(1, turns); + + // None of the emitted values have been lost. + EXPECT_EQ(21, output); +} + +TEST(TransactionTests, LinkedSync) +{ + // Three groups. Each has one event with an observer attached. + // The last observer adds a little delay. + + Group g1; + Group g2; + Group g3; + + EventSource evt1( g1 ); + + int output1 = 0; + int turns1 = 0; + + Observer obs1([&] (const auto& events) + { + ++turns1; + for (int e : events) + output1 += e; + }, evt1); + + Event evt2 = Filter(g2, [] (const auto&) { return true; }, evt1); + + int output2 = 0; + int turns2 = 0; + + Observer obs2([&] (const auto& events) + { + ++turns2; + for (int e : events) + output2 += e; + }, evt2); + + Event evt3 = Filter(g3, [] (const auto&) { return true; }, evt2); + + int output3 = 0; + int turns3 = 0; + + Observer obs3([&] (const auto& events) + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + + ++turns3; + for (int e : events) + output3 += e; + }, evt3); + + SyncPoint sp; + + // Enqueue a transaction that waits on linked nodes. + g1.EnqueueTransaction([&] + { + evt1.Emit(1); + evt1.Emit(2); + }, sp, TransactionFlags::sync_linked); + + // We should wait for all three observers. + bool done = sp.WaitFor(std::chrono::seconds(3)); + + EXPECT_EQ(true, done); + + EXPECT_EQ(1, turns1); + EXPECT_EQ(1, turns2); + EXPECT_EQ(1, turns3); + + EXPECT_EQ(3, output1); + EXPECT_EQ(3, output2); + EXPECT_EQ(3, output3); +} + +TEST(TransactionTests, LinkedSyncMerging) +{ + // Two groups. Each has one event with an observer attached. + // The last observer adds a little delay. + + Group g1; + Group g2; + + EventSource evt1( g1 ); + + int output1 = 0; + int turns1 = 0; + + Observer obs1([&] (const auto& events) + { + ++turns1; + for (int e : events) + output1 += e; + }, evt1); + + Event evt2 = Filter(g2, [] (const auto&) { return true; }, evt1); + + int output2 = 0; + int turns2 = 0; + + Observer obs2([&] (const auto& events) + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + + ++turns2; + for (int e : events) + output2 += e; + }, evt2); + + SyncPoint sp1; + SyncPoint sp2; + + // This transaction blocks the queue for one second. + g1.EnqueueTransaction([&] + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + }); + + // Two more transactions are enqueued using two different sync points. + // The first one should only sync on obs1, not on linked nodes. + g1.EnqueueTransaction([&] + { + evt1.Emit(1); + evt1.Emit(2); + }, sp1, TransactionFlags::allow_merging); + + // The second one should sync on obs2 as well. + g1.EnqueueTransaction([&] + { + evt1.Emit(3); + evt1.Emit(4); + }, sp2, TransactionFlags::allow_merging | TransactionFlags::sync_linked); + + // Should be done after obs1 is done with both transactions (because they have been merged). + bool done = sp1.WaitFor(std::chrono::seconds(5)); + + EXPECT_EQ(true, done); + + EXPECT_EQ(1, turns1); + EXPECT_EQ(0, turns2); + + EXPECT_EQ(10, output1); + EXPECT_EQ(0, output2); + + // Should be done after obs2 is done. + done = sp2.WaitFor(std::chrono::seconds(5)); + + EXPECT_EQ(true, done); + + EXPECT_EQ(1, turns1); + EXPECT_EQ(1, turns2); + + EXPECT_EQ(10, output1); + EXPECT_EQ(10, output2); +} \ No newline at end of file From 238e09e0e73f58290487b43d481cb6592b49b8f3 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 24 Oct 2017 23:51:13 +0200 Subject: [PATCH 245/266] EventLinkNode cleanup. --- include/react/detail/event_nodes.h | 41 +++++++++++++----------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/include/react/detail/event_nodes.h b/include/react/detail/event_nodes.h index f422a903..dfec7020 100644 --- a/include/react/detail/event_nodes.h +++ b/include/react/detail/event_nodes.h @@ -424,13 +424,23 @@ class EventLinkNode : public EventNode public: EventLinkNode(const Group& group, const Event& dep) : EventLinkNode::EventNode( group ), - linkOutput_( dep ) + dep_( dep ), + srcGroup_( dep.GetGroup() ) { this->RegisterMe(NodeCategory::input); + + auto& srcGraphPtr = GetInternals(srcGroup_).GetGraphPtr(); + outputNodeId_ = srcGraphPtr->RegisterNode(&linkOutput_, NodeCategory::linkoutput); + + srcGraphPtr->AttachNode(outputNodeId_, GetInternals(dep).GetNodeId()); } ~EventLinkNode() { + auto& srcGraphPtr = GetInternals(srcGroup_).GetGraphPtr(); + srcGraphPtr->DetachNode(outputNodeId_, GetInternals(dep_).GetNodeId()); + srcGraphPtr->UnregisterNode(outputNodeId_); + auto& linkCache = GetGraphPtr()->GetLinkCache(); linkCache.Erase(this); @@ -449,23 +459,6 @@ class EventLinkNode : public EventNode private: struct VirtualOutputNode : public IReactNode { - VirtualOutputNode(const Event& depIn) : - parent( ), - dep( depIn ), - srcGroup( depIn.GetGroup() ) - { - auto& srcGraphPtr = GetInternals(srcGroup).GetGraphPtr(); - nodeId = srcGraphPtr->RegisterNode(this, NodeCategory::linkoutput); - srcGraphPtr->AttachNode(nodeId, GetInternals(dep).GetNodeId()); - } - - ~VirtualOutputNode() - { - auto& srcGraphPtr = GetInternals(srcGroup).GetGraphPtr(); - srcGraphPtr->DetachNode(nodeId, GetInternals(dep).GetNodeId()); - srcGraphPtr->UnregisterNode(nodeId); - } - virtual UpdateResult Update(TurnId turnId) noexcept override { return UpdateResult::changed; } @@ -475,7 +468,7 @@ class EventLinkNode : public EventNode { auto* rawPtr = p->GetGraphPtr().get(); output[rawPtr].push_back( - [storedParent = std::move(p), storedEvents = GetInternals(dep).Events()] () mutable + [storedParent = std::move(p), storedEvents = GetInternals(p->dep_).Events()] () mutable { NodeId nodeId = storedParent->GetNodeId(); auto& graphPtr = storedParent->GetGraphPtr(); @@ -489,13 +482,13 @@ class EventLinkNode : public EventNode } } - std::weak_ptr parent; - - NodeId nodeId; - Event dep; - Group srcGroup; + std::weak_ptr parent; }; + Event dep_; + Group srcGroup_; + NodeId outputNodeId_; + VirtualOutputNode linkOutput_; }; From 5a9912825d442d5d3ffd0fc09a79408fc6f75d60 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 24 Oct 2017 23:57:06 +0200 Subject: [PATCH 246/266] Renamed and deleted files. --- include/react/{Algorithm.h => algorithm.h} | 0 include/react/{API.h => api.h} | 0 include/react/common/RefCounting.h | 112 ------- include/react/common/SourceIdSet.h | 135 --------- include/react/common/Timing.h | 201 ------------- include/react/common/TopoQueue.h | 332 --------------------- include/react/common/Types.h | 31 -- include/react/common/expected.h | 218 -------------- include/react/common/optional.h | 70 ----- include/react/common/owned_ptr.h | 18 -- include/react/detail/{Defs.h => defs.h} | 0 include/react/{Event.h => event.h} | 0 include/react/{Group.h => group.h} | 0 include/react/{Observer.h => observer.h} | 0 14 files changed, 1117 deletions(-) rename include/react/{Algorithm.h => algorithm.h} (100%) rename include/react/{API.h => api.h} (100%) delete mode 100644 include/react/common/RefCounting.h delete mode 100644 include/react/common/SourceIdSet.h delete mode 100644 include/react/common/Timing.h delete mode 100644 include/react/common/TopoQueue.h delete mode 100644 include/react/common/Types.h delete mode 100644 include/react/common/expected.h delete mode 100644 include/react/common/optional.h delete mode 100644 include/react/common/owned_ptr.h rename include/react/detail/{Defs.h => defs.h} (100%) rename include/react/{Event.h => event.h} (100%) rename include/react/{Group.h => group.h} (100%) rename include/react/{Observer.h => observer.h} (100%) diff --git a/include/react/Algorithm.h b/include/react/algorithm.h similarity index 100% rename from include/react/Algorithm.h rename to include/react/algorithm.h diff --git a/include/react/API.h b/include/react/api.h similarity index 100% rename from include/react/API.h rename to include/react/api.h diff --git a/include/react/common/RefCounting.h b/include/react/common/RefCounting.h deleted file mode 100644 index 37bb5e68..00000000 --- a/include/react/common/RefCounting.h +++ /dev/null @@ -1,112 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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_COMMON_REF_COUNTING_H_INCLUDED -#define REACT_COMMON_REF_COUNTING_H_INCLUDED - -#pragma once - -#include "react/detail/defs.h" - -#include -#include - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -// A non-exception-safe pointer wrapper with conditional intrusive ref counting. -template -class IntrusiveRefCountingPtr -{ - enum - { - tag_ref_counted = 0x1 - }; - -public: - IntrusiveRefCountingPtr() : - ptrData_( reinterpret_cast(nullptr) ) - {} - - IntrusiveRefCountingPtr(T* ptr) : - ptrData_( reinterpret_cast(ptr) ) - { - if (ptr != nullptr && ptr->IsRefCounted()) - { - ptr->IncRefCount(); - ptrData_ |= tag_ref_counted; - } - } - - IntrusiveRefCountingPtr(const IntrusiveRefCountingPtr& other) : - ptrData_( other.ptrData_ ) - { - if (isValidRefCountedPtr()) - Get()->IncRefCount(); - } - - IntrusiveRefCountingPtr(IntrusiveRefCountingPtr&& other) : - ptrData_( other.ptrData_ ) - { - other.ptrData_ = reinterpret_cast(nullptr); - } - - ~IntrusiveRefCountingPtr() - { - if (isValidRefCountedPtr()) - Get()->DecRefCount(); - } - - IntrusiveRefCountingPtr& operator=(const IntrusiveRefCountingPtr& other) - { - if (this != &other) - { - if (other.isValidRefCountedPtr()) - other.Get()->IncRefCount(); - - if (isValidRefCountedPtr()) - Get()->DecRefCount(); - - ptrData_ = other.ptrData_; - } - - return *this; - } - - IntrusiveRefCountingPtr& operator=(IntrusiveRefCountingPtr&& other) - { - if (this != &other) - { - if (isValidRefCountedPtr()) - Get()->DecRefCount(); - - ptrData_ = other.ptrData_; - other.ptrData_ = reinterpret_cast(nullptr); - } - - return *this; - } - - T* Get() const { return reinterpret_cast(ptrData_ & ~tag_ref_counted); } - - T& operator*() const { return *Get(); } - T* operator->() const { return Get(); } - - bool operator==(const IntrusiveRefCountingPtr& other) const { return Get() == other.Get(); } - bool operator!=(const IntrusiveRefCountingPtr& other) const { return !(*this == other); } - -private: - bool isValidRefCountedPtr() const - { - return ptrData_ != reinterpret_cast(nullptr) - && (ptrData_ & tag_ref_counted) != 0; - } - - uintptr_t ptrData_; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_COMMON_REF_COUNTING_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/SourceIdSet.h b/include/react/common/SourceIdSet.h deleted file mode 100644 index 902a39aa..00000000 --- a/include/react/common/SourceIdSet.h +++ /dev/null @@ -1,135 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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_COMMON_SOURCEIDSET_H_INCLUDED -#define REACT_COMMON_SOURCEIDSET_H_INCLUDED - -#pragma once - -#include "react/detail/defs.h" - -#include -#include - -#include "tbb/queuing_mutex.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SourceIdSet -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class SourceIdSet -{ -private: - using MutexT = tbb::queuing_mutex; - using DataT = std::vector; - -public: - void Insert(const T& e) - { - MutexT::scoped_lock lock(mutex_); - - data_.push_back(e); - - isSorted_ = false; - } - - void Insert(SourceIdSet& other) - { - MutexT::scoped_lock myLock(mutex_); - MutexT::scoped_lock otherLock(other.mutex_); - - sort(); - other.sort(); - - auto l = data_.begin(); - auto r = data_.end(); - auto offset = std::distance(l,r); - - // For each element in other, check if it's already contained in this - // if not, add it - for (const auto& e : other.data_) - { - l = std::lower_bound(l, r, e); - - // Already in the set? - if (l < r && *l == e) - continue; - - auto d = std::distance(data_.begin(), l); - - data_.push_back(e); - - // push_back invalidates iterators - l = data_.begin() + d; - r = data_.begin() + offset; - } - - std::inplace_merge(data_.begin(), data_.begin() + offset, data_.end()); - } - - void Erase(const T& e) - { - MutexT::scoped_lock lock(mutex_); - - data_.erase(std::find(data_.begin(), data_.end(), e)); - } - - void Clear() - { - MutexT::scoped_lock lock(mutex_); - - data_.clear(); - isSorted_ = true; - } - - bool IntersectsWith(SourceIdSet& other) - { - MutexT::scoped_lock myLock(mutex_); - MutexT::scoped_lock otherLock(other.mutex_); - - sort(); - other.sort(); - - auto l1 = data_.begin(); - const auto r1 = data_.end(); - - auto l2 = other.data_.begin(); - const auto r2 = other.data_.end(); - - // Is intersection of turn sourceIds and node sourceIds non-empty? - while (l1 != r1 && l2 != r2) - { - if (*l1 < *l2) - l1++; - else if (*l2 < *l1) - l2++; - // Equals => Intersect - else - return true; - } - return false; - } - -private: - MutexT mutex_; - DataT data_; - bool isSorted_ = false; - - void sort() - { - if (isSorted_) - return; - - std::sort(data_.begin(), data_.end()); - isSorted_ = true; - } -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_COMMON_SOURCEIDSET_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/Timing.h b/include/react/common/Timing.h deleted file mode 100644 index 7d6c81ac..00000000 --- a/include/react/common/Timing.h +++ /dev/null @@ -1,201 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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_COMMON_TIMING_H_INCLUDED -#define REACT_COMMON_TIMING_H_INCLUDED - -#pragma once - -#include "react/detail/defs.h" - -#include - -#if _WIN32 || _WIN64 - #define REACT_FIXME_CUSTOM_TIMER 1 -#else - #define REACT_FIXME_CUSTOM_TIMER 0 -#endif - -#if REACT_FIXME_CUSTOM_TIMER - #include -#else - #include -#endif - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// GetPerformanceFrequency -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Todo: Initialization not thread-safe -#if REACT_FIXME_CUSTOM_TIMER -inline const LARGE_INTEGER& GetPerformanceFrequency() -{ - static bool init = false; - static LARGE_INTEGER frequency; - - if (init == false) - { - QueryPerformanceFrequency(&frequency); - init = true; - } - - return frequency; -} -#endif - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ConditionalTimer -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - long long threshold, - bool is_enabled -> -class ConditionalTimer -{ -public: - class ScopedTimer - { - public: - // Note: - // Count is passed by ref so it can be set later if it's not known at time of creation - ScopedTimer(const ConditionalTimer&, const size_t& count); - }; - - void Reset(); - void ForceThresholdExceeded(bool isExceeded); - bool IsThresholdExceeded() const; -}; - -// Disabled -template -< - long long threshold -> -class ConditionalTimer -{ -public: - // Defines scoped timer that does nothing - class ScopedTimer - { - public: - ScopedTimer(const ConditionalTimer&, const size_t& count) {} - }; - - void Reset() {} - void ForceThresholdExceeded(bool isExceeded) {} - bool IsThresholdExceeded() const { return false; } -}; - -// Enabled -template -< - long long threshold -> -class ConditionalTimer -{ -public: -#if REACT_FIXME_CUSTOM_TIMER - using TimestampT = LARGE_INTEGER; -#else - using ClockT = std::chrono::high_resolution_clock; - using TimestampT = std::chrono::time_point; -#endif - - class ScopedTimer - { - public: - ScopedTimer(ConditionalTimer& parent, const size_t& count) : - parent_( parent ), - count_( count ) - { - if (!parent_.shouldMeasure_) - return; - - startMeasure(); - } - - ~ScopedTimer() - { - if (!parent_.shouldMeasure_) - return; - - parent_.shouldMeasure_ = false; - - endMeasure(); - } - - private: - void startMeasure() - { - startTime_ = now(); - } - - void endMeasure() - { -#if REACT_FIXME_CUSTOM_TIMER - TimestampT endTime = now(); - - LARGE_INTEGER durationUS; - - durationUS.QuadPart = endTime.QuadPart - startTime_.QuadPart; - durationUS.QuadPart *= 1000000; - durationUS.QuadPart /= GetPerformanceFrequency().QuadPart; - - parent_.isThresholdExceeded_ = (durationUS.QuadPart - (threshold * count_)) > 0; -#else - using std::chrono::duration_cast; - using std::chrono::microseconds; - - parent_.isThresholdExceeded_ = - duration_cast(now() - startTime_).count() > (threshold * count_); -#endif - } - - ConditionalTimer& parent_; - TimestampT startTime_; - const size_t& count_; - }; - - static TimestampT now() - { -#if REACT_FIXME_CUSTOM_TIMER - TimestampT result; - QueryPerformanceCounter(&result); - return result; -#else - return ClockT::now(); -#endif - } - - void Reset() - { - shouldMeasure_ = true; - isThresholdExceeded_ = false; - } - - void ForceThresholdExceeded(bool isExceeded) - { - shouldMeasure_ = false; - isThresholdExceeded_ = isExceeded; - } - - bool IsThresholdExceeded() const { return isThresholdExceeded_; } - -private: - // Only measure once - bool shouldMeasure_ = true; - - // Until we have measured, assume the threshold is exceeded. - // The cost of initially not parallelizing what should be parallelized is much higher - // than for the other way around. - bool isThresholdExceeded_ = true; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_COMMON_TIMING_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/TopoQueue.h b/include/react/common/TopoQueue.h deleted file mode 100644 index 2c3c4613..00000000 --- a/include/react/common/TopoQueue.h +++ /dev/null @@ -1,332 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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_COMMON_TOPOQUEUE_H_INCLUDED -#define REACT_COMMON_TOPOQUEUE_H_INCLUDED - -#pragma once - -#include "react/detail/defs.h" - -#include -#include -#include -#include -#include - -#include "tbb/enumerable_thread_specific.h" -#include "tbb/tbb_stddef.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// TopoQueue - Sequential -/////////////////////////////////////////////////////////////////////////////////////////////////// - -template -int GetNodeLevel(const T& node) - { return node.level; } - -template -int GetNodeLevel(const T* node) - { return node->level; } - -template -class TopoQueue -{ -private: - struct Entry; - -public: - // Store the level as part of the entry for cheap comparisons - using QueueDataT = std::vector; - using NextDataT = std::vector; - - TopoQueue() = default; - TopoQueue(const TopoQueue&) = default; - - void Push(const T& value) - { - queueData_.emplace_back(value, GetNodeLevel(value)); - } - - bool FetchNext() - { - // Throw away previous values - nextData_.clear(); - - // Find min level of nodes in queue data - minLevel_ = (std::numeric_limits::max)(); - for (const auto& e : queueData_) - if (minLevel_ > e.Level) - minLevel_ = e.Level; - - // Swap entries with min level to the end - auto p = std::partition( - queueData_.begin(), - queueData_.end(), - [minLevel_] (const Entry& e) { return minLevel_ != e.level; }); - - // Reserve once to avoid multiple re-allocations - nextData_.reserve(std::distance(p, queueData_.end())); - - // Move min level values to next data - for (auto it = p; it != queueData_.end(); ++it) - nextData_.push_back(std::move(it->Value)); - - // Truncate moved entries - queueData_.resize(std::distance(queueData_.begin(), p)); - - return !nextData_.empty(); - } - - const NextDataT& NextValues() const { return nextData_; } - -private: - struct Entry - { - Entry() = default; - Entry(const Entry&) = default; - - Entry(const T& value, int level) : Value( value ), Level( level ) {} - - T Value; - int Level; - }; - - struct LevelCompFunctor - { - LevelCompFunctor(int level) : Level( level ) {} - - bool operator()(const Entry& e) const { return e.Level != Level; } - - const int Level; - }; - - NextDataT nextData_; - QueueDataT queueData_; - - TLevelFunc levelFunc_; - - int minLevel_ = (std::numeric_limits::max)(); -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// WeightedRange - Implements tbb range concept -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename TIt, - uint grain_size -> -class WeightedRange -{ -public: - using const_iterator = TIt; - using ValueT = typename TIt::value_type; - - WeightedRange() = default; - WeightedRange(const WeightedRange&) = default; - - WeightedRange(const TIt& a, const TIt& b, uint weight) : - begin_( a ), - end_( b ), - weight_( weight ) - {} - - WeightedRange(WeightedRange& source, tbb::split) - { - uint sum = 0; - TIt p = source.begin_; - while (p != source.end_) - { - // Note: assuming a pair with weight as second until more flexibility is needed - sum += p->second; - ++p; - if (sum >= grain_size) - break; - } - - // New [p,b) - begin_ = p; - end_ = source.end_; - weight_ = source.weight_ - sum; - - // Source [a,p) - source.end_ = p; - source.weight_ = sum; - } - - // tbb range interface - bool empty() const { return !(Size() > 0); } - bool is_divisible() const { return weight_ > grain_size && Size() > 1; } - - // iteration interface - const_iterator begin() const { return begin_; } - const_iterator end() const { return end_; } - - size_t Size() const { return end_ - begin_; } - uint Weight() const { return weight_; } - -private: - TIt begin_; - TIt end_; - uint weight_ = 0; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ConcurrentTopoQueue -/// Usage based on two phases: -/// 1. Multiple threads push nodes to the queue concurrently. -/// 2. FetchNext() prepares all nodes of the next level in NextNodes(). -/// The previous contents of NextNodes() are automatically cleared. -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< typename T, - uint grain_size, - typename TLevelFunc, - typename TWeightFunc -> -class ConcurrentTopoQueue -{ -private: - struct Entry; - -public: - using QueueDataT = std::vector; - using NextDataT = std::vector>; - using NextRangeT = WeightedRange; - - ConcurrentTopoQueue() = default; - ConcurrentTopoQueue(const ConcurrentTopoQueue&) = default; - - template - ConcurrentTopoQueue(FIn1&& levelFunc, FIn2&& weightFunc) : - levelFunc_( std::forward(levelFunc) ), - weightFunc_( std::forward(weightFunc) ) - {} - - void Push(const T& value) - { - auto& t = collectBuffer_.local(); - - auto level = levelFunc_(value); - auto weight = weightFunc_(value); - - t.Data.emplace_back(value,level,weight); - - t.Weight += weight; - - if (t.MinLevel > level) - t.MinLevel = level; - } - - bool FetchNext() - { - nextData_.clear(); - uint totalWeight = 0; - - // Determine current min level - minLevel_ = (std::numeric_limits::max)(); - for (const auto& buf : collectBuffer_) - if (minLevel_ > buf.MinLevel) - minLevel_ = buf.MinLevel; - - // For each thread local buffer... - for (auto& buf : collectBuffer_) - { - auto& v = buf.Data; - - // Swap min level nodes to end of v - auto p = std::partition( - v.begin(), - v.end(), - LevelCompFunctor{ minLevel_ }); - - // Reserve once to avoid multiple re-allocations - nextData_.reserve(std::distance(p, v.end())); - - // Move min level values to global next data - for (auto it = p; it != v.end(); ++it) - nextData_.emplace_back(std::move(it->Value), it->Weight); - - // Truncate remaining - v.resize(std::distance(v.begin(), p)); - - // Calc new min level and weight for this buffer - buf.MinLevel = (std::numeric_limits::max)(); - int oldWeight = buf.Weight; - buf.Weight = 0; - for (const auto& x : v) - { - buf.Weight += x.Weight; - - if (buf.MinLevel > x.Level) - buf.MinLevel = x.Level; - } - - // Add diff to nodes_ weight - totalWeight += oldWeight - buf.Weight; - } - - nextRange_ = NextRangeT{ nextData_.begin(), nextData_.end(), totalWeight }; - - // Found more nodes? - return !nextData_.empty(); - } - - const NextRangeT& NextRange() const - { - return nextRange_; - } - -private: - struct Entry - { - Entry() = default; - Entry(const Entry&) = default; - - Entry(const T& value, int level, uint weight) : - Value( value ), - Level( level ), - Weight( weight ) - {} - - T Value; - int Level; - uint Weight; - }; - - struct LevelCompFunctor - { - LevelCompFunctor(int level) : Level{ level } {} - - bool operator()(const Entry& e) const { return e.Level != Level; } - - const int Level; - }; - - struct ThreadLocalBuffer - { - QueueDataT Data; - int MinLevel = (std::numeric_limits::max)(); - uint Weight = 0; - }; - - int minLevel_ = (std::numeric_limits::max)(); - - NextDataT nextData_; - NextRangeT nextRange_; - - TLevelFunc levelFunc_; - TWeightFunc weightFunc_; - - tbb::enumerable_thread_specific collectBuffer_; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_COMMON_TOPOQUEUE_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/Types.h b/include/react/common/Types.h deleted file mode 100644 index 30a377fa..00000000 --- a/include/react/common/Types.h +++ /dev/null @@ -1,31 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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_COMMON_TYPES_H_INCLUDED -#define REACT_COMMON_TYPES_H_INCLUDED - -#pragma once - -#include "react/detail/defs.h" - -#include -#include - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -using ObjectId = uintptr_t; - -template -ObjectId GetObjectId(const O& obj) -{ - return (ObjectId)&obj; -} - -using UpdateDurationT = std::chrono::duration; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_COMMON_TYPES_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/expected.h b/include/react/common/expected.h deleted file mode 100644 index 5bdff539..00000000 --- a/include/react/common/expected.h +++ /dev/null @@ -1,218 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_COMMON_EXPECTED_H_INCLUDED -#define REACT_COMMON_EXPECTED_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include -#include -#include - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IError -/////////////////////////////////////////////////////////////////////////////////////////////////// -class IErrorCause -{ -public: - virtual ~IErrorCause() = default; - - virtual std::string GetMessage() const = 0; - - virtual bool IsOfType(const char* typeId) const = 0; -}; - - -class AllocationError : public IErrorCause -{ -public: - static constexpr const char* type_id = "react::AllocationError"; - - virtual std::string GetMessage() const - { return "Allocation error."; } - - virtual bool IsOfType(const char* typeId) const - { return typeId == type_id; } -}; - - -class PreconditionError : public IErrorCause -{ -public: - static constexpr const char* type_id = "react::PreconditionError"; - - virtual std::string GetMessage() const - { return "Precondition error."; } - - virtual bool IsOfType(const char* typeId) const - { return typeId == type_id; } -}; - - -class PostconditionError : public IErrorCause -{ -public: - static constexpr const char* type_id = "react::PostconditionError"; - - virtual std::string GetMessage() const - { return "Postcondition error."; } - - virtual bool IsOfType(const char* typeId) const - { return typeId == type_id; } -}; - - -class MissingValueError : public IErrorCause -{ -public: - static constexpr const char* type_id = "react::MissingValueError"; - - virtual std::string GetMessage() const - { return "Missing value error."; } - - virtual bool IsOfType(const char* typeId) const - { return typeId == type_id; } -}; - - -class Error -{ -public: - template - < - typename T, - typename = std::enable_if_t> - > - Error(T cause) : - cause_(std::make_shared(std::move(cause))) - { } - - template - < - typename T, - typename = std::enable_if_t> - > - bool IsCause() const - { return cause_->IsOfType(T::type_id); } - - template - < - typename T, - typename = std::enable_if_t> - > - T* GetCause() const - { return static_cast(cause_.get()); } - - std::string GetMessage() const - { return cause_->GetMessage(); } - -private: - std::shared_ptr cause_; -}; - -static Error* GetErrorSentinel() - { return reinterpret_cast(0x1u); } - -struct ErrorDeleter -{ - void operator()(Error* p) const - { - if (p != GetErrorSentinel()) - delete p; - } -}; - -using UniqueErrorPtrType = std::unique_ptr; - - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Expected -/// -/// TODO: Optimize for different storage types to avoid branch in destructor. -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Expected -{ -public: - Expected() = delete; - - Expected(const Expected&) = delete; - - Expected& operator=(const Expected&) = delete; - - Expected(Expected&&) = default; - - Expected& operator=(Expected&&) = default; - - Expected(const T& value) : - error_( GetErrorSentinel() ) - { - ValueRef() = value; - } - - Expected(T&& value) : - error_( GetErrorSentinel() ) - { - ValueRef() = std::move(value); - } - - Expected(Error err) : - error_(new Error(std::move(err))) - { } - - Expected(UniqueErrorPtrType&& err) : - error_(std::move(err)) - { } - - ~Expected() - { - if (error_.get() == GetErrorSentinel()) - { - Destruct(); - } - } - - explicit operator bool() const - { - return error_.get() == GetErrorSentinel(); - } - - Error GetError() - { return *error_; } - -private: - const T& ValueRef() const - { return *reinterpret_cast(&valueStorage_); } - - T& ValueRef() - { return *reinterpret_cast(&valueStorage_); } - - void Destruct() - { reinterpret_cast(&valueStorage_)->~T(); } - - std::aligned_storage_t valueStorage_; - - std::unique_ptr error_; - - template - friend typename UniqueErrorPtrType UnwindExpected(Expected&& ex); -}; - -template -typename UniqueErrorPtrType UnwindExpected(Expected&& ex) -{ - return std::move(std::move(ex).error_); -} - -/******************************************/ REACT_END /******************************************/ - -#endif // REACT_COMMON_INDEXED_STORAGE_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/optional.h b/include/react/common/optional.h deleted file mode 100644 index 3208c33b..00000000 --- a/include/react/common/optional.h +++ /dev/null @@ -1,70 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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_COMMON_OPTIONAL_H_INCLUDED -#define REACT_COMMON_OPTIONAL_H_INCLUDED - -#pragma once - -#include "react/detail/defs.h" - -#include -#include -#include - -struct NoValueType -{ - struct Tag {}; - - explicit constexpr NoValueType(Tag) - { } -}; - -constexpr NoValueType no_value{ NoValueType::Tag{ } }; - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -template -class OptScalarStorage -{ -public: - OptScalarStorage() : hasValue_( false ) - { } - - OptScalarStorage(NoValueType) : hasValue_( false ) - { } - - OptScalarStorage& operator=(NoValueType) - { - hasValue_ = false; - return *this; - } - - OptScalarStorage(const OptScalarStorage&) = default; - - OptScalarStorage& operator=(const OptScalarStorage&) = default; - - bool HasValue() const - { return hasValue_; } - - const T& Value() const - { return value_; } - - T& Value() - { return value_; } - -private: - T value_; - bool hasValue_; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/******************************************/ REACT_END /******************************************/ - -#endif // REACT_COMMON_OPTIONAL_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/owned_ptr.h b/include/react/common/owned_ptr.h deleted file mode 100644 index f0d06bcb..00000000 --- a/include/react/common/owned_ptr.h +++ /dev/null @@ -1,18 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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_COMMON_OWNED_PTR_H_INCLUDED -#define REACT_COMMON_OWNED_PTR_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/******************************************/ REACT_END /******************************************/ - -#endif // REACT_COMMON_OWNED_PTR_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/Defs.h b/include/react/detail/defs.h similarity index 100% rename from include/react/detail/Defs.h rename to include/react/detail/defs.h diff --git a/include/react/Event.h b/include/react/event.h similarity index 100% rename from include/react/Event.h rename to include/react/event.h diff --git a/include/react/Group.h b/include/react/group.h similarity index 100% rename from include/react/Group.h rename to include/react/group.h diff --git a/include/react/Observer.h b/include/react/observer.h similarity index 100% rename from include/react/Observer.h rename to include/react/observer.h From df1bf4b7c52d6017e74b959dc2d938b952c43f8a Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 25 Oct 2017 01:32:45 +0200 Subject: [PATCH 247/266] Cleanup. Added event tests. --- include/react/detail/event_nodes.h | 30 +- include/react/detail/graph_interface.h | 1 - include/react/detail/node_base.h | 1 - include/react/event.h | 18 +- project/msvc/CppReactTest.vcxproj | 5 +- project/msvc/CppReactTest.vcxproj.filters | 15 +- src/detail/graph_impl.cpp | 5 +- tests/src/MoveTest.h | 134 -------- tests/src/ObserverTest.h | 242 ------------- tests/src/OperationsTest.cpp | 12 - .../src/{MoveTest.cpp => algorithm_tests.cpp} | 0 tests/src/event_tests.cpp | 323 +++++++++++++++++- .../{ObserverTest.cpp => observer_test.cpp} | 0 13 files changed, 354 insertions(+), 432 deletions(-) delete mode 100644 tests/src/MoveTest.h delete mode 100644 tests/src/ObserverTest.h delete mode 100644 tests/src/OperationsTest.cpp rename tests/src/{MoveTest.cpp => algorithm_tests.cpp} (100%) rename tests/src/{ObserverTest.cpp => observer_test.cpp} (100%) diff --git a/include/react/detail/event_nodes.h b/include/react/detail/event_nodes.h index dfec7020..d497acb3 100644 --- a/include/react/detail/event_nodes.h +++ b/include/react/detail/event_nodes.h @@ -121,15 +121,15 @@ class EventMergeNode : public EventNode ~EventMergeNode() { - apply([this] (const auto& ... deps) - { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(deps).GetNodeId())); }, depHolder_); + apply([this] (const auto& ... inputs) + { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(inputs).GetNodeId())); }, inputs_); this->UnregisterMe(); } virtual UpdateResult Update(TurnId turnId) noexcept override { - apply([this] (auto& ... deps) - { REACT_EXPAND_PACK(MergeFromDep(deps)); }, depHolder_); + apply([this] (auto& ... inputs) + { REACT_EXPAND_PACK(MergeFromInput(inputs)); }, inputs_); if (! this->Events().empty()) return UpdateResult::changed; @@ -141,7 +141,7 @@ class EventMergeNode : public EventNode template void MergeFromInput(Event& dep) { - EventInternals& depInternals = GetInternals(dep); + auto& depInternals = GetInternals(dep); this->Events().insert(this->Events().end(), depInternals.Events().begin(), depInternals.Events().end()); } @@ -340,22 +340,23 @@ class EventJoinNode : public EventNode> ~EventJoinNode() { - apply([this] (const auto& ... slots) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(slots.source).GetNodeId())); }, slots_); + apply([this] (const auto& ... slots) + { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(slots.source).GetNodeId())); }, slots_); this->UnregisterMe(); } virtual UpdateResult Update(TurnId turnId) noexcept override { // Move events into buffers. - apply([this, turnId] (Slot& ... slots) { REACT_EXPAND_PACK(FetchBuffer(turnId, slots)); }, slots_); + apply([this, turnId] (Slot& ... slots) + { REACT_EXPAND_PACK(FetchBuffer(turnId, slots)); }, slots_); while (true) { bool isReady = true; // All slots ready? - apply( - [this, &isReady] (Slot& ... slots) + apply([this, &isReady] (Slot& ... slots) { // Todo: combine return values instead REACT_EXPAND_PACK(CheckSlot(slots, isReady)); @@ -366,8 +367,7 @@ class EventJoinNode : public EventNode> break; // Pop values from buffers and emit tuple. - apply( - [this] (Slot& ... slots) + apply([this] (Slot& ... slots) { this->Events().emplace_back(slots.buffer.front() ...); REACT_EXPAND_PACK(slots.buffer.pop_front()); @@ -376,14 +376,9 @@ class EventJoinNode : public EventNode> } if (! this->Events().empty()) - { - this->SetPendingSuccessorCount(successorCount); return UpdateResult::changed; - } else - { return UpdateResult::unchanged; - } } private: @@ -392,7 +387,7 @@ class EventJoinNode : public EventNode> { Slot(const Event& src) : source( src ) - { } + { } Event source; std::deque buffer; @@ -402,7 +397,6 @@ class EventJoinNode : public EventNode> static void FetchBuffer(TurnId turnId, Slot& slot) { slot.buffer.insert(slot.buffer.end(), GetInternals(slot.source).Events().begin(), GetInternals(slot.source).Events().end()); - GetInternals(slot.source).DecrementPendingSuccessorCount(); } template diff --git a/include/react/detail/graph_interface.h b/include/react/detail/graph_interface.h index 6fa51f89..8d0997b3 100644 --- a/include/react/detail/graph_interface.h +++ b/include/react/detail/graph_interface.h @@ -17,7 +17,6 @@ #include #include "react/api.h" -#include "react/common/types.h" #include "react/common/utility.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ diff --git a/include/react/detail/node_base.h b/include/react/detail/node_base.h index 6345d431..2f0c67fd 100644 --- a/include/react/detail/node_base.h +++ b/include/react/detail/node_base.h @@ -15,7 +15,6 @@ #include #include -#include "react/common/types.h" #include "react/common/utility.h" #include "react/detail/graph_interface.h" diff --git a/include/react/event.h b/include/react/event.h index 903d3dc6..5d67c8ce 100644 --- a/include/react/event.h +++ b/include/react/event.h @@ -291,12 +291,14 @@ class EventLink : public Event /// Merge /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Merge(const Group& group, const Event& dep1, const Event& ... deps) -> decltype(auto) +auto Merge(const Group& group, const Event& dep1, const Event& ... deps) -> Event { using REACT_IMPL::EventMergeNode; using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; - return Event::CreateWithNode>(group, SameGroupOrLink(group, dep1), SameGroupOrLink(group, deps) ...); + return CreateWrappedNode, EventMergeNode>( + group, SameGroupOrLink(group, dep1), SameGroupOrLink(group, deps) ...); } template @@ -342,8 +344,8 @@ auto Filter(F&& pred, const Event& dep, const State& ... states) -> Event template auto Transform(const Group& group, F&& op, const Event& dep) -> Event { - auto transformFunc = [capturedOp = std::forward(op)] (EventRange inRange, EventSink out) - { std::transform(inRange.begin(), inRange.end(), out, capturedOp); }; + auto transformFunc = [capturedOp = std::forward(op)] (const EventValueList& evts, EventValueSink out) + { std::transform(evts.begin(), evts.end(), out, capturedOp); }; return Event(group, std::move(transformFunc), dep); } @@ -355,9 +357,9 @@ auto Transform(F&& op, const Event& dep) -> Event template auto Transform(const Group& group, F&& op, const Event& dep, const State& ... states) -> Event { - auto transformFunc = [capturedOp = std::forward(pred)] (EventRange inRange, EventSink out, const Vs& ... values) + auto transformFunc = [capturedOp = std::forward(pred)] (const EventValueList& evts, EventValueSink out, const Us& ... values) { - for (const auto& v : inRange) + for (const auto& v : evts) *out++ = capturedPred(v, values ...); }; @@ -375,10 +377,12 @@ template auto Join(const Group& group, const Event& dep1, const Event& ... deps) -> Event> { using REACT_IMPL::EventJoinNode; + using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; static_assert(sizeof...(Us) > 0, "Join requires at least 2 inputs."); - return Event>::CreateWithNode>( + return CreateWrappedNode>, EventJoinNode>( group, SameGroupOrLink(group, dep1), SameGroupOrLink(group, deps) ...); } diff --git a/project/msvc/CppReactTest.vcxproj b/project/msvc/CppReactTest.vcxproj index 7516767f..eda051bb 100644 --- a/project/msvc/CppReactTest.vcxproj +++ b/project/msvc/CppReactTest.vcxproj @@ -164,9 +164,8 @@ - - - + + diff --git a/project/msvc/CppReactTest.vcxproj.filters b/project/msvc/CppReactTest.vcxproj.filters index 6004eeed..8ed9f84c 100644 --- a/project/msvc/CppReactTest.vcxproj.filters +++ b/project/msvc/CppReactTest.vcxproj.filters @@ -15,15 +15,6 @@ - - Source Files - - - Source Files - - - Source Files - Source Files @@ -39,5 +30,11 @@ Source Files + + Source Files + + + Source Files + \ No newline at end of file diff --git a/src/detail/graph_impl.cpp b/src/detail/graph_impl.cpp index c6ceb6be..fb44a252 100644 --- a/src/detail/graph_impl.cpp +++ b/src/detail/graph_impl.cpp @@ -16,11 +16,8 @@ #include #include -#include "react/detail/graph_impl.h" - -#include "react/common/types.h" -#include "react/common/expected.h" #include "react/detail/graph_interface.h" +#include "react/detail/graph_impl.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ diff --git a/tests/src/MoveTest.h b/tests/src/MoveTest.h deleted file mode 100644 index 0804423a..00000000 --- a/tests/src/MoveTest.h +++ /dev/null @@ -1,134 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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) - -#pragma once - -#include "gtest/gtest.h" - -#include "react/Domain.h" -#include "react/Signal.h" - -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace { - -using namespace react; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MoveTest fixture -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class MoveTest : public testing::Test -{ -public: - template - class MyEngine : public TParams::template EngineT {}; - - REACTIVE_DOMAIN(MyDomain, TParams::mode, MyEngine) - - struct Stats - { - int copyCount = 0; - int moveCount = 0; - }; - - struct CopyCounter - { - int v = 0; - Stats* stats = nullptr; - - CopyCounter() = default; - - CopyCounter(int x, Stats* s) : - v( x ), - stats( s ) - {} - - CopyCounter(const CopyCounter& other) : - v( other.v ), - stats( other.stats ) - { - stats->copyCount++; - } - - CopyCounter(CopyCounter&& other) : - v( other.v ), - stats( other.stats ) - { - stats->moveCount++; - } - - CopyCounter& operator=(const CopyCounter& other) - { - v = other.v; - stats = other.stats; - stats->copyCount++; - return *this; - } - - CopyCounter& operator=(CopyCounter&& other) - { - v = other.v; - stats = other.stats; - stats->moveCount++; - return *this; - } - - CopyCounter operator+(const CopyCounter& r) const - { - return CopyCounter{ v + r.v, stats }; - } - - bool operator==(const CopyCounter& other) const - { - return v == other.v; - } - }; -}; - -TYPED_TEST_CASE_P(MoveTest); - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Copy1 -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(MoveTest, Copy1) -{ - using D = typename Copy1::MyDomain; - using CopyCounter = typename Copy1::CopyCounter; - using Stats = typename Copy1::Stats; - - Stats stats1; - - auto a = MakeVar(CopyCounter{1,&stats1}); - auto b = MakeVar(CopyCounter{10,&stats1}); - auto c = MakeVar(CopyCounter{100,&stats1}); - auto d = MakeVar(CopyCounter{1000,&stats1}); - - // 4x move to value_ - // 4x copy to newValue_ (can't be unitialized for references) - ASSERT_EQ(stats1.copyCount, 4); - ASSERT_EQ(stats1.moveCount, 4); - - auto x = a + b + c + d; - - ASSERT_EQ(stats1.copyCount, 4); - ASSERT_EQ(stats1.moveCount, 7); - ASSERT_EQ(x().v, 1111); - - a <<= CopyCounter{2,&stats1}; - - ASSERT_EQ(stats1.copyCount, 4); - ASSERT_EQ(stats1.moveCount, 10); - ASSERT_EQ(x().v, 1112); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -REGISTER_TYPED_TEST_CASE_P -( - MoveTest, - Copy1 -); - -} // ~namespace diff --git a/tests/src/ObserverTest.h b/tests/src/ObserverTest.h deleted file mode 100644 index 559c0686..00000000 --- a/tests/src/ObserverTest.h +++ /dev/null @@ -1,242 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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) - -#pragma once - -#include "gtest/gtest.h" - -#include - -#include "react/Domain.h" -#include "react/Signal.h" -#include "react/Event.h" -#include "react/Observer.h" - -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace { - -using namespace react; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ObserverTest fixture -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ObserverTest : public testing::Test -{ -public: - template - class MyEngine : public TParams::template EngineT {}; - - REACTIVE_DOMAIN(MyDomain, TParams::mode, MyEngine) -}; - -TYPED_TEST_CASE_P(ObserverTest); - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Detach test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(ObserverTest, Detach) -{ - using D = typename Detach::MyDomain; - - auto a1 = MakeVar(1); - auto a2 = MakeVar(1); - - auto result = a1 + a2; - - int observeCount1 = 0; - int observeCount2 = 0; - int observeCount3 = 0; - - int phase; - - auto obs1 = Observe(result, [&] (int v) - { - observeCount1++; - - if (phase == 0) - ASSERT_EQ(v,3); - else if (phase == 1) - ASSERT_EQ(v,4); - else - ASSERT_TRUE(false); - }); - - auto obs2 = Observe(result, [&] (int v) - { - observeCount2++; - - if (phase == 0) - ASSERT_EQ(v,3); - else if (phase == 1) - ASSERT_EQ(v,4); - else - ASSERT_TRUE(false); - }); - - auto obs3 = Observe(result, [&] (int v) - { - observeCount3++; - - if (phase == 0) - ASSERT_EQ(v,3); - else if (phase == 1) - ASSERT_EQ(v,4); - else - ASSERT_TRUE(false); - }); - - phase = 0; - a1 <<= 2; - ASSERT_EQ(observeCount1,1); - ASSERT_EQ(observeCount2,1); - ASSERT_EQ(observeCount3,1); - - phase = 1; - obs1.Detach(); - a1 <<= 3; - ASSERT_EQ(observeCount1,1); - ASSERT_EQ(observeCount2,2); - ASSERT_EQ(observeCount3,2); - - phase = 2; - obs2.Detach(); - obs3.Detach(); - a1 <<= 4; - ASSERT_EQ(observeCount1,1); - ASSERT_EQ(observeCount2,2); - ASSERT_EQ(observeCount3,2); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ScopedObserver test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(ObserverTest, ScopedObserverTest) -{ - using D = typename ScopedObserverTest::MyDomain; - - std::vector results; - - auto in = MakeVar(1); - - { - ScopedObserver obs = Observe(in, [&] (int v) { - results.push_back(v); - }); - - in <<=2; - } - - in <<=3; - - ASSERT_EQ(results.size(),1); - ASSERT_EQ(results[0], 2); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Synced Observe test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(ObserverTest, SyncedObserveTest) -{ - using D = typename SyncedObserveTest::MyDomain; - - auto in1 = MakeVar(1); - auto in2 = MakeVar(1); - - auto sum = in1 + in2; - auto prod = in1 * in2; - auto diff = in1 - in2; - - auto src1 = MakeEventSource(); - auto src2 = MakeEventSource(); - - Observe(src1, With(sum,prod,diff), [] (Token, int sum, int prod, int diff) { - ASSERT_EQ(sum, 33); - ASSERT_EQ(prod, 242); - ASSERT_EQ(diff, 11); - }); - - Observe(src2, With(sum,prod,diff), [] (int e, int sum, int prod, int diff) { - ASSERT_EQ(e, 42); - ASSERT_EQ(sum, 33); - ASSERT_EQ(prod, 242); - ASSERT_EQ(diff, 11); - }); - - in1 <<= 22; - in2 <<= 11; - - src1.Emit(); - src2.Emit(42); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DetachThisObserver1 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(ObserverTest, DetachThisObserver1) -{ - using D = typename DetachThisObserver1::MyDomain; - - auto src = MakeEventSource(); - - int count = 0; - - Observe(src, [&] (Token) -> ObserverAction { - ++count; - return ObserverAction::stop_and_detach; - }); - - src.Emit(); - src.Emit(); - - printf("Count %d\n", count); - ASSERT_EQ(count, 1); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DetachThisObserver2 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(ObserverTest, DetachThisObserver2) -{ - using D = typename DetachThisObserver2::MyDomain; - - auto in1 = MakeVar(1); - auto in2 = MakeVar(1); - - auto sum = in1 + in2; - auto prod = in1 * in2; - auto diff = in1 - in2; - - auto src = MakeEventSource(); - - int count = 0; - - Observe(src, With(sum,prod,diff), [&] (Token, int sum, int prod, int diff) -> ObserverAction { - ++count; - return ObserverAction::stop_and_detach; - }); - - in1 <<= 22; - in2 <<= 11; - - src.Emit(); - src.Emit(); - - ASSERT_EQ(count, 1); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -REGISTER_TYPED_TEST_CASE_P -( - ObserverTest, - Detach, - ScopedObserverTest, - SyncedObserveTest, - DetachThisObserver1, - DetachThisObserver2 -); - -} // ~namespace diff --git a/tests/src/OperationsTest.cpp b/tests/src/OperationsTest.cpp deleted file mode 100644 index 6054092a..00000000 --- a/tests/src/OperationsTest.cpp +++ /dev/null @@ -1,12 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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) - - -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace { - - -} // ~namespace \ No newline at end of file diff --git a/tests/src/MoveTest.cpp b/tests/src/algorithm_tests.cpp similarity index 100% rename from tests/src/MoveTest.cpp rename to tests/src/algorithm_tests.cpp diff --git a/tests/src/event_tests.cpp b/tests/src/event_tests.cpp index 31565b87..98d27bfc 100644 --- a/tests/src/event_tests.cpp +++ b/tests/src/event_tests.cpp @@ -9,8 +9,12 @@ #include "react/event.h" #include "react/observer.h" -#include +#include #include +#include +#include +#include +#include using namespace react; @@ -216,4 +220,321 @@ TEST(EventTest, Links) EXPECT_EQ(3, output); EXPECT_EQ(3, turns); +} + +TEST(EventTest, EventSources) +{ + Group g; + + EventSource es1( g ); + EventSource es2( g ); + + std::queue results1; + std::queue results2; + + Observer obs1([&] (const auto& events) + { + for (int e : events) + results1.push(e); + }, es1); + + Observer obs2([&] (const auto& events) + { + for (int e : events) + results2.push(e); + }, es2); + + es1.Emit(10); + es1.Emit(20); + es1.Emit(30); + + es2.Emit(40); + es2.Emit(50); + es2.Emit(60); + + // First batch. + EXPECT_FALSE(results1.empty()); + EXPECT_EQ(results1.front(), 10); + results1.pop(); + + EXPECT_FALSE(results1.empty()); + EXPECT_EQ(results1.front(), 20); + results1.pop(); + + EXPECT_FALSE(results1.empty()); + EXPECT_EQ(results1.front(),30); + results1.pop(); + + EXPECT_TRUE(results1.empty()); + + // Second batch. + EXPECT_FALSE(results2.empty()); + EXPECT_EQ(results2.front(),40); + results2.pop(); + + EXPECT_FALSE(results2.empty()); + EXPECT_EQ(results2.front(),50); + results2.pop(); + + EXPECT_FALSE(results2.empty()); + EXPECT_EQ(results2.front(),60); + results2.pop(); + + EXPECT_TRUE(results2.empty()); +} + +TEST(EventTest, Merge1) +{ + Group g; + + EventSource a1( g ); + EventSource a2( g ); + EventSource a3( g ); + + Event merged = Merge(g, a1, a2, a3); + + std::vector results; + + Observer obs1([&] (const auto& events) + { + for (int e : events) + results.push_back(e); + }, merged); + + g.DoTransaction([&] + { + a1.Emit(10); + a2.Emit(20); + a3.Emit(30); + }); + + EXPECT_EQ(results.size(), 3); + + EXPECT_TRUE(std::find(results.begin(), results.end(), 10) != results.end()); + EXPECT_TRUE(std::find(results.begin(), results.end(), 20) != results.end()); + EXPECT_TRUE(std::find(results.begin(), results.end(), 30) != results.end()); +} + +TEST(EventTest, Merge2) +{ + Group g; + + EventSource a1( g ); + EventSource a2( g ); + EventSource a3( g ); + + Event merged = Merge(a1, a2, a3); + + std::vector results; + + Observer obs1([&] (const auto& events) + { + for (const auto& e : events) + results.push_back(e); + }, merged); + + std::string s1("one"); + std::string s2("two"); + std::string s3("three"); + + g.DoTransaction([&] + { + a1.Emit(s1); + a2.Emit(s2); + a3.Emit(s3); + }); + + EXPECT_EQ(results.size(), 3); + + EXPECT_TRUE(std::find(results.begin(), results.end(), "one") != results.end()); + EXPECT_TRUE(std::find(results.begin(), results.end(), "two") != results.end()); + EXPECT_TRUE(std::find(results.begin(), results.end(), "three") != results.end()); +} + +TEST(EventTest, Merge3) +{ + Group g; + + EventSource a1( g ); + EventSource a2( g ); + + Event f1 = Filter([] (int v) { return true; }, a1); + Event f2 = Filter([] (int v) { return true; }, a2); + + Event merged = Merge(f1, f2); + + std::queue results; + + Observer obs1([&] (const auto& events) + { + for (int e : events) + results.push(e); + }, merged); + + a1.Emit(10); + a2.Emit(20); + a1.Emit(30); + + EXPECT_FALSE(results.empty()); + EXPECT_EQ(results.front(), 10); + results.pop(); + + EXPECT_FALSE(results.empty()); + EXPECT_EQ(results.front(), 20); + results.pop(); + + EXPECT_FALSE(results.empty()); + EXPECT_EQ(results.front(), 30); + results.pop(); + + EXPECT_TRUE(results.empty()); +} + +TEST(EventTest, Filter) +{ + Group g; + + EventSource in( g ); + + std::queue results; + + Event filtered = Filter(g, [] (const std::string& s) + { + return s == "Hello World"; + }, in); + + Observer obs1([&] (const auto& events) + { + for (const auto& e : events) + results.push(e); + }, filtered); + + in.Emit(std::string("Hello Worlt")); + in.Emit(std::string("Hello World")); + in.Emit(std::string("Hello Vorld")); + + EXPECT_FALSE(results.empty()); + EXPECT_EQ(results.front(), "Hello World"); + results.pop(); + + EXPECT_TRUE(results.empty()); +} + +TEST(EventTest, Transform) +{ + Group g; + + EventSource in1( g ); + EventSource in2( g ); + + std::vector results; + + Event merged = Merge(in1, in2); + + Event transformed = Transform([] (std::string s) -> std::string + { + std::transform(s.begin(), s.end(), s.begin(), ::toupper); + return s; + }, merged); + + Observer obs1([&] (const auto& events) + { + for (const auto& e : events) + results.push_back(e); + }, transformed); + + in1.Emit(std::string("Hello Worlt")); + in1.Emit(std::string("Hello World")); + in2.Emit(std::string("Hello Vorld")); + + EXPECT_EQ(results.size(), 3); + EXPECT_TRUE(std::find(results.begin(), results.end(), "HELLO WORLT") != results.end()); + EXPECT_TRUE(std::find(results.begin(), results.end(), "HELLO WORLD") != results.end()); + EXPECT_TRUE(std::find(results.begin(), results.end(), "HELLO VORLD") != results.end()); +} + +TEST(EventTest, Chain) +{ + Group g; + + std::vector results; + + EventSource in1( g ); + EventSource in2( g ); + + auto merged = Merge(in1, in2); + int turns = 0; + + Event processed([&] (const auto& events, auto out) + { + for (const auto& e : events) + { + *out = 0.1f * e; + *out = 1.5f * e; + } + + ++turns; + }, merged); + + Observer obs1([&] (const auto& events) + { + for (float e : events) + results.push_back(e); + }, processed); + + g.DoTransaction([&] { + in1.Emit(10); + in1.Emit(20); + }); + + in2 << 30; + + EXPECT_EQ(results.size(), 6); + EXPECT_EQ(turns, 2); + + EXPECT_EQ(results[0], 1.0f); + EXPECT_EQ(results[1], 15.0f); + EXPECT_EQ(results[2], 2.0f); + EXPECT_EQ(results[3], 30.0f); + EXPECT_EQ(results[4], 3.0f); + EXPECT_EQ(results[5], 45.0f); +} + +TEST(EventTest, Join) +{ + Group g; + + EventSource in1( g ); + EventSource in2( g ); + EventSource in3( g ); + + Event> joined = Join(in1, in2, in3); + + std::vector> results; + + Observer obs1([&] (const auto& events) + { + for (const auto& e : events) + results.push_back(e); + }, joined); + + in1.Emit(10); + EXPECT_EQ(results.size(), 0); + + in2.Emit(10); + EXPECT_EQ(results.size(), 0); + + in2.Emit(20); + EXPECT_EQ(results.size(), 0); + + in3.Emit(10); + EXPECT_EQ(results.size(), 1); + EXPECT_EQ(results[0], std::make_tuple(10, 10, 10)); + + in3.Emit(20); + EXPECT_EQ(results.size(), 1); + + in1.Emit(20); + EXPECT_EQ(results.size(), 2); + EXPECT_EQ(results[1], std::make_tuple(20, 20, 20)); } \ No newline at end of file diff --git a/tests/src/ObserverTest.cpp b/tests/src/observer_test.cpp similarity index 100% rename from tests/src/ObserverTest.cpp rename to tests/src/observer_test.cpp From 55fbe9b961784f7820cb63e62e2b1c66842e84a5 Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 26 Oct 2017 00:44:34 +0200 Subject: [PATCH 248/266] Removed obsolete files. Added more tests. --- include/react/algorithm.h | 10 +- include/react/api.h | 2 + include/react/detail/algorithm_nodes.h | 62 +- include/react/detail/event_nodes.h | 16 +- include/react/detail/state_nodes.h | 2 +- include/react/event.h | 43 +- project/msvc/CppReactTest.vcxproj | 1 - project/msvc/CppReactTest.vcxproj.filters | 3 - tests/src/EventStreamTest.h | 335 -------- tests/src/EventStreamTestQ.cpp | 29 - tests/src/ObserverTestQ.cpp | 29 - tests/src/OperationsTest.h | 971 ---------------------- tests/src/OperationsTestQ.cpp | 29 - tests/src/ParallelizationTest.cpp | 12 - tests/src/ParallelizationTest.h | 63 -- tests/src/SignalTest.h | 664 --------------- tests/src/SignalTestQ.cpp | 29 - tests/src/TestUtil.h | 25 - tests/src/TransactionTest.h | 571 ------------- tests/src/algorithm_tests.cpp | 708 +++++++++++++++- tests/src/event_tests.cpp | 149 +++- tests/src/state_tests.cpp | 167 +++- tests/src/transaction_tests.cpp | 7 +- 23 files changed, 1065 insertions(+), 2862 deletions(-) delete mode 100644 tests/src/EventStreamTest.h delete mode 100644 tests/src/EventStreamTestQ.cpp delete mode 100644 tests/src/ObserverTestQ.cpp delete mode 100644 tests/src/OperationsTest.h delete mode 100644 tests/src/OperationsTestQ.cpp delete mode 100644 tests/src/ParallelizationTest.cpp delete mode 100644 tests/src/ParallelizationTest.h delete mode 100644 tests/src/SignalTest.h delete mode 100644 tests/src/SignalTestQ.cpp delete mode 100644 tests/src/TestUtil.h delete mode 100644 tests/src/TransactionTest.h diff --git a/include/react/algorithm.h b/include/react/algorithm.h index 9c1b2023..075f3d3c 100644 --- a/include/react/algorithm.h +++ b/include/react/algorithm.h @@ -15,12 +15,12 @@ #include #include -#include "react/API.h" -#include "react/detail/algorithm_nodes.h" - +#include "react/api.h" #include "react/state.h" #include "react/event.h" +#include "react/detail/algorithm_nodes.h" + /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -66,7 +66,6 @@ template auto Iterate(const Group& group, T&& initialValue, F&& func, const Event& evnt) -> State { using REACT_IMPL::IterateNode; - using REACT_IMPL::IsCallableWith; using REACT_IMPL::SameGroupOrLink; using REACT_IMPL::CreateWrappedNode; @@ -80,7 +79,6 @@ template auto IterateByRef(const Group& group, T&& initialValue, F&& func, const Event& evnt) -> State { using REACT_IMPL::IterateByRefNode; - using REACT_IMPL::IsCallableWith; using REACT_IMPL::SameGroupOrLink; using REACT_IMPL::CreateWrappedNode; @@ -105,7 +103,6 @@ template auto Iterate(const Group& group, T&& initialValue, F&& func, const Event& evnt, const State& ... states) -> State { using REACT_IMPL::SyncedIterateNode; - using REACT_IMPL::IsCallableWith; using REACT_IMPL::SameGroupOrLink; using REACT_IMPL::CreateWrappedNode; @@ -119,7 +116,6 @@ template auto IterateByRef(const Group& group, T&& initialValue, F&& func, const Event& evnt, const State& ... states) -> State { using REACT_IMPL::SyncedIterateByRefNode; - using REACT_IMPL::IsCallableWith; using REACT_IMPL::SameGroupOrLink; using REACT_IMPL::CreateWrappedNode; diff --git a/include/react/api.h b/include/react/api.h index 89ace35c..d8d15204 100644 --- a/include/react/api.h +++ b/include/react/api.h @@ -33,6 +33,8 @@ enum class TransactionFlags REACT_DEFINE_BITMASK_OPERATORS(TransactionFlags) +enum class Token { value }; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// API types /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/algorithm_nodes.h b/include/react/detail/algorithm_nodes.h index b0a7d9da..968080d5 100644 --- a/include/react/detail/algorithm_nodes.h +++ b/include/react/detail/algorithm_nodes.h @@ -44,7 +44,7 @@ class IterateNode : public StateNode virtual UpdateResult Update(TurnId turnId) noexcept override { - S newValue = func_(EventRange( GetInternals(evnt_).Events() ), this->Value()); + S newValue = func_(GetInternals(evnt_).Events(), this->Value()); if (! (newValue == this->Value())) { @@ -81,15 +81,15 @@ class IterateByRefNode : public StateNode ~IterateByRefNode() { - this->DetachFromMe(GetInternals(evnt).GetNodeId()); + this->DetachFromMe(GetInternals(evnt_).GetNodeId()); this->UnregisterMe(); } virtual UpdateResult Update(TurnId turnId) noexcept override { - func_(EventRange( GetInternals(evnt_).Events() ), this->Value()); + func_(GetInternals(evnt_).Events(), this->Value()); - // Always assume change + // Always assume a change return UpdateResult::changed; } @@ -119,7 +119,8 @@ class SyncedIterateNode : public StateNode ~SyncedIterateNode() { - apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(syncs).GetNodeId())); }, syncHolder_); + apply([this] (const auto& ... syncs) + { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(syncs).GetNodeId())); }, syncHolder_); this->DetachFromMe(GetInternals(evnt_).GetNodeId()); this->UnregisterMe(); } @@ -130,12 +131,10 @@ class SyncedIterateNode : public StateNode if (GetInternals(evnt_).Events().empty()) return UpdateResult::unchanged; - S newValue = apply( - [this] (const auto& ... syncs) + S newValue = apply([this] (const auto& ... syncs) { - return func_(EventRange( GetInternals(evnt_).Events() ), this->Value(), GetInternals(syncs).Value() ...); - }, - syncHolder_); + return func_(GetInternals(evnt_).Events(), this->Value(), GetInternals(syncs).Value() ...); + }, syncHolder_); if (! (newValue == this->Value())) { @@ -190,7 +189,7 @@ class SyncedIterateByRefNode : public StateNode apply( [this] (const auto& ... args) { - func_(EventRange( GetInternals(evnt_).Events() ), this->Value(), GetInternals(args).Value() ...); + func_(GetInternals(evnt_).Events(), this->Value(), GetInternals(args).Value() ...); }, syncHolder_); @@ -199,7 +198,7 @@ class SyncedIterateByRefNode : public StateNode private: F func_; - Event events_; + Event evnt_; std::tuple ...> syncHolder_; }; @@ -305,75 +304,70 @@ class SnapshotNode : public StateNode /// MonitorNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class MonitorNode : public EventStreamNode +class MonitorNode : public EventNode { public: - MonitorNode(const Group& group, const State& target) : - MonitorNode::EventStreamNode( group ), - target_( target ) + MonitorNode(const Group& group, const State& input) : + MonitorNode::EventNode( group ), + input_( input ) { this->RegisterMe(); - this->AttachToMe(GetInternals(target).GetNodeId()); + this->AttachToMe(GetInternals(input_).GetNodeId()); } ~MonitorNode() { - this->DetachFromMe(GetInternals(target_).GetNodeId()); + this->DetachFromMe(GetInternals(input_).GetNodeId()); this->UnregisterMe(); } virtual UpdateResult Update(TurnId turnId) noexcept override { - this->Events().push_back(GetInternals(target_).Value()); - + this->Events().push_back(GetInternals(input_).Value()); return UpdateResult::changed; } private: - State target_; + State input_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// PulseNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class PulseNode : public EventStreamNode +class PulseNode : public EventNode { public: - PulseNode(const Group& group, const State& target, const Event& trigger) : - PulseNode::EventStreamNode( group ), - target_( target ), + PulseNode(const Group& group, const State& input, const Event& trigger) : + PulseNode::EventNode( group ), + input_( input ), trigger_( trigger ) { this->RegisterMe(); - this->AttachToMe(GetInternals(target).GetNodeId()); + this->AttachToMe(GetInternals(input).GetNodeId()); this->AttachToMe(GetInternals(trigger).GetNodeId()); } ~PulseNode() { this->DetachFromMe(GetInternals(trigger_).GetNodeId()); - this->DetachFromMe(GetInternals(target_).GetNodeId()); + this->DetachFromMe(GetInternals(input_).GetNodeId()); this->UnregisterMe(); } virtual UpdateResult Update(TurnId turnId) noexcept override { - for (size_t i = 0; i < GetInternals(trigger_).Events().size(); i++) - this->Events().push_back(GetInternals(target_).Value()); + for (size_t i = 0; i < GetInternals(trigger_).Events().size(); ++i) + this->Events().push_back(GetInternals(input_).Value()); if (! this->Events().empty()) - { return UpdateResult::changed; - } else - { return UpdateResult::unchanged; - } } private: - State target_; + State input_; Event trigger_; }; diff --git a/include/react/detail/event_nodes.h b/include/react/detail/event_nodes.h index d497acb3..7c988b23 100644 --- a/include/react/detail/event_nodes.h +++ b/include/react/detail/event_nodes.h @@ -285,27 +285,27 @@ class SyncedEventProcessingNode : public EventNode syncHolder_( syncs ... ) { this->RegisterMe(); - this->AttachToMe(dep->GetNodeId()); - REACT_EXPAND_PACK(this->AttachToMe(syncs->GetNodeId())); + this->AttachToMe(GetInternals(dep).GetNodeId()); + REACT_EXPAND_PACK(this->AttachToMe(GetInternals(syncs).GetNodeId())); } ~SyncedEventProcessingNode() { - apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(syncs->GetNodeId())); }, syncHolder_); - this->DetachFromMe(dep_->GetNodeId()); + apply([this] (const auto& ... syncs) + { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(syncs).GetNodeId())); }, syncHolder_); + this->DetachFromMe(GetInternals(dep_).GetNodeId()); this->UnregisterMe(); } virtual UpdateResult Update(TurnId turnId) noexcept override { // Updates might be triggered even if only sync nodes changed. Ignore those. - if (dep_->Events().empty()) + if (GetInternals(dep_).Events().empty()) return UpdateResult::unchanged; - apply( - [this] (const auto& ... syncs) + apply([this] (const auto& ... syncs) { - func_(EventRange( this->dep_->Events() ), std::back_inserter(this->Events()), syncs->Value() ...); + func_(GetInternals(dep_).Events(), std::back_inserter(this->Events()), GetInternals(syncs).Value() ...); }, syncHolder_); diff --git a/include/react/detail/state_nodes.h b/include/react/detail/state_nodes.h index 655ab542..c3b717b2 100644 --- a/include/react/detail/state_nodes.h +++ b/include/react/detail/state_nodes.h @@ -261,7 +261,7 @@ class StateSlotNode : public StateNode { return UpdateResult::changed; } }; - State input_; + State input_; NodeId inputNodeId_; VirtualInputNode slotInput_; diff --git a/include/react/event.h b/include/react/event.h index 5d67c8ce..ecde17b2 100644 --- a/include/react/event.h +++ b/include/react/event.h @@ -10,17 +10,17 @@ #pragma once #include "react/detail/defs.h" -#include "react/api.h" -#include "react/group.h" -#include "react/common/ptrcache.h" #include #include #include -#include "react/detail/event_nodes.h" +#include "react/api.h" +#include "react/group.h" +#include "react/detail/event_nodes.h" +#include "react/common/ptrcache.h" /*****************************************/ REACT_BEGIN /*****************************************/ @@ -136,12 +136,12 @@ class EventSource : public Event void Emit(E&& value) { EmitValue(std::move(value)); } - template >::type> + template >> void Emit() { EmitValue(Token::value); } EventSource& operator<<(const E& value) - { EmitValue(e); return *this; } + { EmitValue(value); return *this; } EventSource& operator<<(E&& value) { EmitValue(std::move(value)); return *this; } @@ -311,8 +311,8 @@ auto Merge(const Event& dep1, const Event& ... deps) -> decltype(auto) template auto Filter(const Group& group, F&& pred, const Event& dep) -> Event { - auto filterFunc = [capturedPred = std::forward(pred)] (const EventValueList& evts, EventValueSink out) - { std::copy_if(evts.begin(), evts.end(), out, capturedPred); }; + auto filterFunc = [capturedPred = std::forward(pred)] (const EventValueList& events, EventValueSink out) + { std::copy_if(events.begin(), events.end(), out, capturedPred); }; return Event(group, std::move(filterFunc), dep); } @@ -324,19 +324,19 @@ auto Filter(F&& pred, const Event& dep) -> Event template auto Filter(const Group& group, F&& pred, const Event& dep, const State& ... states) -> Event { - auto filterFunc = [capturedPred = std::forward(pred)] (const EventValueList& evts, EventValueSink out, const Us& ... values) + auto filterFunc = [capturedPred = std::forward(pred)] (const EventValueList& evts, EventValueSink out, const Ts& ... values) { for (const auto& v : evts) if (capturedPred(v, values ...)) *out++ = v; }; - return Event(group, std::move(filterFunc), dep, State ...); + return Event(group, std::move(filterFunc), dep, states ...); } template auto Filter(F&& pred, const Event& dep, const State& ... states) -> Event - { return Filter(dep.GetGroup(), std::forward(pred), dep); } + { return Filter(dep.GetGroup(), std::forward(pred), dep, states ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Transform @@ -357,10 +357,10 @@ auto Transform(F&& op, const Event& dep) -> Event template auto Transform(const Group& group, F&& op, const Event& dep, const State& ... states) -> Event { - auto transformFunc = [capturedOp = std::forward(pred)] (const EventValueList& evts, EventValueSink out, const Us& ... values) + auto transformFunc = [capturedOp = std::forward(op)] (const EventValueList& evts, EventValueSink out, const Us& ... values) { for (const auto& v : evts) - *out++ = capturedPred(v, values ...); + *out++ = capturedOp(v, values ...); }; return Event(group, std::move(transformFunc), dep, states ...); @@ -390,23 +390,6 @@ template auto Join(const Event& dep1, const Event& ... deps) -> Event> { return Join(dep1.GetGroup(), dep1, deps ...); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Token -/////////////////////////////////////////////////////////////////////////////////////////////////// -enum class Token { value }; - -/*struct Tokenizer -{ - template - Token operator()(const T&) const { return Token::value; } -}; - -template -auto Tokenize(T&& source) -> decltype(auto) -{ - return Transform(source, Tokenizer{ }); -}*/ - /******************************************/ REACT_END /******************************************/ /***************************************/ REACT_IMPL_BEGIN /**************************************/ diff --git a/project/msvc/CppReactTest.vcxproj b/project/msvc/CppReactTest.vcxproj index eda051bb..9cecab98 100644 --- a/project/msvc/CppReactTest.vcxproj +++ b/project/msvc/CppReactTest.vcxproj @@ -166,7 +166,6 @@ - diff --git a/project/msvc/CppReactTest.vcxproj.filters b/project/msvc/CppReactTest.vcxproj.filters index 8ed9f84c..d60eac41 100644 --- a/project/msvc/CppReactTest.vcxproj.filters +++ b/project/msvc/CppReactTest.vcxproj.filters @@ -15,9 +15,6 @@ - - Source Files - Source Files diff --git a/tests/src/EventStreamTest.h b/tests/src/EventStreamTest.h deleted file mode 100644 index db12a8ea..00000000 --- a/tests/src/EventStreamTest.h +++ /dev/null @@ -1,335 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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) - -#pragma once - -#include -#include - -#include "gtest/gtest.h" - -#include "react/Domain.h" -#include "react/Event.h" - -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace { - -using namespace react; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventStreamTest fixture -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EventStreamTest : public testing::Test -{ -public: - template - class MyEngine : public TParams::template EngineT {}; - - REACTIVE_DOMAIN(MyDomain, TParams::mode, MyEngine) -}; - -TYPED_TEST_CASE_P(EventStreamTest); - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventSources test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(EventStreamTest, EventSources) -{ - using D = typename EventSources::MyDomain; - - auto es1 = MakeEventSource(); - auto es2 = MakeEventSource(); - - std::queue results1; - std::queue results2; - - Observe(es1, [&] (int v) - { - results1.push(v); - }); - - Observe(es2, [&] (int v) - { - results2.push(v); - }); - - es1 << 10 << 20 << 30; - es2 << 40 << 50 << 60; - - // 1 - ASSERT_FALSE(results1.empty()); - ASSERT_EQ(results1.front(),10); - results1.pop(); - - ASSERT_FALSE(results1.empty()); - ASSERT_EQ(results1.front(),20); - results1.pop(); - - ASSERT_FALSE(results1.empty()); - ASSERT_EQ(results1.front(),30); - results1.pop(); - - ASSERT_TRUE(results1.empty()); - - // 2 - ASSERT_FALSE(results2.empty()); - ASSERT_EQ(results2.front(),40); - results2.pop(); - - ASSERT_FALSE(results2.empty()); - ASSERT_EQ(results2.front(),50); - results2.pop(); - - ASSERT_FALSE(results2.empty()); - ASSERT_EQ(results2.front(),60); - results2.pop(); - - ASSERT_TRUE(results2.empty()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventMerge1 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(EventStreamTest, EventMerge1) -{ - using D = typename EventMerge1::MyDomain; - - auto a1 = MakeEventSource(); - auto a2 = MakeEventSource(); - auto a3 = MakeEventSource(); - - auto merged = Merge(a1, a2, a3); - - std::vector results; - - Observe(merged, [&] (int v) - { - results.push_back(v); - }); - - DoTransaction([&] { - a1 << 10; - a2 << 20; - a3 << 30; - }); - - ASSERT_EQ(results.size(), 3); - ASSERT_TRUE(std::find(results.begin(), results.end(), 10) != results.end()); - ASSERT_TRUE(std::find(results.begin(), results.end(), 20) != results.end()); - ASSERT_TRUE(std::find(results.begin(), results.end(), 30) != results.end()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventMerge2 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(EventStreamTest, EventMerge2) -{ - using D = typename EventMerge2::MyDomain; - - auto a1 = MakeEventSource(); - auto a2 = MakeEventSource(); - auto a3 = MakeEventSource(); - - auto merged = Merge(a1, a2, a3); - - std::vector results; - - Observe(merged, [&] (std::string s) - { - results.push_back(s); - }); - - std::string s1("one"); - std::string s2("two"); - std::string s3("three"); - - DoTransaction([&] { - a1 << s1; - a2 << s2; - a3 << s3; - }); - - ASSERT_EQ(results.size(), 3); - ASSERT_TRUE(std::find(results.begin(), results.end(), "one") != results.end()); - ASSERT_TRUE(std::find(results.begin(), results.end(), "two") != results.end()); - ASSERT_TRUE(std::find(results.begin(), results.end(), "three") != results.end()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventMerge3 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(EventStreamTest, EventMerge3) -{ - using D = typename EventMerge3::MyDomain; - - auto a1 = MakeEventSource(); - auto a2 = MakeEventSource(); - - auto f1 = Filter(a1, [] (int v) { return true; }); - auto f2 = Filter(a2, [] (int v) { return true; }); - - auto merged = Merge(f1, f2); - - std::queue results; - - Observe(merged, [&] (int s) - { - results.push(s); - }); - - a1 << 10; - a2 << 20; - a1 << 30; - - ASSERT_FALSE(results.empty()); - ASSERT_EQ(results.front(),10); - results.pop(); - - ASSERT_FALSE(results.empty()); - ASSERT_EQ(results.front(),20); - results.pop(); - - ASSERT_FALSE(results.empty()); - ASSERT_EQ(results.front(),30); - results.pop(); - - ASSERT_TRUE(results.empty()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventFilter test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(EventStreamTest, EventFilter) -{ - using D = typename EventFilter::MyDomain; - - using std::string; - - std::queue results; - - auto in = MakeEventSource(); - - auto filtered = Filter(in, [] (const string& s) - { - return s == "Hello World"; - }); - - - Observe(filtered, [&] (const string& s) - { - results.push(s); - }); - - in << string("Hello Worlt") << string("Hello World") << string("Hello Vorld"); - - ASSERT_FALSE(results.empty()); - ASSERT_EQ(results.front(), "Hello World"); - results.pop(); - - ASSERT_TRUE(results.empty()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventTransform test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(EventStreamTest, EventTransform) -{ - using D = typename EventTransform::MyDomain; - - using std::string; - - std::vector results; - - auto in1 = MakeEventSource(); - auto in2 = MakeEventSource(); - - auto merged = Merge(in1, in2); - - auto transformed = Transform(merged, [] (string s) -> string - { - std::transform(s.begin(), s.end(), s.begin(), ::toupper); - return s; - }); - - Observe(transformed, [&] (const string& s) - { - results.push_back(s); - }); - - in1 << string("Hello Worlt") << string("Hello World"); - in2 << string("Hello Vorld"); - - ASSERT_EQ(results.size(), 3); - ASSERT_TRUE(std::find(results.begin(), results.end(), "HELLO WORLT") != results.end()); - ASSERT_TRUE(std::find(results.begin(), results.end(), "HELLO WORLD") != results.end()); - ASSERT_TRUE(std::find(results.begin(), results.end(), "HELLO VORLD") != results.end()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventProcess test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(EventStreamTest, EventProcess) -{ - using D = typename EventProcess::MyDomain; - - std::vector results; - - auto in1 = MakeEventSource(); - auto in2 = MakeEventSource(); - - auto merged = Merge(in1, in2); - int callCount = 0; - - auto processed = Process(merged, - [&] (EventRange range, EventEmitter out) - { - for (const auto& e : range) - { - *out = 0.1f * e; - *out = 1.5f * e; - } - - callCount++; - }); - - Observe(processed, - [&] (float s) - { - results.push_back(s); - }); - - DoTransaction([&] { - in1 << 10 << 20; - }); - - in2 << 30; - - ASSERT_EQ(results.size(), 6); - ASSERT_EQ(callCount, 2); - - ASSERT_EQ(results[0], 1.0f); - ASSERT_EQ(results[1], 15.0f); - ASSERT_EQ(results[2], 2.0f); - ASSERT_EQ(results[3], 30.0f); - ASSERT_EQ(results[4], 3.0f); - ASSERT_EQ(results[5], 45.0f); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -REGISTER_TYPED_TEST_CASE_P -( - EventStreamTest, - EventSources, - EventMerge1, - EventMerge2, - EventMerge3, - EventFilter, - EventTransform, - EventProcess -); - -} // ~namespace diff --git a/tests/src/EventStreamTestQ.cpp b/tests/src/EventStreamTestQ.cpp deleted file mode 100644 index 26d07507..00000000 --- a/tests/src/EventStreamTestQ.cpp +++ /dev/null @@ -1,29 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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) - -#include "EventStreamTest.h" -#include "TestUtil.h" - -#include "react/engine/PulsecountEngine.h" -#include "react/engine/ToposortEngine.h" -#include "react/engine/SubtreeEngine.h" - -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace { - -using namespace react; - -using P1 = DomainParams; -using P2 = DomainParams; -using P3 = DomainParams; -using P4 = DomainParams; - -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, EventStreamTest, P1); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, EventStreamTest, P2); -INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, EventStreamTest, P3); -INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, EventStreamTest, P4); - -} // ~namespace \ No newline at end of file diff --git a/tests/src/ObserverTestQ.cpp b/tests/src/ObserverTestQ.cpp deleted file mode 100644 index 4dfe55f6..00000000 --- a/tests/src/ObserverTestQ.cpp +++ /dev/null @@ -1,29 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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) - -#include "ObserverTest.h" -#include "TestUtil.h" - -#include "react/engine/PulsecountEngine.h" -#include "react/engine/ToposortEngine.h" -#include "react/engine/SubtreeEngine.h" - -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace { - -using namespace react; - -using P1 = DomainParams; -using P2 = DomainParams; -using P3 = DomainParams; -using P4 = DomainParams; - -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, ObserverTest, P1); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, ObserverTest, P2); -INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, ObserverTest, P3); -INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, ObserverTest, P4); - -} // ~namespace \ No newline at end of file diff --git a/tests/src/OperationsTest.h b/tests/src/OperationsTest.h deleted file mode 100644 index c7fb6d3f..00000000 --- a/tests/src/OperationsTest.h +++ /dev/null @@ -1,971 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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) - -#pragma once - -#include "gtest/gtest.h" - -#include -#include -#include - -#include "react/Domain.h" -#include "react/Signal.h" -#include "react/Event.h" -#include "react/Observer.h" -#include "react/Algorithm.h" - -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace { - -using namespace react; -using namespace std; - -template -struct Incrementer { T operator()(Token, T v) const { return v+1; } }; - -template -struct Decrementer { T operator()(Token, T v) const { return v-1; } }; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventStreamTest fixture -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class OperationsTest : public testing::Test -{ -public: - template - class MyEngine : public TParams::template EngineT {}; - - REACTIVE_DOMAIN(MyDomain, TParams::mode, MyEngine) -}; - -TYPED_TEST_CASE_P(OperationsTest); - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Iterate1 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, Iterate1) -{ - using D = typename Iterate1::MyDomain; - - auto numSrc = MakeEventSource(); - auto numFold = Iterate(numSrc, 0, [] (int d, int v) { - return v + d; - }); - - for (auto i=1; i<=100; i++) - { - numSrc << i; - } - - ASSERT_EQ(numFold(), 5050); - - auto charSrc = MakeEventSource(); - auto strFold = Iterate(charSrc, string(""), [] (char c, string s) { - return s + c; - }); - - charSrc << 'T' << 'e' << 's' << 't'; - - ASSERT_EQ(strFold(), "Test"); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Iterate2 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, Iterate2) -{ - using D = typename Iterate2::MyDomain; - - auto numSrc = MakeEventSource(); - auto numFold = Iterate(numSrc, 0, [] (int d, int v) { - return v + d; - }); - - int c = 0; - - Observe(numFold, [&] (int v) { - c++; - ASSERT_EQ(v, 5050); - }); - - DoTransaction([&] { - for (auto i=1; i<=100; i++) - numSrc << i; - }); - - ASSERT_EQ(numFold(), 5050); - ASSERT_EQ(c, 1); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Iterate3 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, Iterate3) -{ - using D = typename Iterate3::MyDomain; - - auto trigger = MakeEventSource(); - - { - auto inc = Iterate(trigger, 0, Incrementer{}); - for (auto i=1; i<=100; i++) - trigger.Emit(); - - ASSERT_EQ(inc(), 100); - } - - { - auto dec = Iterate(trigger, 100, Decrementer{}); - for (auto i=1; i<=100; i++) - trigger.Emit(); - - ASSERT_EQ(dec(), 0); - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Monitor1 -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, Monitor1) -{ - using D = typename Monitor1::MyDomain; - - auto target = MakeVar(10); - - vector results; - - auto filterFunc = [] (int v) { - return v > 10; - }; - - auto obs = Observe(Monitor(target).Filter(filterFunc), [&] (int v) { - results.push_back(v); - }); - - target <<= 10; - target <<= 20; - target <<= 20; - target <<= 10; - - ASSERT_EQ(results.size(), 1); - ASSERT_EQ(results[0], 20); - - obs.Detach(); - - target <<= 100; - - ASSERT_EQ(results.size(), 1); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Hold1 -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, Hold1) -{ - using D = typename Hold1::MyDomain; - - auto src = MakeEventSource(); - - auto h = Hold(src, 0); - - ASSERT_EQ(h(), 0); - - src << 10; - - ASSERT_EQ(h(), 10); - - src << 20 << 30; - - ASSERT_EQ(h(), 30); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Hold1 -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, Pulse1) -{ - using D = typename Pulse1::MyDomain; - - auto trigger = MakeEventSource(); - auto target = MakeVar(10); - - vector results; - - auto p = Pulse(trigger, target); - - Observe(p, [&] (int v) { - results.push_back(v); - }); - - target <<= 10; - trigger.Emit(); - - ASSERT_EQ(results[0], 10); - - target <<= 20; - trigger.Emit(); - - ASSERT_EQ(results[1], 20); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Snapshot1 -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, Snapshot1) -{ - using D = typename Snapshot1::MyDomain; - - auto trigger = MakeEventSource(); - auto target = MakeVar(10); - - auto snap = Snapshot(trigger, target); - - target <<= 10; - trigger.Emit(); - target <<= 20; - - ASSERT_EQ(snap(), 10); - - target <<= 20; - trigger.Emit(); - target <<= 30; - - ASSERT_EQ(snap(), 20); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IterateByRef1 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, IterateByRef1) -{ - using D = typename IterateByRef1::MyDomain; - - auto src = MakeEventSource(); - auto f = Iterate( - src, - std::vector(), - [] (int d, std::vector& v) { - v.push_back(d); - }); - - // Push - for (auto i=1; i<=100; i++) - src << i; - - ASSERT_EQ(f().size(), 100); - - // Check - for (auto i=1; i<=100; i++) - ASSERT_EQ(f()[i-1], i); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IterateByRef2 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, IterateByRef2) -{ - using D = typename IterateByRef2::MyDomain; - - auto src = MakeEventSource(); - auto x = Iterate( - src, - std::vector(), - [] (Token, std::vector& v) { - v.push_back(123); - }); - - // Push - for (auto i=0; i<100; i++) - src.Emit(); - - ASSERT_EQ(x().size(), 100); - - // Check - for (auto i=0; i<100; i++) - ASSERT_EQ(x()[i], 123); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SyncedTransform1 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, SyncedTransform1) -{ - using D = typename SyncedTransform1::MyDomain; - - auto in1 = MakeVar(1); - auto in2 = MakeVar(1); - - auto sum = in1 + in2; - auto prod = in1 * in2; - auto diff = in1 - in2; - - auto src1 = MakeEventSource(); - auto src2 = MakeEventSource(); - - auto out1 = Transform(src1, With(sum,prod,diff), [] (Token, int sum, int prod, int diff) { - return make_tuple(sum, prod, diff); - }); - - auto out2 = Transform(src2, With(sum,prod,diff), [] (int e, int sum, int prod, int diff) { - return make_tuple(e, sum, prod, diff); - }); - - int obsCount1 = 0; - int obsCount2 = 0; - - { - auto obs1 = Observe(out1, [&] (const tuple& t) { - ++obsCount1; - - ASSERT_EQ(get<0>(t), 33); - ASSERT_EQ(get<1>(t), 242); - ASSERT_EQ(get<2>(t), 11); - }); - - auto obs2 = Observe(out2, [&] (const tuple& t) { - ++obsCount2; - - ASSERT_EQ(get<0>(t), 42); - ASSERT_EQ(get<1>(t), 33); - ASSERT_EQ(get<2>(t), 242); - ASSERT_EQ(get<3>(t), 11); - }); - - in1 <<= 22; - in2 <<= 11; - - src1.Emit(); - src2.Emit(42); - - ASSERT_EQ(obsCount1, 1); - ASSERT_EQ(obsCount2, 1); - - obs1.Detach(); - obs2.Detach(); - } - - { - auto obs1 = Observe(out1, [&] (const tuple& t) { - ++obsCount1; - - ASSERT_EQ(get<0>(t), 330); - ASSERT_EQ(get<1>(t), 24200); - ASSERT_EQ(get<2>(t), 110); - }); - - auto obs2 = Observe(out2, [&] (const tuple& t) { - ++obsCount2; - - ASSERT_EQ(get<0>(t), 420); - ASSERT_EQ(get<1>(t), 330); - ASSERT_EQ(get<2>(t), 24200); - ASSERT_EQ(get<3>(t), 110); - }); - - in1 <<= 220; - in2 <<= 110; - - src1.Emit(); - src2.Emit(420); - - ASSERT_EQ(obsCount1, 2); - ASSERT_EQ(obsCount2, 2); - - obs1.Detach(); - obs2.Detach(); - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SyncedIterate1 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, SyncedIterate1) -{ - using D = typename SyncedIterate1::MyDomain; - - auto in1 = MakeVar(1); - auto in2 = MakeVar(1); - - auto op1 = in1 + in2; - auto op2 = (in1 + in2) * 10; - - auto src1 = MakeEventSource(); - auto src2 = MakeEventSource(); - - auto out1 = Iterate( - src1, - make_tuple(0,0), - With(op1,op2), - [] (Token, const tuple& t, int op1, int op2) { - return make_tuple(get<0>(t) + op1, get<1>(t) + op2); - }); - - auto out2 = Iterate( - src2, - make_tuple(0,0,0), - With(op1,op2), - [] (int e, const tuple& t, int op1, int op2) { - return make_tuple(get<0>(t) + e, get<1>(t) + op1, get<2>(t) + op2); - }); - - int obsCount1 = 0; - int obsCount2 = 0; - - { - auto obs1 = Observe(out1, [&] (const tuple& t) { - ++obsCount1; - - ASSERT_EQ(get<0>(t), 33); - ASSERT_EQ(get<1>(t), 330); - }); - - auto obs2 = Observe(out2, [&] (const tuple& t) { - ++obsCount2; - - ASSERT_EQ(get<0>(t), 42); - ASSERT_EQ(get<1>(t), 33); - ASSERT_EQ(get<2>(t), 330); - }); - - in1 <<= 22; - in2 <<= 11; - - src1.Emit(); - src2.Emit(42); - - ASSERT_EQ(obsCount1, 1); - ASSERT_EQ(obsCount2, 1); - - obs1.Detach(); - obs2.Detach(); - } - - { - auto obs1 = Observe(out1, [&] (const tuple& t) { - ++obsCount1; - - ASSERT_EQ(get<0>(t), 33 + 330); - ASSERT_EQ(get<1>(t), 330 + 3300); - }); - - auto obs2 = Observe(out2, [&] (const tuple& t) { - ++obsCount2; - - ASSERT_EQ(get<0>(t), 42 + 420); - ASSERT_EQ(get<1>(t), 33 + 330); - ASSERT_EQ(get<2>(t), 330 + 3300); - }); - - in1 <<= 220; - in2 <<= 110; - - src1.Emit(); - src2.Emit(420); - - ASSERT_EQ(obsCount1, 2); - ASSERT_EQ(obsCount2, 2); - - obs1.Detach(); - obs2.Detach(); - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SyncedIterate2 test (by ref) -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, SyncedIterate2) -{ - using D = typename SyncedIterate2::MyDomain; - - auto in1 = MakeVar(1); - auto in2 = MakeVar(1); - - auto op1 = in1 + in2; - auto op2 = (in1 + in2) * 10; - - auto src1 = MakeEventSource(); - auto src2 = MakeEventSource(); - - auto out1 = Iterate( - src1, - vector{}, - With(op1,op2), - [] (Token, vector& v, int op1, int op2) -> void { - v.push_back(op1); - v.push_back(op2); - }); - - auto out2 = Iterate( - src2, - vector{}, - With(op1,op2), - [] (int e, vector& v, int op1, int op2) -> void { - v.push_back(e); - v.push_back(op1); - v.push_back(op2); - }); - - int obsCount1 = 0; - int obsCount2 = 0; - - { - auto obs1 = Observe(out1, [&] (const vector& v) { - ++obsCount1; - - ASSERT_EQ(v.size(), 2); - - ASSERT_EQ(v[0], 33); - ASSERT_EQ(v[1], 330); - }); - - auto obs2 = Observe(out2, [&] (const vector& v) { - ++obsCount2; - - ASSERT_EQ(v.size(), 3); - - ASSERT_EQ(v[0], 42); - ASSERT_EQ(v[1], 33); - ASSERT_EQ(v[2], 330); - }); - - in1 <<= 22; - in2 <<= 11; - - src1.Emit(); - src2.Emit(42); - - ASSERT_EQ(obsCount1, 1); - ASSERT_EQ(obsCount2, 1); - - obs1.Detach(); - obs2.Detach(); - } - - { - auto obs1 = Observe(out1, [&] (const vector& v) { - ++obsCount1; - - ASSERT_EQ(v.size(), 4); - - ASSERT_EQ(v[0], 33); - ASSERT_EQ(v[1], 330); - ASSERT_EQ(v[2], 330); - ASSERT_EQ(v[3], 3300); - }); - - auto obs2 = Observe(out2, [&] (const vector& v) { - ++obsCount2; - - ASSERT_EQ(v.size(), 6); - - ASSERT_EQ(v[0], 42); - ASSERT_EQ(v[1], 33); - ASSERT_EQ(v[2], 330); - - ASSERT_EQ(v[3], 420); - ASSERT_EQ(v[4], 330); - ASSERT_EQ(v[5], 3300); - }); - - in1 <<= 220; - in2 <<= 110; - - src1.Emit(); - src2.Emit(420); - - ASSERT_EQ(obsCount1, 2); - ASSERT_EQ(obsCount2, 2); - - obs1.Detach(); - obs2.Detach(); - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SyncedIterate3 test (event range) -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, SyncedIterate3) -{ - using D = typename SyncedIterate3::MyDomain; - - auto in1 = MakeVar(1); - auto in2 = MakeVar(1); - - auto op1 = in1 + in2; - auto op2 = (in1 + in2) * 10; - - auto src1 = MakeEventSource(); - auto src2 = MakeEventSource(); - - auto out1 = Iterate( - src1, - make_tuple(0,0), - With(op1,op2), - [] (EventRange range, const tuple& t, int op1, int op2) { - return make_tuple( - get<0>(t) + (op1 * range.Size()), - get<1>(t) + (op2 * range.Size())); - }); - - auto out2 = Iterate( - src2, - make_tuple(0,0,0), - With(op1,op2), - [] (EventRange range, const tuple& t, int op1, int op2) { - int sum = 0; - for (const auto& e : range) - sum += e; - - return make_tuple( - get<0>(t) + sum, - get<1>(t) + (op1 * range.Size()), - get<2>(t) + (op2 * range.Size())); - }); - - int obsCount1 = 0; - int obsCount2 = 0; - - { - auto obs1 = Observe(out1, [&] (const tuple& t) { - ++obsCount1; - - ASSERT_EQ(get<0>(t), 33); - ASSERT_EQ(get<1>(t), 330); - }); - - auto obs2 = Observe(out2, [&] (const tuple& t) { - ++obsCount2; - - ASSERT_EQ(get<0>(t), 42); - ASSERT_EQ(get<1>(t), 33); - ASSERT_EQ(get<2>(t), 330); - }); - - in1 <<= 22; - in2 <<= 11; - - src1.Emit(); - src2.Emit(42); - - ASSERT_EQ(obsCount1, 1); - ASSERT_EQ(obsCount2, 1); - - obs1.Detach(); - obs2.Detach(); - } - - { - auto obs1 = Observe(out1, [&] (const tuple& t) { - ++obsCount1; - - ASSERT_EQ(get<0>(t), 33 + 330); - ASSERT_EQ(get<1>(t), 330 + 3300); - }); - - auto obs2 = Observe(out2, [&] (const tuple& t) { - ++obsCount2; - - ASSERT_EQ(get<0>(t), 42 + 420); - ASSERT_EQ(get<1>(t), 33 + 330); - ASSERT_EQ(get<2>(t), 330 + 3300); - }); - - in1 <<= 220; - in2 <<= 110; - - src1.Emit(); - src2.Emit(420); - - ASSERT_EQ(obsCount1, 2); - ASSERT_EQ(obsCount2, 2); - - obs1.Detach(); - obs2.Detach(); - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SyncedIterate4 test (event range, by ref) -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, SyncedIterate4) -{ - using D = typename SyncedIterate4::MyDomain; - - auto in1 = MakeVar(1); - auto in2 = MakeVar(1); - - auto op1 = in1 + in2; - auto op2 = (in1 + in2) * 10; - - auto src1 = MakeEventSource(); - auto src2 = MakeEventSource(); - - auto out1 = Iterate( - src1, - vector{}, - With(op1,op2), - [] (EventRange range, vector& v, int op1, int op2) -> void { - for (const auto& e : range) - { - v.push_back(op1); - v.push_back(op2); - } - }); - - auto out2 = Iterate( - src2, - vector{}, - With(op1,op2), - [] (EventRange range, vector& v, int op1, int op2) -> void { - for (const auto& e : range) - { - v.push_back(e); - v.push_back(op1); - v.push_back(op2); - } - }); - - int obsCount1 = 0; - int obsCount2 = 0; - - { - auto obs1 = Observe(out1, [&] (const vector& v) { - ++obsCount1; - - ASSERT_EQ(v.size(), 2); - - ASSERT_EQ(v[0], 33); - ASSERT_EQ(v[1], 330); - }); - - auto obs2 = Observe(out2, [&] (const vector& v) { - ++obsCount2; - - ASSERT_EQ(v.size(), 3); - - ASSERT_EQ(v[0], 42); - ASSERT_EQ(v[1], 33); - ASSERT_EQ(v[2], 330); - }); - - in1 <<= 22; - in2 <<= 11; - - src1.Emit(); - src2.Emit(42); - - ASSERT_EQ(obsCount1, 1); - ASSERT_EQ(obsCount2, 1); - - obs1.Detach(); - obs2.Detach(); - } - - { - auto obs1 = Observe(out1, [&] (const vector& v) { - ++obsCount1; - - ASSERT_EQ(v.size(), 4); - - ASSERT_EQ(v[0], 33); - ASSERT_EQ(v[1], 330); - ASSERT_EQ(v[2], 330); - ASSERT_EQ(v[3], 3300); - }); - - auto obs2 = Observe(out2, [&] (const vector& v) { - ++obsCount2; - - ASSERT_EQ(v.size(), 6); - - ASSERT_EQ(v[0], 42); - ASSERT_EQ(v[1], 33); - ASSERT_EQ(v[2], 330); - - ASSERT_EQ(v[3], 420); - ASSERT_EQ(v[4], 330); - ASSERT_EQ(v[5], 3300); - }); - - in1 <<= 220; - in2 <<= 110; - - src1.Emit(); - src2.Emit(420); - - ASSERT_EQ(obsCount1, 2); - ASSERT_EQ(obsCount2, 2); - - obs1.Detach(); - obs2.Detach(); - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventFilter1 -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, SyncedEventFilter1) -{ - using D = typename SyncedEventFilter1::MyDomain; - - using std::string; - - std::queue results; - - auto in = MakeEventSource(); - - auto sig1 = MakeVar(1338); - auto sig2 = MakeVar(1336); - - auto filtered = Filter( - in, - With(sig1, sig2), - [] (const string& s, int sig1, int sig2) { - return s == "Hello World" && sig1 > sig2; - }); - - - Observe(filtered, [&] (const string& s) - { - results.push(s); - }); - - in << string("Hello Worlt") << string("Hello World") << string("Hello Vorld"); - sig1 <<= 1335; - in << string("Hello Vorld"); - - ASSERT_FALSE(results.empty()); - ASSERT_EQ(results.front(), "Hello World"); - results.pop(); - - ASSERT_TRUE(results.empty()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SyncedEventTransform1 -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, SyncedEventTransform1) -{ - using D = typename SyncedEventTransform1::MyDomain; - - using std::string; - - std::vector results; - - auto in1 = MakeEventSource(); - auto in2 = MakeEventSource(); - - auto merged = Merge(in1, in2); - - auto first = MakeVar(string("Ace")); - auto last = MakeVar(string("McSteele")); - - auto transformed = Transform( - merged, - With(first, last), - [] (string s, const string& first, const string& last) -> string { - std::transform(s.begin(), s.end(), s.begin(), ::toupper); - s += string(", ") + first + string(" ") + last; - return s; - }); - - Observe(transformed, [&] (const string& s) { - results.push_back(s); - }); - - in1 << string("Hello Worlt") << string("Hello World"); - - DoTransaction([&] { - in2 << string("Hello Vorld"); - first.Set(string("Alice")); - last.Set(string("Anderson")); - }); - - ASSERT_EQ(results.size(), 3); - ASSERT_TRUE(std::find(results.begin(), results.end(), "HELLO WORLT, Ace McSteele") != results.end()); - ASSERT_TRUE(std::find(results.begin(), results.end(), "HELLO WORLD, Ace McSteele") != results.end()); - ASSERT_TRUE(std::find(results.begin(), results.end(), "HELLO VORLD, Alice Anderson") != results.end()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SyncedEventProcess1 -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(OperationsTest, SyncedEventProcess1) -{ - using D = typename SyncedEventProcess1::MyDomain; - - std::vector results; - - auto in1 = MakeEventSource(); - auto in2 = MakeEventSource(); - - auto mult = MakeVar(10); - - auto merged = Merge(in1, in2); - int callCount = 0; - - auto processed = Process(merged, - With(mult), - [&] (EventRange range, EventEmitter out, int mult) - { - for (const auto& e : range) - { - *out = 0.1f * e * mult; - *out = 1.5f * e * mult; - } - - callCount++; - }); - - Observe(processed, - [&] (float s) - { - results.push_back(s); - }); - - DoTransaction([&] { - in1 << 10 << 20; - }); - - in2 << 30; - - ASSERT_EQ(results.size(), 6); - ASSERT_EQ(callCount, 2); - - ASSERT_EQ(results[0], 10.0f); - ASSERT_EQ(results[1], 150.0f); - ASSERT_EQ(results[2], 20.0f); - ASSERT_EQ(results[3], 300.0f); - ASSERT_EQ(results[4], 30.0f); - ASSERT_EQ(results[5], 450.0f); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -REGISTER_TYPED_TEST_CASE_P -( - OperationsTest, - Iterate1, - Iterate2, - Iterate3, - Monitor1, - Hold1, - Pulse1, - Snapshot1, - IterateByRef1, - IterateByRef2, - SyncedTransform1, - SyncedIterate1, - SyncedIterate2, - SyncedIterate3, - SyncedIterate4, - SyncedEventFilter1, - SyncedEventTransform1, - SyncedEventProcess1 -); - -} // ~namespace diff --git a/tests/src/OperationsTestQ.cpp b/tests/src/OperationsTestQ.cpp deleted file mode 100644 index 576c0752..00000000 --- a/tests/src/OperationsTestQ.cpp +++ /dev/null @@ -1,29 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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) - -#include "OperationsTest.h" -#include "TestUtil.h" - -#include "react/engine/PulsecountEngine.h" -#include "react/engine/ToposortEngine.h" -#include "react/engine/SubtreeEngine.h" - -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace { - -using namespace react; - -using P1 = DomainParams; -using P2 = DomainParams; -using P3 = DomainParams; -using P4 = DomainParams; - -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, OperationsTest, P1); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, OperationsTest, P2); -INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, OperationsTest, P3); -INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, OperationsTest, P4); - -} // ~namespace \ No newline at end of file diff --git a/tests/src/ParallelizationTest.cpp b/tests/src/ParallelizationTest.cpp deleted file mode 100644 index 6054092a..00000000 --- a/tests/src/ParallelizationTest.cpp +++ /dev/null @@ -1,12 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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) - - -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace { - - -} // ~namespace \ No newline at end of file diff --git a/tests/src/ParallelizationTest.h b/tests/src/ParallelizationTest.h deleted file mode 100644 index 145c730c..00000000 --- a/tests/src/ParallelizationTest.h +++ /dev/null @@ -1,63 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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) - -#pragma once - -#include "gtest/gtest.h" - -#include "react/Domain.h" -#include "react/Signal.h" -#include "react/Event.h" -#include "react/Observer.h" -#include "react/Algorithm.h" - -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace { - -using namespace react; -using namespace std; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ParallelizationTest fixture -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ParallelizationTest : public testing::Test -{ -public: - template - class MyEngine : public TParams::template EngineT {}; - - REACTIVE_DOMAIN(MyDomain, TParams::mode, MyEngine) -}; - -TYPED_TEST_CASE_P(ParallelizationTest); - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Iterate1 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(ParallelizationTest, WeightHint1) -{ - using D = typename WeightHint1::MyDomain; - - auto sig = MakeVar(0); - auto evn = MakeEventSource(); - auto cont = MakeContinuation(sig, [] (int v) {}); - auto obs = Observe(evn, [] (int v) {}); - - sig.SetWeightHint(WeightHint::heavy); - evn.SetWeightHint(WeightHint::automatic); - cont.SetWeightHint(WeightHint::light); - obs.SetWeightHint(WeightHint::automatic); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -REGISTER_TYPED_TEST_CASE_P -( - ParallelizationTest, - WeightHint1 -); - -} // ~namespace diff --git a/tests/src/SignalTest.h b/tests/src/SignalTest.h deleted file mode 100644 index 69ae81de..00000000 --- a/tests/src/SignalTest.h +++ /dev/null @@ -1,664 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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) - -#pragma once - -#include "gtest/gtest.h" - -#include -#include - -#include "react/Domain.h" -#include "react/Signal.h" - -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace { - -using namespace react; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalTest fixture -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class SignalTest : public testing::Test -{ -public: - template - class MyEngine : public TParams::template EngineT {}; - - REACTIVE_DOMAIN(MyDomain, TParams::mode, MyEngine) -}; - -TYPED_TEST_CASE_P(SignalTest); - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeVars test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(SignalTest, MakeVars) -{ - using D = typename MakeVars::MyDomain; - - auto v1 = MakeVar(1); - auto v2 = MakeVar(2); - auto v3 = MakeVar(3); - auto v4 = MakeVar(4); - - ASSERT_EQ(v1(),1); - ASSERT_EQ(v2(),2); - ASSERT_EQ(v3(),3); - ASSERT_EQ(v4(),4); - - v1 <<= 10; - v2 <<= 20; - v3 <<= 30; - v4 <<= 40; - - ASSERT_EQ(v1(),10); - ASSERT_EQ(v2(),20); - ASSERT_EQ(v3(),30); - ASSERT_EQ(v4(),40); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Signals1 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(SignalTest, Signals1) -{ - using D = typename Signals1::MyDomain; - - auto v1 = MakeVar(1); - auto v2 = MakeVar(2); - auto v3 = MakeVar(3); - auto v4 = MakeVar(4); - - auto s1 = MakeSignal(With(v1,v2), [] (int a, int b) { - return a + b; - }); - - auto s2 = MakeSignal(With(v3,v4), [] (int a, int b) { - return a + b; - }); - - auto s3 = s1 + s2; - - ASSERT_EQ(s1(),3); - ASSERT_EQ(s2(),7); - ASSERT_EQ(s3(),10); - - v1 <<= 10; - v2 <<= 20; - v3 <<= 30; - v4 <<= 40; - - ASSERT_EQ(s1(),30); - ASSERT_EQ(s2(),70); - ASSERT_EQ(s3(),100); - - bool b = false; - - b = IsSignal::value; - ASSERT_TRUE(b); - - b = IsSignal::value; - ASSERT_TRUE(b); - - b = IsSignal::value; - ASSERT_TRUE(b); - - b = IsSignal::value; - ASSERT_FALSE(b); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Signals2 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(SignalTest, Signals2) -{ - using D = typename Signals2::MyDomain; - - auto a1 = MakeVar(1); - auto a2 = MakeVar(1); - - auto b1 = a1 + 0; - auto b2 = a1 + a2; - auto b3 = a2 + 0; - - auto c1 = b1 + b2; - auto c2 = b2 + b3; - - auto result = c1 + c2; - - int observeCount = 0; - - Observe(result, [&observeCount] (int v) - { - observeCount++; - if (observeCount == 1) - ASSERT_EQ(v,9); - else - ASSERT_EQ(v,12); - }); - - ASSERT_EQ(a1(),1); - ASSERT_EQ(a2(),1); - - ASSERT_EQ(b1(),1); - ASSERT_EQ(b2(),2); - ASSERT_EQ(b3(),1); - - ASSERT_EQ(c1(),3); - ASSERT_EQ(c2(),3); - - ASSERT_EQ(result(),6); - - a1 <<= 2; - - ASSERT_EQ(observeCount,1); - - ASSERT_EQ(a1(),2); - ASSERT_EQ(a2(),1); - - ASSERT_EQ(b1(),2); - ASSERT_EQ(b2(),3); - ASSERT_EQ(b3(),1); - - ASSERT_EQ(c1(),5); - ASSERT_EQ(c2(),4); - - ASSERT_EQ(result(),9); - - a2 <<= 2; - - ASSERT_EQ(observeCount,2); - - ASSERT_EQ(a1(),2); - ASSERT_EQ(a2(),2); - - ASSERT_EQ(b1(),2); - ASSERT_EQ(b2(),4); - ASSERT_EQ(b3(),2); - - ASSERT_EQ(c1(),6); - ASSERT_EQ(c2(),6); - - ASSERT_EQ(result(),12); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Signals3 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(SignalTest, Signals3) -{ - using D = typename Signals3::MyDomain; - - auto a1 = MakeVar(1); - auto a2 = MakeVar(1); - - auto b1 = a1 + 0; - auto b2 = a1 + a2; - auto b3 = a2 + 0; - - auto c1 = b1 + b2; - auto c2 = b2 + b3; - - auto result = c1 + c2; - - int observeCount = 0; - - Observe(result, [&observeCount] (int v) - { - observeCount++; - ASSERT_EQ(v,12); - }); - - ASSERT_EQ(a1(),1); - ASSERT_EQ(a2(),1); - - ASSERT_EQ(b1(),1); - ASSERT_EQ(b2(),2); - ASSERT_EQ(b3(),1); - - ASSERT_EQ(c1(),3); - ASSERT_EQ(c2(),3); - - ASSERT_EQ(result(),6); - - DoTransaction([&] { - a1 <<= 2; - a2 <<= 2; - }); - - ASSERT_EQ(observeCount,1); - - ASSERT_EQ(a1(),2); - ASSERT_EQ(a2(),2); - - ASSERT_EQ(b1(),2); - ASSERT_EQ(b2(),4); - ASSERT_EQ(b3(),2); - - ASSERT_EQ(c1(),6); - ASSERT_EQ(c2(),6); - - ASSERT_EQ(result(),12); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Signals4 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(SignalTest, Signals4) -{ - using D = typename Signals4::MyDomain; - - auto a1 = MakeVar(1); - auto a2 = MakeVar(1); - - auto b1 = a1 + a2; - auto b2 = b1 + a2; - - ASSERT_EQ(a1(),1); - ASSERT_EQ(a2(),1); - - ASSERT_EQ(b1(),2); - ASSERT_EQ(b2(),3); - - a1 <<= 10; - - ASSERT_EQ(a1(),10); - ASSERT_EQ(a2(),1); - - ASSERT_EQ(b1(),11); - ASSERT_EQ(b2(),12); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// FunctionBind1 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(SignalTest, FunctionBind1) -{ - using D = typename FunctionBind1::MyDomain; - - auto v1 = MakeVar(2); - auto v2 = MakeVar(30); - auto v3 = MakeVar(10); - - auto signal = (v1, v2, v3) ->* [=] (int a, int b, int c) -> int - { - return a * b * c; - }; - - ASSERT_EQ(signal(),600); - v3 <<= 100; - ASSERT_EQ(signal(),6000); -} - -int myfunc(int a, int b) { return a + b; } -float myfunc2(int a) { return a / 2.0f; } -float myfunc3(float a, float b) { return a * b; } - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// FunctionBind2 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(SignalTest, FunctionBind2) -{ - using D = typename FunctionBind2::MyDomain; - - auto a = MakeVar(1); - auto b = MakeVar(1); - - auto c = ((a+b), (a+100)) ->* &myfunc; - auto d = c ->* &myfunc2; - auto e = (d,d) ->* &myfunc3; - auto f = -e + 100; - - ASSERT_EQ(c(),103); - ASSERT_EQ(d(),51.5f); - ASSERT_EQ(e(),2652.25f); - ASSERT_EQ(f(),-2552.25); - - a <<= 10; - - ASSERT_EQ(c(),121); - ASSERT_EQ(d(),60.5f); - ASSERT_EQ(e(),3660.25f); - ASSERT_EQ(f(),-3560.25f); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Flatten1 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(SignalTest, Flatten1) -{ - using D = typename Flatten1::MyDomain; - - auto inner1 = MakeVar(123); - auto inner2 = MakeVar(789); - - auto outer = MakeVar(inner1); - - auto flattened = Flatten(outer); - - std::queue results; - - Observe(flattened, [&] (int v) - { - results.push(v); - }); - - ASSERT_TRUE(outer().Equals(inner1)); - ASSERT_EQ(flattened(),123); - - inner1 <<= 456; - - ASSERT_EQ(flattened(),456); - - ASSERT_EQ(results.front(), 456); - results.pop(); - ASSERT_TRUE(results.empty()); - - outer <<= inner2; - - ASSERT_TRUE(outer().Equals(inner2)); - ASSERT_EQ(flattened(),789); - - ASSERT_EQ(results.front(), 789); - results.pop(); - ASSERT_TRUE(results.empty()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Flatten2 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(SignalTest, Flatten2) -{ - using D = typename Flatten2::MyDomain; - - auto a0 = MakeVar(100); - - auto inner1 = MakeVar(200); - - auto a1 = MakeVar(300); - auto a2 = a1 + 0; - auto a3 = a2 + 0; - auto a4 = a3 + 0; - auto a5 = a4 + 0; - auto a6 = a5 + 0; - auto inner2 = a6 + 0; - - ASSERT_EQ(inner1(),200); - ASSERT_EQ(inner2(),300); - - auto outer = MakeVar(inner1); - - auto flattened = Flatten(outer); - - ASSERT_EQ(flattened(), 200); - - int observeCount = 0; - - Observe(flattened, [&observeCount] (int v) - { - observeCount++; - }); - - auto o1 = a0 + flattened; - auto o2 = o1 + 0; - auto o3 = o2 + 0; - auto result = o3 + 0; - - ASSERT_EQ(result(), 100 + 200); - - inner1 <<= 1234; - - ASSERT_EQ(result(), 100 + 1234); - ASSERT_EQ(observeCount, 1); - - outer <<= inner2; - - ASSERT_EQ(result(), 100 + 300); - ASSERT_EQ(observeCount, 2); - - DoTransaction([&] { - a0 <<= 5000; - a1 <<= 6000; - }); - - ASSERT_EQ(result(), 5000 + 6000); - ASSERT_EQ(observeCount, 3); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Flatten3 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(SignalTest, Flatten3) -{ - using D = typename Flatten3::MyDomain; - - auto inner1 = MakeVar(10); - - auto a1 = MakeVar(20); - auto a2 = a1 + 0; - auto a3 = a2 + 0; - auto inner2 = a3 + 0; - - auto outer = MakeVar(inner1); - - auto a0 = MakeVar(30); - - auto flattened = Flatten(outer); - - int observeCount = 0; - - Observe(flattened, [&observeCount] (int v) - { - observeCount++; - }); - - auto result = flattened + a0; - - ASSERT_EQ(result(), 10 + 30); - ASSERT_EQ(observeCount, 0); - - DoTransaction([&] { - inner1 <<= 1000; - a0 <<= 200000; - a1 <<= 50000; - outer <<= inner2; - }); - - ASSERT_EQ(result(), 50000 + 200000); - ASSERT_EQ(observeCount, 1); - - DoTransaction([&] { - a0 <<= 667; - a1 <<= 776; - }); - - ASSERT_EQ(result(), 776 + 667); - ASSERT_EQ(observeCount, 2); - - DoTransaction([&] { - inner1 <<= 999; - a0 <<= 888; - }); - - ASSERT_EQ(result(), 776 + 888); - ASSERT_EQ(observeCount, 2); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Flatten4 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(SignalTest, Flatten4) -{ - using D = typename Flatten4::MyDomain; - - std::vector results; - - auto a1 = MakeVar(100); - auto inner1 = a1 + 0; - - auto a2 = MakeVar(200); - auto inner2 = a2; - - auto a3 = MakeVar(200); - - auto outer = MakeVar(inner1); - - auto flattened = Flatten(outer); - - auto result = flattened + a3; - - Observe(result, [&] (int v) { - results.push_back(v); - }); - - DoTransaction([&] { - a3 <<= 400; - outer <<= inner2; - }); - - ASSERT_EQ(results.size(), 1); - - ASSERT_TRUE(std::find(results.begin(), results.end(), 600) != results.end()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Member1 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(SignalTest, Member1) -{ - using D = typename Member1::MyDomain; - - auto outer = MakeVar(10); - auto inner = MakeVar(outer); - - auto flattened = inner.Flatten(); - - Observe(flattened, [] (int v) { - ASSERT_EQ(v, 30); - }); - - outer <<= 30; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Modify1 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(SignalTest, Modify1) -{ - using D = typename Modify1::MyDomain; - - using std::vector; - - auto v = MakeVar(vector{}); - - int obsCount = 0; - - Observe(v, [&] (const vector& v) { - ASSERT_EQ(v[0], 30); - ASSERT_EQ(v[1], 50); - ASSERT_EQ(v[2], 70); - - obsCount++; - }); - - v.Modify([] (vector& v) { - v.push_back(30); - v.push_back(50); - v.push_back(70); - }); - - ASSERT_EQ(obsCount, 1); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Modify2 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(SignalTest, Modify2) -{ - using D = typename Modify2::MyDomain; - - using std::vector; - - auto v = MakeVar(vector{}); - - int obsCount = 0; - - Observe(v, [&] (const vector& v) { - ASSERT_EQ(v[0], 30); - ASSERT_EQ(v[1], 50); - ASSERT_EQ(v[2], 70); - - obsCount++; - }); - - DoTransaction([&] { - v.Modify([] (vector& v) { - v.push_back(30); - }); - - v.Modify([] (vector& v) { - v.push_back(50); - }); - - v.Modify([] (vector& v) { - v.push_back(70); - }); - }); - - - ASSERT_EQ(obsCount, 1); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Modify3 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(SignalTest, Modify3) -{ - using D = typename Modify3::MyDomain; - - using std::vector; - - auto vect = MakeVar(vector{}); - - int obsCount = 0; - - Observe(vect, [&] (const vector& v) { - ASSERT_EQ(v[0], 30); - ASSERT_EQ(v[1], 50); - ASSERT_EQ(v[2], 70); - - obsCount++; - }); - - // Also terrible - DoTransaction([&] { - - vect.Set(vector{ 30, 50 }); - - vect.Modify([] (vector& v) { - v.push_back(70); - }); - }); - - ASSERT_EQ(obsCount, 1); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -REGISTER_TYPED_TEST_CASE_P -( - SignalTest, - MakeVars, - Signals1, Signals2, Signals3, Signals4, - FunctionBind1, FunctionBind2, - Flatten1, Flatten2, Flatten3, Flatten4, - Member1, - Modify1, Modify2, Modify3 - -); - -} // ~namespace diff --git a/tests/src/SignalTestQ.cpp b/tests/src/SignalTestQ.cpp deleted file mode 100644 index d91875c7..00000000 --- a/tests/src/SignalTestQ.cpp +++ /dev/null @@ -1,29 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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) - -#include "SignalTest.h" -#include "TestUtil.h" - -#include "react/engine/PulsecountEngine.h" -#include "react/engine/ToposortEngine.h" -#include "react/engine/SubtreeEngine.h" - -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace { - -using namespace react; - -using P1 = DomainParams; -using P2 = DomainParams; -using P3 = DomainParams; -using P4 = DomainParams; - -INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, SignalTest, P1); -INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, SignalTest, P2); -INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, SignalTest, P3); -INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, SignalTest, P4); - -} // ~namespace \ No newline at end of file diff --git a/tests/src/TestUtil.h b/tests/src/TestUtil.h deleted file mode 100644 index 0d7b9cb6..00000000 --- a/tests/src/TestUtil.h +++ /dev/null @@ -1,25 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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) - -#pragma once - -#ifndef REACT_TESTS_TESTUTIL_H_INCLUDED -#define REACT_TESTS_TESTUTIL_H_INCLUDED - -template -< - EDomainMode m, - template class TTEngine -> -struct DomainParams -{ - static const EDomainMode mode = m; - - template - using EngineT = TTEngine; -}; - -#endif // REACT_TESTS_TESTUTIL_H_INCLUDED \ No newline at end of file diff --git a/tests/src/TransactionTest.h b/tests/src/TransactionTest.h deleted file mode 100644 index 0501a81c..00000000 --- a/tests/src/TransactionTest.h +++ /dev/null @@ -1,571 +0,0 @@ - -// Copyright Sebastian Jeckel 2017. -// 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) - -#pragma once - -#include "gtest/gtest.h" - -#include -#include -#include - -#include "react/Domain.h" -#include "react/Signal.h" -#include "react/Event.h" -#include "react/Observer.h" -#include "react/Algorithm.h" - -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace { - -using namespace react; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalTest fixture -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class TransactionTest : public testing::Test -{ -public: - template - class MyEngine : public TParams::template EngineT {}; - - REACTIVE_DOMAIN(MyDomain, TParams::mode, MyEngine) -}; - -TYPED_TEST_CASE_P(TransactionTest); - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Concurrent transactions test 1 -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(TransactionTest, Concurrent1) -{ - using D = typename Concurrent1::MyDomain; - - std::vector results; - - auto n1 = MakeVar(1); - auto n2 = n1 + 1; - auto n3 = n2 + n1 + 1; - auto n4 = n3 + 1; - auto n5 = n4 + n3 + n1 + 1; - auto n6 = n5 + 1; - auto n7 = n6 + n5 + 1; - auto n8 = n7 + 1; - auto n9 = n8 + n7 + n5 + n1 + 1; - auto n10 = n9 + 1; - auto n11 = n10 + n9 + 1; - auto n12 = n11 + 1; - auto n13 = n12 + n11 + n9 + 1; - auto n14 = n13 + 1; - auto n15 = n14 + n13 + 1; - auto n16 = n15 + 1; - auto n17 = n16 + n15 + n13 + n9 + 1; - - Observe(n17, [&] (int v) - { - results.push_back(v); - }); - - n1 <<= 10; // 7732 - n1 <<= 100; // 68572 - n1 <<= 1000; // 676972 - - ASSERT_EQ(results.size(), 3); - - ASSERT_TRUE(std::find(results.begin(), results.end(), 7732) != results.end()); - ASSERT_TRUE(std::find(results.begin(), results.end(), 68572) != results.end()); - ASSERT_TRUE(std::find(results.begin(), results.end(), 676972) != results.end()); - - // Reset - n1 <<= 1; - results.clear(); - - // Now do the same from 3 threads - - std::thread t1([&] { n1 <<= 10; }); - std::thread t2([&] { n1 <<= 100; }); - std::thread t3([&] { n1 <<= 1000; }); - - t1.join(); - t2.join(); - t3.join(); - - ASSERT_EQ(results.size(), 3); - - ASSERT_TRUE(std::find(results.begin(), results.end(), 7732) != results.end()); - ASSERT_TRUE(std::find(results.begin(), results.end(), 68572) != results.end()); - ASSERT_TRUE(std::find(results.begin(), results.end(), 676972) != results.end()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Concurrent transactions test 2 -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(TransactionTest, Concurrent2) -{ - using D = typename Concurrent2::MyDomain; - - std::vector results; - - auto in = MakeVar(-1); - - // 1. Generate graph - Signal n0 = in; - - auto next = n0; - - for (int i=0; i < 100; i++) - { - auto q = next + 0; - next = q; - } - - Observe(next, [&] (int v) - { - results.push_back(v); - }); - - // 2. Send events - std::thread t1([&] - { - for (int i=0; i<100; i++) - { - std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 50)); - in <<= i; - } - }); - - std::thread t2([&] - { - for (int i=100; i<200; i++) - { - std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 50)); - in <<= i; - } - }); - - std::thread t3([&] - { - for (int i=200; i<300; i++) - { - std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 50)); - in <<= i; - } - }); - - t1.join(); - t2.join(); - t3.join(); - - ASSERT_EQ(results.size(), 300); - - for (int i=0; i<300; i++) - { - ASSERT_TRUE(std::find(results.begin(), results.end(), i) != results.end()); - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Concurrent transactions test 3 -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(TransactionTest, Concurrent3) -{ - using D = typename Concurrent3::MyDomain; - - std::vector results; - - std::function f_0 = [] (int a) -> int - { - for (int i = 0; i<(a+1)*100; i++); - return a + 1; - }; - - std::function f_n = [] (int a, int b) -> int - { - for (int i = 0; i<(a+b)*100; i++); - return a + b; - }; - - auto n1 = MakeVar(1); - auto n2 = n1 ->* f_0; - auto n3 = ((n2, n1) ->* f_n) ->* f_0; - auto n4 = n3 ->* f_0; - auto n5 = ((((n4, n3) ->* f_n), n1) ->* f_n) ->* f_0; - auto n6 = n5 ->* f_0; - auto n7 = ((n6, n5) ->* f_n) ->* f_0; - auto n8 = n7 ->* f_0; - auto n9 = ((((((n8, n7) ->* f_n), n5) ->* f_n), n1) ->* f_n) ->* f_0; - auto n10 = n9 ->* f_0; - auto n11 = ((n10, n9) ->* f_n) ->* f_0; - auto n12 = n11 ->* f_0; - auto n13 = ((((n12, n11) ->* f_n), n9) ->* f_n) ->* f_0; - auto n14 = n13 ->* f_0; - auto n15 = ((n14, n13) ->* f_n) ->* f_0; - auto n16 = n15 ->* f_0; - auto n17 = ((((((n16, n15) ->* f_n), n13) ->* f_n), n9) ->* f_n) ->* f_0; - - Observe(n17, [&] (int v) - { - results.push_back(v); - }); - - n1 <<= 1000; // 676972 - n1 <<= 100; // 68572 - n1 <<= 10; // 7732 - - - ASSERT_EQ(results.size(), 3); - - ASSERT_TRUE(std::find(results.begin(), results.end(), 7732) != results.end()); - ASSERT_TRUE(std::find(results.begin(), results.end(), 68572) != results.end()); - ASSERT_TRUE(std::find(results.begin(), results.end(), 676972) != results.end()); - - // Reset - n1 <<= 1; - results.clear(); - - std::thread t3([&] { n1 <<= 1000; }); - std::thread t2([&] { n1 <<= 100; }); - std::thread t1([&] { n1 <<= 10; }); - - t3.join(); - t2.join(); - t1.join(); - - - ASSERT_EQ(results.size(), 3); - - ASSERT_TRUE(std::find(results.begin(), results.end(), 7732) != results.end()); - ASSERT_TRUE(std::find(results.begin(), results.end(), 68572) != results.end()); - ASSERT_TRUE(std::find(results.begin(), results.end(), 676972) != results.end()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Merging1 -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(TransactionTest, Merging1) -{ - using D = typename Merging1::MyDomain; - - std::vector results; - - std::atomic shouldSpin(false); - - std::function f = [&shouldSpin] (int a) -> int - { - while (shouldSpin); - - return a; - }; - - auto n1 = MakeVar(1); - auto n2 = n1 ->* f; - - Observe(n2, [&] (int v) { - results.push_back(v); - }); - - // Todo: improve this as it'll fail occasionally - shouldSpin = true; - std::thread t1([&] { - DoTransaction(allow_merging, [&] { - n1 <<= 2; - }); - }); - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - std::thread t2([&] { - DoTransaction(allow_merging, [&] { - n1 <<= 3; - }); - }); - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - std::thread t3([&] { - DoTransaction(allow_merging, [&] { - n1 <<= 4; - }); - }); - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - std::thread t4([&] { - DoTransaction(allow_merging, [&] { - n1 <<= 5; - }); - - }); - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - shouldSpin = false; - - t1.join(); - t2.join(); - t3.join(); - t4.join(); - - ASSERT_EQ(results.size(), 2); - ASSERT_TRUE(results[0] == 2); - ASSERT_TRUE(results[1] == 5); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Async1 -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(TransactionTest, Async1) -{ - using D = typename Async1::MyDomain; - - std::vector results; - - auto in = MakeVar(1); - auto s1 = in * 1000; - - Observe(s1, [&] (int v) { - results.push_back(v); - }); - - TransactionStatus st; - - AsyncTransaction(st, [&] { - in <<= 10; - }); - - AsyncTransaction(st, [&] { - in <<= 20; - }); - - AsyncTransaction(st, [&] { - in <<= 30; - }); - - st.Wait(); - - ASSERT_EQ(results.size(), 3); - ASSERT_TRUE(results[0] == 10000); - ASSERT_TRUE(results[1] == 20000); - ASSERT_TRUE(results[2] == 30000); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// AsyncMerging1 -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(TransactionTest, AsyncMerging1) -{ - using D = typename AsyncMerging1::MyDomain; - - std::vector results; - - auto in = MakeVar(1); - auto s1 = in * 1000; - - Observe(s1, [&] (int v) { - results.push_back(v); - }); - - TransactionStatus st; - - AsyncTransaction(allow_merging, st, [&] { - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - in <<= 10; - }); - - // Make sure other async transaction gets to start - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - - // These two can still be pulled in after the first input function is done - AsyncTransaction(allow_merging, st, [&] { - in <<= 20; - }); - - AsyncTransaction(allow_merging, st, [&] { - in <<= 30; - }); - - // Can't be merged - AsyncTransaction(st, [&] { - in <<= 40; - }); - - // These two should be merged again - AsyncTransaction(allow_merging, st, [&] { - in <<= 50; - }); - - AsyncTransaction(allow_merging, st, [&] { - in <<= 60; - }); - - st.Wait(); - - ASSERT_EQ(results.size(), 3); - ASSERT_TRUE(results[0] == 30000); - ASSERT_TRUE(results[1] == 40000); - ASSERT_TRUE(results[2] == 60000); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Continuation1 -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(TransactionTest, Continuation1) -{ - using D = typename Continuation1::MyDomain; - - std::vector results; - - auto in = MakeVar(0); - - auto cont = MakeContinuation(in, [&] (int v) { - results.push_back(v); - - if (v < 10) - in <<= v + 1; - }); - - in <<= 1; - - ASSERT_EQ(results.size(), 10); - for (int i=0; i<10; i++) - ASSERT_TRUE(results[i] == i+1); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Continuation2 -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(TransactionTest, Continuation2) -{ - using L = typename Continuation2::MyDomain; - - REACTIVE_DOMAIN(R, sequential_concurrent) - - std::vector results; - - auto srcL = MakeVar(0); - auto srcR = MakeVar(0); - - auto contL = MakeContinuation(srcL, [&] (int v) { - results.push_back(v); - if (v < 10) - srcR <<= v+1; - }); - - auto contR = MakeContinuation(Monitor(srcR), [&] (int v) { - results.push_back(v); - if (v < 10) - srcL <<= v+1; - }); - - srcL <<= 1; - - ASSERT_EQ(results.size(), 10); - for (int i=0; i<10; i++) - ASSERT_TRUE(results[i] == i+1); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Continuation3 -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(TransactionTest, Continuation3) -{ - using L = typename Continuation3::MyDomain; - - REACTIVE_DOMAIN(R, sequential_concurrent) - - std::vector results; - - auto srcL = MakeVar(0); - auto depL1 = MakeVar(100); - auto depL2 = MakeVar(10); - auto srcR = MakeVar(0); - - auto contL = MakeContinuation( - Monitor(srcL), - With(depL1, depL2), - [&] (int v, int depL1, int depL2) { - ASSERT_EQ(depL1, v*100); - ASSERT_EQ(depL2, v*10); - results.push_back(v); - if (v < 10) - srcR <<= v+1; - }); - - auto contR = MakeContinuation( - Monitor(srcR), - [&] (int v) { - results.push_back(v); - - v++; - depL1 <<= v*100; - depL2 <<= v*10; - if (v < 10) - srcL <<= v; - }); - - srcL <<= 1; - - ASSERT_EQ(results.size(), 10); - for (int i=0; i<10; i++) - ASSERT_TRUE(results[i] == i+1); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Continuation4 test -/////////////////////////////////////////////////////////////////////////////////////////////////// -TYPED_TEST_P(TransactionTest, Continuation4) -{ - using D = typename Continuation4::MyDomain; - - using std::vector; - - auto vect = MakeVar(vector{}); - - int count = 0; - - auto cont = MakeContinuation(vect, [&] (const vector& v) - { - if (count == 0) - { - ASSERT_EQ(v[0], 30); - - vect.Modify([] (vector& v) { - v.push_back(50); - }); - } - else if (count == 1) - { - ASSERT_EQ(v[1], 50); - - vect.Modify([] (vector& v) { - v.push_back(70); - }); - } - else - { - ASSERT_EQ(v[2], 70); - } - - count++; - }); - - vect.Modify([] (vector& v) { - v.push_back(30); - }); - - ASSERT_EQ(count, 3); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -REGISTER_TYPED_TEST_CASE_P -( - TransactionTest, - Concurrent1, - Concurrent2, - Concurrent3, - Merging1, - Async1, - AsyncMerging1, - Continuation1, - Continuation2, - Continuation3, - Continuation4 -); - -} // ~namespace diff --git a/tests/src/algorithm_tests.cpp b/tests/src/algorithm_tests.cpp index 6054092a..49bd221c 100644 --- a/tests/src/algorithm_tests.cpp +++ b/tests/src/algorithm_tests.cpp @@ -4,9 +4,713 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#include "gtest/gtest.h" + +#include "react/algorithm.h" +#include "react/observer.h" + +#include +#include +#include +#include +#include +#include + +using namespace react; + +TEST(AlgorithmTest, Hold) +{ + // Hold last value of event source in state. + + Group g; + + EventSource evt1( g ); + + State st = Hold(1, evt1); + + int output = 0; + int turns = 0; + + Observer obs([&] (int v) + { + ++turns; + output = v; + }, st); + + // Initial call. Output should take the value of initial value. + EXPECT_EQ(1, output); + EXPECT_EQ(1, turns); + + // Event changes value. + evt1.Emit(10); + + EXPECT_EQ(10, output); + EXPECT_EQ(2, turns); + + // New event, but same value, observer should not be called. + evt1.Emit(10); + + EXPECT_EQ(10, output); + EXPECT_EQ(2, turns); +} + +TEST(AlgorithmTest, Monitor1) +{ + // Emit events when value of state changes. + + Group g; + + StateVar st( g, 1 ); + + Event evt = Monitor( st ); + + int output = 0; + int turns = 0; + + Observer obs([&] (const auto& events) + { + ++turns; + + for (int e : events) + output += e; + }, evt); + + EXPECT_EQ(0, output); + EXPECT_EQ(0, turns); + + // Change from 1 -> 10: Event. + st.Set(10); + + // Change from 10 -> 20: Event. + st.Set(20); + + // Change from 20 -> 20: No event. + st.Set(20); + + // 10 + 20 were the changes. + EXPECT_EQ(30, output); + EXPECT_EQ(2, turns); +} + +TEST(AlgorithmTest, Monitor2) +{ + // Monitor state changes and filter the resulting events. + + Group g; + + StateVar target( g, 10 ); + + std::vector results; + + auto filterFunc = [] (int v) { return v > 10; }; + + { + // Observer is created in a nested scope so it gets destructed before the end of this function. + + Observer obs([&] (const auto& events) + { + for (int e : events) + results.push_back(e); + }, Filter(filterFunc, Monitor(target))); + + // Change the value a couple of times. + target.Set(10); // Change, but <= 10 + target.Set(20); // Change + target.Set(20); // No change + target.Set(10); // Change, but <= 10 + + // Only 1 non-filtered change should go through. + EXPECT_EQ(results.size(), 1); + EXPECT_EQ(results[0], 20); + } + + target.Set(100); // Change, >100, but observer is gone. + + // No changes to results without the observer. + ASSERT_EQ(results.size(), 1); +} + +TEST(AlgorithmTest, Snapshot) +{ + Group g; + + StateVar sv( g, 1 ); + EventSource<> es( g ); + + State st = Snapshot( sv, es ); + + int output = 0; + int turns = 0; + + Observer obs([&] (int v) + { + ++turns; + output = v; + }, st); + + EXPECT_EQ(1, output); + EXPECT_EQ(1, turns); + + sv.Set(10); + + EXPECT_EQ(1, output); + EXPECT_EQ(1, turns); + + es.Emit(); + + EXPECT_EQ(10, output); + EXPECT_EQ(2, turns); +} + +TEST(AlgorithmTest, Pulse) +{ + Group g; + + StateVar sv( g, 1 ); + EventSource<> es( g ); + + Event st = Pulse( sv, es ); + + int output = 0; + int turns = 0; + + Observer obs([&] (const auto& events) + { + for (int e : events) + { + ++turns; + output += e; + } + }, st); + + EXPECT_EQ(0, output); + EXPECT_EQ(0, turns); + + sv.Set(10); + + EXPECT_EQ(0, output); + EXPECT_EQ(0, turns); + + es.Emit(); + + EXPECT_EQ(10, output); + EXPECT_EQ(1, turns); +} + +TEST(AlgorithmTest, Iterate1) +{ + Group g; + + EventSource numSrc( g ); + + State numFold = Iterate(0, [] (const auto& events, int v) + { + for (int e : events) + v += e; + return v; + }, numSrc); + + for (int i=1; i<=100; ++i) + numSrc << i; + + int output = 0; + Observer obs([&] (int v) { output = v; }, numFold); + + EXPECT_EQ(output, 5050); +} + +TEST(AlgorithmTest, Iterate2) +{ + Group g; + + EventSource charSrc( g ); + + State strFold = Iterate(std::string(""), [] (const auto& events, std::string s) + { + for (char c : events) + s += c; + return s; + }, charSrc); + + std::string output; + Observer obs([&] (const auto& v) { output = v; }, strFold); + + charSrc << 'T' << 'e' << 's' << 't'; + + EXPECT_EQ(output, "Test"); +} + +TEST(AlgorithmTest, Iterate3) +{ + Group g; + + EventSource numSrc( g ); + + State numFold = Iterate(0, [] (const auto& events, int v) + { + for (int e : events) + v += e; + return v; + }, numSrc); + + int turns = 0; + int output = 0; + + Observer obs([&] (const auto& v) + { + ++turns; + output = v; + }, numFold); + + g.DoTransaction([&] + { + for (int i=1; i<=100; i++) + numSrc << i; + }); + + EXPECT_EQ(turns, 2); + EXPECT_EQ(output, 5050); +} + +template +struct Incrementer +{ + T operator()(const EventValueList& events, T v) const + { + for (auto e : events) + ++v; + return v; + } +}; + +template +struct Decrementer +{ + T operator()(const EventValueList& events, T v) const + { + for (auto e : events) + --v; + return v; + } +}; + +TEST(AlgorithmTest, Iterate4) +{ + Group g; + + EventSource<> trigger( g ); + + { + State inc = Iterate(0, Incrementer{ }, trigger); + for (int i=1; i<=100; i++) + trigger.Emit(); + + int output = 0; + Observer obs([&] (int v) { output = v; }, inc); + + EXPECT_EQ(output, 100); + } + + { + State dec = Iterate(200, Decrementer{ }, trigger); + for (int i=1; i<=100; i++) + trigger.Emit(); + + int output = 0; + Observer obs([&] (int v) { output = v; }, dec); + + ASSERT_EQ(output, 100); + } +} + +TEST(AlgorithmTest, IterateByRef1) +{ + Group g; + + EventSource src( g ); + + auto x = IterateByRef>(std::vector{ }, [] (const auto& events, auto& v) + { + for (int e : events) + v.push_back(e); + }, src); + + std::vector output; + Observer obs([&] (const auto& v) { output = v; }, x); + + // Push + for (int i=1; i<=100; i++) + src << i; + + EXPECT_EQ(output.size(), 100); + + // Check + for (int i=1; i<=100; i++) + EXPECT_EQ(output[i-1], i); +} + +TEST(AlgorithmTest, IterateByRef2) +{ + Group g; + + EventSource<> src( g ); + + auto x = IterateByRef>(std::vector{ }, [] (const auto& events, std::vector& v) + { + for (Token e : events) + v.push_back(123); + }, src); + + std::vector output; + Observer obs([&] (const auto& v) { output = v; }, x); + + // Push + for (auto i=0; i<100; i++) + src.Emit(); + + EXPECT_EQ(output.size(), 100); + + // Check + for (auto i=0; i<100; i++) + EXPECT_EQ(output[i], 123); +} -/////////////////////////////////////////////////////////////////////////////////////////////////// namespace { +template +T Sum(T a, T b) { return a + b; } + +template +T Prod(T a, T b) { return a * b; } + +template +T Diff(T a, T b) { return a - b; } + +} //~namespace + +TEST(AlgorithmTest, TransformWithState) +{ + Group g; + + StateVar in1( g ); + StateVar in2( g ); + + State sum( Sum, in1, in2 ); + State prod( Prod, in1, in2 ); + State diff( Diff, in1, in2 ); + + EventSource<> src1( g ); + EventSource src2( g ); + + auto out1 = Transform>([] (Token, int sum, int prod, int diff) + { + return std::make_tuple(sum, prod, diff); + }, src1, sum, prod, diff); + + auto out2 = Transform>([] (int e, int sum, int prod, int diff) + { + return std::make_tuple(e, sum, prod, diff); + }, src2, sum, prod, diff); + + int turns1 = 0; + int turns2 = 0; + + { + std::tuple output1; + + Observer obs1([&] (const auto& events) + { + for (const auto& e : events) + { + ++turns1; + output1 = e; + } + }, out1); + + std::tuple output2; + + Observer obs2([&] (const auto& events) + { + for (const auto& e : events) + { + ++turns2; + output2 = e; + } + }, out2); + + in1.Set(22); + in2.Set(11); + + src1.Emit(); + src2.Emit(42); + + EXPECT_EQ(std::get<0>(output1), 33); + EXPECT_EQ(std::get<1>(output1), 242); + EXPECT_EQ(std::get<2>(output1), 11); + + EXPECT_EQ(std::get<0>(output2), 42); + EXPECT_EQ(std::get<1>(output2), 33); + EXPECT_EQ(std::get<2>(output2), 242); + EXPECT_EQ(std::get<3>(output2), 11); + + EXPECT_EQ(turns1, 1); + EXPECT_EQ(turns2, 1); + } + + { + std::tuple output1; + + Observer obs1([&] (const auto& events) + { + for (const auto& e : events) + { + ++turns1; + output1 = e; + } + }, out1); + + std::tuple output2; + + Observer obs2([&] (const auto& events) + { + for (const auto& e : events) + { + ++turns2; + output2 = e; + } + }, out2); + + in1.Set(220); + in2.Set(110); + + src1.Emit(); + src2.Emit(420); + + EXPECT_EQ(std::get<0>(output1), 330); + EXPECT_EQ(std::get<1>(output1), 24200); + EXPECT_EQ(std::get<2>(output1), 110); + + EXPECT_EQ(std::get<0>(output2), 420); + EXPECT_EQ(std::get<1>(output2), 330); + EXPECT_EQ(std::get<2>(output2), 24200); + EXPECT_EQ(std::get<3>(output2), 110); + + EXPECT_EQ(turns1, 2); + EXPECT_EQ(turns2, 2); + } +} + +TEST(AlgorithmTest, IterateWithState) +{ + Group g; + + StateVar in1( g ); + StateVar in2( g ); + + State op1(Sum, in1, in2); + State op2([] (int a, int b) { return (a + b) * 10; }, in1, in2); + + EventSource<> src1( g ); + EventSource src2( g ); + + auto out1 = Iterate>(std::make_tuple(0, 0), [] (const auto& events, std::tuple t, int op1, int op2) + { + for (const auto& e : events) + t = std::make_tuple(std::get<0>(t) + op1, std::get<1>(t) + op2); + + return t; + + }, src1, op1, op2); + + auto out2 = Iterate>(std::make_tuple(0, 0, 0), [] (const auto& events, std::tuple t, int op1, int op2) + { + for (const auto& e : events) + t = std::make_tuple(std::get<0>(t) + e, std::get<1>(t) + op1, std::get<2>(t) + op2); + + return t; + }, src2, op1, op2); + + int turns1 = 0; + int turns2 = 0; + + { + std::tuple output1; + + Observer obs1([&] (const auto& v) + { + ++turns1; + output1 = v; + }, out1); + + std::tuple output2; + + Observer obs2([&] (const auto& v) + { + ++turns2; + output2 = v; + }, out2); + + in1.Set(22); + in2.Set(11); + + src1.Emit(); + src2.Emit(42); + + EXPECT_EQ(std::get<0>(output1), 33); + EXPECT_EQ(std::get<1>(output1), 330); + + EXPECT_EQ(std::get<0>(output2), 42); + EXPECT_EQ(std::get<1>(output2), 33); + EXPECT_EQ(std::get<2>(output2), 330); + + EXPECT_EQ(turns1, 2); + EXPECT_EQ(turns2, 2); + } + + { + std::tuple output1; + + Observer obs1([&] (const auto& v) + { + ++turns1; + output1 = v; + }, out1); + + std::tuple output2; + + Observer obs2([&] (const auto& v) + { + ++turns2; + output2 = v; + }, out2); + + in1.Set(220); + in2.Set(110); + + src1.Emit(); + src2.Emit(420); + + EXPECT_EQ(std::get<0>(output1), 33 + 330); + EXPECT_EQ(std::get<1>(output1), 330 + 3300); + + EXPECT_EQ(std::get<0>(output2), 42 + 420); + EXPECT_EQ(std::get<1>(output2), 33 + 330); + EXPECT_EQ(std::get<2>(output2), 330 + 3300); + + EXPECT_EQ(turns1, 4); + EXPECT_EQ(turns2, 4); + } +} + +TEST(AlgorithmTest, IterateByRefWithState) +{ + Group g; + + StateVar in1( g ); + StateVar in2( g ); + + State op1( Sum, in1, in2 ); + State op2([] (int a, int b) { return (a + b) * 10; }, in1, in2); + + EventSource<> src1( g ); + EventSource src2( g ); + + auto out1 = IterateByRef>(std::vector{ }, [] (const auto& events, std::vector& v, int op1, int op2) + { + for (const auto& e : events) + { + v.push_back(op1); + v.push_back(op2); + } + }, src1, op1, op2); + + auto out2 = IterateByRef>(std::vector{ }, [] (const auto& events, std::vector& v, int op1, int op2) + { + for (const auto& e : events) + { + v.push_back(e); + v.push_back(op1); + v.push_back(op2); + } + }, src2, op1, op2); + + int turns1 = 0; + int turns2 = 0; + + { + std::vector output1; + + Observer obs1([&] (const std::vector& v) + { + ++turns1; + output1 = v; + }, out1); + + std::vector output2; + + Observer obs2([&] (const std::vector& v) + { + ++turns2; + output2 = v; + }, out2); + + in1.Set(22); + in2.Set(11); + + src1.Emit(); + src2.Emit(42); + + EXPECT_EQ(output1.size(), 2); + EXPECT_EQ(output1[0], 33); + EXPECT_EQ(output1[1], 330); + + EXPECT_EQ(output2.size(), 3); + EXPECT_EQ(output2[0], 42); + EXPECT_EQ(output2[1], 33); + EXPECT_EQ(output2[2], 330); + + EXPECT_EQ(turns1, 2); + EXPECT_EQ(turns2, 2); + } + + { + std::vector output1; + + Observer obs1([&] (const std::vector& v) + { + ++turns1; + output1 = v; + }, out1); + + std::vector output2; + + Observer obs2([&] (const std::vector& v) + { + ++turns2; + output2 = v; + }, out2); + + in1.Set(220); + in2.Set(110); + + src1.Emit(); + src2.Emit(420); + + EXPECT_EQ(output1.size(), 4); + EXPECT_EQ(output1[0], 33); + EXPECT_EQ(output1[1], 330); + EXPECT_EQ(output1[2], 330); + EXPECT_EQ(output1[3], 3300); + + EXPECT_EQ(output2.size(), 6); + EXPECT_EQ(output2[0], 42); + EXPECT_EQ(output2[1], 33); + EXPECT_EQ(output2[2], 330); + EXPECT_EQ(output2[3], 420); + EXPECT_EQ(output2[4], 330); + EXPECT_EQ(output2[5], 3300); -} // ~namespace \ No newline at end of file + EXPECT_EQ(turns1, 4); + EXPECT_EQ(turns2, 4); + } +} diff --git a/tests/src/event_tests.cpp b/tests/src/event_tests.cpp index 98d27bfc..168ac619 100644 --- a/tests/src/event_tests.cpp +++ b/tests/src/event_tests.cpp @@ -7,6 +7,7 @@ #include "gtest/gtest.h" #include "react/event.h" +#include "react/state.h" #include "react/observer.h" #include @@ -168,10 +169,7 @@ TEST(EventTest, Transactions) g.DoTransaction([&] { - evt.Emit(1); - evt.Emit(1); - evt.Emit(1); - evt.Emit(1); + evt << 1 << 1 << 1 << 1; }); EXPECT_EQ(4, output); @@ -212,9 +210,9 @@ TEST(EventTest, Links) output += e; }, slot); - evt1.Emit(1); - evt2.Emit(1); - evt3.Emit(1); + evt1 << 1; + evt2 << 1; + evt3 << 1; std::this_thread::sleep_for(std::chrono::seconds(1)); @@ -244,13 +242,8 @@ TEST(EventTest, EventSources) results2.push(e); }, es2); - es1.Emit(10); - es1.Emit(20); - es1.Emit(30); - - es2.Emit(40); - es2.Emit(50); - es2.Emit(60); + es1 << 10 << 20 << 30; + es2 << 40 << 50 << 60; // First batch. EXPECT_FALSE(results1.empty()); @@ -453,7 +446,7 @@ TEST(EventTest, Transform) EXPECT_TRUE(std::find(results.begin(), results.end(), "HELLO VORLD") != results.end()); } -TEST(EventTest, Chain) +TEST(EventTest, Flow) { Group g; @@ -537,4 +530,130 @@ TEST(EventTest, Join) in1.Emit(20); EXPECT_EQ(results.size(), 2); EXPECT_EQ(results[1], std::make_tuple(20, 20, 20)); +} + +TEST(EventTest, FilterWithState) +{ + Group g; + + EventSource in( g ); + + StateVar sig1( g, 1338 ); + StateVar sig2( g, 1336 ); + + EventSource in2( g ); + + auto filtered = Filter([] (const std::string& s, int sig1, int sig2) + { + return s == "Hello World" && sig1 > sig2; + }, in, sig1, sig2); + + std::queue results; + + Observer obs([&] (const auto& events) + { + for (const auto& e : events) + results.push(e); + }, filtered); + + in << std::string("Hello Worlt") << std::string("Hello World") << std::string("Hello Vorld"); + sig1.Set(1335); + in << std::string("Hello Vorld"); + + EXPECT_FALSE(results.empty()); + EXPECT_EQ(results.front(), "Hello World"); + results.pop(); + + EXPECT_TRUE(results.empty()); +} + +TEST(EventTest, TransformWithState) +{ + Group g; + + std::vector results; + + EventSource in1( g ); + EventSource in2( g ); + + Event merged = Merge(in1, in2); + + StateVar first( g, "Ace" ); + StateVar last( g, "McSteele" ); + + auto transformed = Transform([] (std::string s, const std::string& first, const std::string& last) -> std::string + { + std::transform(s.begin(), s.end(), s.begin(), ::toupper); + s += std::string(", ") + first + std::string(" ") + last; + return s; + }, merged, first, last); + + Observer obs([&] (const auto& events) + { + for (const auto& e : events) + results.push_back(e); + }, transformed); + + in1 << std::string("Hello Worlt") << std::string("Hello World"); + + g.DoTransaction([&] + { + in2 << std::string("Hello Vorld"); + first.Set(std::string("Alice")); + last.Set(std::string("Anderson")); + }); + + EXPECT_EQ(results.size(), 3); + EXPECT_TRUE(std::find(results.begin(), results.end(), "HELLO WORLT, Ace McSteele") != results.end()); + EXPECT_TRUE(std::find(results.begin(), results.end(), "HELLO WORLD, Ace McSteele") != results.end()); + EXPECT_TRUE(std::find(results.begin(), results.end(), "HELLO VORLD, Alice Anderson") != results.end()); +} + +TEST(EventTest, FlowWithState) +{ + Group g; + + std::vector results; + + EventSource in1( g ); + EventSource in2( g ); + + StateVar mult( g, 10 ); + + Event merged = Merge(in1, in2); + int callCount = 0; + + Event processed([&] (const auto& events, auto out, int mult) + { + for (const auto& e : events) + { + *out = 0.1f * e * mult; + *out = 1.5f * e * mult; + } + + callCount++; + }, merged, mult); + + Observer obs([&] (const auto& events) + { + for (float e : events) + results.push_back(e); + }, processed); + + g.DoTransaction([&] + { + in1 << 10 << 20; + }); + + in2 << 30; + + EXPECT_EQ(results.size(), 6); + EXPECT_EQ(callCount, 2); + + EXPECT_EQ(results[0], 10.0f); + EXPECT_EQ(results[1], 150.0f); + EXPECT_EQ(results[2], 20.0f); + EXPECT_EQ(results[3], 300.0f); + EXPECT_EQ(results[4], 30.0f); + EXPECT_EQ(results[5], 450.0f); } \ No newline at end of file diff --git a/tests/src/state_tests.cpp b/tests/src/state_tests.cpp index 7cca28db..953cedc8 100644 --- a/tests/src/state_tests.cpp +++ b/tests/src/state_tests.cpp @@ -202,6 +202,9 @@ TEST(StateTest, Links) std::this_thread::sleep_for(std::chrono::seconds(1)); } +namespace +{ + template static T Sum2(T a, T b) { @@ -214,7 +217,9 @@ static T Sum3(T a, T b, T c) return a + b + c; } -TEST(StateTest, StateCombination) +} // ~namespace + +TEST(StateTest, StateCombination1) { Group g; @@ -259,4 +264,164 @@ TEST(StateTest, StateCombination) EXPECT_EQ(3, output2); EXPECT_EQ(4, turns2); +} + +TEST(StateTest, StateCombination2) +{ + Group g; + + std::vector results; + + StateVar n1( g, 1 ); + + State n2([] (int n1) + { return n1 + 1; }, n1); + + State n3([] (int n1, int n2) + { return n2 + n1 + 1; }, n1, n2); + + State n4([] (int n3) + { return n3 + 1; }, n3); + + State n5([] (int n1, int n3, int n4) + { return n4 + n3 + n1 + 1; }, n1, n3, n4); + + State n6([] (int n5) + { return n5 + 1; }, n5); + + State n7([] (int n5, int n6) + { return n6 + n5 + 1; }, n5, n6); + + State n8([] (int n7) + { return n7 + 1; }, n7); + + State n9([] (int n1, int n5, int n7, int n8) + { return n8 + n7 + n5 + n1 + 1; }, n1, n5, n7, n8); + + State n10([] (int n9) + { return n9 + 1; }, n9); + + State n11([] (int n9, int n10) + { return n10 + n9 + 1; }, n9, n10); + + State n12([] (int n11) + { return n11 + 1; }, n11); + + State n13([] (int n9, int n11, int n12) + { return n12 + n11 + n9 + 1; }, n9, n11, n12); + + State n14([] (int n13) + { return n13 + 1; }, n13); + + State n15([] (int n13, int n14) + { return n14 + n13 + 1; }, n13, n14); + + State n16([] (int n15) + { return n15 + 1; }, n15); + + State n17([] (int n9, int n13, int n15, int n16) + { return n16 + n15 + n13 + n9 + 1; }, n9, n13, n15, n16); + + Observer obs([&] (int v) { results.push_back(v); }, n17); + + n1.Set(10); // 7732 + n1.Set(100); // 68572 + n1.Set(1000); // 676972 + + EXPECT_EQ(results.size(), 4); + + EXPECT_EQ(results[0], 1648); + EXPECT_EQ(results[1], 7732); + EXPECT_EQ(results[2], 68572); + EXPECT_EQ(results[3], 676972); +} + +TEST(StateTest, Modify1) +{ + Group g; + + std::vector results; + + StateVar> var( g, std::vector{ } ); + + int turns = 0; + + Observer obs([&] (const std::vector& v) + { + ++turns; + results = v; + }, var); + + var.Modify([] (std::vector& v) + { + v.push_back(30); + v.push_back(50); + v.push_back(70); + }); + + EXPECT_EQ(results[0], 30); + EXPECT_EQ(results[1], 50); + EXPECT_EQ(results[2], 70); + + EXPECT_EQ(turns, 2); +} + +TEST(StateTest, Modify2) +{ + Group g; + + std::vector results; + + StateVar> var( g, std::vector{ } ); + + int turns = 0; + + Observer obs([&] (const std::vector& v) + { + ++turns; + results = v; + }, var); + + g.DoTransaction([&] + { + var.Modify([] (std::vector& v) { v.push_back(30); }); + var.Modify([] (std::vector& v) { v.push_back(50); }); + var.Modify([] (std::vector& v) { v.push_back(70); }); + }); + + EXPECT_EQ(results[0], 30); + EXPECT_EQ(results[1], 50); + EXPECT_EQ(results[2], 70); + + EXPECT_EQ(turns, 2); +} + +TEST(StateTest, Modify3) +{ + Group g; + + std::vector results; + + StateVar> var( g, std::vector{ } ); + + int turns = 0; + + Observer obs([&] (const std::vector& v) + { + ++turns; + results = v; + }, var); + + // Also terrible + g.DoTransaction([&] + { + var.Set(std::vector{ 30, 50 }); + var.Modify([] (std::vector& v) { v.push_back(70); }); + }); + + EXPECT_EQ(results[0], 30); + EXPECT_EQ(results[1], 50); + EXPECT_EQ(results[2], 70); + + ASSERT_EQ(turns, 2); } \ No newline at end of file diff --git a/tests/src/transaction_tests.cpp b/tests/src/transaction_tests.cpp index 33812c75..01e65ac9 100644 --- a/tests/src/transaction_tests.cpp +++ b/tests/src/transaction_tests.cpp @@ -6,6 +6,7 @@ #include "gtest/gtest.h" +#include "react/state.h" #include "react/event.h" #include "react/observer.h" @@ -14,7 +15,7 @@ using namespace react; -TEST(TransactionTests, Merging) +TEST(TransactionTest, Merging) { Group g; @@ -68,7 +69,7 @@ TEST(TransactionTests, Merging) EXPECT_EQ(21, output); } -TEST(TransactionTests, LinkedSync) +TEST(TransactionTest, LinkedSync) { // Three groups. Each has one event with an observer attached. // The last observer adds a little delay. @@ -138,7 +139,7 @@ TEST(TransactionTests, LinkedSync) EXPECT_EQ(3, output3); } -TEST(TransactionTests, LinkedSyncMerging) +TEST(TransactionTest, LinkedSyncMerging) { // Two groups. Each has one event with an observer attached. // The last observer adds a little delay. From 6eb759c7e12e9647c9f41e35b35f665cc518f8a9 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 30 Oct 2017 02:07:57 +0100 Subject: [PATCH 249/266] Refactoring of object creation: -Ctor +static Create. Cleanup. Object state. --- CMakeLists.txt | 6 +- benchmarks/src/Main.cpp | 10 +- include/react/api.h | 23 ++++ include/react/common/syncpoint.h | 3 +- include/react/detail/event_nodes.h | 7 +- include/react/detail/state_nodes.h | 92 ++++++++++++-- include/react/event.h | 149 +++++++++++----------- include/react/observer.h | 55 ++++----- include/react/state.h | 192 ++++++++++++++++++++--------- project/msvc/CppReact.vcxproj | 1 + tests/CMakeLists.txt | 16 +-- tests/src/algorithm_tests.cpp | 112 ++++++++--------- tests/src/event_tests.cpp | 122 +++++++++--------- tests/src/state_tests.cpp | 159 ++++++++++++++++-------- tests/src/transaction_tests.cpp | 18 +-- 15 files changed, 594 insertions(+), 371 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ae21be06..3b6477a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,11 +13,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/include") ### CppReact add_library(CppReact - src/engine/PulsecountEngine.cpp - src/engine/SubtreeEngine.cpp - src/engine/ToposortEngine.cpp - src/logging/EventLog.cpp - src/logging/EventRecords.cpp) + src/detail/graph_impl.cpp) target_link_libraries(CppReact tbb) diff --git a/benchmarks/src/Main.cpp b/benchmarks/src/Main.cpp index bc42b726..a191ccbc 100644 --- a/benchmarks/src/Main.cpp +++ b/benchmarks/src/Main.cpp @@ -222,10 +222,18 @@ int main() } +struct Data +{ + State a; + State b; + + State (a, b); +}; + #endif #include "react/common/expected.h" -#include "react/signal.h" +#include "react/state.h" #include "react/event.h" #include "react/algorithm.h" #include diff --git a/include/react/api.h b/include/react/api.h index d8d15204..95ce57f6 100644 --- a/include/react/api.h +++ b/include/react/api.h @@ -9,6 +9,8 @@ #pragma once +#include + #include "react/detail/defs.h" #include "react/common/utility.h" @@ -35,6 +37,14 @@ REACT_DEFINE_BITMASK_OPERATORS(TransactionFlags) enum class Token { value }; +enum class InPlaceTag +{ + value = 1 +}; + +static constexpr InPlaceTag in_place = InPlaceTag::value; + + /////////////////////////////////////////////////////////////////////////////////////////////////// /// API types /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -55,6 +65,13 @@ class StateSlot; template class StateLink; +// Object state +template +class ObjectContext; + +template +class ObjectState; + // Event enum class Token; @@ -67,6 +84,12 @@ class EventSource; template class EventSlot; +template +using EventValueList = std::vector; + +template +using EventValueSink = std::back_insert_iterator>; + // Observer class Observer; diff --git a/include/react/common/syncpoint.h b/include/react/common/syncpoint.h index b4fc73f8..1d47952a 100644 --- a/include/react/common/syncpoint.h +++ b/include/react/common/syncpoint.h @@ -16,6 +16,7 @@ #include #include #include +#include /*****************************************/ REACT_BEGIN /*****************************************/ @@ -29,8 +30,6 @@ class SyncPoint class Dependency; private: - - class ISyncTarget { public: diff --git a/include/react/detail/event_nodes.h b/include/react/detail/event_nodes.h index 7c988b23..c918afec 100644 --- a/include/react/detail/event_nodes.h +++ b/include/react/detail/event_nodes.h @@ -27,11 +27,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Iterators for event processing /////////////////////////////////////////////////////////////////////////////////////////////////// -template -using EventValueList = std::vector; - -template -using EventValueSink = std::back_insert_iterator>; /******************************************/ REACT_END /******************************************/ @@ -493,6 +488,8 @@ template class EventInternals { public: + EventInternals() = default; + EventInternals(const EventInternals&) = default; EventInternals& operator=(const EventInternals&) = default; diff --git a/include/react/detail/state_nodes.h b/include/react/detail/state_nodes.h index c3b717b2..ea78c20b 100644 --- a/include/react/detail/state_nodes.h +++ b/include/react/detail/state_nodes.h @@ -20,12 +20,6 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -bool Equals(const L& lhs, const R& rhs); - /////////////////////////////////////////////////////////////////////////////////////////////////// /// StateNode /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -33,12 +27,6 @@ template class StateNode : public NodeBase { public: - StateNode(StateNode&&) = default; - StateNode& operator=(StateNode&&) = default; - - StateNode(const StateNode&) = delete; - StateNode& operator=(const StateNode&) = delete; - explicit StateNode(const Group& group) : StateNode::NodeBase( group ), value_( ) @@ -50,6 +38,12 @@ class StateNode : public NodeBase value_( std::forward(value) ) { } + template + StateNode(InPlaceTag, const Group& group, Ts&& ... args) : + StateNode::NodeBase( group ), + value_( std::forward(args) ... ) + { } + S& Value() { return value_; } @@ -153,7 +147,7 @@ class StateVarNode : public StateNode }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// StateOpNode +/// StateFuncNode /////////////////////////////////////////////////////////////////////////////////////////////////// template class StateFuncNode : public StateNode @@ -344,6 +338,64 @@ class StateLinkNode : public StateNode VirtualOutputNode linkOutput_; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ObjectStateNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class ObjectStateNode : public StateNode> +{ +public: + ObjectStateNode(const Group& group, S&& obj, const std::initializer_list& memberIds) : + ObjectStateNode::StateNode( group, std::move(obj) ) + { + this->RegisterMe(); + + if (memberIds.size() == 0) + { + apply([this] (const auto& ... members) + { + REACT_EXPAND_PACK(memberIds_.push_back(GetInternals(members).GetNodeId())); + REACT_EXPAND_PACK(this->AttachToMe(GetInternals(members).GetNodeId())); + }, this->Value().GetObject().GetReactiveMembers()); + } + else + { + memberIds_.reserve(memberIds.size()); + + for (NodeId id : memberIds) + { + memberIds_.push_back(id); + this->AttachToMe(id); + } + } + } + + template + ObjectStateNode(InPlaceTag, const Group& group, Us&& ... args) : + ObjectStateNode::StateNode( in_place, group, group, std::forward(args) ... ) + { + this->RegisterMe(); + + apply([this] (const auto& ... members) + { REACT_EXPAND_PACK(memberIds_.push_back(GetInternals(members).GetNodeId())); }, this->Value().GetObject().GetReactiveMembers()); + } + + ~ObjectStateNode() + { + for (NodeId id : memberIds_) + this->DetachFromMe(id); + + this->UnregisterMe(); + } + + virtual UpdateResult Update(TurnId turnId) noexcept override + { + return UpdateResult::changed; + } + +private: + std::vector memberIds_; +}; /////////////////////////////////////////////////////////////////////////////////////////////////// /// StateInternals @@ -352,6 +404,8 @@ template class StateInternals { public: + StateInternals() = default; + StateInternals(const StateInternals&) = default; StateInternals& operator=(const StateInternals&) = default; @@ -381,6 +435,18 @@ class StateInternals std::shared_ptr> nodePtr_; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SameGroupOrLink +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +static State SameGroupOrLink(const Group& targetGroup, const State& dep) +{ + if (dep.GetGroup() == targetGroup) + return dep; + else + return StateLink::Create(targetGroup, dep); +} + /****************************************/ REACT_IMPL_END /***************************************/ #endif // REACT_DETAIL_GRAPH_STATENODES_H_INCLUDED \ No newline at end of file diff --git a/include/react/event.h b/include/react/event.h index ecde17b2..aeaaa6f6 100644 --- a/include/react/event.h +++ b/include/react/event.h @@ -31,35 +31,33 @@ template class Event : protected REACT_IMPL::EventInternals { public: - Event(const Event&) = default; - Event& operator=(const Event&) = default; - - Event(Event&&) = default; - Event& operator=(Event&&) = default; - // Construct with explicit group template - Event(const Group& group, F&& func, const Event& dep) : - Event::Event( CreateProcessingNode(group, std::forward(func), dep) ) - { } + static Event Create(const Group& group, F&& func, const Event& dep) + { return CreateProcessingNode(group, std::forward(func), dep); } // Construct with implicit group template - Event(F&& func, const Event& dep) : - Event::Event( CreateProcessingNode(dep.GetGroup(), std::forward(func), dep) ) - { } + static Event Create(F&& func, const Event& dep) + { return CreateProcessingNode(dep.GetGroup(), std::forward(func), dep); } // Construct with explicit group template - Event(const Group& group, F&& func, const Event& dep, const State& ... states) : - Event::Event( CreateSyncedProcessingNode(group, std::forward(func), dep, states ...) ) - { } + static Event Create(const Group& group, F&& func, const Event& dep, const State& ... states) + { return CreateSyncedProcessingNode(group, std::forward(func), dep, states ...); } // Construct with implicit group template - Event(F&& func, const Event& dep, const State& ... states) : - Event::Event( CreateSyncedProcessingNode(dep.GetGroup(), std::forward(func), dep, states ...) ) - { } + static Event Create(F&& func, const Event& dep, const State& ... states) + { return CreateSyncedProcessingNode(dep.GetGroup(), std::forward(func), dep, states ...); } + + Event() = default; + + Event(const Event&) = default; + Event& operator=(const Event&) = default; + + Event(Event&&) = default; + Event& operator=(Event&&) = default; auto Tokenize() const -> decltype(auto) { return REACT::Tokenize(*this); } @@ -83,13 +81,12 @@ class Event : protected REACT_IMPL::EventInternals { return e; } protected: - // Private node ctor - explicit Event(std::shared_ptr>&& nodePtr) : + Event(std::shared_ptr>&& nodePtr) : Event::EventInternals( std::move(nodePtr) ) { } template - auto CreateProcessingNode(const Group& group, F&& func, const Event& dep) -> decltype(auto) + static auto CreateProcessingNode(const Group& group, F&& func, const Event& dep) -> decltype(auto) { using REACT_IMPL::EventProcessingNode; using REACT_IMPL::SameGroupOrLink; @@ -99,7 +96,7 @@ class Event : protected REACT_IMPL::EventInternals } template - auto CreateSyncedProcessingNode(const Group& group, F&& func, const Event& dep, const State& ... syncs) -> decltype(auto) + static auto CreateSyncedProcessingNode(const Group& group, F&& func, const Event& dep, const State& ... syncs) -> decltype(auto) { using REACT_IMPL::SyncedEventProcessingNode; using REACT_IMPL::SameGroupOrLink; @@ -119,16 +116,17 @@ template class EventSource : public Event { public: + // Construct event source + static EventSource Create(const Group& group) + { return CreateSourceNode(group); } + + EventSource() = default; + EventSource(const EventSource&) = default; EventSource& operator=(const EventSource&) = default; EventSource(EventSource&& other) = default; EventSource& operator=(EventSource&& other) = default; - - // Construct event source - explicit EventSource(const Group& group) : - EventSource::Event( CreateSourceNode(group) ) - { } void Emit(const E& value) { EmitValue(value); } @@ -147,13 +145,17 @@ class EventSource : public Event { EmitValue(std::move(value)); return *this; } protected: - auto CreateSourceNode(const Group& group) -> decltype(auto) + EventSource(std::shared_ptr>&& nodePtr) : + EventSource::Event( std::move(nodePtr) ) + { } + +private: + static auto CreateSourceNode(const Group& group) -> decltype(auto) { using REACT_IMPL::EventSourceNode; return std::make_shared>(group); } -private: template void EmitValue(T&& value) { @@ -177,17 +179,18 @@ template class EventSlot : public Event { public: + // Construct emtpy slot + static EventSlot Create(const Group& group) + { return CreateSlotNode(group); } + + EventSlot() = default; + EventSlot(const EventSlot&) = default; EventSlot& operator=(const EventSlot&) = default; EventSlot(EventSlot&&) = default; EventSlot& operator=(EventSlot&&) = default; - // Construct emtpy slot - EventSlot(const Group& group) : - EventSlot::Event( CreateSlotNode(group) ) - { } - void Add(const Event& input) { AddInput(input); } @@ -198,13 +201,17 @@ class EventSlot : public Event { RemoveAllInputs(); } protected: - auto CreateSlotNode(const Group& group) -> decltype(auto) + EventSlot(std::shared_ptr>&& nodePtr) : + EventSlot::Event( std::move(nodePtr) ) + { } + +private: + static auto CreateSlotNode(const Group& group) -> decltype(auto) { using REACT_IMPL::EventSlotNode; return std::make_shared>(group); } -private: void AddInput(const Event& input) { using REACT_IMPL::NodeId; @@ -252,18 +259,24 @@ template class EventLink : public Event { public: + // Construct with group + static EventLink Create(const Group& group, const Event& input) + { return GetOrCreateLinkNode(group, input); } + + EventLink() = default; + EventLink(const EventLink&) = default; EventLink& operator=(const EventLink&) = default; EventLink(EventLink&&) = default; EventLink& operator=(EventLink&&) = default; - // Construct with group - EventLink(const Group& group, const Event& input) : - EventLink::Event( GetOrCreateLinkNode(group, input) ) +protected: + EventLink(std::shared_ptr>&& nodePtr) : + EventLink::Event( std::move(nodePtr) ) { } -protected: +private: static auto GetOrCreateLinkNode(const Group& group, const Event& input) -> decltype(auto) { using REACT_IMPL::EventLinkNode; @@ -274,9 +287,7 @@ class EventLink : public Event ReactGraph::LinkCache& linkCache = GetInternals(group).GetGraphPtr()->GetLinkCache(); - std::shared_ptr nodePtr = linkCache.LookupOrCreate( - k, - [&] + std::shared_ptr nodePtr = linkCache.LookupOrCreate(k, [&] { auto nodePtr = std::make_shared>(group, input); nodePtr->SetWeakSelfPtr(std::weak_ptr>{ nodePtr }); @@ -291,7 +302,7 @@ class EventLink : public Event /// Merge /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Merge(const Group& group, const Event& dep1, const Event& ... deps) -> Event +static auto Merge(const Group& group, const Event& dep1, const Event& ... deps) -> Event { using REACT_IMPL::EventMergeNode; using REACT_IMPL::SameGroupOrLink; @@ -302,79 +313,79 @@ auto Merge(const Group& group, const Event& dep1, const Event& ... deps) } template -auto Merge(const Event& dep1, const Event& ... deps) -> decltype(auto) +static auto Merge(const Event& dep1, const Event& ... deps) -> decltype(auto) { return Merge(dep1.GetGroup(), dep1, deps ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Filter /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Filter(const Group& group, F&& pred, const Event& dep) -> Event +static auto Filter(const Group& group, F&& pred, const Event& dep) -> Event { auto filterFunc = [capturedPred = std::forward(pred)] (const EventValueList& events, EventValueSink out) { std::copy_if(events.begin(), events.end(), out, capturedPred); }; - return Event(group, std::move(filterFunc), dep); + return Event::Create(group, std::move(filterFunc), dep); } template -auto Filter(F&& pred, const Event& dep) -> Event +static auto Filter(F&& pred, const Event& dep) -> Event { return Filter(dep.GetGroup(), std::forward(pred), dep); } template -auto Filter(const Group& group, F&& pred, const Event& dep, const State& ... states) -> Event +static auto Filter(const Group& group, F&& pred, const Event& dep, const State& ... states) -> Event { auto filterFunc = [capturedPred = std::forward(pred)] (const EventValueList& evts, EventValueSink out, const Ts& ... values) - { - for (const auto& v : evts) - if (capturedPred(v, values ...)) - *out++ = v; - }; + { + for (const auto& v : evts) + if (capturedPred(v, values ...)) + *out++ = v; + }; - return Event(group, std::move(filterFunc), dep, states ...); + return Event::Create(group, std::move(filterFunc), dep, states ...); } template -auto Filter(F&& pred, const Event& dep, const State& ... states) -> Event +static auto Filter(F&& pred, const Event& dep, const State& ... states) -> Event { return Filter(dep.GetGroup(), std::forward(pred), dep, states ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Transform /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Transform(const Group& group, F&& op, const Event& dep) -> Event +static auto Transform(const Group& group, F&& op, const Event& dep) -> Event { auto transformFunc = [capturedOp = std::forward(op)] (const EventValueList& evts, EventValueSink out) { std::transform(evts.begin(), evts.end(), out, capturedOp); }; - return Event(group, std::move(transformFunc), dep); + return Event::Create(group, std::move(transformFunc), dep); } template -auto Transform(F&& op, const Event& dep) -> Event +static auto Transform(F&& op, const Event& dep) -> Event { return Transform(dep.GetGroup(), std::forward(op), dep); } template -auto Transform(const Group& group, F&& op, const Event& dep, const State& ... states) -> Event +static auto Transform(const Group& group, F&& op, const Event& dep, const State& ... states) -> Event { auto transformFunc = [capturedOp = std::forward(op)] (const EventValueList& evts, EventValueSink out, const Us& ... values) - { - for (const auto& v : evts) - *out++ = capturedOp(v, values ...); - }; + { + for (const auto& v : evts) + *out++ = capturedOp(v, values ...); + }; - return Event(group, std::move(transformFunc), dep, states ...); + return Event::Create(group, std::move(transformFunc), dep, states ...); } template -auto Transform(F&& op, const Event& dep, const State& ... states) -> Event +static auto Transform(F&& op, const Event& dep, const State& ... states) -> Event { return Transform(dep.GetGroup(), std::forward(op), dep, states ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Join /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Join(const Group& group, const Event& dep1, const Event& ... deps) -> Event> +static auto Join(const Group& group, const Event& dep1, const Event& ... deps) -> Event> { using REACT_IMPL::EventJoinNode; using REACT_IMPL::SameGroupOrLink; @@ -387,7 +398,7 @@ auto Join(const Group& group, const Event& dep1, const Event& ... deps) } template -auto Join(const Event& dep1, const Event& ... deps) -> Event> +static auto Join(const Event& dep1, const Event& ... deps) -> Event> { return Join(dep1.GetGroup(), dep1, deps ...); } /******************************************/ REACT_END /******************************************/ @@ -400,7 +411,7 @@ static Event SameGroupOrLink(const Group& targetGroup, const Event& dep) if (dep.GetGroup() == targetGroup) return dep; else - return EventLink{ targetGroup, dep }; + return EventLink::Create(targetGroup, dep); } /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/observer.h b/include/react/observer.h index 3f228ffd..a5360478 100644 --- a/include/react/observer.h +++ b/include/react/observer.h @@ -29,57 +29,50 @@ class Observer : protected REACT_IMPL::ObserverInternals using NodeType = REACT_IMPL::ObserverNode; public: - Observer(const Observer&) = default; - Observer& operator=(const Observer&) = default; - - Observer(Observer&&) = default; - Observer& operator=(Observer&&) = default; - // Construct state observer with explicit group template - Observer(const Group& group, F&& func, const State& subject1, const State& ... subjects) : - Observer::Observer( CreateStateObserverNode(group, std::forward(func), subject1, subjects ...)) - { } + static Observer Create(const Group& group, F&& func, const State& subject1, const State& ... subjects) + { return CreateStateObserverNode(group, std::forward(func), subject1, subjects ...); } // Construct state observer with implicit group template - Observer(F&& func, const State& subject1, const State& ... subjects) : - Observer::Observer( CreateStateObserverNode(subject1.GetGroup(), std::forward(func), subject1, subjects ...)) - { } + static Observer Create(F&& func, const State& subject1, const State& ... subjects) + { return CreateStateObserverNode(subject1.GetGroup(), std::forward(func), subject1, subjects ...); } // Construct event observer with explicit group template - Observer(const Group& group, F&& func, const Event& subject) : - Observer::Observer( CreateEventObserverNode(group, std::forward(func), subject)) - { } + static Observer Create(const Group& group, F&& func, const Event& subject) + { return CreateEventObserverNode(group, std::forward(func), subject); } // Construct event observer with implicit group template - Observer(F&& func, const Event& subject) : - Observer::Observer( CreateEventObserverNode(subject.GetGroup(), std::forward(func), subject)) - { } + static Observer Create(F&& func, const Event& subject) + { return CreateEventObserverNode(subject.GetGroup(), std::forward(func), subject); } // Constructed synced event observer with explicit group template - Observer(const Group& group, F&& func, const Event& subject, const State& ... states) : - Observer::Observer( CreateSyncedEventObserverNode(group, std::forward(func), subject, states ...)) - { } + static Observer Create(const Group& group, F&& func, const Event& subject, const State& ... states) + { return CreateSyncedEventObserverNode(group, std::forward(func), subject, states ...); } // Constructed synced event observer with implicit group template - Observer(F&& func, const Event& subject, const State& ... states) : - Observer::Observer( CreateSyncedEventObserverNode(subject.GetGroup(), std::forward(func), subject, states ...)) - { } + static Observer Create(F&& func, const Event& subject, const State& ... states) + { return CreateSyncedEventObserverNode(subject.GetGroup(), std::forward(func), subject, states ...); } + + Observer(const Observer&) = default; + Observer& operator=(const Observer&) = default; -public: //Internal - // Private node ctor - explicit Observer(std::shared_ptr&& nodePtr) : + Observer(Observer&&) = default; + Observer& operator=(Observer&&) = default; + +protected: //Internal + Observer(std::shared_ptr&& nodePtr) : nodePtr_(std::move(nodePtr)) { } -protected: +private: template - auto CreateStateObserverNode(const Group& group, F&& func, const State& dep1, const State& ... deps) -> decltype(auto) + static auto CreateStateObserverNode(const Group& group, F&& func, const State& dep1, const State& ... deps) -> decltype(auto) { using REACT_IMPL::StateObserverNode; return std::make_shared::type, T1, Ts ...>>( @@ -87,7 +80,7 @@ class Observer : protected REACT_IMPL::ObserverInternals } template - auto CreateEventObserverNode(const Group& group, F&& func, const Event& dep) -> decltype(auto) + static auto CreateEventObserverNode(const Group& group, F&& func, const Event& dep) -> decltype(auto) { using REACT_IMPL::EventObserverNode; return std::make_shared::type, T>>( @@ -95,7 +88,7 @@ class Observer : protected REACT_IMPL::ObserverInternals } template - auto CreateSyncedEventObserverNode(const Group& group, F&& func, const Event& dep, const State& ... syncs) -> decltype(auto) + static auto CreateSyncedEventObserverNode(const Group& group, F&& func, const Event& dep, const State& ... syncs) -> decltype(auto) { using REACT_IMPL::SyncedEventObserverNode; return std::make_shared::type, T, Us ...>>( diff --git a/include/react/state.h b/include/react/state.h index 95d80b6c..6c93de6c 100644 --- a/include/react/state.h +++ b/include/react/state.h @@ -13,15 +13,13 @@ #include "react/api.h" #include "react/group.h" #include "react/common/ptrcache.h" +#include "react/detail/state_nodes.h" #include #include #include #include -#include "react/detail/state_nodes.h" - - /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -31,23 +29,23 @@ template class State : protected REACT_IMPL::StateInternals { public: - State(const State&) = default; - State& operator=(const State&) = default; - - State(State&&) = default; - State& operator=(State&&) = default; - // Construct with explicit group template - explicit State(const Group& group, F&& func, const State& dep1, const State& ... deps) : - State::StateInternals( CreateFuncNode(group, std::forward(func), dep1, deps ...) ) - { } + static State Create(const Group& group, F&& func, const State& dep1, const State& ... deps) + { return CreateFuncNode(group, std::forward(func), dep1, deps ...); } // Construct with implicit group template - explicit State(F&& func, const State& dep1, const State& ... deps) : - State::StateInternals( CreateFuncNode(dep1.GetGroup(), std::forward(func), dep1, deps ...) ) - { } + static State Create(F&& func, const State& dep1, const State& ... deps) + { return CreateFuncNode(dep1.GetGroup(), std::forward(func), dep1, deps ...); } + + State() = default; + + State(const State&) = default; + State& operator=(const State&) = default; + + State(State&&) = default; + State& operator=(State&&) = default; auto GetGroup() const -> const Group& { return this->GetNodePtr()->GetGroup(); } @@ -68,13 +66,13 @@ class State : protected REACT_IMPL::StateInternals { return s; } protected: - explicit State(std::shared_ptr>&& nodePtr) : + State(std::shared_ptr>&& nodePtr) : State::StateInternals( std::move(nodePtr) ) { } private: template - auto CreateFuncNode(const Group& group, F&& func, const State& dep1, const State& ... deps) -> decltype(auto) + static auto CreateFuncNode(const Group& group, F&& func, const State& dep1, const State& ... deps) -> decltype(auto) { using REACT_IMPL::StateFuncNode; using REACT_IMPL::SameGroupOrLink; @@ -94,23 +92,23 @@ template class StateVar : public State { public: + // Construct with group + default + static StateVar Create(const Group& group) + { return CreateVarNode(group); } + + // Construct with group + value + template + static StateVar Create(const Group& group, T&& value) + { return CreateVarNode(group, std::forward(value)); } + + StateVar() = default; + StateVar(const StateVar&) = default; StateVar& operator=(const StateVar&) = default; StateVar(StateVar&&) = default; StateVar& operator=(StateVar&&) = default; - // Construct with group + default - explicit StateVar(const Group& group) : - StateVar::State( CreateVarNode(group) ) - { } - - // Construct with group + value - template - StateVar(const Group& group, T&& value) : - StateVar::State( CreateVarNode(group, std::forward(value)) ) - { } - void Set(const S& newValue) { SetValue(newValue); } @@ -128,7 +126,7 @@ class StateVar : public State { return !(a == b); } protected: - explicit StateVar(std::shared_ptr>&& nodePtr) : + StateVar(std::shared_ptr>&& nodePtr) : StateVar::State( std::move(nodePtr) ) { } @@ -182,22 +180,22 @@ template class StateSlot : public State { public: + // Construct with explicit group + static StateSlot Create(const Group& group, const State& input) + { return CreateSlotNode(group, input); } + + // Construct with implicit group + static StateSlot Create(const State& input) + { return CreateSlotNode(input.GetGroup(), input); } + + StateSlot() = default; + StateSlot(const StateSlot&) = default; StateSlot& operator=(const StateSlot&) = default; StateSlot(StateSlot&&) = default; StateSlot& operator=(StateSlot&&) = default; - // Construct with explicit group - StateSlot(const Group& group, const State& input) : - StateSlot::State( CreateSlotNode(group, input) ) - { } - - // Construct with implicit group - explicit StateSlot(const State& input) : - StateSlot::State( CreateSlotNode(input.GetGroup(), input) ) - { } - void Set(const State& newInput) { SetInput(newInput); } @@ -205,7 +203,7 @@ class StateSlot : public State { SetInput(newInput); } protected: - explicit StateSlot(std::shared_ptr>&& nodePtr) : + StateSlot(std::shared_ptr>&& nodePtr) : StateSlot::State( std::move(nodePtr) ) { } @@ -240,18 +238,24 @@ template class StateLink : public State { public: + // Construct with group + static StateLink Create(const Group& group, const State& input) + { return GetOrCreateLinkNode(group, input); } + + StateLink() = default; + StateLink(const StateLink&) = default; StateLink& operator=(const StateLink&) = default; StateLink(StateLink&&) = default; StateLink& operator=(StateLink&&) = default; - // Construct with group - StateLink(const Group& group, const State& input) : - StateLink::State( GetOrCreateLinkNode(group, input) ) +protected: + StateLink(std::shared_ptr>&& nodePtr) : + StateLink::State( std::move(nodePtr) ) { } -protected: +private: static auto GetOrCreateLinkNode(const Group& group, const State& input) -> decltype(auto) { using REACT_IMPL::StateLinkNode; @@ -262,9 +266,7 @@ class StateLink : public State ReactGraph::LinkCache& linkCache = GetInternals(group).GetGraphPtr()->GetLinkCache(); - std::shared_ptr nodePtr = linkCache.LookupOrCreate( - k, - [&] + std::shared_ptr nodePtr = linkCache.LookupOrCreate(k, [&] { auto nodePtr = std::make_shared>(group, input); nodePtr->SetWeakSelfPtr(std::weak_ptr>{ nodePtr }); @@ -275,19 +277,97 @@ class StateLink : public State } }; -/******************************************/ REACT_END /******************************************/ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ObjectContext +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class ObjectContext +{ +public: + ObjectContext() = default; + + ObjectContext(const ObjectContext&) = default; + ObjectContext& operator=(const ObjectContext&) = default; + + ObjectContext(ObjectContext&&) = default; + ObjectContext& operator=(ObjectContext&&) = default; + + const S& GetObject() const + { return object_; } -/***************************************/ REACT_IMPL_BEGIN /**************************************/ + template + const U& Get(const State& member) const + { return GetInternals(member).Value(); } + template + const EventValueList& Get(const Event& member) const + { return GetInternals(member).Events(); } + +private: + template + explicit ObjectContext(Us&& ... args) : + object_( std::forward(args) ... ) + { } + + S object_; + + template + friend class impl::StateNode; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ObjectState +/////////////////////////////////////////////////////////////////////////////////////////////////// template -static State SameGroupOrLink(const Group& targetGroup, const State& dep) +class ObjectState : public State> { - if (dep.GetGroup() == targetGroup) - return dep; - else - return StateLink( targetGroup, dep ); -} +public: + // Construct with group + template + static ObjectState Create(const Group& group, S&& obj, const Us& ... members) + { + using REACT_IMPL::NodeId; + + std::initializer_list memberIds = { GetInternals(members).GetNodeId() ... }; + + return CreateObjectStateNode(group, std::move(obj), memberIds); + } + + template + static ObjectState Create(InPlaceTag, const Group& group, Us&& ... args) + { return CreateObjectStateNode(in_place, group, std::forward(args) ...); } + + ObjectState() = default; -/****************************************/ REACT_IMPL_END /***************************************/ + ObjectState(const ObjectState&) = default; + ObjectState& operator=(const ObjectState&) = default; + + ObjectState(ObjectState&&) = default; + ObjectState& operator=(ObjectState&&) = default; + +protected: + ObjectState(std::shared_ptr>>&& nodePtr) : + ObjectState::State( std::move(nodePtr) ) + { } + +private: + static auto CreateObjectStateNode(const Group& group, S&& obj, const std::initializer_list& memberIds) -> decltype(auto) + { + using REACT_IMPL::ObjectStateNode; + + return std::make_shared>(group, std::move(obj), memberIds); + } + + template + static auto CreateObjectStateNode(InPlaceTag, const Group& group, Us&& ... args) -> decltype(auto) + { + using REACT_IMPL::ObjectStateNode; + using REACT_IMPL::SameGroupOrLink; + + return std::make_shared>(in_place, group, std::forward(args) ...); + } +}; + +/******************************************/ REACT_END /******************************************/ #endif // REACT_STATE_H_INCLUDED \ No newline at end of file diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index 1867d880..088c482f 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -22,6 +22,7 @@ {5E56AAB9-4E33-4B9E-A315-E85CEDB75CF1} CppReact 8.1 + CppReact diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1946c5e0..82b7e451 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,15 +9,11 @@ add_subdirectory($ENV{GTEST_DIR} ${CMAKE_CURRENT_BINARY_DIR}/gtest) ### CppReactTest add_executable(CppReactTest - src/EventStreamTest.cpp - src/EventStreamTestQ.cpp - src/MoveTest.cpp - src/ObserverTest.cpp - src/ObserverTestQ.cpp - src/OperationsTest.cpp - src/OperationsTestQ.cpp - src/SignalTest.cpp - src/SignalTestQ.cpp - src/TransactionTest.cpp) + src/algorithm_tests.cpp + src/common_tests.cpp + src/event_tests.cpp + src/observer_tests.cpp + src/state_tests.cpp + src/transaction_tests.cpp) target_link_libraries(CppReactTest CppReact gtest gtest_main) diff --git a/tests/src/algorithm_tests.cpp b/tests/src/algorithm_tests.cpp index 49bd221c..32cac388 100644 --- a/tests/src/algorithm_tests.cpp +++ b/tests/src/algorithm_tests.cpp @@ -24,14 +24,14 @@ TEST(AlgorithmTest, Hold) Group g; - EventSource evt1( g ); + auto evt1 = EventSource::Create(g); State st = Hold(1, evt1); int output = 0; int turns = 0; - Observer obs([&] (int v) + auto obs = Observer::Create([&] (int v) { ++turns; output = v; @@ -60,14 +60,14 @@ TEST(AlgorithmTest, Monitor1) Group g; - StateVar st( g, 1 ); + auto st = StateVar::Create(g, 1); Event evt = Monitor( st ); int output = 0; int turns = 0; - Observer obs([&] (const auto& events) + auto obs = Observer::Create([&] (const auto& events) { ++turns; @@ -98,7 +98,7 @@ TEST(AlgorithmTest, Monitor2) Group g; - StateVar target( g, 10 ); + auto target = StateVar::Create(g, 10); std::vector results; @@ -107,7 +107,7 @@ TEST(AlgorithmTest, Monitor2) { // Observer is created in a nested scope so it gets destructed before the end of this function. - Observer obs([&] (const auto& events) + auto obs = Observer::Create([&] (const auto& events) { for (int e : events) results.push_back(e); @@ -134,15 +134,15 @@ TEST(AlgorithmTest, Snapshot) { Group g; - StateVar sv( g, 1 ); - EventSource<> es( g ); + auto sv = StateVar::Create(g, 1); + auto es = EventSource<>::Create(g); State st = Snapshot( sv, es ); int output = 0; int turns = 0; - Observer obs([&] (int v) + auto obs = Observer::Create([&] (int v) { ++turns; output = v; @@ -166,15 +166,15 @@ TEST(AlgorithmTest, Pulse) { Group g; - StateVar sv( g, 1 ); - EventSource<> es( g ); + auto sv = StateVar::Create(g, 1); + auto es = EventSource<>::Create(g); Event st = Pulse( sv, es ); int output = 0; int turns = 0; - Observer obs([&] (const auto& events) + auto obs = Observer::Create([&] (const auto& events) { for (int e : events) { @@ -201,7 +201,7 @@ TEST(AlgorithmTest, Iterate1) { Group g; - EventSource numSrc( g ); + auto numSrc = EventSource::Create(g); State numFold = Iterate(0, [] (const auto& events, int v) { @@ -214,7 +214,7 @@ TEST(AlgorithmTest, Iterate1) numSrc << i; int output = 0; - Observer obs([&] (int v) { output = v; }, numFold); + auto obs = Observer::Create([&] (int v) { output = v; }, numFold); EXPECT_EQ(output, 5050); } @@ -223,7 +223,7 @@ TEST(AlgorithmTest, Iterate2) { Group g; - EventSource charSrc( g ); + auto charSrc = EventSource::Create(g); State strFold = Iterate(std::string(""), [] (const auto& events, std::string s) { @@ -233,7 +233,7 @@ TEST(AlgorithmTest, Iterate2) }, charSrc); std::string output; - Observer obs([&] (const auto& v) { output = v; }, strFold); + auto obs = Observer::Create([&] (const auto& v) { output = v; }, strFold); charSrc << 'T' << 'e' << 's' << 't'; @@ -244,7 +244,7 @@ TEST(AlgorithmTest, Iterate3) { Group g; - EventSource numSrc( g ); + auto numSrc = EventSource::Create(g); State numFold = Iterate(0, [] (const auto& events, int v) { @@ -256,7 +256,7 @@ TEST(AlgorithmTest, Iterate3) int turns = 0; int output = 0; - Observer obs([&] (const auto& v) + auto obs = Observer::Create([&] (const auto& v) { ++turns; output = v; @@ -298,7 +298,7 @@ TEST(AlgorithmTest, Iterate4) { Group g; - EventSource<> trigger( g ); + auto trigger = EventSource<>::Create(g); { State inc = Iterate(0, Incrementer{ }, trigger); @@ -306,7 +306,7 @@ TEST(AlgorithmTest, Iterate4) trigger.Emit(); int output = 0; - Observer obs([&] (int v) { output = v; }, inc); + auto obs = Observer::Create([&] (int v) { output = v; }, inc); EXPECT_EQ(output, 100); } @@ -317,7 +317,7 @@ TEST(AlgorithmTest, Iterate4) trigger.Emit(); int output = 0; - Observer obs([&] (int v) { output = v; }, dec); + auto obs = Observer::Create([&] (int v) { output = v; }, dec); ASSERT_EQ(output, 100); } @@ -327,7 +327,7 @@ TEST(AlgorithmTest, IterateByRef1) { Group g; - EventSource src( g ); + auto src = EventSource::Create(g); auto x = IterateByRef>(std::vector{ }, [] (const auto& events, auto& v) { @@ -336,7 +336,7 @@ TEST(AlgorithmTest, IterateByRef1) }, src); std::vector output; - Observer obs([&] (const auto& v) { output = v; }, x); + auto obs = Observer::Create([&] (const auto& v) { output = v; }, x); // Push for (int i=1; i<=100; i++) @@ -353,7 +353,7 @@ TEST(AlgorithmTest, IterateByRef2) { Group g; - EventSource<> src( g ); + auto src = EventSource<>::Create(g); auto x = IterateByRef>(std::vector{ }, [] (const auto& events, std::vector& v) { @@ -362,7 +362,7 @@ TEST(AlgorithmTest, IterateByRef2) }, src); std::vector output; - Observer obs([&] (const auto& v) { output = v; }, x); + auto obs = Observer::Create([&] (const auto& v) { output = v; }, x); // Push for (auto i=0; i<100; i++) @@ -392,15 +392,15 @@ TEST(AlgorithmTest, TransformWithState) { Group g; - StateVar in1( g ); - StateVar in2( g ); + auto in1 = StateVar::Create(g); + auto in2 = StateVar::Create(g); - State sum( Sum, in1, in2 ); - State prod( Prod, in1, in2 ); - State diff( Diff, in1, in2 ); + auto sum = State::Create(Sum, in1, in2); + auto prod = State::Create(Prod, in1, in2); + auto diff = State::Create(Diff, in1, in2); - EventSource<> src1( g ); - EventSource src2( g ); + auto src1 = EventSource<>::Create(g); + auto src2 = EventSource::Create(g); auto out1 = Transform>([] (Token, int sum, int prod, int diff) { @@ -418,7 +418,7 @@ TEST(AlgorithmTest, TransformWithState) { std::tuple output1; - Observer obs1([&] (const auto& events) + auto obs1 = Observer::Create([&] (const auto& events) { for (const auto& e : events) { @@ -429,7 +429,7 @@ TEST(AlgorithmTest, TransformWithState) std::tuple output2; - Observer obs2([&] (const auto& events) + auto obs2 = Observer::Create([&] (const auto& events) { for (const auto& e : events) { @@ -460,7 +460,7 @@ TEST(AlgorithmTest, TransformWithState) { std::tuple output1; - Observer obs1([&] (const auto& events) + auto obs1 = Observer::Create([&] (const auto& events) { for (const auto& e : events) { @@ -471,7 +471,7 @@ TEST(AlgorithmTest, TransformWithState) std::tuple output2; - Observer obs2([&] (const auto& events) + auto obs2 = Observer::Create([&] (const auto& events) { for (const auto& e : events) { @@ -504,14 +504,14 @@ TEST(AlgorithmTest, IterateWithState) { Group g; - StateVar in1( g ); - StateVar in2( g ); + auto in1 = StateVar::Create(g); + auto in2 = StateVar::Create(g); - State op1(Sum, in1, in2); - State op2([] (int a, int b) { return (a + b) * 10; }, in1, in2); + auto op1 = State::Create(Sum, in1, in2); + auto op2 = State::Create([] (int a, int b) { return (a + b) * 10; }, in1, in2); - EventSource<> src1( g ); - EventSource src2( g ); + auto src1 = EventSource<>::Create(g); + auto src2 = EventSource::Create(g); auto out1 = Iterate>(std::make_tuple(0, 0), [] (const auto& events, std::tuple t, int op1, int op2) { @@ -536,7 +536,7 @@ TEST(AlgorithmTest, IterateWithState) { std::tuple output1; - Observer obs1([&] (const auto& v) + auto obs1 = Observer::Create([&] (const auto& v) { ++turns1; output1 = v; @@ -544,7 +544,7 @@ TEST(AlgorithmTest, IterateWithState) std::tuple output2; - Observer obs2([&] (const auto& v) + auto obs2 = Observer::Create([&] (const auto& v) { ++turns2; output2 = v; @@ -570,7 +570,7 @@ TEST(AlgorithmTest, IterateWithState) { std::tuple output1; - Observer obs1([&] (const auto& v) + auto obs1 = Observer::Create([&] (const auto& v) { ++turns1; output1 = v; @@ -578,7 +578,7 @@ TEST(AlgorithmTest, IterateWithState) std::tuple output2; - Observer obs2([&] (const auto& v) + auto obs2 = Observer::Create([&] (const auto& v) { ++turns2; output2 = v; @@ -606,14 +606,14 @@ TEST(AlgorithmTest, IterateByRefWithState) { Group g; - StateVar in1( g ); - StateVar in2( g ); + auto in1 = StateVar::Create(g); + auto in2 = StateVar::Create(g); - State op1( Sum, in1, in2 ); - State op2([] (int a, int b) { return (a + b) * 10; }, in1, in2); + auto op1 = State::Create(Sum, in1, in2); + auto op2 = State::Create([] (int a, int b) { return (a + b) * 10; }, in1, in2); - EventSource<> src1( g ); - EventSource src2( g ); + auto src1 = EventSource<>::Create(g); + auto src2 = EventSource::Create(g); auto out1 = IterateByRef>(std::vector{ }, [] (const auto& events, std::vector& v, int op1, int op2) { @@ -640,7 +640,7 @@ TEST(AlgorithmTest, IterateByRefWithState) { std::vector output1; - Observer obs1([&] (const std::vector& v) + auto obs1 = Observer::Create([&] (const std::vector& v) { ++turns1; output1 = v; @@ -648,7 +648,7 @@ TEST(AlgorithmTest, IterateByRefWithState) std::vector output2; - Observer obs2([&] (const std::vector& v) + auto obs2 = Observer::Create([&] (const std::vector& v) { ++turns2; output2 = v; @@ -676,7 +676,7 @@ TEST(AlgorithmTest, IterateByRefWithState) { std::vector output1; - Observer obs1([&] (const std::vector& v) + auto obs1 = Observer::Create([&] (const std::vector& v) { ++turns1; output1 = v; @@ -684,7 +684,7 @@ TEST(AlgorithmTest, IterateByRefWithState) std::vector output2; - Observer obs2([&] (const std::vector& v) + auto obs2 = Observer::Create([&] (const std::vector& v) { ++turns2; output2 = v; diff --git a/tests/src/event_tests.cpp b/tests/src/event_tests.cpp index 168ac619..c4a3200c 100644 --- a/tests/src/event_tests.cpp +++ b/tests/src/event_tests.cpp @@ -25,7 +25,7 @@ TEST(EventTest, Construction) // Event source { - EventSource t1( g ); + auto t1 = EventSource::Create(g); EventSource t2( t1 ); EventSource t3( std::move(t1) ); @@ -37,7 +37,7 @@ TEST(EventTest, Construction) // Event slot { - EventSlot t1( g ); + auto t1 = EventSlot::Create(g); EventSlot t2( t1 ); EventSlot t3( std::move(t1) ); @@ -49,9 +49,9 @@ TEST(EventTest, Construction) // Event link { - EventSlot s1( g ); + auto s1 = EventSlot::Create(g); - EventLink t1( g, s1 ); + auto t1 = EventLink::Create(g, s1); EventLink t2( t1 ); EventLink t3( std::move(t1) ); @@ -66,11 +66,11 @@ TEST(EventTest, BasicOutput) { Group g; - EventSource evt( g ); + auto evt = EventSource::Create(g); int output = 0; - Observer obs([&] (const auto& events) + auto obs = Observer::Create([&] (const auto& events) { for (int e : events) output += e; @@ -89,15 +89,15 @@ TEST(EventTest, Slots) { Group g; - EventSource evt1( g ); - EventSource evt2( g ); + auto evt1 = EventSource::Create(g); + auto evt2 = EventSource::Create(g); - EventSlot slot( g ); + auto slot = EventSlot::Create(g); int output = 0; int turns = 0; - Observer obs([&] (const auto& events) + auto obs = Observer::Create([&] (const auto& events) { ++turns; @@ -153,12 +153,12 @@ TEST(EventTest, Transactions) { Group g; - EventSource evt( g ); + auto evt = EventSource::Create(g); int output = 0; int turns = 0; - Observer obs([&] (const auto& events) + auto obs = Observer::Create([&] (const auto& events) { ++turns; for (int e : events) @@ -182,17 +182,17 @@ TEST(EventTest, Links) Group g2; Group g3; - EventSource evt1( g1 ); - EventSource evt2( g2 ); - EventSource evt3( g3 ); + auto evt1 = EventSource::Create(g1); + auto evt2 = EventSource::Create(g2); + auto evt3 = EventSource::Create(g3); - EventSlot slot( g1 ); + auto slot = EventSlot::Create(g1); // Same group slot.Add(evt1); // Explicit link - EventLink lnk2( g1, evt2 ); + auto lnk2 = EventLink::Create(g1, evt2); slot.Add(lnk2); // Implicit link @@ -203,7 +203,7 @@ TEST(EventTest, Links) EXPECT_EQ(0, output); - Observer obs([&] (const auto& events) + auto obs = Observer::Create([&] (const auto& events) { ++turns; for (int e : events) @@ -224,19 +224,19 @@ TEST(EventTest, EventSources) { Group g; - EventSource es1( g ); - EventSource es2( g ); + auto es1 = EventSource::Create(g); + auto es2 = EventSource::Create(g); std::queue results1; std::queue results2; - Observer obs1([&] (const auto& events) + auto obs1 = Observer::Create([&] (const auto& events) { for (int e : events) results1.push(e); }, es1); - Observer obs2([&] (const auto& events) + auto obs2 = Observer::Create([&] (const auto& events) { for (int e : events) results2.push(e); @@ -280,15 +280,15 @@ TEST(EventTest, Merge1) { Group g; - EventSource a1( g ); - EventSource a2( g ); - EventSource a3( g ); + auto a1 = EventSource::Create(g); + auto a2 = EventSource::Create(g); + auto a3 = EventSource::Create(g); Event merged = Merge(g, a1, a2, a3); std::vector results; - Observer obs1([&] (const auto& events) + auto obs1 = Observer::Create([&] (const auto& events) { for (int e : events) results.push_back(e); @@ -312,15 +312,15 @@ TEST(EventTest, Merge2) { Group g; - EventSource a1( g ); - EventSource a2( g ); - EventSource a3( g ); + auto a1 = EventSource::Create(g); + auto a2 = EventSource::Create(g); + auto a3 = EventSource::Create(g); Event merged = Merge(a1, a2, a3); std::vector results; - Observer obs1([&] (const auto& events) + auto obs1 = Observer::Create([&] (const auto& events) { for (const auto& e : events) results.push_back(e); @@ -348,8 +348,8 @@ TEST(EventTest, Merge3) { Group g; - EventSource a1( g ); - EventSource a2( g ); + auto a1 = EventSource::Create(g); + auto a2 = EventSource::Create(g); Event f1 = Filter([] (int v) { return true; }, a1); Event f2 = Filter([] (int v) { return true; }, a2); @@ -358,7 +358,7 @@ TEST(EventTest, Merge3) std::queue results; - Observer obs1([&] (const auto& events) + auto obs1 = Observer::Create([&] (const auto& events) { for (int e : events) results.push(e); @@ -387,7 +387,7 @@ TEST(EventTest, Filter) { Group g; - EventSource in( g ); + auto in = EventSource::Create(g); std::queue results; @@ -396,7 +396,7 @@ TEST(EventTest, Filter) return s == "Hello World"; }, in); - Observer obs1([&] (const auto& events) + auto obs1 = Observer::Create([&] (const auto& events) { for (const auto& e : events) results.push(e); @@ -417,8 +417,8 @@ TEST(EventTest, Transform) { Group g; - EventSource in1( g ); - EventSource in2( g ); + auto in1 = EventSource::Create(g); + auto in2 = EventSource::Create(g); std::vector results; @@ -430,7 +430,7 @@ TEST(EventTest, Transform) return s; }, merged); - Observer obs1([&] (const auto& events) + auto obs1 = Observer::Create([&] (const auto& events) { for (const auto& e : events) results.push_back(e); @@ -452,13 +452,13 @@ TEST(EventTest, Flow) std::vector results; - EventSource in1( g ); - EventSource in2( g ); + auto in1 = EventSource::Create(g); + auto in2 = EventSource::Create(g); auto merged = Merge(in1, in2); int turns = 0; - Event processed([&] (const auto& events, auto out) + auto processed = Event::Create([&] (const auto& events, auto out) { for (const auto& e : events) { @@ -469,7 +469,7 @@ TEST(EventTest, Flow) ++turns; }, merged); - Observer obs1([&] (const auto& events) + auto obs1 = Observer::Create([&] (const auto& events) { for (float e : events) results.push_back(e); @@ -497,15 +497,15 @@ TEST(EventTest, Join) { Group g; - EventSource in1( g ); - EventSource in2( g ); - EventSource in3( g ); + auto in1 = EventSource::Create(g); + auto in2 = EventSource::Create(g); + auto in3 = EventSource::Create(g); Event> joined = Join(in1, in2, in3); std::vector> results; - Observer obs1([&] (const auto& events) + auto obs1 = Observer::Create([&] (const auto& events) { for (const auto& e : events) results.push_back(e); @@ -536,12 +536,12 @@ TEST(EventTest, FilterWithState) { Group g; - EventSource in( g ); + auto in = EventSource::Create(g); - StateVar sig1( g, 1338 ); - StateVar sig2( g, 1336 ); + auto sig1 = StateVar::Create(g, 1338); + auto sig2 = StateVar::Create(g, 1336); - EventSource in2( g ); + auto in2 = EventSource::Create(g); auto filtered = Filter([] (const std::string& s, int sig1, int sig2) { @@ -550,7 +550,7 @@ TEST(EventTest, FilterWithState) std::queue results; - Observer obs([&] (const auto& events) + auto obs = Observer::Create([&] (const auto& events) { for (const auto& e : events) results.push(e); @@ -573,13 +573,13 @@ TEST(EventTest, TransformWithState) std::vector results; - EventSource in1( g ); - EventSource in2( g ); + auto in1 = EventSource::Create(g); + auto in2 = EventSource::Create(g); Event merged = Merge(in1, in2); - StateVar first( g, "Ace" ); - StateVar last( g, "McSteele" ); + auto first = StateVar::Create(g, "Ace"); + auto last = StateVar::Create(g, "McSteele"); auto transformed = Transform([] (std::string s, const std::string& first, const std::string& last) -> std::string { @@ -588,7 +588,7 @@ TEST(EventTest, TransformWithState) return s; }, merged, first, last); - Observer obs([&] (const auto& events) + auto obs = Observer::Create([&] (const auto& events) { for (const auto& e : events) results.push_back(e); @@ -615,15 +615,15 @@ TEST(EventTest, FlowWithState) std::vector results; - EventSource in1( g ); - EventSource in2( g ); + auto in1 = EventSource::Create(g); + auto in2 = EventSource::Create(g); - StateVar mult( g, 10 ); + auto mult = StateVar::Create(g, 10); Event merged = Merge(in1, in2); int callCount = 0; - Event processed([&] (const auto& events, auto out, int mult) + auto processed = Event::Create([&] (const auto& events, auto out, int mult) { for (const auto& e : events) { @@ -634,7 +634,7 @@ TEST(EventTest, FlowWithState) callCount++; }, merged, mult); - Observer obs([&] (const auto& events) + auto obs = Observer::Create([&] (const auto& events) { for (float e : events) results.push_back(e); diff --git a/tests/src/state_tests.cpp b/tests/src/state_tests.cpp index 953cedc8..10e41205 100644 --- a/tests/src/state_tests.cpp +++ b/tests/src/state_tests.cpp @@ -20,7 +20,7 @@ TEST(StateTest, Construction) // State variable { - StateVar t1( g, 0 ); + auto t1 = StateVar::Create(g, 0); StateVar t2( t1 ); StateVar t3( std::move(t1) ); @@ -32,9 +32,9 @@ TEST(StateTest, Construction) // State slot { - StateVar t0( g, 0 ); + auto t0 = StateVar::Create(g, 0); - StateSlot t1( g, t0 ); + auto t1 = StateSlot::Create(g, t0); StateSlot t2( t1 ); StateSlot t3( std::move(t1) ); @@ -46,11 +46,11 @@ TEST(StateTest, Construction) // State link { - StateVar t0( g, 0 ); + auto t0 = StateVar::Create(g, 0); - StateSlot s1( g, t0 ); + auto s1 = StateSlot::Create(g, t0); - StateLink t1( g, s1 ); + auto t1 = StateLink::Create(g, s1); StateLink t2( t1 ); StateLink t3( std::move(t1) ); @@ -65,11 +65,11 @@ TEST(StateTest, BasicOutput) { Group g; - StateVar st( g ); + auto st = StateVar::Create(g); int output = 0; - Observer obs([&] (const auto& v) + auto obs2 = Observer::Create([&] (const auto& v) { output += v; }, st); @@ -87,15 +87,15 @@ TEST(StateTest, Slots) { Group g; - StateVar st1( g ); - StateVar st2( g ); + auto st1 = StateVar::Create(g); + auto st2 = StateVar::Create(g); - StateSlot slot( g, st1 ); + auto slot = StateSlot::Create(g, st1); int output = 0; int turns = 0; - Observer obs([&] (const auto& v) + auto obs = Observer::Create([&] (const auto& v) { ++turns; output += v; @@ -125,12 +125,12 @@ TEST(StateTest, Transactions) { Group g; - StateVar st( g, 1 ); + auto st = StateVar::Create(g, 1); int output = 0; int turns = 0; - Observer obs([&] (const auto& v) + auto obs = Observer::Create([&] (const auto& v) { ++turns; output += v; @@ -156,16 +156,16 @@ TEST(StateTest, Links) Group g2; Group g3; - StateVar st1( g1, 1 ); - StateVar st2( g2, 2 ); - StateVar st3( g3, 3 ); + auto st1 = StateVar::Create(g1, 1); + auto st2 = StateVar::Create(g2, 2); + auto st3 = StateVar::Create(g3, 3); - StateSlot slot( g1, st1 ); + auto slot = StateSlot::Create(g1, st1); int output = 0; int turns = 0; - Observer obs([&] (const auto& v) + auto obs = Observer::Create([&] (const auto& v) { ++turns; output = v; @@ -177,7 +177,7 @@ TEST(StateTest, Links) EXPECT_EQ(2, turns); // Explicit link - StateLink lnk2( g1, st2 ); + auto lnk2 = StateLink::Create(g1, st2); slot.Set(lnk2); std::this_thread::sleep_for(std::chrono::seconds(1)); EXPECT_EQ(2, output); @@ -223,21 +223,21 @@ TEST(StateTest, StateCombination1) { Group g; - StateVar a( g, 0 ); - StateVar b( g, 0 ); - StateVar c( g, 0 ); + auto a = StateVar::Create(g, 0); + auto b = StateVar::Create(g, 0); + auto c = StateVar::Create(g, 0); - State s1(Sum2, a, b); + auto s1 = State::Create(Sum2, a, b); - State x(Sum2, s1, c); - State y(Sum3, a, b, c); + auto x = State::Create(Sum2, s1, c); + auto y = State::Create(Sum3, a, b, c); int output1 = 0; int output2 = 0; int turns1 = 0; int turns2 = 0; - Observer obs1([&] (int v) + auto obs1 = Observer::Create([&] (int v) { ++turns1; output1 = v; @@ -246,7 +246,7 @@ TEST(StateTest, StateCombination1) EXPECT_EQ(0, output1); EXPECT_EQ(1, turns1); - Observer obs2([&] (int v) + auto obs2 = Observer::Create([&] (int v) { ++turns2; output2 = v; @@ -272,57 +272,57 @@ TEST(StateTest, StateCombination2) std::vector results; - StateVar n1( g, 1 ); + auto n1 = StateVar::Create(g, 1); - State n2([] (int n1) + auto n2 = State::Create([] (int n1) { return n1 + 1; }, n1); - State n3([] (int n1, int n2) + auto n3 = State::Create([] (int n1, int n2) { return n2 + n1 + 1; }, n1, n2); - State n4([] (int n3) + auto n4 = State::Create([] (int n3) { return n3 + 1; }, n3); - State n5([] (int n1, int n3, int n4) + auto n5 = State::Create([] (int n1, int n3, int n4) { return n4 + n3 + n1 + 1; }, n1, n3, n4); - State n6([] (int n5) + auto n6 = State::Create([] (int n5) { return n5 + 1; }, n5); - State n7([] (int n5, int n6) + auto n7 = State::Create([] (int n5, int n6) { return n6 + n5 + 1; }, n5, n6); - State n8([] (int n7) + auto n8 = State::Create([] (int n7) { return n7 + 1; }, n7); - State n9([] (int n1, int n5, int n7, int n8) + auto n9 = State::Create([] (int n1, int n5, int n7, int n8) { return n8 + n7 + n5 + n1 + 1; }, n1, n5, n7, n8); - State n10([] (int n9) + auto n10 = State::Create([] (int n9) { return n9 + 1; }, n9); - State n11([] (int n9, int n10) + auto n11 = State::Create([] (int n9, int n10) { return n10 + n9 + 1; }, n9, n10); - State n12([] (int n11) + auto n12 = State::Create([] (int n11) { return n11 + 1; }, n11); - State n13([] (int n9, int n11, int n12) + auto n13 = State::Create([] (int n9, int n11, int n12) { return n12 + n11 + n9 + 1; }, n9, n11, n12); - State n14([] (int n13) + auto n14 = State::Create([] (int n13) { return n13 + 1; }, n13); - State n15([] (int n13, int n14) + auto n15 = State::Create([] (int n13, int n14) { return n14 + n13 + 1; }, n13, n14); - State n16([] (int n15) + auto n16 = State::Create([] (int n15) { return n15 + 1; }, n15); - State n17([] (int n9, int n13, int n15, int n16) + auto n17 = State::Create([] (int n9, int n13, int n15, int n16) { return n16 + n15 + n13 + n9 + 1; }, n9, n13, n15, n16); - Observer obs([&] (int v) { results.push_back(v); }, n17); + auto obs = Observer::Create([&] (int v) { results.push_back(v); }, n17); n1.Set(10); // 7732 n1.Set(100); // 68572 @@ -342,11 +342,11 @@ TEST(StateTest, Modify1) std::vector results; - StateVar> var( g, std::vector{ } ); + auto var = StateVar>::Create(g, std::vector{ }); int turns = 0; - Observer obs([&] (const std::vector& v) + auto obs = Observer::Create([&] (const std::vector& v) { ++turns; results = v; @@ -372,11 +372,11 @@ TEST(StateTest, Modify2) std::vector results; - StateVar> var( g, std::vector{ } ); + auto var = StateVar>::Create(g, std::vector{ }); int turns = 0; - Observer obs([&] (const std::vector& v) + auto obs = Observer::Create([&] (const std::vector& v) { ++turns; results = v; @@ -402,17 +402,16 @@ TEST(StateTest, Modify3) std::vector results; - StateVar> var( g, std::vector{ } ); + auto var = StateVar>::Create(g, std::vector{ }); int turns = 0; - Observer obs([&] (const std::vector& v) + auto obs = Observer::Create([&] (const std::vector& v) { ++turns; results = v; }, var); - // Also terrible g.DoTransaction([&] { var.Set(std::vector{ 30, 50 }); @@ -424,4 +423,58 @@ TEST(StateTest, Modify3) EXPECT_EQ(results[2], 70); ASSERT_EQ(turns, 2); +} + +class Widget +{ +public: + StateVar color; + + StateVar width; + StateVar height; + + auto GetReactiveMembers() const -> decltype(auto) + { return std::tie(color, width, height); } + + Widget(const Group& group) + { + color = StateVar::Create(group, 0); + width = StateVar::Create(group, 0); + height = StateVar::Create(group, 0); + } + + Widget(const Group& group, int value) + { + color = StateVar::Create(group, value); + width = StateVar::Create(group, value); + height = StateVar::Create(group, value); + } + + +}; + +TEST(StateTest, Object) +{ + Group g; + + Widget w1( g ); + Widget w2( g ); + + auto st1 = ObjectState::Create(g, std::move(w1), w1.color, w1.width, w1.height); + + auto st2 = ObjectState::Create(in_place, g, 1337); + + auto st3 = ObjectState::Create(g, std::move(w2)); + + auto obs = Observer::Create([&] (const auto& ctx) + { + const Widget& widget = ctx.GetObject(); + + int color = ctx.Get(widget.color); + int width = ctx.Get(widget.width); + int height = ctx.Get(widget.height); + }, st1); + + w1.color.Set(10); + w2.color.Set(20); } \ No newline at end of file diff --git a/tests/src/transaction_tests.cpp b/tests/src/transaction_tests.cpp index 01e65ac9..6c0f8101 100644 --- a/tests/src/transaction_tests.cpp +++ b/tests/src/transaction_tests.cpp @@ -19,12 +19,12 @@ TEST(TransactionTest, Merging) { Group g; - EventSource evt( g ); + auto evt = EventSource::Create(g); int output = 0; int turns = 0; - Observer obs([&] (const auto& events) + auto obs = Observer::Create([&] (const auto& events) { ++turns; for (int e : events) @@ -78,12 +78,12 @@ TEST(TransactionTest, LinkedSync) Group g2; Group g3; - EventSource evt1( g1 ); + auto evt1 = EventSource::Create(g1); int output1 = 0; int turns1 = 0; - Observer obs1([&] (const auto& events) + auto obs1 = Observer::Create([&] (const auto& events) { ++turns1; for (int e : events) @@ -95,7 +95,7 @@ TEST(TransactionTest, LinkedSync) int output2 = 0; int turns2 = 0; - Observer obs2([&] (const auto& events) + auto obs2 = Observer::Create([&] (const auto& events) { ++turns2; for (int e : events) @@ -107,7 +107,7 @@ TEST(TransactionTest, LinkedSync) int output3 = 0; int turns3 = 0; - Observer obs3([&] (const auto& events) + auto obs3 = Observer::Create([&] (const auto& events) { std::this_thread::sleep_for(std::chrono::seconds(1)); @@ -147,12 +147,12 @@ TEST(TransactionTest, LinkedSyncMerging) Group g1; Group g2; - EventSource evt1( g1 ); + auto evt1 = EventSource::Create(g1); int output1 = 0; int turns1 = 0; - Observer obs1([&] (const auto& events) + auto obs1 = Observer::Create([&] (const auto& events) { ++turns1; for (int e : events) @@ -164,7 +164,7 @@ TEST(TransactionTest, LinkedSyncMerging) int output2 = 0; int turns2 = 0; - Observer obs2([&] (const auto& events) + auto obs2 = Observer::Create([&] (const auto& events) { std::this_thread::sleep_for(std::chrono::seconds(1)); From 9fe1af70f4910216132f7aec109f11d92d8e6ef8 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 30 Oct 2017 22:02:12 +0100 Subject: [PATCH 250/266] Commented out benchmarks for now. --- benchmarks/src/BenchmarkBase.h | 2 +- benchmarks/src/BenchmarkFanout.h | 8 ++++++-- benchmarks/src/BenchmarkGrid.h | 13 +++++++++++-- benchmarks/src/BenchmarkRandom.h | 11 +++++++---- benchmarks/src/BenchmarkSequence.h | 8 ++++++-- 5 files changed, 31 insertions(+), 11 deletions(-) diff --git a/benchmarks/src/BenchmarkBase.h b/benchmarks/src/BenchmarkBase.h index d27d64b1..6684c062 100644 --- a/benchmarks/src/BenchmarkBase.h +++ b/benchmarks/src/BenchmarkBase.h @@ -14,7 +14,7 @@ #include #include -#include "react/common/Util.h" +#include "react/common/utility.h" /////////////////////////////////////////////////////////////////////////////////////////////////// // Get unique random numbers from range. diff --git a/benchmarks/src/BenchmarkFanout.h b/benchmarks/src/BenchmarkFanout.h index a4855d0f..099f26d2 100644 --- a/benchmarks/src/BenchmarkFanout.h +++ b/benchmarks/src/BenchmarkFanout.h @@ -6,6 +6,8 @@ #pragma once +#if 0 + #ifndef CPP_REACT_BENCHMARK_FANOUT_H #define CPP_REACT_BENCHMARK_FANOUT_H @@ -15,7 +17,7 @@ #include "BenchmarkBase.h" -#include "react/Signal.h" +#include "react/state.h" /* using namespace react; @@ -84,4 +86,6 @@ struct Benchmark_Fanout */ -#endif // CPP_REACT_BENCHMARK_FANOUT_H \ No newline at end of file +#endif // CPP_REACT_BENCHMARK_FANOUT_H + +#endif \ No newline at end of file diff --git a/benchmarks/src/BenchmarkGrid.h b/benchmarks/src/BenchmarkGrid.h index 8f17d6f2..ad2d90af 100644 --- a/benchmarks/src/BenchmarkGrid.h +++ b/benchmarks/src/BenchmarkGrid.h @@ -6,6 +6,11 @@ #pragma once +#if 0 + +#ifndef REACT_BENCHMARK_GRID_H +#define REACT_BENCHMARK_GRID_H + #include #include #include @@ -14,7 +19,7 @@ #include "BenchmarkBase.h" -#include "react/Signal.h" +#include "react/state.h" using namespace react; @@ -159,4 +164,8 @@ struct Benchmark_Grid return d; } -}; \ No newline at end of file +}; + +#endif // REACT_BENCHMARK_GRID_H + +#endif \ No newline at end of file diff --git a/benchmarks/src/BenchmarkRandom.h b/benchmarks/src/BenchmarkRandom.h index cec422da..80257410 100644 --- a/benchmarks/src/BenchmarkRandom.h +++ b/benchmarks/src/BenchmarkRandom.h @@ -6,16 +6,17 @@ #pragma once +#if 0 + #include #include #include #include #include "BenchmarkBase.h" -#include "react/common/Types.h" -#include "react/Group.h" -#include "react/Signal.h" +#include "react/group.h" +#include "react/state.h" using namespace react; /* @@ -320,4 +321,6 @@ struct Benchmark_Random return d; } -};*/ \ No newline at end of file +};*/ + +#endif \ No newline at end of file diff --git a/benchmarks/src/BenchmarkSequence.h b/benchmarks/src/BenchmarkSequence.h index 705e8f58..f02c4890 100644 --- a/benchmarks/src/BenchmarkSequence.h +++ b/benchmarks/src/BenchmarkSequence.h @@ -6,13 +6,15 @@ #pragma once +#if 0 + #include #include #include #include "BenchmarkBase.h" -#include "react/Signal.h" +#include "react/state.h" /* @@ -77,4 +79,6 @@ struct Benchmark_Sequence return d; } -};*/ \ No newline at end of file +};*/ + +#endif \ No newline at end of file From 35cea007cd3715eb97eccfed06d38808a7c9fd64 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 30 Oct 2017 22:02:52 +0100 Subject: [PATCH 251/266] Fixed examples. --- benchmarks/src/Main.cpp | 36 ----- examples/src/BasicAlgorithms.cpp | 197 +++++++++++++++----------- examples/src/BasicComposition.cpp | 148 ++++++------------- examples/src/BasicEvents.cpp | 75 ++++------ examples/src/BasicObservers.cpp | 53 ++----- examples/src/BasicSignals.cpp | 83 +++++------ examples/src/BasicSynchronization.cpp | 159 ++++++--------------- 7 files changed, 287 insertions(+), 464 deletions(-) diff --git a/benchmarks/src/Main.cpp b/benchmarks/src/Main.cpp index a191ccbc..d8bc4d76 100644 --- a/benchmarks/src/Main.cpp +++ b/benchmarks/src/Main.cpp @@ -4,7 +4,6 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -//#define REACT_ENABLE_LOGGING #if 0 //#include "tbb/tick_count.h" //#include "tbb/tbbmalloc_proxy.h" @@ -218,46 +217,11 @@ int main() //runBenchmarks(); //debugBenchmarks(); //profileBenchmark(); - - } -struct Data -{ - State a; - State b; - - State (a, b); -}; - #endif -#include "react/common/expected.h" -#include "react/state.h" -#include "react/event.h" -#include "react/algorithm.h" -#include - -#define unwind_on_error(x) if (!x) return UnwindExpected(std::move(x)); - -using namespace react; - - int main() { - Group g; - - VarSignal t1(g); - VarSignal t2(g); - - EventSource e1(g); - EventSource e2(g); - - auto h1 = Hold(1, e1); - auto h2 = Hold(2, e2); - - auto m1 = Monitor(t1); - auto it1 = Iterate(10, [] (EventRange evnts, int b) { return b; }, e1); - return 0; } \ No newline at end of file diff --git a/examples/src/BasicAlgorithms.cpp b/examples/src/BasicAlgorithms.cpp index 93235700..d6f8aced 100644 --- a/examples/src/BasicAlgorithms.cpp +++ b/examples/src/BasicAlgorithms.cpp @@ -9,10 +9,10 @@ #include #include -#include "react/Signal.h" -#include "react/Event.h" -#include "react/Observer.h" -#include "react/Algorithm.h" +#include "react/state.h" +#include "react/event.h" +#include "react/observer.h" +#include "react/algorithm.h" /////////////////////////////////////////////////////////////////////////////////////////////////// /// Example 1 - Converting events to signals @@ -22,33 +22,35 @@ namespace example1 using namespace std; using namespace react; - Group group; + Group g; struct Sensor { - EventSource samples { group }; - Signal lastSample = Hold(0, samples); + EventSource samples = EventSource::Create(g); + State lastSample = Hold(0, samples); + + auto GetReactiveMembers() const -> decltype(auto) + { return std::tie(lastSample); } }; void Run() { cout << "Example 1 - Converting events to signals" << endl; - Sensor mySensor; + auto sensor = ObjectState::Create(g, Sensor{ }); - Observer obs( - [] (int v) + auto obs = Observer::Create(g, [] (const auto& ctx) { - cout << v << endl; - }, - mySensor.lastSample); + const Sensor& obj = ctx.GetObject(); + cout << ctx.Get(obj.lastSample) << endl; + }, sensor); - mySensor.samples << 20 << 21 << 21 << 22; // output: 20, 21, 22 + sensor->samples << 20 << 21 << 21 << 22; // output: 20, 21, 22 - group.DoTransaction([&] - { - mySensor.samples << 30 << 31 << 31 << 32; - }); // output: 32 + g.DoTransaction([&] + { + sensor->samples << 30 << 31 << 31 << 32; + }); // output: 32 cout << endl; } @@ -62,12 +64,12 @@ namespace example2 using namespace std; using namespace react; - Group group; + Group g; struct Employee { - VarSignal name { group, string( "Bob" ) }; - VarSignal salary { group, 3000 }; + StateVar name = StateVar::Create(g, string( "Bob" )); + StateVar salary = StateVar::Create(g, 66666); }; void Run() @@ -76,13 +78,13 @@ namespace example2 Employee bob; - Observer obs( - [] (EventRange in, const string& name) + auto obs = Observer::Create([] (const auto& events, const string& name) { - for (int newSalary : in) + for (int newSalary : events) cout << name << " now earns " << newSalary << endl; - }, - Monitor(bob.salary), bob.name); + }, Monitor(bob.salary), bob.name); + + bob.salary.Set(66667); cout << endl; } @@ -96,21 +98,18 @@ namespace example3 using namespace std; using namespace react; - Group group; + Group g; struct Counter { - EventSource<> increment { group }; + EventSource<> increment = EventSource<>::Create(g); - Signal count = Iterate( - 0, - [] (EventRange<> in, int count) + State count = Iterate(0, [] (const auto& events, int count) { - for (auto _ : in) + for (auto e : events) ++count; return count; - }, - increment); + }, increment); }; void Run() @@ -123,7 +122,10 @@ namespace example3 myCounter.increment.Emit(); myCounter.increment.Emit(); - cout << myCounter.count.Value() << endl; // output: 3 + auto obs = Observer::Create([] (int v) + { + cout << v << endl; // output: 3 + }, myCounter.count); cout << endl; } @@ -137,33 +139,34 @@ namespace example4 using namespace std; using namespace react; - Group group; + Group g; struct Sensor { - EventSource input{ group }; + EventSource input = EventSource::Create(g); - Signal count = Iterate( - 0, - [] (EventRange in, int count) + State count = Iterate(0, [] (const auto& events, int count) { - for (auto _ : in) - count++; + for (auto e : events) + ++count; return count; - }, - input); + }, input); - Signal sum = Iterate( - 0.0f, - [] (EventRange in, float sum) + State sum = Iterate(0.0f, [] (const auto& events, float sum) { - for (auto v : in) - sum += v; + for (auto e : events) + sum += e; return sum; - }, - input); + }, input); - VarSignal average{ group }; + State average = State::Create([] (int c, float s) + { + if (c != 0) + return s / c; + else + return 0.0f; + + }, count, sum); }; void Run() @@ -174,7 +177,10 @@ namespace example4 mySensor.input << 10.0f << 5.0f << 10.0f << 8.0f; - cout << "Average: " << mySensor.average.Value() << endl; // output: 8.25 + auto obs = Observer::Create([] (float v) + { + cout << "Average: " << v << endl; // output: 8.25 + }, mySensor.average); cout << endl; } @@ -188,16 +194,16 @@ namespace example5 using namespace std; using namespace react; - Group group; + Group g; enum ECmd { increment, decrement, reset }; class Counter { private: - static int DoCounterLoop(EventRange in, int count, int delta, int start) + static int DoCounterLoop(const EventValueList& cmds, int count, int delta, int start) { - for (int cmd : in) + for (int cmd : cmds) { if (cmd == increment) count += delta; @@ -211,12 +217,12 @@ namespace example5 } public: - EventSource update{ group }; + EventSource update = EventSource::Create(g); - VarSignal delta{ group, 1 }; - VarSignal start{ group, 0 }; + StateVar delta = StateVar::Create(g, 1); + StateVar start = StateVar::Create(g, 0); - Signal count{ Iterate(start.Value(), DoCounterLoop, update, delta, start) }; + State count = Iterate(0, DoCounterLoop, update, delta, start); }; void Run() @@ -225,23 +231,44 @@ namespace example5 Counter myCounter; - cout << "Start: " << myCounter.count.Value() << endl; // output: 0 + { + auto obs = Observer::Create([] (int v) + { + cout << "Start: " << v << endl; // output: 0 + }, myCounter.count); + } + myCounter.update.Emit(increment); myCounter.update.Emit(increment); myCounter.update.Emit(increment); - cout << "3x increment by 1: " << myCounter.count.Value() << endl; // output: 3 + { + auto obs = Observer::Create([] (int v) + { + cout << "3x increment by 1: " << v << endl; // output: 3 + }, myCounter.count); + } - myCounter.delta <<= 5; + myCounter.delta.Set(5); myCounter.update.Emit(decrement); - cout << "1x decrement by 5: " << myCounter.count.Value() << endl; // output: -2 + { + auto obs = Observer::Create([] (int v) + { + cout << "1x decrement by 5: " << v << endl; // output: -2 + }, myCounter.count); + } - myCounter.start <<= 100; + myCounter.start.Set(100); myCounter.update.Emit(reset); - cout << "reset to 100: " << myCounter.count.Value() << endl; // output: 100 + { + auto obs = Observer::Create([] (int v) + { + cout << "reset to 100: " << v << endl; // output: 100 + }, myCounter.count); + } cout << endl; } @@ -255,32 +282,32 @@ namespace example6 using namespace std; using namespace react; - Group group; + Group g; class Sensor { private: - static void DoIterateAllSamples(EventRange in, vector& all) + static void DoIterateAllSamples(const EventValueList& events, vector& all) { - for (int input : in) + for (int input : events) all.push_back(input); } - static void DoIterateCritSamples(EventRange in, vector& critical, int threshold) + static void DoIterateCritSamples(const EventValueList events, vector& critical, int threshold) { - for (int input : in) + for (int input : events) if (input > threshold) critical.push_back(input); } public: - EventSource input{ group }; + EventSource input = EventSource::Create(g); - VarSignal threshold{ group, 42 }; + StateVar threshold = StateVar::Create(g, 42); - Signal> allSamples{ Iterate>(vector{ }, DoIterateAllSamples, input) }; + State> allSamples = IterateByRef>(vector{ }, DoIterateAllSamples, input); - Signal> criticalSamples{ Iterate>(vector{ }, DoIterateCritSamples, input, threshold) }; + State> criticalSamples = IterateByRef>(vector{ }, DoIterateCritSamples, input, threshold); }; void Run() @@ -292,15 +319,23 @@ namespace example6 mySensor.input << 40 << 29 << 43 << 50; cout << "All samples: "; - for (auto const& v : mySensor.allSamples.Value()) - cout << v << " "; + { + auto obs = Observer::Create([] (const auto& allSamples) + { + for (auto const& v : allSamples) + cout << v << " "; + }, mySensor.allSamples); + } cout << endl; cout << "Critical samples: "; - for (auto const& v : mySensor.criticalSamples.Value()) - cout << v << " "; - cout << endl; - + { + auto obs = Observer::Create([] (const auto& criticalSamples) + { + for (auto const& v : criticalSamples) + cout << v << " "; + }, mySensor.criticalSamples); + } cout << endl; } } diff --git a/examples/src/BasicComposition.cpp b/examples/src/BasicComposition.cpp index e2fbf3b1..acec053e 100644 --- a/examples/src/BasicComposition.cpp +++ b/examples/src/BasicComposition.cpp @@ -8,10 +8,9 @@ #include #include -#include "react/Domain.h" -#include "react/Signal.h" -#include "react/Event.h" -#include "react/Observer.h" +#include "react/state.h" +#include "react/event.h" +#include "react/observer.h" /////////////////////////////////////////////////////////////////////////////////////////////////// /// Example 1 - Reactive class members @@ -21,155 +20,99 @@ namespace example1 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) + Group g; class Shape { public: - USING_REACTIVE_DOMAIN(D) + StateVar width = StateVar::Create(g, 0); + StateVar height = StateVar::Create(g, 0); - VarSignalT Width = MakeVar(0); - VarSignalT Height = MakeVar(0); + State size = State::Create(g, CalcSize, width, height); - SignalT Size = Width * Height; + auto GetReactiveMembers() const -> decltype(auto) + { return std::tie(width, height, size); } - EventSourceT<> HasMoved = MakeEventSource(); + private: + static int CalcSize(int w, int h) + { return w * h; } }; void Run() { cout << "Example 1 - Reactive class members" << endl; - Shape myShape; + auto myShape = ObjectState::Create(g, Shape()); - Observe(myShape.Size, [] (int newValue) { - cout << "Size changed to " << newValue << endl; - }); + auto obs = Observer::Create([] (const auto& ctx) + { + const Shape& shape = ctx.GetObject(); + cout << "Size is " << ctx.Get(shape.size) << endl; + }, myShape); - DoTransaction([&] { - myShape.Width <<= 4; - myShape.Height <<= 4; - }); // output: Size changed to 16 + g.DoTransaction([&] + { + myShape->width.Set(4); + myShape->height.Set(4); + }); // output: Size changed to 16 cout << endl; } } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 2 - Signals of references +/// Example 2 - Slots /////////////////////////////////////////////////////////////////////////////////////////////////// namespace example2 { using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) + Group g; class Company { public: - const char* Name; + StateVar name; Company(const char* name) : - Name( name ) - {} + name( StateVar::Create(g, name) ) + { } - // Note: To be used as a signal value type, - // values of the type must be comparable bool operator==(const Company& other) const - { - return this == &other; - } + { return name == other.name; } }; class Employee { public: - USING_REACTIVE_DOMAIN(D) + StateSlot myCompanyName; - VarSignalT MyCompany; + Employee(const Company& company) : + myCompanyName( StateSlot::Create(company.name) ) + { } - Employee(Company& company) : - MyCompany( MakeVar(ref(company)) ) - {} + void SetCompany(const Company& company) + { myCompanyName.Set(company.name); } }; void Run() { - cout << "Example 2 - Signals of references" << endl; + cout << "Example 2 - Slots" << endl; - Company company1( "MetroTec" ); - Company company2( "ACME" ); - - Employee bob( company1 ); - - Observe(bob.MyCompany, [] (const Company& company) { - cout << "Bob works for " << company.Name << endl; - }); - - bob.MyCompany <<= ref(company2); // output: Bob now works for ACME - - cout << endl; - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 3 - Dynamic signal references -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace example3 -{ - using namespace std; - using namespace react; - - REACTIVE_DOMAIN(D, sequential) - - class Company - { - public: - USING_REACTIVE_DOMAIN(D) - - VarSignalT Name; - - Company(const char* name) : - Name( MakeVar(string( name )) ) - {} - - bool operator==(const Company& other) const - { - return this == &other; - } - }; - - class Employee - { - public: - USING_REACTIVE_DOMAIN(D) - - VarSignalT MyCompany; - - Employee(Company& company) : - MyCompany( MakeVar(ref(company)) ) - {} - }; - - void Run() - { - cout << "Example 3 - Dynamic signal references" << endl; - - Company company1( "MetroTec" ); - Company company2( "ACME" ); + Company company1( "MetroTec" ); + Company company2( "ACME" ); Employee alice( company1 ); - auto obs = Observe( - REACTIVE_REF(alice.MyCompany, Name), - [] (const string& name) { + auto obs = Observer::Create([] (const string& name) + { cout << "Alice now works for " << name << endl; - }); + }, alice.myCompanyName); - company1.Name <<= string( "ModernTec" ); // output: Alice now works for ModernTec - alice.MyCompany <<= ref(company2); // output: Alice now works for ACME - company2.Name <<= string( "A.C.M.E." ); // output: Alice now works for A.C.M.E. + company1.name.Set(string( "ModernTec" )); // output: Alice now works for ModernTec + alice.SetCompany(company2); // output: Alice now works for ACME + company2.name.Set(string( "A.C.M.E." )); // output: Alice now works for A.C.M.E. cout << endl; } @@ -181,10 +124,7 @@ namespace example3 int main() { example1::Run(); - example2::Run(); - example3::Run(); - return 0; } \ No newline at end of file diff --git a/examples/src/BasicEvents.cpp b/examples/src/BasicEvents.cpp index 53e97984..98fe1ef3 100644 --- a/examples/src/BasicEvents.cpp +++ b/examples/src/BasicEvents.cpp @@ -27,19 +27,17 @@ namespace example1 // An event source that emits values of type string namespace v1 { - EventSource mySource( group ); + EventSource mySource = EventSource::Create(group); void Run() { cout << "Example 1 - Hello world (string source)" << endl; - Observer obs( - [] (EventRange in) + auto obs = Observer::Create([] (const auto& events) { - for (const auto& s : in) - cout << s << std::endl; - }, - mySource); + for (const auto& e : events) + cout << e << std::endl; + }, mySource); mySource << string("Hello world #1"); @@ -53,7 +51,7 @@ namespace example1 // An event source without an explicit value type namespace v2 { - EventSource<> helloWorldTrigger( group ); + EventSource<> helloWorldTrigger = EventSource<>::Create(group); void Run() { @@ -61,13 +59,11 @@ namespace example1 int count = 0; - Observer obs( - [&] (EventRange<> in) + auto obs = Observer::Create([&] (const auto& events) { - for (auto t : in) + for (auto t : events) cout << "Hello world #" << ++count << endl; - }, - helloWorldTrigger); + }, helloWorldTrigger); helloWorldTrigger.Emit(); @@ -90,8 +86,8 @@ namespace example2 Group group; // An event stream that merges both sources - EventSource<> leftClick( group ); - EventSource<> rightClick( group ); + EventSource<> leftClick = EventSource<>::Create(group); + EventSource<> rightClick = EventSource<>::Create(group); Event<> anyClick = Merge(leftClick, rightClick); @@ -101,13 +97,11 @@ namespace example2 int count = 0; - Observer obs( - [&] (EventRange<> in) + auto obs = Observer::Create([&] (const auto& events) { - for (auto t : in) + for (auto t : events) cout << "clicked #" << ++count << endl; - }, - anyClick); + }, anyClick); leftClick.Emit(); // output: clicked #1 rightClick.Emit(); // output: clicked #2 @@ -126,7 +120,7 @@ namespace example3 Group group; - EventSource numbers( group ); + EventSource numbers = EventSource::Create(group); Event greater10 = Filter([] (int n) { return n > 10; }, numbers); @@ -134,13 +128,11 @@ namespace example3 { cout << "Example 3 - Filtering events" << endl; - Observer obs( - [&] (EventRange in) + auto obs = Observer::Create([&] (const auto& events) { - for (auto n : in) + for (auto n : events) cout << n << endl; - }, - greater10); + }, greater10); numbers << 5 << 11 << 7 << 100; // output: 11, 100 @@ -162,34 +154,30 @@ namespace example4 enum class Tag { normal, critical }; using TaggedNum = pair; - EventSource numbers( group ); + EventSource numbers = EventSource::Create(group); - Event tagged = Transform( - [] (int n) + Event tagged = Transform([] (int n) { if (n > 10) return TaggedNum( Tag::critical, n ); else return TaggedNum( Tag::normal, n ); - }, - numbers); + }, numbers); void Run() { cout << "Example 4 - Transforming events" << endl; - Observer obs( - [] (EventRange in) + auto obs = Observer::Create([] (const auto& events) { - for (TaggedNum e : in) + for (TaggedNum e : events) { if (e.first == Tag::critical) cout << "(critical) " << e.second << endl; else cout << "(normal) " << e.second << endl; } - }, - tagged); + }, tagged); numbers << 5; // output: (normal) 5 numbers << 20; // output: (critical) 20 @@ -208,25 +196,24 @@ namespace example5 Group group; - EventSource src( group ); + EventSource src = EventSource::Create(group); void Run() { cout << "Example 5 - Queuing multiple inputs" << endl; - Observer obs( - [] (EventRange in) + auto obs = Observer::Create([] (const auto& events) { - for (int e : in) + for (int e : events) cout << e << endl; }, src); // output: 1, 2, 3, 4 group.DoTransaction([] - { - src << 1 << 2 << 3; - src << 4; - }); + { + src << 1 << 2 << 3; + src << 4; + }); cout << endl; } diff --git a/examples/src/BasicObservers.cpp b/examples/src/BasicObservers.cpp index 8eb03b81..54b24857 100644 --- a/examples/src/BasicObservers.cpp +++ b/examples/src/BasicObservers.cpp @@ -8,9 +8,9 @@ #include #include -#include "react/Signal.h" -#include "react/Event.h" -#include "react/Observer.h" +#include "react/state.h" +#include "react/event.h" +#include "react/observer.h" /////////////////////////////////////////////////////////////////////////////////////////////////// /// Example 1 - Creating subject-bound observers @@ -22,55 +22,21 @@ namespace example1 Group group; - VarSignal x( group, 1 ); + StateVar x = StateVar::Create(group, 1); void Run() { - cout << "Example 1 - Creating subject-bound observers (v1)" << endl; + cout << "Example 1 - Creating observers" << endl; { - Signal mySignal( [] (int x) { return x; }, x ); + auto st = State::Create([] (int x) { return x; }, x); - Observer obs( [] (int mySignal) { cout << mySignal << endl; }, mySignal ); + auto obs = Observer::Create([] (int v) { cout << v << endl; }, st); - x <<= 2; // output: 2 + x.Set(2); // output: 2 } - x <<= 3; // no ouput - - cout << endl; - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 2 - Detaching observers manually -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace example2 -{ - using namespace std; - using namespace react; - - Group group; - - EventSource<> trigger( group ); - - void Run() - { - cout << "Example 2 - Detaching observers manually" << endl; - - { - Observer obs( - [] (EventRange<> in) - { - for (auto _ : in) - cout << "Triggered!" << endl; - }, - trigger); - - trigger.Emit(); // output: Triggered! - } - - trigger.Emit(); // no output + x.Set(3); // no ouput cout << endl; } @@ -82,7 +48,6 @@ namespace example2 int main() { example1::Run(); - example2::Run(); return 0; } \ No newline at end of file diff --git a/examples/src/BasicSignals.cpp b/examples/src/BasicSignals.cpp index 749116c8..c743de83 100644 --- a/examples/src/BasicSignals.cpp +++ b/examples/src/BasicSignals.cpp @@ -11,8 +11,8 @@ #include #include -#include "react/Signal.h" -#include "react/Observer.h" +#include "react/state.h" +#include "react/observer.h" /////////////////////////////////////////////////////////////////////////////////////////////////// /// Example 1 - Hello world @@ -35,21 +35,20 @@ namespace example1 Group group; // The two words - VarSignal firstWord( group, string("Change") ); - VarSignal secondWord( group, string("me!") ); + StateVar firstWord = StateVar::Create(group, string("Change")); + StateVar secondWord = StateVar::Create(group, string("me!")); // A signal that concatenates both words - Signal bothWords(ConcatFunc, firstWord, secondWord); + State bothWords = State::Create(ConcatFunc, firstWord, secondWord); void Run() { - Observer obs{ PrintFunc, bothWords }; + auto obs = Observer::Create(PrintFunc, bothWords); cout << "Example 1 - Hello world" << endl; - firstWord <<= string("Hello"); - - secondWord <<= string("World"); + firstWord.Set(string("Hello")); + secondWord.Set(string("World")); cout << endl; } @@ -65,22 +64,21 @@ namespace example2 Group group; - VarSignal x( group, 1 ); + StateVar x = StateVar::Create(group, 1); - Signal xAbs( [] (int v) { return abs(v); }, x); + State xAbs = State::Create([] (int v) { return abs(v); }, x); void Run() { cout << "Example 2 - Reacting to value changes" << endl; - Observer obs( - [] (int newValue) { cout << "xAbs changed to " << newValue << endl; }, - xAbs ); + auto obs = Observer::Create([] (int newValue) + { cout << "xAbs changed to " << newValue << endl; }, xAbs); // initially x is 1 - x <<= 2; // output: xAbs changed to 2 - x <<= -3; // output: xAbs changed to 3 - x <<= 3; // no output, xAbs is still 3 + x.Set(2); // output: xAbs changed to 2 + x.Set(-3); // output: xAbs changed to 3 + x.Set(3); // no output, xAbs is still 3 cout << endl; } @@ -99,29 +97,28 @@ namespace example3 Group group; - VarSignal a( group, 1 ); - VarSignal b( group, 1 ); + StateVar a = StateVar::Create(group, 1); + StateVar b = StateVar::Create(group, 1); - Signal x( sumFunc, a, b ); - Signal y( sumFunc, a, b ); - Signal z( sumFunc, x, y ); + State x = State::Create(sumFunc, a, b); + State y = State::Create(sumFunc, a, b); + State z = State::Create(sumFunc, x, y); void Run() { cout << "Example 3 - Changing multiple inputs" << endl; - Observer obs( - [] (int newValue) { cout << "z changed to " << newValue << endl; }, - z ); + auto obs = Observer::Create([] (int newValue) + { cout << "z changed to " << newValue << endl; }, z); - a <<= 2; // output: z changed to 6 - b <<= 2; // output: z changed to 8 + a.Set(2); // output: z changed to 6 + b.Set(2); // output: z changed to 8 group.DoTransaction([&] - { - a <<= 4; - b <<= 4; - }); // output: z changed to 16 + { + a.Set(4); + b.Set(4); + }); // output: z changed to 16 cout << endl; } @@ -137,7 +134,7 @@ namespace example4 Group group; - VarSignal> data( group ); + StateVar> data = StateVar>::Create(group); void Run() { @@ -149,8 +146,13 @@ namespace example4 data.Modify([] (vector& data) { data.push_back("World"); }); -// for (const auto& s : data.Value()) -// cout << s << " "; + auto obs = Observer::Create([] (const vector& data) + { + for (const auto& s : data) + cout << s << " "; + }, data); + + cout << endl; // output: Hello World @@ -185,12 +187,11 @@ namespace example5 } // Input operands - VarSignal a( group, 1 ); - VarSignal b( group, 2 ); + StateVar a = StateVar::Create(group, 1); + StateVar b = StateVar::Create(group, 2); // The expression vector - Signal expressions( - [] (int a, int b) + State expressions = State::Create([] (int a, int b) { ExprVectType result; result.push_back(make_pair(MakeExprStr(a, b, "+"), a + b)); @@ -203,10 +204,10 @@ namespace example5 { cout << "Example 5 - Complex signals (v3)" << endl; - Observer obs(PrintExpressions, expressions); + auto obs = Observer::Create(PrintExpressions, expressions); - a <<= 50; - b <<= 60; + a.Set(50); + b.Set(60); cout << endl; } diff --git a/examples/src/BasicSynchronization.cpp b/examples/src/BasicSynchronization.cpp index 701b5001..d5b29de7 100644 --- a/examples/src/BasicSynchronization.cpp +++ b/examples/src/BasicSynchronization.cpp @@ -8,10 +8,9 @@ #include "tbb/tick_count.h" -#include "react/Domain.h" -#include "react/Signal.h" -#include "react/Event.h" -#include "react/Observer.h" +#include "react/state.h" +#include "react/event.h" +#include "react/observer.h" /////////////////////////////////////////////////////////////////////////////////////////////////// /// Example 1 - Asynchronous transactions @@ -21,50 +20,41 @@ namespace example1 using namespace react; using namespace std; - Group<> group; + Group group; class Sensor { public: - Sensor(const Sensor&) = default; - Sensor& operator=(const Sensor&) = default; - Sensor(Sensor&&) = default; - Sensor& operator=(Sensor&&) = default; - - explicit Sensor(const Group<>& group) : - Samples( group ) - { } - - EventSource Samples; + EventSource samples = EventSource::Create(group); }; void Run() { cout << "Example 1 - Asynchronous transactions" << endl; - Sensor mySensor( group ); + Sensor mySensor; - Observer<> obs( - [&] (EventRange<> in) + auto obs = Observer::Create([&] (const auto& events) { - for (auto t : in) - cout << v << endl; - }, - mySensor.Samples); + for (auto t : events) + cout << t << endl; + }, mySensor.samples); - TransactionStatus status; + SyncPoint sp; - AsyncTransaction(status, [&] { - mySensor.Samples << 30 << 31 << 31 << 32; - }); + group.EnqueueTransaction([&] + { + mySensor.samples << 30 << 31 << 31 << 32; + }, sp); - AsyncTransaction(status, [&] { - mySensor.Samples << 40 << 41 << 51 << 62; - }); + group.EnqueueTransaction([&] + { + mySensor.samples << 40 << 41 << 51 << 62; + }, sp); // Waits until both transactions are completed. // This does not mean that both transactions are interleaved. - status.Wait(); + sp.Wait(); cout << endl; } @@ -78,17 +68,15 @@ namespace example2 using namespace react; using namespace std; - REACTIVE_DOMAIN(D, sequential_concurrent) + Group group; class Sensor { public: - USING_REACTIVE_DOMAIN(D) - - EventSourceT Samples = MakeEventSource(); + EventSource samples = EventSource::Create(group); }; - const int K = 100000; + const int K = 10000; namespace v1 { @@ -99,11 +87,13 @@ namespace example2 Sensor mySensor; int sum = 0; - Observe(mySensor.Samples, [&] (int v) { - sum += v; - }); + auto obs = Observer::Create([&] (const auto& events) + { + for (int e : events) + sum += e; + }, mySensor.samples); - TransactionStatus status; + SyncPoint sp; cout << "Executing " << K << " async transactions..."; @@ -111,12 +101,13 @@ namespace example2 for (int i=0; i < K; i++) { - AsyncTransaction(status, [&] { - mySensor.Samples << 3 << 4 << 2 << 1; - }); + group.EnqueueTransaction([&] + { + mySensor.samples << 3 << 4 << 2 << 1; + }, sp); } - status.Wait(); + sp.Wait(); double d = (tbb::tick_count::now() - t0).seconds(); @@ -138,11 +129,13 @@ namespace example2 Sensor mySensor; int sum = 0; - Observe(mySensor.Samples, [&] (int v) { - sum += v; - }); + auto obs = Observer::Create([&] (const auto& events) + { + for (int e : events) + sum += e; + }, mySensor.samples); - TransactionStatus status; + SyncPoint sp; cout << "Executing " << K << " async transactions..."; @@ -150,12 +143,13 @@ namespace example2 for (int i=0; i < K; i++) { - AsyncTransaction(allow_merging, status, [&] { - mySensor.Samples << 3 << 4 << 2 << 1; - }); + group.EnqueueTransaction([&] + { + mySensor.samples << 3 << 4 << 2 << 1; + }, sp, TransactionFlags::allow_merging); } - status.Wait(); + sp.Wait(); double d = (tbb::tick_count::now() - t0).seconds(); @@ -169,67 +163,6 @@ namespace example2 } } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 3 - Continuations (1) -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace example3 -{ - using namespace react; - using namespace std; - - REACTIVE_DOMAIN(D, sequential_concurrent) - - class Widget - { - public: - USING_REACTIVE_DOMAIN(D) - - VarSignalT Label1 = MakeVar(string( "Change" )); - VarSignalT Label2 = MakeVar(string( "me!" )); - - EventSourceT<> Reset = MakeEventSource(); - - Widget() : - resetCont_ - ( - MakeContinuation( - Reset, - [this] (Token) { - Label1 <<= string( "Change" ); - Label2 <<= string( "me!" ); - }) - ) - {} - - private: - Continuation resetCont_; - }; - - void Run() - { - cout << "Example 3 - Continuations (1)" << endl; - - Widget myWidget; - - Observe(myWidget.Label1, [&] (const string& v) { - cout << "Label 1 changed to " << v << endl; - }); - - Observe(myWidget.Label2, [&] (const string& v) { - cout << "Label 2 changed to " << v << endl; - }); - - myWidget.Label1 <<= "Hello"; - myWidget.Label2 <<= "world"; - - cout << "Resetting..." << endl; - - myWidget.Reset(); - - cout << endl; - } -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Run examples /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -240,7 +173,5 @@ int main() example2::v1::Run(); example2::v2::Run(); - example3::Run(); - return 0; } \ No newline at end of file From 6a936621281532a914c75b1886da54f56d1d9832 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 30 Oct 2017 22:03:57 +0100 Subject: [PATCH 252/266] Fixes + cleanup. --- include/react/detail/graph_impl.h | 26 ++++++++++++++++++++++ include/react/detail/observer_nodes.h | 2 +- include/react/detail/state_nodes.h | 12 ++++++---- include/react/group.h | 32 --------------------------- include/react/state.h | 19 ++++++++++++---- project/msvc/CppReact.sln | 11 --------- project/msvc/CppReact.vcxproj | 3 --- project/msvc/CppReact.vcxproj.filters | 12 ---------- tests/src/state_tests.cpp | 4 ++-- 9 files changed, 52 insertions(+), 69 deletions(-) diff --git a/include/react/detail/graph_impl.h b/include/react/detail/graph_impl.h index 42c04789..14391d14 100644 --- a/include/react/detail/graph_impl.h +++ b/include/react/detail/graph_impl.h @@ -216,6 +216,32 @@ void ReactGraph::EnqueueTransaction(F&& func, SyncPoint::Dependency dep, Transac transactionQueue_.Push(std::forward(func), std::move(dep), flags); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// GroupInternals +/////////////////////////////////////////////////////////////////////////////////////////////////// +class GroupInternals +{ +public: + GroupInternals() : + graphPtr_( std::make_shared() ) + { } + + GroupInternals(const GroupInternals&) = default; + GroupInternals& operator=(const GroupInternals&) = default; + + GroupInternals(GroupInternals&&) = default; + GroupInternals& operator=(GroupInternals&&) = default; + + auto GetGraphPtr() -> std::shared_ptr& + { return graphPtr_; } + + auto GetGraphPtr() const -> const std::shared_ptr& + { return graphPtr_; } + +private: + std::shared_ptr graphPtr_; +}; + /****************************************/ REACT_IMPL_END /***************************************/ #endif // REACT_DETAIL_PROPAGATION_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/observer_nodes.h b/include/react/detail/observer_nodes.h index 9634c89c..dd051384 100644 --- a/include/react/detail/observer_nodes.h +++ b/include/react/detail/observer_nodes.h @@ -150,7 +150,7 @@ class SyncedEventObserverNode : public ObserverNode return UpdateResult::unchanged; apply([this] (const auto& ... syncs) - { func_(EventRange( GetInternals(this->subject_).Events() ), GetInternals(syncs).Value() ...); }, syncHolder_); + { func_(GetInternals(this->subject_).Events(), GetInternals(syncs).Value() ...); }, syncHolder_); return UpdateResult::unchanged; } diff --git a/include/react/detail/state_nodes.h b/include/react/detail/state_nodes.h index ea78c20b..aa8c8b85 100644 --- a/include/react/detail/state_nodes.h +++ b/include/react/detail/state_nodes.h @@ -346,7 +346,8 @@ class ObjectStateNode : public StateNode> { public: ObjectStateNode(const Group& group, S&& obj, const std::initializer_list& memberIds) : - ObjectStateNode::StateNode( group, std::move(obj) ) + ObjectStateNode::StateNode( group, &object_ ), + object_( std::move(obj) ) { this->RegisterMe(); @@ -356,7 +357,7 @@ class ObjectStateNode : public StateNode> { REACT_EXPAND_PACK(memberIds_.push_back(GetInternals(members).GetNodeId())); REACT_EXPAND_PACK(this->AttachToMe(GetInternals(members).GetNodeId())); - }, this->Value().GetObject().GetReactiveMembers()); + }, object_.GetReactiveMembers()); } else { @@ -372,16 +373,18 @@ class ObjectStateNode : public StateNode> template ObjectStateNode(InPlaceTag, const Group& group, Us&& ... args) : - ObjectStateNode::StateNode( in_place, group, group, std::forward(args) ... ) + ObjectStateNode::StateNode( group, &object_ ), + object_( group, std::forward(args) ... ) { this->RegisterMe(); apply([this] (const auto& ... members) - { REACT_EXPAND_PACK(memberIds_.push_back(GetInternals(members).GetNodeId())); }, this->Value().GetObject().GetReactiveMembers()); + { REACT_EXPAND_PACK(memberIds_.push_back(GetInternals(members).GetNodeId())); }, object_.GetReactiveMembers()); } ~ObjectStateNode() { + // TODO: This must happen inside of the node for (NodeId id : memberIds_) this->DetachFromMe(id); @@ -394,6 +397,7 @@ class ObjectStateNode : public StateNode> } private: + S object_; std::vector memberIds_; }; diff --git a/include/react/group.h b/include/react/group.h index 45ec476a..d534d7e4 100644 --- a/include/react/group.h +++ b/include/react/group.h @@ -20,38 +20,6 @@ #include "react/detail/graph_interface.h" #include "react/detail/graph_impl.h" -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// GroupInternals -/////////////////////////////////////////////////////////////////////////////////////////////////// -class GroupInternals -{ - using GraphType = REACT_IMPL::ReactGraph; - -public: - GroupInternals() : - graphPtr_( std::make_shared() ) - { } - - GroupInternals(const GroupInternals&) = default; - GroupInternals& operator=(const GroupInternals&) = default; - - GroupInternals(GroupInternals&&) = default; - GroupInternals& operator=(GroupInternals&&) = default; - - auto GetGraphPtr() -> std::shared_ptr& - { return graphPtr_; } - - auto GetGraphPtr() const -> const std::shared_ptr& - { return graphPtr_; } - -private: - std::shared_ptr graphPtr_; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/state.h b/include/react/state.h index 6c93de6c..d8235d37 100644 --- a/include/react/state.h +++ b/include/react/state.h @@ -39,6 +39,11 @@ class State : protected REACT_IMPL::StateInternals static State Create(F&& func, const State& dep1, const State& ... deps) { return CreateFuncNode(dep1.GetGroup(), std::forward(func), dep1, deps ...); } + // Construct with constant value + template + static State Create(const Group& group, T&& init) + { return CreateFuncNode(group, [value = std::move(init)] { return value; }); } + State() = default; State(const State&) = default; @@ -292,8 +297,11 @@ class ObjectContext ObjectContext(ObjectContext&&) = default; ObjectContext& operator=(ObjectContext&&) = default; + S& GetObject() + { return *objectPtr_; } + const S& GetObject() const - { return object_; } + { return *objectPtr_; } template const U& Get(const State& member) const @@ -305,11 +313,11 @@ class ObjectContext private: template - explicit ObjectContext(Us&& ... args) : - object_( std::forward(args) ... ) + explicit ObjectContext(S* objectPtr) : + objectPtr_( objectPtr ) { } - S object_; + S* objectPtr_; template friend class impl::StateNode; @@ -345,6 +353,9 @@ class ObjectState : public State> ObjectState(ObjectState&&) = default; ObjectState& operator=(ObjectState&&) = default; + S* operator->() + { return &this->Value().GetObject(); } + protected: ObjectState(std::shared_ptr>>&& nodePtr) : ObjectState::State( std::move(nodePtr) ) diff --git a/project/msvc/CppReact.sln b/project/msvc/CppReact.sln index 5f987e52..6d60b1c9 100644 --- a/project/msvc/CppReact.sln +++ b/project/msvc/CppReact.sln @@ -17,8 +17,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "4_Benchmarks", "4_Benchmark EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2_Tests", "2_Tests", "{3F97AA87-0A03-4428-94C1-C9B4007C2C80}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CppReactExample", "CppReactExample.vcxproj", "{CC0CD982-AE7D-4797-A122-58E6BBE70DDB}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example_BasicAlgorithms", "Example_BasicAlgorithms.vcxproj", "{617019A2-97BE-4A60-8EC4-3547D8C54533}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example_BasicComposition", "Example_BasicComposition.vcxproj", "{D7B70D3B-F14D-4A85-B164-EAB88C358E85}" @@ -67,14 +65,6 @@ Global {52A9EC67-C6A7-453B-AD65-F027CA07AF44}.Release|Win32.Build.0 = Release|Win32 {52A9EC67-C6A7-453B-AD65-F027CA07AF44}.Release|x64.ActiveCfg = Release|x64 {52A9EC67-C6A7-453B-AD65-F027CA07AF44}.Release|x64.Build.0 = Release|x64 - {CC0CD982-AE7D-4797-A122-58E6BBE70DDB}.Debug|Win32.ActiveCfg = Debug|Win32 - {CC0CD982-AE7D-4797-A122-58E6BBE70DDB}.Debug|Win32.Build.0 = Debug|Win32 - {CC0CD982-AE7D-4797-A122-58E6BBE70DDB}.Debug|x64.ActiveCfg = Debug|x64 - {CC0CD982-AE7D-4797-A122-58E6BBE70DDB}.Debug|x64.Build.0 = Debug|x64 - {CC0CD982-AE7D-4797-A122-58E6BBE70DDB}.Release|Win32.ActiveCfg = Release|Win32 - {CC0CD982-AE7D-4797-A122-58E6BBE70DDB}.Release|Win32.Build.0 = Release|Win32 - {CC0CD982-AE7D-4797-A122-58E6BBE70DDB}.Release|x64.ActiveCfg = Release|x64 - {CC0CD982-AE7D-4797-A122-58E6BBE70DDB}.Release|x64.Build.0 = Release|x64 {617019A2-97BE-4A60-8EC4-3547D8C54533}.Debug|Win32.ActiveCfg = Debug|Win32 {617019A2-97BE-4A60-8EC4-3547D8C54533}.Debug|Win32.Build.0 = Debug|Win32 {617019A2-97BE-4A60-8EC4-3547D8C54533}.Debug|x64.ActiveCfg = Debug|x64 @@ -151,7 +141,6 @@ Global {5E56AAB9-4E33-4B9E-A315-E85CEDB75CF1} = {91AFD614-F7E6-48CE-9808-642EAF476B66} {F9115FB9-61DD-4B3C-BCE8-7D26372B05F7} = {D6F88FF5-E55C-4E65-ABBB-B4298AB84D40} {52A9EC67-C6A7-453B-AD65-F027CA07AF44} = {3F97AA87-0A03-4428-94C1-C9B4007C2C80} - {CC0CD982-AE7D-4797-A122-58E6BBE70DDB} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} {617019A2-97BE-4A60-8EC4-3547D8C54533} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} {D7B70D3B-F14D-4A85-B164-EAB88C358E85} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} {BD777649-97F1-4810-BF21-CB27F7672BF4} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index 088c482f..47748a0d 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -177,9 +177,6 @@ - - - diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index 040e8acc..5652b8e2 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -19,9 +19,6 @@ {11a75126-8bfd-4693-be4b-4e06ab73450c} - - {3f444875-ab1a-4bbd-99eb-7018c930098a} - {45678bfc-0ce5-4e47-a365-54a72d8ecb6d} @@ -83,15 +80,6 @@ - - Source Files\engine - - - Source Files\engine - - - Source Files\engine - Source Files\detail diff --git a/tests/src/state_tests.cpp b/tests/src/state_tests.cpp index 10e41205..ab9d70b8 100644 --- a/tests/src/state_tests.cpp +++ b/tests/src/state_tests.cpp @@ -475,6 +475,6 @@ TEST(StateTest, Object) int height = ctx.Get(widget.height); }, st1); - w1.color.Set(10); - w2.color.Set(20); + st1->color.Set(10); + st2->color.Set(20); } \ No newline at end of file From 40ea6e424d2fcd37abb4946a015e79de9a6ebf2a Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 30 Oct 2017 22:06:00 +0100 Subject: [PATCH 253/266] Removed react player. No longer supported with new codebase. --- tools/ReactPlayer/ReactPlayer.swf | Bin 13905 -> 0 bytes tools/ReactPlayer/readme.txt | 8 -------- 2 files changed, 8 deletions(-) delete mode 100644 tools/ReactPlayer/ReactPlayer.swf delete mode 100644 tools/ReactPlayer/readme.txt diff --git a/tools/ReactPlayer/ReactPlayer.swf b/tools/ReactPlayer/ReactPlayer.swf deleted file mode 100644 index ee457a7c92999db5bab5c1804898901227834e32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13905 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 RqIW2BVhCTz(3;UA{|CuOHLm~w diff --git a/tools/ReactPlayer/readme.txt b/tools/ReactPlayer/readme.txt deleted file mode 100644 index 6d22437d..00000000 --- a/tools/ReactPlayer/readme.txt +++ /dev/null @@ -1,8 +0,0 @@ -load LOGNAME : Loads event log from LOGNAME.txt. -play [PROFILE] : Starts event playback. Profiles: normal (default), verbose, fast, faster, forward -pause : Pauses event playback. -step : Steps to the next event. -reload : Reloads the current event log. -debugmode STATE : Enable/disable debug mode. States: 0, 1 -showthreads TURN : Show thread markers for given turn. -clearthreads : Clear thread markers. \ No newline at end of file From 928e8b9157c874347e92db92deb7a95d5a39209b Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 30 Oct 2017 22:06:24 +0100 Subject: [PATCH 254/266] Removed test example. --- examples/src/Main.cpp | 488 ------------------------------------------ 1 file changed, 488 deletions(-) delete mode 100644 examples/src/Main.cpp diff --git a/examples/src/Main.cpp b/examples/src/Main.cpp deleted file mode 100644 index 3d3bb8e4..00000000 --- a/examples/src/Main.cpp +++ /dev/null @@ -1,488 +0,0 @@ - -// 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) - -#include -#include -#include -#include -#include - -#include "react/Signal.h" -#include "react/Event.h" -#include "react/Algorithm.h" -#include "react/Observer.h" - -#include "react/common/expected.h" - -using namespace react; - -template -class GridGraphGenerator -{ -public: - using SignalType = Signal; - - using Func1T = std::function; - using Func2T = std::function; - - using SignalVectType = std::vector; - - SignalVectType inputSignals; - SignalVectType outputSignals; - - Func1T function1; - Func2T function2; - - std::vector widths; - - void Generate(const Group& group) - { - SignalVectType buf1 = std::move(inputSignals); - SignalVectType buf2; - - SignalVectType* curBuf = &buf1; - SignalVectType* nextBuf = &buf2; - - size_t curWidth = buf1.size(); - - size_t nodeCount = 1; - nodeCount += curWidth; - - for (auto targetWidth : widths) - { - while (curWidth != targetWidth) - { - // Grow or shrink? - bool shouldGrow = targetWidth > curWidth; - - auto l = curBuf->begin(); - auto r = curBuf->begin(); - if (r != curBuf->end()) - ++r; - - if (shouldGrow) - { - auto s = SignalType{ group, function1, *l }; - nextBuf->push_back(std::move(s)); - } - - while (r != curBuf->end()) - { - auto s = SignalType{ group, function2, *l, *r }; - nextBuf->push_back(std::move(s)); - ++nodeCount; - ++l; ++r; - } - - if (shouldGrow) - { - auto s = SignalType{ group, function1, *l }; - nextBuf->push_back(std::move(s)); - ++nodeCount; - } - - curBuf->clear(); - - // Swap buffer pointers - SignalVectType* t = curBuf; - curBuf = nextBuf; - nextBuf = t; - - if (shouldGrow) - ++curWidth; - else - --curWidth; - } - } - - //printf ("NODE COUNT %d\n", nodeCount); - - outputSignals.clear(); - outputSignals.insert(outputSignals.begin(), curBuf->begin(), curBuf->end()); - } -}; - - -template -T Multiply(T a, T b) -{ - return a * b; -} - -template void PrintValue(T v) -{ - printf("Value: %d\n", v); -} - -template void PrintArea(T v) -{ - printf("Area: %d\n", v); -} - -template void PrintVolume(T v) -{ - printf("Volume: %d\n", v); -} - -template void PrintEvents(EventRange evts) -{ - printf("Processing events...\n"); - - for (const auto& e : evts) - printf(" Event: %d\n", e); -} - -template void PrintSyncedEvents(EventRange evts, int a, int b) -{ - printf("Processing events...\n"); - - for (const auto& e : evts) - printf(" Event: %d, %d, %d\n", e, a, b); -} - -template bool FilterFunc(T v) -{ - return v > 10; -} - -template T IterFunc1(EventRange evts, T v) -{ - return v + 1; -} - -template T IterFunc2(EventRange evts, T v, T a1, T a2) -{ - return v + 1; -} - -int main1() -{ - Group group; - - { - // Signals - VarSignal x{ group, 0 }; - VarSignal y{ group, 0 }; - VarSignal z{ group, 0 }; - - Signal area{ Multiply, x, y }; - Signal volume{ Multiply, area, z }; - - Observer areaObs{ PrintArea, area }; - Observer volumeObs{ PrintVolume, volume }; - - x.Set(2); // a: 0, v: 0 - y.Set(2); // a: 4, v: 0 - z.Set(2); // a: 4, v: 8 - - group.DoTransaction([&] - { - x.Set(100); - y <<= 3; - y <<= 4; - }); - - // a: 400, v: 800 - } - - { - // Events - EventSource button1{ group }; - EventSource button2{ group }; - - Event anyButton = Merge(button1, button2); - Event filtered = Filter(FilterFunc, anyButton); - - Observer eventObs{ PrintEvents, anyButton }; - - button1.Emit(1); - button2.Emit(2); - - group.DoTransaction([&] - { - for (int i=0; i<10; ++i) - button1.Emit(42); - }); - } - - { - // Dynamic signals - VarSignal s1{ group, 10 }; - VarSignal s2{ group, 22 }; - - SignalSlot slot{ s1 }; - - Observer areaObs{ PrintValue, slot }; - - s1.Set(42); - - slot.Set(s2); - - s2.Set(667); - } - - { - // Dynamic events - EventSource s1{ group }; - EventSource s2{ group }; - - EventSlot slot{ group }; - - Observer eventObs{ PrintEvents, slot }; - - slot.Add(s1); - slot.Add(s2); - - slot.Remove(s1); - slot.RemoveAll(); - } - - // Links - { - Group group1; - Group group2; - - VarSignal s1{ group1, 10 }; - VarSignal s2{ group2, 11 }; - - Signal v{ Multiply, s1, s2 }; - - Observer obs{ PrintValue, v }; - - s1.Set(555); - - std::this_thread::sleep_for(std::chrono::seconds(5)); - } - - { - Group group1; - Group group2; - - VarSignal s1{ group1, 10 }; - VarSignal s2{ group2, 11 }; - - EventSource e1{ group1 }; - EventSource e2{ group2 }; - - auto hold = Hold(group1, 0, e1); - - auto merged = Merge(group2, e1, e2); - - auto joined1 = Join(e1, e2); - auto joined2 = Join(group1, e1, e2); - - Observer eventObs1{ PrintEvents, merged }; - Observer eventObs2{ group2, PrintSyncedEvents, merged, s1, s2 }; - - e1.Emit(222); - - std::this_thread::sleep_for(std::chrono::seconds(5)); - } - - { - Group group1; - Group group2; - - VarSignal s1{ group1, 10 }; - VarSignal s2{ group2, 11 }; - - EventSource e1{ group1 }; - EventSource e2{ group2 }; - - auto hold1 = Hold(group1, 0, e1); - auto hold2 = Hold(0, e1); - - auto monitor1 = Monitor(group1, s1); - auto monitor2 = Monitor(s1); - - auto snapshot1 = Snapshot(group1, s1, e1); - auto snapshot2 = Snapshot(s1, e1); - - auto pulse1 = Pulse(group1, s1, e1); - auto pulse2 = Pulse(s1, e1); - - auto merged = Merge(group2, e1, e2); - - auto joined1 = Join(e1, e2); - auto joined2 = Join(group1, e1, e2); - - auto iter1 = Iterate(group, 0, IterFunc1, e1); - auto iter2 = Iterate(0, IterFunc1, e1); - - auto iter3 = Iterate(group, 0, IterFunc2, e1, s1, s2); - auto iter4 = Iterate(0, IterFunc2, e1, s1, s2); - - Observer eventObs{ PrintEvents, merged }; - Observer eventObs2{ group2, PrintSyncedEvents, merged, s1, s2 }; - - e1.Emit(222); - - std::this_thread::sleep_for(std::chrono::seconds(5)); - - GridGraphGenerator grid; - } - - return 0; -} - - -int main() -{ - Group group; - - Group extGroup; - - // Dynamic events - EventSource s1{ group }; - EventSource s2{ group }; - EventSource s3{ extGroup }; - //EventSource s4{ extGroup }; - - EventSlot slot{ group }; - - Observer eventObs{ PrintEvents, slot }; - //Observer extObs{ PrintEvents, link1 }; - - //slot.Add(s1); - //slot.Add(s2); - - group.DoTransaction([&] - { - s1.Emit(1); - s2.Emit(2); - - - slot.Add(s3); - slot.Add(s3); - slot.Add(s3); - slot.Add(s3); - }); - - s3.Emit(3); - - std::this_thread::sleep_for(std::chrono::seconds(5)); - - Expected x; - - return 0; -} - - - - - - - - -int main2() -{ - Group group; - - VarSignal in{ group, 1 }; - Signal in2 = in; - - GridGraphGenerator generator; - - generator.inputSignals.push_back(in2); - - generator.widths.push_back(100); - generator.widths.push_back(1); - - int updateCount = 0; - - generator.function1 = [&] (int a) { ++updateCount; return a; }; - generator.function2 = [&] (int a, int b) { ++updateCount; return a + b; }; - - generator.Generate(group); - - updateCount = 0; - - auto t0 = tbb::tick_count::now(); - for (int i = 0; i < 10000; i++) - in <<= 10 + i; - auto t1 = tbb::tick_count::now(); - - double d = (t1 - t0).seconds(); - printf("updateCount %d\n", updateCount); - printf("Time %g\n", d); - - return 0; -} - - - - - - - - - - - - - -/* - -int main7() -{ - Group group1; - Group group2; - Group group3; - - VarSignal x{ group1, 0 }; - VarSignal y{ group2, 0 }; - VarSignal z{ group3, 0 }; - - Signal area{ Multiply, x, y }; - Signal volume{ Multiply, area, z }; - - Observer obs{ PrintAreaAndVolume, area, volume }; - - Signal> volumeHistory = Iterate>( vector{ }, PushToVector, Monitor(volume)); - - x <<= 2; - y <<= 2; - z <<= 2; - - group.DoTransaction([&] - { - x <<= 100; - y <<= 200; - z <<= 300; - }); - - obs.Cancel(); - - x <<= 42; - - printf("History:\n"); - for (auto t : volumeHistory.Value()) - printf("%d ", t); - printf("\n"); - - return 0; -} - - -int main3() -{ - using namespace std; - using namespace react; - - Group group1; - Group group2; - - auto sig1 = VarSignal( 10, group1 ); - - auto link1 = SignalLink( sig1, group2 ); - auto link2 = SignalLink( sig1, group2 ); - - sig1.Set(10); - - return 0; -} - -*/ \ No newline at end of file From eae1a8c20d67faa6182d684fa3eedf01543c0c9c Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 30 Oct 2017 22:17:26 +0100 Subject: [PATCH 255/266] Updated readme. --- README.md | 185 +++++++----------------------------------------------- 1 file changed, 24 insertions(+), 161 deletions(-) diff --git a/README.md b/README.md index 5c27b6bc..0654cddc 100644 --- a/README.md +++ b/README.md @@ -1,177 +1,40 @@ # ![C++React](http://schlangster.github.io/cpp.react//media/logo_banner3.png) -C++React is reactive programming library for C++11. +C++React is reactive programming library for C++14 that enables declarative definition of data dependencies between state and event flows. +Based on these definitions, propagation of changes is handled automatically. -Generally speaking, it provides abstractions to handle change propagation and data processing for a push-based event model. -A more practical description is that it enables coordinated, multi-layered - _and potentially parallel_ - execution of callbacks. -All this happens implicitly, based on declarative definitions, with guarantees regarding +Here's a simple example: -- _update minimality_ - nothing is re-calculated or processed unnecessarily; -- _glitch freedom_ - no transiently inconsistent data sets; -- _thread safety_ - no data races for parallel execution by avoiding side effects. - -The core abstractions of the library are - -- _signals_, reactive variables that are automatically re-calculated when their dependencies change, and -- _event streams_ as composable first class objects. - -Signals specifically deal with aspects of time-varying state, whereas event streams facilitate event processing in general. - -Additional features include - -- a publish/subscribe mechanism for callbacks with side effects; -- a set of operations and algorithms to combine signals and events; -- a domain model to encapsulate multiple reactive systems; -- transactions to group related events, supporting both synchronous and asynchrounous execution. - - -## Documentation - -[If you're interested in learning about C++React, have a look at its documentation.](http://schlangster.github.io/cpp.react/) - - -## Using the library - -This library is a work-in-progress. It should not be considered release quality yet and its API might still change. -It is, however, in a perfectly usable state and has already received a fair amount of testing and tuning. - -### Dependencies - -* [Intel TBB 4.2](https://www.threadingbuildingblocks.org/) (required) -* [Google test framework](https://code.google.com/p/googletest/) (optional, to compile the unit tests) -* [Boost 1.55.0 C++ Libraries](http://www.boost.org/) (optional, to include Reactor.h, which requires `boost::coroutine`) - -### Compiling - -C++React has been tested with the following compilers: - -* Visual Studio 2013.2 -* GCC 4.8.2 -* Clang 3.4 - -To build with Visual Studio, use the pre-made solution found in `project/msvc/`. - -To build with GCC or Clang, use [CMake](http://www.cmake.org/): -``` -mkdir build -cd build -cmake .. -make ``` - -For more details, refer to the [Build instructions](https://github.com/schlangster/cpp.react/wiki/Build-instructions). - - -## Features by example - -### Signals - -Signals are self-updating reactive variables. -They can be combined in expressions to create new signals, which are automatically re-calculated when their dependencies change. - -```C++ -using namespace std; using namespace react; -// Defines a reactive domain that uses single-threaded, sequential updating -REACTIVE_DOMAIN(D, sequential) +void AddNumbers(int a, int b) { return a + b; } -// Defines aliases for types of the given domain, -// e.g. using VarSignalT = VarSignal -USING_REACTIVE_DOMAIN(D) +// Two state variable objects. You can change their values manually. +auto a = StateVar::Create(0); +auto b = StateVar::Create(0); -// Two reactive variables that can be manipulated imperatively -// to input external changes -VarSignalT width = MakeVar(1); -VarSignalT height = MakeVar(2); +// Another state object. Its value is calculated automatically based on the given function and arguments. +// If the arguments change, the value is re-calculated. +auto sum = State::Create(AddNumbers, a, b); -// A signal that depends on width and height and multiplies their values -SignalT area = MakeSignal( - With(width, height), - [] (int w, int h) { - return w * h; - }); +// sum == 0 +a.Set(21); +// sum == 21 +b.Set(21); +// sum == 42 ``` -Signal values can be accessed imperatively: -```C++ -cout << "area: " << area.Value() << endl; // => area: 2 -// Width changed, so area is re-calculated automatically -width.Set(10); +The underlying system constructs a dependency graph to collect which values are affected by a change, and in which order they have to be re-calculated. +This guarantees several properties: +- _correctness_ - no updates are forgotten; +- _consistency_ - no value is updated before all its incoming dependencies have been updated; +- _efficiency_ - no value is re-calculated more than once per update cycle, and changes are only propagated along paths where a new value is different from the old one. -cout << "area: " << area.Value() << endl; // => area: 20 -``` - -Or, instead of using `Value()` to pull the new value, callback functions can be registered to receive notifications on a change: -```C++ -Observe(area, [] (int newValue) { - cout << "area changed: " << newValue << endl; -}); -``` - -Overloaded operators for signal types allow to omit `MakeSignal` for a more concise syntax: -```C++ -// Lift as reactive expression - equivalent to previous example -SignalT area = width * height; -``` - -### Event streams - -Unlike signals, event streams are not centered on changing state, but represent flows of discrete values. -They are first-class objects and can be merged, filtered, transformed or composed to more complex types: - -```C++ -using namespace std; -using namespace react; - -REACTIVE_DOMAIN(D, sequential) -USING_REACTIVE_DOMAIN(D) - -// Two event sources -EventSourceT leftClick = MakeEventSource(); -EventSourceT rightClick = MakeEventSource(); - -// Merge both event streams -EventsT anyClick = leftClick | rightClick; - -// React to events -Observe(anyClick, [] (Token) { - cout << "clicked!" << endl; -}); -``` -``` -leftClick.Emit(); // => clicked! -rightClick.Emit(); // => clicked! -``` - -### Parallelism and concurrency - -When enabling it through the concurrency policy, updates are automatically parallelized: - -```C++ -REACTIVE_DOMAIN(D, parallel) - -VarSignalT in = MakeVar(0); - -SignalT op1 = MakeSignal(in, - [] (int in) { - int result = doCostlyOperation1(in); - return result; - }); - -SignalT op2 = MakeSignal(in, - [] (int in) { - int result = doCostlyOperation2(in); - return result; - }); - -// op1 and op2 can be re-calculated in parallel -SignalT out = op1 + op2; -``` +The system also knows when it's safe to update values in parallel from multiple threads, and it can do live profiling to decide when that's worthwhile. -## Acknowledgements +## Development status -The API of C++React has been inspired by the following two research papers: +I'm currently in the process of rewriting the library more or less from scratch and many things are still broken or outdated. -* [Deprecating the Observer Pattern with Scala.React](http://infoscience.epfl.ch/record/176887/files/DeprecatingObservers2012.pdf) -* [REScala: Bridging Between Object-oriented and Functional Style in Reactive Applications](http://www.stg.tu-darmstadt.de/media/st/research/rescala_folder/REScala-Bridging-The-Gap-Between-Object-Oriented-And-Functional-Style-In-Reactive-Applications.pdf) +[The old, but stable and documented version is still available in this branch.](https://github.com/schlangster/cpp.react/tree/legacy1) \ No newline at end of file From 5467003302e656c8fd983e619ccfe02a96007ffb Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 30 Oct 2017 22:18:58 +0100 Subject: [PATCH 256/266] Readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0654cddc..f38e6486 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ![C++React](http://schlangster.github.io/cpp.react//media/logo_banner3.png) -C++React is reactive programming library for C++14 that enables declarative definition of data dependencies between state and event flows. +C++React is reactive programming library for C++14. It enables the declarative definition of data dependencies between state and event flows. Based on these definitions, propagation of changes is handled automatically. Here's a simple example: From 76be2a9438047148bcdd412eb7fde2148e3c57cf Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 1 Nov 2017 01:29:13 +0100 Subject: [PATCH 257/266] Added Ref wrapper template. StateVars always contain elements by-value, but for State it might be convenient to use pointers/references, especially when dealing with class types. Use CreateRef for State -> State> binding. Semantically, it's the same as using State, but avoids the null check. The State> changes when the referenced state changes. --- include/react/api.h | 21 ++++++++++++++ include/react/detail/state_nodes.h | 44 ++++++++++++++++++++++++------ include/react/state.h | 19 +++++++++---- 3 files changed, 71 insertions(+), 13 deletions(-) diff --git a/include/react/api.h b/include/react/api.h index 95ce57f6..2223cae8 100644 --- a/include/react/api.h +++ b/include/react/api.h @@ -9,6 +9,7 @@ #pragma once +#include #include #include "react/detail/defs.h" @@ -93,6 +94,26 @@ using EventValueSink = std::back_insert_iterator>; // Observer class Observer; +// Ref +template +using Ref = std::reference_wrapper; + +template +bool HasChanged(const T& a, const T& b) + { return !(a == b); } + +template +bool HasChanged(const Ref& a, const Ref& b) + { return true; } + +template +void ListInsert(T& list, V&& value) + { list.push_back(std::forward(value)); } + +template +void MapInsert(T& map, V&& value) + { map.insert(std::forward(value)); } + /******************************************/ REACT_END /******************************************/ #endif // REACT_TYPETRAITS_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/state_nodes.h b/include/react/detail/state_nodes.h index aa8c8b85..dbc57fb3 100644 --- a/include/react/detail/state_nodes.h +++ b/include/react/detail/state_nodes.h @@ -171,22 +171,19 @@ class StateFuncNode : public StateNode } virtual UpdateResult Update(TurnId turnId) noexcept override - { - bool changed = false; - + { S newValue = apply([this] (const auto& ... deps) { return this->func_(GetInternals(deps).Value() ...); }, depHolder_); - if (! (this->Value() == newValue)) + if (HasChanged(this->Value(), newValue)) { this->Value() = std::move(newValue); - changed = true; - } - - if (changed) return UpdateResult::changed; + } else + { return UpdateResult::unchanged; + } } private: @@ -439,6 +436,37 @@ class StateInternals std::shared_ptr> nodePtr_; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// StateRefNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class StateRefNode : public StateNode> +{ +public: + StateRefNode(const Group& group, const State& input) : + StateRefNode::StateNode( group, std::cref(GetInternals(input).Value()) ), + input_( input ) + { + this->RegisterMe(); + this->AttachToMe(GetInternals(input).GetNodeId()); + } + + ~StateRefNode() + { + this->DetachFromMe(GetInternals(input_).GetNodeId()); + this->UnregisterMe(); + } + + virtual UpdateResult Update(TurnId turnId) noexcept override + { + this->Value() = std::cref(GetInternals(input_).Value()); + return UpdateResult::changed; + } + +private: + State input_; +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// SameGroupOrLink /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/state.h b/include/react/state.h index d8235d37..40eeb574 100644 --- a/include/react/state.h +++ b/include/react/state.h @@ -202,10 +202,7 @@ class StateSlot : public State StateSlot& operator=(StateSlot&&) = default; void Set(const State& newInput) - { SetInput(newInput); } - - void operator<<=(const State& newInput) - { SetInput(newInput); } + { SetSlotInput(newInput); } protected: StateSlot(std::shared_ptr>&& nodePtr) : @@ -221,7 +218,7 @@ class StateSlot : public State return std::make_shared>(group, SameGroupOrLink(group, input)); } - void SetInput(const State& newInput) + void SetSlotInput(const State& newInput) { using REACT_IMPL::NodeId; using REACT_IMPL::StateSlotNode; @@ -282,6 +279,18 @@ class StateLink : public State } }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// CreateRef +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +auto CreateRef(const State& state) -> State> +{ + using REACT_IMPL::StateRefNode; + using REACT_IMPL::CreateWrappedNode; + + return CreateWrappedNode>, StateRefNode>(state.GetGroup(), state); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// ObjectContext /////////////////////////////////////////////////////////////////////////////////////////////////// From 90057e3e977d4d5d53a03bd5fb4c5f2c82b0e4e5 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 1 Nov 2017 01:30:02 +0100 Subject: [PATCH 258/266] Cleanup. --- include/react/detail/event_nodes.h | 14 ++++---------- include/react/event.h | 18 +++++++++--------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/include/react/detail/event_nodes.h b/include/react/detail/event_nodes.h index c918afec..fce5b4b1 100644 --- a/include/react/detail/event_nodes.h +++ b/include/react/detail/event_nodes.h @@ -45,12 +45,6 @@ template class EventNode : public NodeBase { public: - EventNode(EventNode&&) = default; - EventNode& operator=(EventNode&&) = default; - - EventNode(const EventNode&) = delete; - EventNode& operator=(const EventNode&) = delete; - explicit EventNode(const Group& group) : EventNode::NodeBase( group ) { } @@ -161,7 +155,7 @@ class EventSlotNode : public EventNode ~EventSlotNode() { - RemoveAllInputs(); + RemoveAllSlotInputs(); this->DetachFromMe(inputNodeId_); this->UnregisterMe(); @@ -182,7 +176,7 @@ class EventSlotNode : public EventNode return UpdateResult::unchanged; } - void AddInput(const Event& input) + void AddSlotInput(const Event& input) { auto it = std::find(inputs_.begin(), inputs_.end(), input); if (it == inputs_.end()) @@ -192,7 +186,7 @@ class EventSlotNode : public EventNode } } - void RemoveInput(const Event& input) + void RemoveSlotInput(const Event& input) { auto it = std::find(inputs_.begin(), inputs_.end(), input); if (it != inputs_.end()) @@ -202,7 +196,7 @@ class EventSlotNode : public EventNode } } - void RemoveAllInputs() + void RemoveAllSlotInputs() { for (const auto& e : inputs_) this->DetachFromMe(GetInternals(e).GetNodeId()); diff --git a/include/react/event.h b/include/react/event.h index aeaaa6f6..8dd05d46 100644 --- a/include/react/event.h +++ b/include/react/event.h @@ -192,13 +192,13 @@ class EventSlot : public Event EventSlot& operator=(EventSlot&&) = default; void Add(const Event& input) - { AddInput(input); } + { AddSlotInput(input); } void Remove(const Event& input) - { RemoveInput(input); } + { RemoveSlotInput(input); } void RemoveAll() - { RemoveAllInputs(); } + { RemoveAllSlotInputs(); } protected: EventSlot(std::shared_ptr>&& nodePtr) : @@ -212,7 +212,7 @@ class EventSlot : public Event return std::make_shared>(group); } - void AddInput(const Event& input) + void AddSlotInput(const Event& input) { using REACT_IMPL::NodeId; using SlotNodeType = REACT_IMPL::EventSlotNode; @@ -222,10 +222,10 @@ class EventSlot : public Event NodeId nodeId = castedPtr->GetInputNodeId(); auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); - graphPtr->PushInput(nodeId, [this, castedPtr, &input] { castedPtr->AddInput(SameGroupOrLink(GetGroup(), input)); }); + graphPtr->PushInput(nodeId, [this, castedPtr, &input] { castedPtr->AddSlotInput(SameGroupOrLink(GetGroup(), input)); }); } - void RemoveInput(const Event& input) + void RemoveSlotInput(const Event& input) { using REACT_IMPL::NodeId; using SlotNodeType = REACT_IMPL::EventSlotNode; @@ -235,10 +235,10 @@ class EventSlot : public Event NodeId nodeId = castedPtr->GetInputNodeId(); auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); - graphPtr->PushInput(nodeId, [this, castedPtr, &input] { castedPtr->RemoveInput(SameGroupOrLink(GetGroup(), input)); }); + graphPtr->PushInput(nodeId, [this, castedPtr, &input] { castedPtr->RemoveSlotInput(SameGroupOrLink(GetGroup(), input)); }); } - void RemoveAllInputs() + void RemoveAllSlotInputs() { using REACT_IMPL::NodeId; using SlotNodeType = REACT_IMPL::EventSlotNode; @@ -248,7 +248,7 @@ class EventSlot : public Event NodeId nodeId = castedPtr->GetInputNodeId(); auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); - graphPtr->PushInput(nodeId, [castedPtr] { castedPtr->RemoveAllInputs(); }); + graphPtr->PushInput(nodeId, [castedPtr] { castedPtr->RemoveAllSlotInputs(); }); } }; From 8e1f42fe37558d8ad224d6bdc8e2f6117e36aee2 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 1 Nov 2017 01:32:28 +0100 Subject: [PATCH 259/266] WIP: Flattening for nested state. --- examples/src/BasicAlgorithms.cpp | 120 ++++++++++++- include/react/algorithm.h | 84 +++++++++ include/react/detail/algorithm_nodes.h | 236 +++++++++++++++++++++++++ include/react/detail/graph_interface.h | 3 +- 4 files changed, 440 insertions(+), 3 deletions(-) diff --git a/examples/src/BasicAlgorithms.cpp b/examples/src/BasicAlgorithms.cpp index d6f8aced..b2fa8b96 100644 --- a/examples/src/BasicAlgorithms.cpp +++ b/examples/src/BasicAlgorithms.cpp @@ -340,17 +340,133 @@ namespace example6 } } + +using namespace react; + + +template +class MyContainer { }; + +template +struct Flattened : public C +{ + using C::C; + + Flattened(const C& base) : + C( base ) + { } + + Flattened(const C& base, int m) : + C( base ), + mode( m ) + { } + + template + T* Flatten(State& signal) + { + if (mode == 1) + { + memberIds_.push_back(GetInternals(signal).GetNodeId()); + } + + return &GetInternals(signal).Value(); + } + +public: + int mode = 0; + std::vector memberIds_; +}; + +Group g; + +struct MyClass +{ + StateVar a = StateVar::Create(g, 10); + StateVar b = StateVar::Create(g, 20); + + int hello = 12435; + + bool operator==(const MyClass& other) + { return hello == other.hello; } + + struct Flat; +}; + +struct MyClass::Flat : public Flattened +{ + using Flattened::Flattened; + + int* a = this->Flatten(MyClass::a); + int* b = this->Flatten(MyClass::b); +}; + + +void test1() +{ + using namespace react; + + /*StateVar x; + State y; + State z; + + State>> list; + State>> map; + + State> flatlist = FlattenList(list); + State> flatmap = FlattenMap(map); + + MyClass cls1; + MyClass::Flat cls2(cls1); + + StateVar> sig; + + Flatten(g, sig);*/ + + StateVar st = StateVar::Create(g); + + State> ref = CreateRef(st); + + auto obs2 = Observer::Create([] (const MyClass& obj) + { + printf("aa %d\n", obj.hello); + }, ref); + + auto flat = FlattenObject(st); + + auto flat2 = FlattenObject(ref); + + auto obs = Observer::Create([] (const MyClass::Flat& obj) + { + int x = *obj.a; + int y = *obj.b; + printf("%d\n", x); + printf("%d\n", y); + }, flat2); + + + GetInternals(st).Value().a.Set(999); + + MyClass a2{ }; + a2.hello = 3333; + + st.Set(a2); + st.Set(a2); + +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Run examples /////////////////////////////////////////////////////////////////////////////////////////////////// int main() { - example1::Run(); + /*example1::Run(); example2::Run(); example3::Run(); example4::Run(); example5::Run(); - example6::Run(); + example6::Run();*/ + + test1(); return 0; } \ No newline at end of file diff --git a/include/react/algorithm.h b/include/react/algorithm.h index 075f3d3c..1d95bbce 100644 --- a/include/react/algorithm.h +++ b/include/react/algorithm.h @@ -169,6 +169,90 @@ template auto Pulse(const State& state, const Event& evnt) -> Event { return Pulse(state.GetGroup(), state, evnt); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Flatten +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +auto Flatten(const Group& group, const State>& state) -> State +{ + using REACT_IMPL::FlattenStateNode; + using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; + + return CreateWrappedNode, FlattenStateNode>(group, SameGroupOrLink(group, state)); +} + +template +auto Flatten(const State>& state) -> State + { return Flatten(state.GetGroup(), state); } + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// FlattenList +/////////////////////////////////////////////////////////////////////////////////////////////////// +template

(waitingStatePtr) ) {} std::function InputFunc; - - // Blocking condition variable for sync merged - BlockingCondition* Cond; - - // Status for async merged - AsyncStatePtrT StatusPtr; + WaitingStatePtrT WaitingStatePtr; }; using MergedDataVectT = std::vector; bool isMergeable_; - QueueEntry* successor_ = nullptr; + QueueEntry* successor_ = nullptr; MergedDataVectT merged_; - BlockingCondition blockCondition_; + UniqueWaitingState waitingState_; + size_t waitingStatePtrCount_ = 0; }; template @@ -397,23 +413,24 @@ class TransactionQueue { bool merged = false; - BlockingCondition caller; + UniqueWaitingState st; + WaitingStatePtrT p( &st ); {// seqMutex_ SeqMutexT::scoped_lock lock(seqMutex_); if (tail_) - merged = tail_->TryMerge(std::forward(inputFunc), &caller, nullptr); + merged = tail_->TryMerge(std::forward(inputFunc), p); }// ~seqMutex_ if (merged) - caller.WaitForUnblock(); + p->Wait(); return merged; } template - bool TryMergeAsync(F&& inputFunc, AsyncStatePtrT&& statusPtr) + bool TryMergeAsync(F&& inputFunc, WaitingStatePtrT&& waitingStatePtr) { bool merged = false; @@ -421,36 +438,37 @@ class TransactionQueue SeqMutexT::scoped_lock lock(seqMutex_); if (tail_) - merged = tail_->TryMerge( - std::forward(inputFunc), nullptr, std::move(statusPtr)); + merged = tail_->TryMerge(std::forward(inputFunc), std::move(waitingStatePtr)); }// ~seqMutex_ return merged; } - void EnterQueue(QueueEntry& turn) + void EnterQueue(QueueEntry& tr) { {// seqMutex_ SeqMutexT::scoped_lock lock(seqMutex_); if (tail_) - tail_->Append(turn); + tail_->Append(tr); - tail_ = &turn; + tail_ = &tr; }// ~seqMutex_ - turn.WaitForUnblock(); + tr.WaitForUnblock(); } - void ExitQueue(QueueEntry& turn) - {// seqMutex_ - SeqMutexT::scoped_lock lock(seqMutex_); + void ExitQueue(QueueEntry& tr) + { + {// seqMutex_ + SeqMutexT::scoped_lock lock(seqMutex_); - turn.UnblockSuccessors(); + if (tail_ == &tr) + tail_ = nullptr; + }// ~seqMutex_ - if (tail_ == &turn) - tail_ = nullptr; - }// ~seqMutex_ + tr.Release(); + } private: using SeqMutexT = tbb::queuing_mutex; @@ -470,7 +488,7 @@ class InputManager : struct AsyncItem { TurnFlagsT Flags; - AsyncStatePtrT StatusPtr; + WaitingStatePtrT WaitingStatePtr; TransactionFuncT Func; }; @@ -480,6 +498,8 @@ class InputManager : using TransactionQueueT = TransactionQueue; using ContinuationManagerT = ContinuationManager; + using QueueEntryT = typename TransactionQueueT::QueueEntry; + public: using TurnT = typename D::TurnT; using Engine = typename D::Engine; @@ -501,7 +521,8 @@ class InputManager : bool shouldPropagate = false; - typename TransactionQueueT::QueueEntry tr( flags ); + QueueEntryT tr( flags ); + transactionQueue_.EnterQueue(tr); // Phase 1 - Input admission @@ -525,18 +546,16 @@ class InputManager : if (shouldPropagate) Engine::Propagate(turn); - transactionQueue_.ExitQueue(tr); - - processContinuations(flags); + finalizeSyncTransaction(tr); } template - void AsyncTransaction(TurnFlagsT flags, const AsyncStatePtrT& statusPtr, F&& func) + void AsyncTransaction(TurnFlagsT flags, const WaitingStatePtrT& waitingStatePtr, F&& func) { - if (statusPtr != nullptr) - statusPtr->IncWaitCount(); + if (waitingStatePtr != nullptr) + waitingStatePtr->IncWaitCount(); - asyncQueue_.push(AsyncItem{ flags, statusPtr, std::forward(func) } ); + asyncQueue_.push(AsyncItem{ flags, waitingStatePtr, std::forward(func) } ); } template @@ -566,17 +585,16 @@ class InputManager : } //IContinuationTarget - virtual void AsyncContinuation(TransactionFuncT&& cont) override + virtual void AsyncContinuation(TurnFlagsT flags, const WaitingStatePtrT& waitingStatePtr, + TransactionFuncT&& cont) override { - DoTransaction(0, std::move(cont)); - // Todo: fixme - //AsyncTransaction(0, nullptr, std::move(cont)); + AsyncTransaction(flags, waitingStatePtr, std::move(cont)); } //~IContinuationTarget - void StoreContinuation(IContinuationTarget& target, TransactionFuncT&& cont) + void StoreContinuation(IContinuationTarget& target, TurnFlagsT flags, TransactionFuncT&& cont) { - continuationManager_.StoreContinuation(target, std::move(cont)); + continuationManager_.StoreContinuation(target, flags, std::move(cont)); } void QueueObserverForDetach(IObserver& obs) @@ -599,7 +617,7 @@ class InputManager : template void addSimpleInput(R& r, V&& v) { - typename TransactionQueueT::QueueEntry tr( 0 ); + QueueEntryT tr( 0 ); transactionQueue_.EnterQueue(tr); @@ -612,15 +630,13 @@ class InputManager : if (r.ApplyInput(&turn)) Engine::Propagate(turn); - transactionQueue_.ExitQueue(tr); - - processContinuations(0); + finalizeSyncTransaction(tr); } template void modifySimpleInput(R& r, const F& func) { - typename TransactionQueueT::QueueEntry tr( 0 ); + QueueEntryT tr( 0 ); transactionQueue_.EnterQueue(tr); @@ -634,9 +650,28 @@ class InputManager : Engine::Propagate(turn); - transactionQueue_.ExitQueue(tr); + finalizeSyncTransaction(tr); + } + + void finalizeSyncTransaction(QueueEntryT& tr) + { + continuationManager_.template DetachQueuedObservers(); + + if (continuationManager_.HasContinuations()) + { + UniqueWaitingState st; + WaitingStatePtrT p( &st ); + + continuationManager_.StartContinuations(p); - processContinuations(0); + transactionQueue_.ExitQueue(tr); + + p->Wait(); + } + else + { + transactionQueue_.ExitQueue(tr); + } } // This input is part of an active transaction @@ -656,12 +691,11 @@ class InputManager : void processAsyncQueue() { - AsyncItem item; - - AsyncStatePtrT savedStatusPtr = nullptr; - TurnFlagsT savedFlags = 0; + AsyncItem item; + + TurnFlagsT savedFlags = 0; - std::vector statusPtrStash; + std::vector waitingStatePtrs; bool skipPop = false; @@ -677,13 +711,14 @@ class InputManager : bool canMerge = (item.Flags & allow_merging) != 0; if (canMerge && transactionQueue_.TryMergeAsync( std::move(item.Func), - std::move(item.StatusPtr))) + std::move(item.WaitingStatePtr))) continue; bool shouldPropagate = false; + + QueueEntryT tr( item.Flags ); // Blocks until turn is at the front of the queue - typename TransactionQueueT::QueueEntry tr( item.Flags ); transactionQueue_.EnterQueue(tr); TurnT turn( nextTurnId(), item.Flags ); @@ -696,11 +731,13 @@ class InputManager : // Input of current item item.Func(); - // Merged inputs that arrived while this item was queued - tr.RunMergedInputs(); + // Merged sync inputs that arrived while this item was queued + // Commented out, because thats no longer possible + //tr.RunMergedInputs(); - // Save data, because item might be re-used for next input - savedStatusPtr = std::move(item.StatusPtr); + // Save data, item might be re-used for next input + if (item.WaitingStatePtr != nullptr) + waitingStatePtrs.push_back(std::move(item.WaitingStatePtr)); savedFlags = item.Flags; // If the current item supports merging, try to add more mergeable inputs @@ -714,8 +751,9 @@ class InputManager : if (canMergeNext) { item.Func(); - if (item.StatusPtr != nullptr) - statusPtrStash.push_back(std::move(item.StatusPtr)); + + if (item.WaitingStatePtr != nullptr) + waitingStatePtrs.push_back(std::move(item.WaitingStatePtr)); ++extraCount; } @@ -746,31 +784,61 @@ class InputManager : if (shouldPropagate) Engine::Propagate(turn); - transactionQueue_.ExitQueue(tr); + continuationManager_.template DetachQueuedObservers(); - processContinuations(savedFlags); + //printf("1\n"); + //for (const auto& p : waitingStatePtrs) + // printf("%08X\n", p.Get()); - // Decrement transaction status counts of processed items - if (savedStatusPtr != nullptr) - savedStatusPtr->DecWaitCount(); + // Has continuations? If so, status ptrs have to be passed on to + // continuation transactions + if (continuationManager_.HasContinuations()) + { + // Merge all waiting state ptrs for this transaction into a single vector + tr.MoveWaitingStatePtrs(waitingStatePtrs); - for (auto& p : statusPtrStash) - p->DecWaitCount(); - statusPtrStash.clear(); - } - } + // More than 1 waiting state -> create collection from vector + if (waitingStatePtrs.size() > 1) + { + /* printf("2\n"); + for (const auto& p : waitingStatePtrs) + printf("%08X\n", p.Get());*/ - void processContinuations(TurnFlagsT flags) - { - // No merging for continuations - flags &= ~allow_merging; + WaitingStatePtrT p + ( + SharedWaitingStateCollection::Create(std::move(waitingStatePtrs)) + ); - continuationManager_.template DetachQueuedObservers(); + continuationManager_.StartContinuations(p); - while (continuationManager_.HasNext()) - { - continuationManager_.ProcessNext(); - continuationManager_.template DetachQueuedObservers(); + transactionQueue_.ExitQueue(tr); + p->DecWaitCount(); + } + // Exactly one status ptr -> pass it on directly + else if (waitingStatePtrs.size() == 1) + { + WaitingStatePtrT p( std::move(waitingStatePtrs[0]) ); + + continuationManager_.StartContinuations(p); + + transactionQueue_.ExitQueue(tr); + p->DecWaitCount(); + } + // No status ptrs + else + { + continuationManager_.StartContinuations(nullptr); + } + } + else + { + transactionQueue_.ExitQueue(tr); + + for (auto& p : waitingStatePtrs) + p->DecWaitCount(); + } + + waitingStatePtrs.clear(); } } diff --git a/include/react/detail/graph/ContinuationNodes.h b/include/react/detail/graph/ContinuationNodes.h index 1b165370..5c9db4cb 100644 --- a/include/react/detail/graph/ContinuationNodes.h +++ b/include/react/detail/graph/ContinuationNodes.h @@ -38,9 +38,14 @@ class ContinuationNode : public ReactiveNode { public: - ContinuationNode() = default; + ContinuationNode(TurnFlagsT turnFlags) : + turnFlags_( turnFlags ) + {} virtual bool IsOutputNode() const { return true; } + +protected: + TurnFlagsT turnFlags_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -59,8 +64,9 @@ class SignalContinuationNode : public ContinuationNode public: template - SignalContinuationNode(const std::shared_ptr>& trigger, F&& func) : - SignalContinuationNode::ContinuationNode( ), + SignalContinuationNode(TurnFlagsT turnFlags, + const std::shared_ptr>& trigger, F&& func) : + SignalContinuationNode::ContinuationNode( turnFlags ), trigger_( trigger ), func_( std::forward(func) ) { @@ -101,6 +107,7 @@ class SignalContinuationNode : public ContinuationNode DomainSpecificInputManager::Instance() .StoreContinuation( DomainSpecificInputManager::Instance(), + this->turnFlags_, std::move(cont)); REACT_LOG(D::Log().template Append( @@ -129,8 +136,9 @@ class EventContinuationNode : public ContinuationNode public: template - EventContinuationNode(const std::shared_ptr>& trigger, F&& func) : - EventContinuationNode::ContinuationNode( ), + EventContinuationNode(TurnFlagsT turnFlags, + const std::shared_ptr>& trigger, F&& func) : + EventContinuationNode::ContinuationNode( turnFlags ), trigger_( trigger ), func_( std::forward(func) ) { @@ -174,6 +182,7 @@ class EventContinuationNode : public ContinuationNode DomainSpecificInputManager::Instance() .StoreContinuation( DomainSpecificInputManager::Instance(), + this->turnFlags_, std::move(cont)); REACT_LOG(D::Log().template Append( @@ -229,9 +238,10 @@ class SyncedContinuationNode : public ContinuationNode public: template - SyncedContinuationNode(const std::shared_ptr>& trigger, F&& func, + SyncedContinuationNode(TurnFlagsT turnFlags, + const std::shared_ptr>& trigger, F&& func, const std::shared_ptr>& ... deps) : - SyncedContinuationNode::ContinuationNode( ), + SyncedContinuationNode::ContinuationNode( turnFlags ), trigger_( trigger ), func_( std::forward(func) ), deps_( deps ... ) @@ -292,6 +302,7 @@ class SyncedContinuationNode : public ContinuationNode DomainSpecificInputManager::Instance() .StoreContinuation( DomainSpecificInputManager::Instance(), + this->turnFlags_, std::move(cont)); REACT_LOG(D::Log().template Append( diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index 1a7496bb..d62e935b 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -83,6 +83,7 @@ + diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index 868e9439..cf59babe 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -144,6 +144,9 @@ Header Files\detail\graph + + Header Files\common + From 7a52758c6da0758aa7a94e2cfe3c2695308e3ba3 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 22 Jul 2014 02:59:56 +0200 Subject: [PATCH 176/266] Fixed some errors and warnings on GCC/clang. --- benchmarks/src/BenchmarkRandom.h | 1 + include/react/common/Concurrency.h | 2 +- include/react/detail/ReactiveInput.h | 6 +----- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/benchmarks/src/BenchmarkRandom.h b/benchmarks/src/BenchmarkRandom.h index 743a7363..63cbd996 100644 --- a/benchmarks/src/BenchmarkRandom.h +++ b/benchmarks/src/BenchmarkRandom.h @@ -14,6 +14,7 @@ #include "BenchmarkBase.h" #include "react/common/Types.h" +#include "react/Domain.h" #include "react/Signal.h" using namespace react; diff --git a/include/react/common/Concurrency.h b/include/react/common/Concurrency.h index e312be20..e27fd9ba 100644 --- a/include/react/common/Concurrency.h +++ b/include/react/common/Concurrency.h @@ -86,7 +86,7 @@ class WaitingStateBase : public IWaitingState auto Run(F&& func) -> decltype(func(false)) {// mutex_ std::lock_guard scopedLock(mutex_); - return func(blocked_); + return func(isWaiting_); }// ~mutex_ template diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index fa1c0152..906e52fe 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -692,8 +692,6 @@ class InputManager : void processAsyncQueue() { AsyncItem item; - - TurnFlagsT savedFlags = 0; std::vector waitingStatePtrs; @@ -732,13 +730,11 @@ class InputManager : item.Func(); // Merged sync inputs that arrived while this item was queued - // Commented out, because thats no longer possible - //tr.RunMergedInputs(); + tr.RunMergedInputs(); // Save data, item might be re-used for next input if (item.WaitingStatePtr != nullptr) waitingStatePtrs.push_back(std::move(item.WaitingStatePtr)); - savedFlags = item.Flags; // If the current item supports merging, try to add more mergeable inputs // to this turn From 1f24f0f933be4ca59048b891d323abf05bedc675 Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 24 Jul 2014 23:09:05 +0200 Subject: [PATCH 177/266] -TurnFlags+TransactionFlags --- include/react/Algorithm.h | 80 ++++++++++--------- include/react/Domain.h | 48 ++++++----- include/react/detail/EngineBase.h | 2 +- include/react/detail/ReactiveInput.h | 43 +++++----- .../react/detail/graph/ContinuationNodes.h | 10 +-- include/react/engine/PulsecountEngine.h | 2 +- include/react/engine/SubtreeEngine.h | 2 +- include/react/engine/ToposortEngine.h | 4 +- src/engine/PulsecountEngine.cpp | 2 +- src/engine/SubtreeEngine.cpp | 2 +- src/engine/ToposortEngine.cpp | 4 +- 11 files changed, 106 insertions(+), 93 deletions(-) diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index 3e62ffd8..90af2917 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -39,6 +39,43 @@ enum class Token; template class SignalPack; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Hold - Hold the most recent event in a signal +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename V, + typename T = typename std::decay::type +> +auto Hold(const Events& events, V&& init) + -> Signal +{ + using REACT_IMPL::HoldNode; + + return Signal( + std::make_shared>( + std::forward(init), events.NodePtr())); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Monitor - Emits value changes of target signal +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename S +> +auto Monitor(const Signal& target) + -> Events +{ + using REACT_IMPL::MonitorNode; + + return Events( + std::make_shared>( + target.NodePtr())); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Iterate - Iteratively combines signal value with values from event stream /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -123,25 +160,6 @@ auto Iterate(const Events& events, V&& init, depPack.Data); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Hold - Hold the most recent event in a signal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename V, - typename T = typename std::decay::type -> -auto Hold(const Events& events, V&& init) - -> Signal -{ - using REACT_IMPL::HoldNode; - - return Signal( - std::make_shared>( - std::forward(init), events.NodePtr())); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Snapshot - Sets signal value to value of other signal when event is received /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -161,23 +179,7 @@ auto Snapshot(const Events& trigger, const Signal& target) target.NodePtr(), trigger.NodePtr())); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Monitor - Emits value changes of target signal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -auto Monitor(const Signal& target) - -> Events -{ - using REACT_IMPL::MonitorNode; - return Events( - std::make_shared>( - target.NodePtr())); -} /////////////////////////////////////////////////////////////////////////////////////////////////// /// Pulse - Emits value of target signal when event is received @@ -199,21 +201,21 @@ auto Pulse(const Events& trigger, const Signal& target) } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// OnChanged - Emits token when target signal was changed +/// Changed - Emits token when target signal was changed /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename S > -auto OnChanged(const Signal& target) +auto Changed(const Signal& target) -> Events { return Monitor(target).Tokenize(); } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// OnChangedTo - Emits token when target signal was changed to value +/// ChangedTo - Emits token when target signal was changed to value /////////////////////////////////////////////////////////////////////////////////////////////////// template < @@ -221,7 +223,7 @@ template typename V, typename S = typename std::decay::type > -auto OnChangedTo(const Signal& target, V&& value) +auto ChangedTo(const Signal& target, V&& value) -> Events { return Monitor(target) diff --git a/include/react/Domain.h b/include/react/Domain.h index 2b84d200..54896fe5 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -68,10 +68,10 @@ class SignalPack; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Common types & constants /////////////////////////////////////////////////////////////////////////////////////////////////// -using REACT_IMPL::TurnFlagsT; +using REACT_IMPL::TransactionFlagsT; -// ETurnFlags -using REACT_IMPL::ETurnFlags; +// ETransactionFlags +using REACT_IMPL::ETransactionFlags; using REACT_IMPL::allow_merging; #ifdef REACT_ENABLE_LOGGING @@ -87,9 +87,8 @@ enum EDomainMode parallel_concurrent }; -// Expose enum types so aliases for engines can be declared, but don't +// Expose enum type so aliases for engines can be declared, but don't // expose the actual enum values as they are reserved for internal use. -using REACT_IMPL::EInputMode; using REACT_IMPL::EPropagationMode; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -101,21 +100,26 @@ class TransactionStatus using PtrT = REACT_IMPL::WaitingStatePtrT; public: - TransactionStatus() : + inline TransactionStatus() : statePtr_( StateT::Create() ) {} - TransactionStatus(const TransactionStatus&) = default; + TransactionStatus(const TransactionStatus&) = delete; + TransactionStatus& operator=(const TransactionStatus&) = delete; - TransactionStatus(TransactionStatus&& other) : + inline TransactionStatus(TransactionStatus&& other) : statePtr_( std::move(other.statePtr_) ) - {} - - TransactionStatus& operator=(const TransactionStatus&) = default; + { + other.statePtr_ = StateT::Create(); + } - TransactionStatus& operator=(TransactionStatus&& other) + inline TransactionStatus& operator=(TransactionStatus&& other) { - statePtr_ = std::move(other.statePtr_); + if (this != &other) + { + statePtr_ = std::move(other.statePtr_); + other.statePtr_ = StateT::Create(); + } return *this; } @@ -132,7 +136,7 @@ class TransactionStatus friend void AsyncTransaction(TransactionStatus& status, F&& func); template - friend void AsyncTransaction(TurnFlagsT flags, TransactionStatus& status, F&& func); + friend void AsyncTransaction(TransactionFlagsT flags, TransactionStatus& status, F&& func); }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -242,7 +246,7 @@ template typename S, typename FIn > -auto MakeContinuation(TurnFlagsT flags, const Signal& trigger, FIn&& func) +auto MakeContinuation(TransactionFlagsT flags, const Signal& trigger, FIn&& func) -> Continuation { static_assert(DOut::is_concurrent, @@ -279,7 +283,7 @@ template typename E, typename FIn > -auto MakeContinuation(TurnFlagsT flags, const Events& trigger, FIn&& func) +auto MakeContinuation(TransactionFlagsT flags, const Events& trigger, FIn&& func) -> Continuation { static_assert(DOut::is_concurrent, @@ -317,7 +321,7 @@ template typename FIn, typename ... TDepValues > -auto MakeContinuation(TurnFlagsT flags, const Events& trigger, +auto MakeContinuation(TransactionFlagsT flags, const Events& trigger, const SignalPack& depPack, FIn&& func) -> Continuation { @@ -329,7 +333,7 @@ auto MakeContinuation(TurnFlagsT flags, const Events& trigger, struct NodeBuilder_ { - NodeBuilder_(TurnFlagsT flags, const Events& trigger, FIn&& func) : + NodeBuilder_(TransactionFlagsT flags, const Events& trigger, FIn&& func) : MyFlags( flags ), MyTrigger( trigger ), MyFunc( std::forward(func) ) @@ -345,7 +349,7 @@ auto MakeContinuation(TurnFlagsT flags, const Events& trigger, std::forward(MyFunc), deps.NodePtr() ...)); } - TurnFlagsT MyFlags; + TransactionFlagsT MyFlags; const Events& MyTrigger; FIn MyFunc; }; @@ -381,7 +385,7 @@ void DoTransaction(F&& func) } template -void DoTransaction(TurnFlagsT flags, F&& func) +void DoTransaction(TransactionFlagsT flags, F&& func) { using REACT_IMPL::DomainSpecificInputManager; DomainSpecificInputManager::Instance().DoTransaction(flags, std::forward(func)); @@ -401,7 +405,7 @@ void AsyncTransaction(F&& func) } template -void AsyncTransaction(TurnFlagsT flags, F&& func) +void AsyncTransaction(TransactionFlagsT flags, F&& func) { static_assert(D::is_concurrent, "AsyncTransaction requires concurrent domain."); @@ -422,7 +426,7 @@ void AsyncTransaction(TransactionStatus& status, F&& func) } template -void AsyncTransaction(TurnFlagsT flags, TransactionStatus& status, F&& func) +void AsyncTransaction(TransactionFlagsT flags, TransactionStatus& status, F&& func) { static_assert(D::is_concurrent, "AsyncTransaction requires concurrent domain."); diff --git a/include/react/detail/EngineBase.h b/include/react/detail/EngineBase.h index ca9afad5..7b9af9de 100644 --- a/include/react/detail/EngineBase.h +++ b/include/react/detail/EngineBase.h @@ -34,7 +34,7 @@ class TurnBase { public: - inline TurnBase(TurnIdT id, TurnFlagsT flags) : + inline TurnBase(TurnIdT id, TransactionFlagsT flags) : id_( id ) {} diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index 906e52fe..906e6e0f 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -45,12 +45,12 @@ class IObserver; using TurnIdT = uint; using TransactionFuncT = std::function; -enum ETurnFlags +enum ETransactionFlags { allow_merging = 1 << 0 }; -using TurnFlagsT = std::underlying_type::type; +using TransactionFlagsT = std::underlying_type::type; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EInputMode @@ -66,7 +66,8 @@ enum EInputMode /////////////////////////////////////////////////////////////////////////////////////////////////// struct IContinuationTarget { - virtual void AsyncContinuation(TurnFlagsT, const WaitingStatePtrT&, TransactionFuncT&&) = 0; + virtual void AsyncContinuation(TransactionFlagsT, const WaitingStatePtrT&, + TransactionFuncT&&) = 0; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -89,7 +90,8 @@ template class ContinuationManager { public: - void StoreContinuation(IContinuationTarget& target, TurnFlagsT flags, TransactionFuncT&& cont); + void StoreContinuation(IContinuationTarget& target, TransactionFlagsT flags, + TransactionFuncT&& cont); bool HasContinuations() const; void StartContinuations(const WaitingStatePtrT& waitingStatePtr); @@ -106,14 +108,14 @@ class ContinuationManager { struct Data_ { - Data_(IContinuationTarget* target, TurnFlagsT flags, TransactionFuncT&& func) : + Data_(IContinuationTarget* target, TransactionFlagsT flags, TransactionFuncT&& func) : Target( target ), Flags( flags ), Func( func ) {} IContinuationTarget* Target; - TurnFlagsT Flags; + TransactionFlagsT Flags; TransactionFuncT Func; }; @@ -121,7 +123,8 @@ class ContinuationManager using ObsVectT = std::vector; public: - void StoreContinuation(IContinuationTarget& target, TurnFlagsT flags, TransactionFuncT&& cont) + void StoreContinuation(IContinuationTarget& target, TransactionFlagsT flags, + TransactionFuncT&& cont) { storedContinuations_.emplace_back(&target, flags, std::move(cont)); } @@ -166,14 +169,14 @@ class ContinuationManager { struct Data_ { - Data_(IContinuationTarget* target, TurnFlagsT flags, TransactionFuncT&& func) : + Data_(IContinuationTarget* target, TransactionFlagsT flags, TransactionFuncT&& func) : Target( target ), Flags( flags ), Func( func ) {} IContinuationTarget* Target; - TurnFlagsT Flags; + TransactionFlagsT Flags; TransactionFuncT Func; }; @@ -181,7 +184,8 @@ class ContinuationManager using ObsVectT = std::vector; public: - void StoreContinuation(IContinuationTarget& target, TurnFlagsT flags, TransactionFuncT&& cont) + void StoreContinuation(IContinuationTarget& target, TransactionFlagsT flags, + TransactionFuncT&& cont) { storedContinuations_.local().emplace_back(&target, flags, std::move(cont)); ++contCount_; @@ -238,7 +242,7 @@ class TransactionQueue class QueueEntry { public: - explicit QueueEntry(TurnFlagsT flags); + explicit QueueEntry(TransactionFlagsT flags); void RunMergedInputs(); @@ -268,7 +272,7 @@ class TransactionQueue class QueueEntry { public: - explicit QueueEntry(TurnFlagsT flags) {} + explicit QueueEntry(TransactionFlagsT flags) {} void RunMergedInputs() {} @@ -298,7 +302,7 @@ class TransactionQueue class QueueEntry { public: - explicit QueueEntry(TurnFlagsT flags) : + explicit QueueEntry(TransactionFlagsT flags) : isMergeable_( (flags & allow_merging) != 0 ) {} @@ -487,7 +491,7 @@ class InputManager : private: struct AsyncItem { - TurnFlagsT Flags; + TransactionFlagsT Flags; WaitingStatePtrT WaitingStatePtr; TransactionFuncT Func; }; @@ -511,7 +515,7 @@ class InputManager : } template - void DoTransaction(TurnFlagsT flags, F&& func) + void DoTransaction(TransactionFlagsT flags, F&& func) { // Attempt to add input to another turn. // If successful, blocks until other turn is done and returns. @@ -550,7 +554,8 @@ class InputManager : } template - void AsyncTransaction(TurnFlagsT flags, const WaitingStatePtrT& waitingStatePtr, F&& func) + void AsyncTransaction(TransactionFlagsT flags, const WaitingStatePtrT& waitingStatePtr, + F&& func) { if (waitingStatePtr != nullptr) waitingStatePtr->IncWaitCount(); @@ -585,14 +590,16 @@ class InputManager : } //IContinuationTarget - virtual void AsyncContinuation(TurnFlagsT flags, const WaitingStatePtrT& waitingStatePtr, + virtual void AsyncContinuation(TransactionFlagsT flags, + const WaitingStatePtrT& waitingStatePtr, TransactionFuncT&& cont) override { AsyncTransaction(flags, waitingStatePtr, std::move(cont)); } //~IContinuationTarget - void StoreContinuation(IContinuationTarget& target, TurnFlagsT flags, TransactionFuncT&& cont) + void StoreContinuation(IContinuationTarget& target, TransactionFlagsT flags, + TransactionFuncT&& cont) { continuationManager_.StoreContinuation(target, flags, std::move(cont)); } diff --git a/include/react/detail/graph/ContinuationNodes.h b/include/react/detail/graph/ContinuationNodes.h index 5c9db4cb..85cfa5be 100644 --- a/include/react/detail/graph/ContinuationNodes.h +++ b/include/react/detail/graph/ContinuationNodes.h @@ -38,14 +38,14 @@ class ContinuationNode : public ReactiveNode { public: - ContinuationNode(TurnFlagsT turnFlags) : + ContinuationNode(TransactionFlagsT turnFlags) : turnFlags_( turnFlags ) {} virtual bool IsOutputNode() const { return true; } protected: - TurnFlagsT turnFlags_; + TransactionFlagsT turnFlags_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -64,7 +64,7 @@ class SignalContinuationNode : public ContinuationNode public: template - SignalContinuationNode(TurnFlagsT turnFlags, + SignalContinuationNode(TransactionFlagsT turnFlags, const std::shared_ptr>& trigger, F&& func) : SignalContinuationNode::ContinuationNode( turnFlags ), trigger_( trigger ), @@ -136,7 +136,7 @@ class EventContinuationNode : public ContinuationNode public: template - EventContinuationNode(TurnFlagsT turnFlags, + EventContinuationNode(TransactionFlagsT turnFlags, const std::shared_ptr>& trigger, F&& func) : EventContinuationNode::ContinuationNode( turnFlags ), trigger_( trigger ), @@ -238,7 +238,7 @@ class SyncedContinuationNode : public ContinuationNode public: template - SyncedContinuationNode(TurnFlagsT turnFlags, + SyncedContinuationNode(TransactionFlagsT turnFlags, const std::shared_ptr>& trigger, F&& func, const std::shared_ptr>& ... deps) : SyncedContinuationNode::ContinuationNode( turnFlags ), diff --git a/include/react/engine/PulsecountEngine.h b/include/react/engine/PulsecountEngine.h index 4bf2537b..ed813e54 100644 --- a/include/react/engine/PulsecountEngine.h +++ b/include/react/engine/PulsecountEngine.h @@ -40,7 +40,7 @@ using tbb::task_list; class Turn : public TurnBase { public: - Turn(TurnIdT id, TurnFlagsT flags); + Turn(TurnIdT id, TransactionFlagsT flags); }; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/engine/SubtreeEngine.h b/include/react/engine/SubtreeEngine.h index 97d74af4..9a84d7cb 100644 --- a/include/react/engine/SubtreeEngine.h +++ b/include/react/engine/SubtreeEngine.h @@ -40,7 +40,7 @@ using tbb::spin_rw_mutex; class Turn : public TurnBase { public: - Turn(TurnIdT id, TurnFlagsT flags); + Turn(TurnIdT id, TransactionFlagsT flags); }; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/engine/ToposortEngine.h b/include/react/engine/ToposortEngine.h index dfefde60..9f8f693c 100644 --- a/include/react/engine/ToposortEngine.h +++ b/include/react/engine/ToposortEngine.h @@ -84,7 +84,7 @@ struct DynRequestData class SeqTurn : public TurnBase { public: - SeqTurn(TurnIdT id, TurnFlagsT flags); + SeqTurn(TurnIdT id, TransactionFlagsT flags); }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -93,7 +93,7 @@ class SeqTurn : public TurnBase class ParTurn : public TurnBase { public: - ParTurn(TurnIdT id, TurnFlagsT flags); + ParTurn(TurnIdT id, TransactionFlagsT flags); }; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/engine/PulsecountEngine.cpp b/src/engine/PulsecountEngine.cpp index 291d7a31..fed31bf6 100644 --- a/src/engine/PulsecountEngine.cpp +++ b/src/engine/PulsecountEngine.cpp @@ -179,7 +179,7 @@ class UpdaterTask: public task /////////////////////////////////////////////////////////////////////////////////////////////////// /// Turn /////////////////////////////////////////////////////////////////////////////////////////////////// -Turn::Turn(TurnIdT id, TurnFlagsT flags) : +Turn::Turn(TurnIdT id, TransactionFlagsT flags) : TurnBase( id, flags ) {} diff --git a/src/engine/SubtreeEngine.cpp b/src/engine/SubtreeEngine.cpp index c04f5edf..21d54880 100644 --- a/src/engine/SubtreeEngine.cpp +++ b/src/engine/SubtreeEngine.cpp @@ -23,7 +23,7 @@ static const uint dfs_threshold = 3; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Turn /////////////////////////////////////////////////////////////////////////////////////////////////// -Turn::Turn(TurnIdT id, TurnFlagsT flags) : +Turn::Turn(TurnIdT id, TransactionFlagsT flags) : TurnBase( id, flags ) {} diff --git a/src/engine/ToposortEngine.cpp b/src/engine/ToposortEngine.cpp index ae96728a..3d746452 100644 --- a/src/engine/ToposortEngine.cpp +++ b/src/engine/ToposortEngine.cpp @@ -13,14 +13,14 @@ namespace toposort { /////////////////////////////////////////////////////////////////////////////////////////////////// /// SeqTurn /////////////////////////////////////////////////////////////////////////////////////////////////// -SeqTurn::SeqTurn(TurnIdT id, TurnFlagsT flags) : +SeqTurn::SeqTurn(TurnIdT id, TransactionFlagsT flags) : TurnBase( id, flags ) {} /////////////////////////////////////////////////////////////////////////////////////////////////// /// ParTurn /////////////////////////////////////////////////////////////////////////////////////////////////// -ParTurn::ParTurn(TurnIdT id, TurnFlagsT flags) : +ParTurn::ParTurn(TurnIdT id, TransactionFlagsT flags) : TurnBase( id, flags ) {} From aa5a32bb44f3cda054f9e19f6ee7c55911ba63d7 Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 24 Jul 2014 23:09:20 +0200 Subject: [PATCH 178/266] Updated readme. --- README.md | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 36439dbe..08b1f928 100644 --- a/README.md +++ b/README.md @@ -6,20 +6,20 @@ Generally speaking, it provides abstractions to handle change propagation and da A more practical description is that it enables coordinated, multi-layered - and potentially parallel - execution of callbacks. All this happens implicitly, based on declarative definitions, with guarantees regarding -- update-minimality - nothing is re-calculated or processed unnecessarily; -- glitch-freedom - no transiently inconsistent data sets; -- thread-safety - no data races for parallel execution. +- _update minimality_ - nothing is re-calculated or processed unnecessarily; +- _glitch freedom_ - no transiently inconsistent data sets; +- _thread safety_ - no data races for parallel execution. The core abstractions of the library are -- **signals**, reactive variables that are automatically re-calculated when their dependencies change, and -- **event streams** as composable first class objects. +- _signals_, reactive variables that are automatically re-calculated when their dependencies change, and +- _event streams_ as composable first class objects. Signals specifically deal with aspects of time-varying state, whereas event streams facilitate event processing in general. Additional features include -- a publish/subscribe mechanism for callbacks with side-effects; +- a publish/subscribe mechanism for callbacks with side effects; - a set of operations and algorithms to combine signals and events; - a domain model to encapsulate multiple reactive systems; - transactions to group related events, supporting both synchronous and asynchrounous execution. @@ -27,21 +27,20 @@ Additional features include ## Documentation -If you're interested in learning about C++React, [have a look at its documentation](http://schlangster.github.io/cpp.react/). - -It's still incomplete, but it already contains plenty of useful information and examples. +[If you're interested in learning about C++React, have a look at its documentation.](http://schlangster.github.io/cpp.react/) ## Development This library is a work-in-progress and should not be considered release quality yet. -Don't let this statement stop you from giving it a try though. -It's in a usable state and has already received a fair amount of testing and tuning. +It is, however, in a perfectly usable state and has already received a fair amount of testing and tuning. ### Compiling -The following compilers are supported at the moment: +C++React requires a compiler with support for several C++11 features. + +It has been tested with the following compilers: * Visual Studio 2013.2 * GCC 4.8.2 @@ -57,7 +56,7 @@ cmake .. make ``` -For more details, see [Build instructions](https://github.com/schlangster/cpp.react/wiki/Build-instructions). +For more details, refer to the [Build instructions](https://github.com/schlangster/cpp.react/wiki/Build-instructions). ### Dependencies @@ -72,7 +71,7 @@ For more details, see [Build instructions](https://github.com/schlangster/cpp.re ### Signals Signals are self-updating reactive variables. -They can be combined to expressions to create new signals, which are automatically re-calculated whenever one of their data dependencies changes. +They can be combined as expressions to create new signals, which are automatically re-calculated whenever one of their dependencies changes. ```C++ using namespace std; From 5d9fc6085b013fdc5a3257457cef2f493d85b8c1 Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 25 Jul 2014 00:39:49 +0200 Subject: [PATCH 179/266] Updated readme. --- README.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 08b1f928..1ad5d031 100644 --- a/README.md +++ b/README.md @@ -36,11 +36,16 @@ This library is a work-in-progress and should not be considered release quality It is, however, in a perfectly usable state and has already received a fair amount of testing and tuning. -### Compiling +### Dependencies + +* [Intel TBB 4.2](https://www.threadingbuildingblocks.org/) (required) +* [Google test framework](https://code.google.com/p/googletest/) (optional, to compile the unit tests) +* [Boost 1.55.0 C++ Libraries](http://www.boost.org/) (optional, to include Reactor.h, which requires `boost::coroutine`) -C++React requires a compiler with support for several C++11 features. -It has been tested with the following compilers: +### Compiling + +C++React has been tested with the following compilers: * Visual Studio 2013.2 * GCC 4.8.2 @@ -59,13 +64,6 @@ make For more details, refer to the [Build instructions](https://github.com/schlangster/cpp.react/wiki/Build-instructions). -### Dependencies - -* [Intel TBB 4.2](https://www.threadingbuildingblocks.org/) (required) -* [Google test framework](https://code.google.com/p/googletest/) (optional, to compile the unit tests) -* [Boost 1.55.0 C++ Libraries](http://www.boost.org/) (optional, to include Reactor.h, which requires `boost::coroutine`) - - ## Features by example ### Signals From d5cb1352cf805f6e166830d84146d9fc7a387bd6 Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 25 Jul 2014 11:27:26 +0200 Subject: [PATCH 180/266] Updated readme. --- README.md | 52 ++++++++++++++++++++++------------------------------ 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 1ad5d031..a580cce6 100644 --- a/README.md +++ b/README.md @@ -143,45 +143,37 @@ The change propagation is handled implicitly. Depending on the selected concurrency policy, updates can be parallelized: ```C++ -using namespace react; - - // Sequential updating -{ - REACTIVE_DOMAIN(D, sequential) - - auto a = MakeVar(1); - auto b = MakeVar(2); - auto c = MakeVar(3); +REACTIVE_DOMAIN(D, sequential) - auto x = (a + b) * c; +VarSignalT a = MakeVar(1); +VarSignalT b = MakeVar(2); +VarSignalT c = MakeVar(3); - b <<= 20; -} +// Using overloaded arithmetic operators instead of MakeSignal +SignalT x = (a + b) * c; +``` +```C++ // Parallel updating -{ - REACTIVE_DOMAIN(D, parallel) +REACTIVE_DOMAIN(D, parallel) - auto in = MakeVar(0); +VarSignalT in = MakeVar(0); - auto op1 = MakeSignal(in, [] (int in) - { - int result = in /* Costly operation #1 */; - return result; - }; - - auto op2 = MakeSignal(in, [] (int in) - { - int result = in /* Costly operation #2 */; - return result; - }; +SignalT op1 = MakeSignal(in, [] (int in) +{ + int result = doCostlyOperation1(in); + return result; +}; - auto out = op1 + op2; +SignalT op2 = MakeSignal(in, [] (int in) +{ + int result = doCostlyOperation2(in); + return result; +}; - // op1 and op2 can be re-calculated in parallel - in <<= 123456789; -} +// op1 and op2 can be re-calculated in parallel +SignalT out = op1 + op2; ``` ### More examples From b26f1729de084c5712b4e165e7967c5d734757c0 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 28 Jul 2014 20:02:14 +0200 Subject: [PATCH 181/266] Added ReactPlayer tool. --- tools/ReactPlayer/ReactPlayer.swf | Bin 0 -> 13905 bytes tools/ReactPlayer/readme.txt | 8 ++++++++ 2 files changed, 8 insertions(+) create mode 100644 tools/ReactPlayer/ReactPlayer.swf create mode 100644 tools/ReactPlayer/readme.txt diff --git a/tools/ReactPlayer/ReactPlayer.swf b/tools/ReactPlayer/ReactPlayer.swf new file mode 100644 index 0000000000000000000000000000000000000000..ee457a7c92999db5bab5c1804898901227834e32 GIT binary patch literal 13905 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 RqIW2BVhCTz(3;UA{|CuOHLm~w literal 0 HcmV?d00001 diff --git a/tools/ReactPlayer/readme.txt b/tools/ReactPlayer/readme.txt new file mode 100644 index 00000000..6d22437d --- /dev/null +++ b/tools/ReactPlayer/readme.txt @@ -0,0 +1,8 @@ +load LOGNAME : Loads event log from LOGNAME.txt. +play [PROFILE] : Starts event playback. Profiles: normal (default), verbose, fast, faster, forward +pause : Pauses event playback. +step : Steps to the next event. +reload : Reloads the current event log. +debugmode STATE : Enable/disable debug mode. States: 0, 1 +showthreads TURN : Show thread markers for given turn. +clearthreads : Clear thread markers. \ No newline at end of file From 1bf14350c7fe3b328f9c6aa58487e9093795f30c Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 29 Jul 2014 00:18:26 +0200 Subject: [PATCH 182/266] Added x64 platform to MVSC solution. --- project/msvc/CppReact.sln | 46 +++++++++++ project/msvc/CppReact.vcxproj | 72 ++++++++++++++++++ project/msvc/CppReactBenchmark.vcxproj | 65 ++++++++++++++++ project/msvc/CppReactExample.vcxproj | 68 +++++++++++++++++ project/msvc/CppReactTest.vcxproj | 76 +++++++++++++++++++ project/msvc/Example_BasicAlgorithms.vcxproj | 62 +++++++++++++++ project/msvc/Example_BasicComposition.vcxproj | 62 +++++++++++++++ project/msvc/Example_BasicEvents.vcxproj | 62 +++++++++++++++ project/msvc/Example_BasicObservers.vcxproj | 62 +++++++++++++++ project/msvc/Example_BasicReactors.vcxproj | 62 +++++++++++++++ project/msvc/Example_BasicSignals.vcxproj | 62 +++++++++++++++ .../msvc/Example_BasicSynchronization.vcxproj | 62 +++++++++++++++ 12 files changed, 761 insertions(+) diff --git a/project/msvc/CppReact.sln b/project/msvc/CppReact.sln index 08c58e9d..b414852d 100644 --- a/project/msvc/CppReact.sln +++ b/project/msvc/CppReact.sln @@ -36,53 +36,99 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {5E56AAB9-4E33-4B9E-A315-E85CEDB75CF1}.Debug|Win32.ActiveCfg = Debug|Win32 {5E56AAB9-4E33-4B9E-A315-E85CEDB75CF1}.Debug|Win32.Build.0 = Debug|Win32 + {5E56AAB9-4E33-4B9E-A315-E85CEDB75CF1}.Debug|x64.ActiveCfg = Debug|x64 + {5E56AAB9-4E33-4B9E-A315-E85CEDB75CF1}.Debug|x64.Build.0 = Debug|x64 {5E56AAB9-4E33-4B9E-A315-E85CEDB75CF1}.Release|Win32.ActiveCfg = Release|Win32 {5E56AAB9-4E33-4B9E-A315-E85CEDB75CF1}.Release|Win32.Build.0 = Release|Win32 + {5E56AAB9-4E33-4B9E-A315-E85CEDB75CF1}.Release|x64.ActiveCfg = Release|x64 + {5E56AAB9-4E33-4B9E-A315-E85CEDB75CF1}.Release|x64.Build.0 = Release|x64 {F9115FB9-61DD-4B3C-BCE8-7D26372B05F7}.Debug|Win32.ActiveCfg = Debug|Win32 {F9115FB9-61DD-4B3C-BCE8-7D26372B05F7}.Debug|Win32.Build.0 = Debug|Win32 + {F9115FB9-61DD-4B3C-BCE8-7D26372B05F7}.Debug|x64.ActiveCfg = Debug|x64 + {F9115FB9-61DD-4B3C-BCE8-7D26372B05F7}.Debug|x64.Build.0 = Debug|x64 {F9115FB9-61DD-4B3C-BCE8-7D26372B05F7}.Release|Win32.ActiveCfg = Release|Win32 {F9115FB9-61DD-4B3C-BCE8-7D26372B05F7}.Release|Win32.Build.0 = Release|Win32 + {F9115FB9-61DD-4B3C-BCE8-7D26372B05F7}.Release|x64.ActiveCfg = Release|x64 + {F9115FB9-61DD-4B3C-BCE8-7D26372B05F7}.Release|x64.Build.0 = Release|x64 {52A9EC67-C6A7-453B-AD65-F027CA07AF44}.Debug|Win32.ActiveCfg = Debug|Win32 {52A9EC67-C6A7-453B-AD65-F027CA07AF44}.Debug|Win32.Build.0 = Debug|Win32 + {52A9EC67-C6A7-453B-AD65-F027CA07AF44}.Debug|x64.ActiveCfg = Debug|x64 + {52A9EC67-C6A7-453B-AD65-F027CA07AF44}.Debug|x64.Build.0 = Debug|x64 {52A9EC67-C6A7-453B-AD65-F027CA07AF44}.Release|Win32.ActiveCfg = Release|Win32 {52A9EC67-C6A7-453B-AD65-F027CA07AF44}.Release|Win32.Build.0 = Release|Win32 + {52A9EC67-C6A7-453B-AD65-F027CA07AF44}.Release|x64.ActiveCfg = Release|x64 + {52A9EC67-C6A7-453B-AD65-F027CA07AF44}.Release|x64.Build.0 = Release|x64 {CC0CD982-AE7D-4797-A122-58E6BBE70DDB}.Debug|Win32.ActiveCfg = Debug|Win32 {CC0CD982-AE7D-4797-A122-58E6BBE70DDB}.Debug|Win32.Build.0 = Debug|Win32 + {CC0CD982-AE7D-4797-A122-58E6BBE70DDB}.Debug|x64.ActiveCfg = Debug|x64 + {CC0CD982-AE7D-4797-A122-58E6BBE70DDB}.Debug|x64.Build.0 = Debug|x64 {CC0CD982-AE7D-4797-A122-58E6BBE70DDB}.Release|Win32.ActiveCfg = Release|Win32 {CC0CD982-AE7D-4797-A122-58E6BBE70DDB}.Release|Win32.Build.0 = Release|Win32 + {CC0CD982-AE7D-4797-A122-58E6BBE70DDB}.Release|x64.ActiveCfg = Release|x64 + {CC0CD982-AE7D-4797-A122-58E6BBE70DDB}.Release|x64.Build.0 = Release|x64 {617019A2-97BE-4A60-8EC4-3547D8C54533}.Debug|Win32.ActiveCfg = Debug|Win32 {617019A2-97BE-4A60-8EC4-3547D8C54533}.Debug|Win32.Build.0 = Debug|Win32 + {617019A2-97BE-4A60-8EC4-3547D8C54533}.Debug|x64.ActiveCfg = Debug|x64 + {617019A2-97BE-4A60-8EC4-3547D8C54533}.Debug|x64.Build.0 = Debug|x64 {617019A2-97BE-4A60-8EC4-3547D8C54533}.Release|Win32.ActiveCfg = Release|Win32 {617019A2-97BE-4A60-8EC4-3547D8C54533}.Release|Win32.Build.0 = Release|Win32 + {617019A2-97BE-4A60-8EC4-3547D8C54533}.Release|x64.ActiveCfg = Release|x64 + {617019A2-97BE-4A60-8EC4-3547D8C54533}.Release|x64.Build.0 = Release|x64 {D7B70D3B-F14D-4A85-B164-EAB88C358E85}.Debug|Win32.ActiveCfg = Debug|Win32 {D7B70D3B-F14D-4A85-B164-EAB88C358E85}.Debug|Win32.Build.0 = Debug|Win32 + {D7B70D3B-F14D-4A85-B164-EAB88C358E85}.Debug|x64.ActiveCfg = Debug|x64 + {D7B70D3B-F14D-4A85-B164-EAB88C358E85}.Debug|x64.Build.0 = Debug|x64 {D7B70D3B-F14D-4A85-B164-EAB88C358E85}.Release|Win32.ActiveCfg = Release|Win32 {D7B70D3B-F14D-4A85-B164-EAB88C358E85}.Release|Win32.Build.0 = Release|Win32 + {D7B70D3B-F14D-4A85-B164-EAB88C358E85}.Release|x64.ActiveCfg = Release|x64 + {D7B70D3B-F14D-4A85-B164-EAB88C358E85}.Release|x64.Build.0 = Release|x64 {BD777649-97F1-4810-BF21-CB27F7672BF4}.Debug|Win32.ActiveCfg = Debug|Win32 {BD777649-97F1-4810-BF21-CB27F7672BF4}.Debug|Win32.Build.0 = Debug|Win32 + {BD777649-97F1-4810-BF21-CB27F7672BF4}.Debug|x64.ActiveCfg = Debug|x64 + {BD777649-97F1-4810-BF21-CB27F7672BF4}.Debug|x64.Build.0 = Debug|x64 {BD777649-97F1-4810-BF21-CB27F7672BF4}.Release|Win32.ActiveCfg = Release|Win32 {BD777649-97F1-4810-BF21-CB27F7672BF4}.Release|Win32.Build.0 = Release|Win32 + {BD777649-97F1-4810-BF21-CB27F7672BF4}.Release|x64.ActiveCfg = Release|x64 + {BD777649-97F1-4810-BF21-CB27F7672BF4}.Release|x64.Build.0 = Release|x64 {CC66BFA0-D609-46E0-9FD1-F9CC902410B1}.Debug|Win32.ActiveCfg = Debug|Win32 {CC66BFA0-D609-46E0-9FD1-F9CC902410B1}.Debug|Win32.Build.0 = Debug|Win32 + {CC66BFA0-D609-46E0-9FD1-F9CC902410B1}.Debug|x64.ActiveCfg = Debug|x64 + {CC66BFA0-D609-46E0-9FD1-F9CC902410B1}.Debug|x64.Build.0 = Debug|x64 {CC66BFA0-D609-46E0-9FD1-F9CC902410B1}.Release|Win32.ActiveCfg = Release|Win32 {CC66BFA0-D609-46E0-9FD1-F9CC902410B1}.Release|Win32.Build.0 = Release|Win32 + {CC66BFA0-D609-46E0-9FD1-F9CC902410B1}.Release|x64.ActiveCfg = Release|x64 + {CC66BFA0-D609-46E0-9FD1-F9CC902410B1}.Release|x64.Build.0 = Release|x64 {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Debug|Win32.ActiveCfg = Debug|Win32 {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Debug|Win32.Build.0 = Debug|Win32 + {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Debug|x64.ActiveCfg = Debug|x64 + {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Debug|x64.Build.0 = Debug|x64 {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Release|Win32.ActiveCfg = Release|Win32 {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Release|Win32.Build.0 = Release|Win32 + {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Release|x64.ActiveCfg = Release|x64 + {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Release|x64.Build.0 = Release|x64 {230C9137-CCD0-47E2-8F1F-2E1DD19984A1}.Debug|Win32.ActiveCfg = Debug|Win32 {230C9137-CCD0-47E2-8F1F-2E1DD19984A1}.Debug|Win32.Build.0 = Debug|Win32 + {230C9137-CCD0-47E2-8F1F-2E1DD19984A1}.Debug|x64.ActiveCfg = Debug|x64 + {230C9137-CCD0-47E2-8F1F-2E1DD19984A1}.Debug|x64.Build.0 = Debug|x64 {230C9137-CCD0-47E2-8F1F-2E1DD19984A1}.Release|Win32.ActiveCfg = Release|Win32 {230C9137-CCD0-47E2-8F1F-2E1DD19984A1}.Release|Win32.Build.0 = Release|Win32 + {230C9137-CCD0-47E2-8F1F-2E1DD19984A1}.Release|x64.ActiveCfg = Release|x64 + {230C9137-CCD0-47E2-8F1F-2E1DD19984A1}.Release|x64.Build.0 = Release|x64 {269329F8-A9E1-41AC-9C37-3A82A082A62C}.Debug|Win32.ActiveCfg = Debug|Win32 {269329F8-A9E1-41AC-9C37-3A82A082A62C}.Debug|Win32.Build.0 = Debug|Win32 + {269329F8-A9E1-41AC-9C37-3A82A082A62C}.Debug|x64.ActiveCfg = Debug|x64 + {269329F8-A9E1-41AC-9C37-3A82A082A62C}.Debug|x64.Build.0 = Debug|x64 {269329F8-A9E1-41AC-9C37-3A82A082A62C}.Release|Win32.ActiveCfg = Release|Win32 {269329F8-A9E1-41AC-9C37-3A82A082A62C}.Release|Win32.Build.0 = Release|Win32 + {269329F8-A9E1-41AC-9C37-3A82A082A62C}.Release|x64.ActiveCfg = Release|x64 + {269329F8-A9E1-41AC-9C37-3A82A082A62C}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index d62e935b..16ae1f97 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -5,10 +5,18 @@ Debug Win32 + + Debug + x64 + Release Win32 + + Release + x64 + {5E56AAB9-4E33-4B9E-A315-E85CEDB75CF1} @@ -21,6 +29,12 @@ v120 MultiByte + + StaticLibrary + true + v120 + MultiByte + StaticLibrary false @@ -28,24 +42,45 @@ true MultiByte + + StaticLibrary + false + v120 + true + MultiByte + + + + + + + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + Level3 @@ -62,6 +97,22 @@ $(OutDir)$(TargetName)$(TargetExt) + + + Level3 + Disabled + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + true + %(PreprocessorDefinitions) + + + true + + + $(OutDir)$(TargetName)$(TargetExt) + + Level3 @@ -82,9 +133,30 @@ $(OutDir)$(TargetName)$(TargetExt) + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + true + %(PreprocessorDefinitions) + + + true + true + true + + + $(OutDir)$(TargetName)$(TargetExt) + + + diff --git a/project/msvc/CppReactBenchmark.vcxproj b/project/msvc/CppReactBenchmark.vcxproj index 348829f8..7613239a 100644 --- a/project/msvc/CppReactBenchmark.vcxproj +++ b/project/msvc/CppReactBenchmark.vcxproj @@ -5,10 +5,18 @@ Debug Win32 + + Debug + x64 + Release Win32 + + Release + x64 + {F9115FB9-61DD-4B3C-BCE8-7D26372B05F7} @@ -21,6 +29,12 @@ v120 MultiByte + + Application + true + v120 + MultiByte + Application false @@ -28,24 +42,42 @@ true MultiByte + + Application + false + v120 + true + MultiByte + + + + + + + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + Level3 @@ -60,6 +92,20 @@ Console + + + Level3 + Disabled + true + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;_VARIADIC_MAX=10;%(PreprocessorDefinitions) + + + true + Console + + Level3 @@ -79,6 +125,25 @@ false + + + Level3 + MaxSpeed + true + true + true + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;_VARIADIC_MAX=10;%(PreprocessorDefinitions) + + + true + true + true + Console + false + + {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} diff --git a/project/msvc/CppReactExample.vcxproj b/project/msvc/CppReactExample.vcxproj index b0277b9b..323c777f 100644 --- a/project/msvc/CppReactExample.vcxproj +++ b/project/msvc/CppReactExample.vcxproj @@ -5,10 +5,18 @@ Debug Win32 + + Debug + x64 + Release Win32 + + Release + x64 + {CC0CD982-AE7D-4797-A122-58E6BBE70DDB} @@ -22,6 +30,12 @@ v120 MultiByte + + Application + true + v120 + MultiByte + Application false @@ -29,24 +43,45 @@ true MultiByte + + Application + false + v120 + true + MultiByte + + + + + + + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + Level3 @@ -61,6 +96,20 @@ Console + + + Level3 + Disabled + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + true + _VARIADIC_MAX=10;%(PreprocessorDefinitions) + + + true + Console + + Level3 @@ -80,6 +129,25 @@ false + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + true + _VARIADIC_MAX=10;%(PreprocessorDefinitions) + + + true + true + true + Console + false + + {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} diff --git a/project/msvc/CppReactTest.vcxproj b/project/msvc/CppReactTest.vcxproj index 68e94238..ff65e1d5 100644 --- a/project/msvc/CppReactTest.vcxproj +++ b/project/msvc/CppReactTest.vcxproj @@ -5,10 +5,18 @@ Debug Win32 + + Debug + x64 + Release Win32 + + Release + x64 + {52A9EC67-C6A7-453B-AD65-F027CA07AF44} @@ -21,6 +29,12 @@ v120 MultiByte + + Application + true + v120 + MultiByte + Application false @@ -28,24 +42,45 @@ true MultiByte + + Application + false + v120 + true + MultiByte + + + + + + + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + Level3 @@ -63,6 +98,24 @@ Console + + + Level3 + Disabled + true + $(SolutionDir)..\..\include;$(GTestDir)\include;%(AdditionalIncludeDirectories) + _VARIADIC_MAX=10;%(PreprocessorDefinitions) + true + 4503;%(DisableSpecificWarnings) + /bigobj %(AdditionalOptions) + + + true + $(GTestDir)\msvc\gtest-md\Debug;%(AdditionalLibraryDirectories) + gtestd.lib;gtest_main-mdd.lib;%(AdditionalDependencies) + Console + + Level3 @@ -85,6 +138,29 @@ false + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)..\..\include;$(GTestDir)\include;%(AdditionalIncludeDirectories) + _VARIADIC_MAX=10;%(PreprocessorDefinitions) + true + 4503;%(DisableSpecificWarnings) + /bigobj %(AdditionalOptions) + + + true + true + true + $(GTestDir)\msvc\gtest-md\Release;%(AdditionalLibraryDirectories) + gtest.lib;gtest_main-md.lib;%(AdditionalDependencies) + Console + false + + {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} diff --git a/project/msvc/Example_BasicAlgorithms.vcxproj b/project/msvc/Example_BasicAlgorithms.vcxproj index a53d10f8..cd4b6e9b 100644 --- a/project/msvc/Example_BasicAlgorithms.vcxproj +++ b/project/msvc/Example_BasicAlgorithms.vcxproj @@ -5,10 +5,18 @@ Debug Win32 + + Debug + x64 + Release Win32 + + Release + x64 + {617019A2-97BE-4A60-8EC4-3547D8C54533} @@ -21,6 +29,12 @@ v120 MultiByte + + Application + true + v120 + MultiByte + Application false @@ -28,24 +42,45 @@ true MultiByte + + Application + false + v120 + true + MultiByte + + + + + + + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + Level3 @@ -58,6 +93,18 @@ Console + + + Level3 + Disabled + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + Console + + Level3 @@ -73,6 +120,21 @@ true + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + true + true + + {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} diff --git a/project/msvc/Example_BasicComposition.vcxproj b/project/msvc/Example_BasicComposition.vcxproj index ba21df79..8b336087 100644 --- a/project/msvc/Example_BasicComposition.vcxproj +++ b/project/msvc/Example_BasicComposition.vcxproj @@ -5,10 +5,18 @@ Debug Win32 + + Debug + x64 + Release Win32 + + Release + x64 + {D7B70D3B-F14D-4A85-B164-EAB88C358E85} @@ -21,6 +29,12 @@ v120 MultiByte + + Application + true + v120 + MultiByte + Application false @@ -28,24 +42,45 @@ true MultiByte + + Application + false + v120 + true + MultiByte + + + + + + + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + Level3 @@ -58,6 +93,18 @@ Console + + + Level3 + Disabled + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + Console + + Level3 @@ -73,6 +120,21 @@ true + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + true + true + + {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} diff --git a/project/msvc/Example_BasicEvents.vcxproj b/project/msvc/Example_BasicEvents.vcxproj index 2de69525..2e3bad5e 100644 --- a/project/msvc/Example_BasicEvents.vcxproj +++ b/project/msvc/Example_BasicEvents.vcxproj @@ -5,10 +5,18 @@ Debug Win32 + + Debug + x64 + Release Win32 + + Release + x64 + {BD777649-97F1-4810-BF21-CB27F7672BF4} @@ -21,6 +29,12 @@ v120 MultiByte + + Application + true + v120 + MultiByte + Application false @@ -28,24 +42,45 @@ true MultiByte + + Application + false + v120 + true + MultiByte + + + + + + + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + Level3 @@ -58,6 +93,18 @@ Console + + + Level3 + Disabled + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + Console + + Level3 @@ -73,6 +120,21 @@ true + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + true + true + + {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} diff --git a/project/msvc/Example_BasicObservers.vcxproj b/project/msvc/Example_BasicObservers.vcxproj index 4eddc801..f99889bf 100644 --- a/project/msvc/Example_BasicObservers.vcxproj +++ b/project/msvc/Example_BasicObservers.vcxproj @@ -5,10 +5,18 @@ Debug Win32 + + Debug + x64 + Release Win32 + + Release + x64 + {CC66BFA0-D609-46E0-9FD1-F9CC902410B1} @@ -21,6 +29,12 @@ v120 MultiByte + + Application + true + v120 + MultiByte + Application false @@ -28,24 +42,45 @@ true MultiByte + + Application + false + v120 + true + MultiByte + + + + + + + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + Level3 @@ -58,6 +93,18 @@ Console + + + Level3 + Disabled + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + Console + + Level3 @@ -73,6 +120,21 @@ true + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + true + true + + diff --git a/project/msvc/Example_BasicReactors.vcxproj b/project/msvc/Example_BasicReactors.vcxproj index 92ab34cd..f05f8265 100644 --- a/project/msvc/Example_BasicReactors.vcxproj +++ b/project/msvc/Example_BasicReactors.vcxproj @@ -5,10 +5,18 @@ Debug Win32 + + Debug + x64 + Release Win32 + + Release + x64 + {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96} @@ -21,6 +29,12 @@ v120 MultiByte + + Application + true + v120 + MultiByte + Application false @@ -28,24 +42,45 @@ true MultiByte + + Application + false + v120 + true + MultiByte + + + + + + + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + Level3 @@ -58,6 +93,18 @@ Console + + + Level3 + Disabled + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + Console + + Level3 @@ -73,6 +120,21 @@ true + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + true + true + + diff --git a/project/msvc/Example_BasicSignals.vcxproj b/project/msvc/Example_BasicSignals.vcxproj index 495b1813..653861ba 100644 --- a/project/msvc/Example_BasicSignals.vcxproj +++ b/project/msvc/Example_BasicSignals.vcxproj @@ -5,10 +5,18 @@ Debug Win32 + + Debug + x64 + Release Win32 + + Release + x64 + {230C9137-CCD0-47E2-8F1F-2E1DD19984A1} @@ -21,6 +29,12 @@ v120 MultiByte + + Application + true + v120 + MultiByte + Application false @@ -28,24 +42,45 @@ true MultiByte + + Application + false + v120 + true + MultiByte + + + + + + + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + Level3 @@ -58,6 +93,18 @@ Console + + + Level3 + Disabled + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + Console + + Level3 @@ -73,6 +120,21 @@ true + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + true + true + + {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} diff --git a/project/msvc/Example_BasicSynchronization.vcxproj b/project/msvc/Example_BasicSynchronization.vcxproj index 5369559a..919643b3 100644 --- a/project/msvc/Example_BasicSynchronization.vcxproj +++ b/project/msvc/Example_BasicSynchronization.vcxproj @@ -5,10 +5,18 @@ Debug Win32 + + Debug + x64 + Release Win32 + + Release + x64 + {269329F8-A9E1-41AC-9C37-3A82A082A62C} @@ -21,6 +29,12 @@ v120 MultiByte + + Application + true + v120 + MultiByte + Application false @@ -28,24 +42,45 @@ true MultiByte + + Application + false + v120 + true + MultiByte + + + + + + + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + $(SolutionDir)..\..\build\$(Configuration)\ $(OutDir)$(ProjectName)\ + + $(SolutionDir)..\..\build\$(Configuration)\ + $(OutDir)$(ProjectName)\ + Level3 @@ -58,6 +93,18 @@ Console + + + Level3 + Disabled + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + Console + + Level3 @@ -73,6 +120,21 @@ true + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)..\..\include;%(AdditionalIncludeDirectories) + + + true + true + true + + From 2b35b924dded93b7e640968fc3568fbb4f4cfa0f Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 29 Jul 2014 00:30:33 +0200 Subject: [PATCH 183/266] Fixed #1. --- benchmarks/src/BenchmarkGrid.h | 6 +++--- benchmarks/src/BenchmarkLifeSim.h | 2 +- src/engine/PulsecountEngine.cpp | 11 +++++++---- src/engine/SubtreeEngine.cpp | 5 ++++- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/benchmarks/src/BenchmarkGrid.h b/benchmarks/src/BenchmarkGrid.h index f1e6af3c..551f3db8 100644 --- a/benchmarks/src/BenchmarkGrid.h +++ b/benchmarks/src/BenchmarkGrid.h @@ -35,7 +35,7 @@ class GridGraphGenerator using Func2T = std::function; using SignalVectT = std::vector; - using WidthVectT = std::vector; + using WidthVectT = std::vector; SignalVectT InputSignals; SignalVectT OutputSignals; @@ -56,9 +56,9 @@ class GridGraphGenerator SignalVectT* curBuf = &buf1; SignalVectT* nextBuf = &buf2; - int curWidth = InputSignals.size(); + size_t curWidth = InputSignals.size(); - int nodeCount = 1; + size_t nodeCount = 1; nodeCount += curWidth; for (auto targetWidth : Widths) diff --git a/benchmarks/src/BenchmarkLifeSim.h b/benchmarks/src/BenchmarkLifeSim.h index 26e5760d..b53856dd 100644 --- a/benchmarks/src/BenchmarkLifeSim.h +++ b/benchmarks/src/BenchmarkLifeSim.h @@ -288,7 +288,7 @@ struct Benchmark_LifeSim : public BenchmarkBase vector>> animals; std::mt19937 gen( 2015 ); - std::uniform_int_distribution dist( 0u, theWorld.Regions.size()-1 ); + std::uniform_int_distribution dist( 0u, theWorld.Regions.size()-1 ); for (int i=0; i void spawnTasks ( task& rootTask, task_list& spawnList, - const uint count, TIt start, TIt end, + const size_t count, TIt start, TIt end, TArgs& ... args ) { - rootTask.set_ref_count(1 + count); + assert(1 + count <= + static_cast((std::numeric_limits::max)())); - for (uint i=0; i < (count - 1); i++) + rootTask.set_ref_count(1 + static_cast(count)); + + for (size_t i=0; i < (count - 1); i++) { spawnList.push_back(*new(rootTask.allocate_child()) TTask(args ..., start, start + chunk_size)); @@ -230,7 +233,7 @@ void spawnTasks void EngineBase::Propagate(Turn& turn) { - const uint initialTaskCount = (changedInputs_.size() - 1) / chunk_size + 1; + const size_t initialTaskCount = (changedInputs_.size() - 1) / chunk_size + 1; spawnTasks(rootTask_, spawnList_, initialTaskCount, changedInputs_.begin(), changedInputs_.end()); diff --git a/src/engine/SubtreeEngine.cpp b/src/engine/SubtreeEngine.cpp index 21d54880..9189c697 100644 --- a/src/engine/SubtreeEngine.cpp +++ b/src/engine/SubtreeEngine.cpp @@ -178,7 +178,10 @@ void EngineBase::Propagate(Turn& turn) // Phase 2 isInPhase2_ = true; - rootTask_.set_ref_count(1 + subtreeRoots_.size()); + assert((1 + subtreeRoots_.size()) <= + static_cast((std::numeric_limits::max)())); + + rootTask_.set_ref_count(1 + static_cast(subtreeRoots_.size())); for (auto* node : subtreeRoots_) { From aeb7c84f4baff04fab0992ece831df525a1d60c8 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 29 Jul 2014 00:33:42 +0200 Subject: [PATCH 184/266] Added missing reactive types to TypeTraits.h. --- include/react/TypeTraits.h | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/include/react/TypeTraits.h b/include/react/TypeTraits.h index 3b14e7a7..7141ac14 100644 --- a/include/react/TypeTraits.h +++ b/include/react/TypeTraits.h @@ -34,6 +34,15 @@ class EventSource; template class TempEvents; +template +class Observer; + +template +class ScopedObserver; + +template +class Continuation; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// IsSignal /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -64,6 +73,27 @@ struct IsEvent> { static const bool value = true; }; template struct IsEvent> { static const bool value = true; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// IsObserver +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct IsObserver { static const bool value = false; }; + +template +struct IsObserver> { static const bool value = true; }; + +template +struct IsObserver> { static const bool value = true; }; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// IsContinuation +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct IsContinuation { static const bool value = false; }; + +template +struct IsContinuation> { static const bool value = true; }; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// IsReactive /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -88,6 +118,15 @@ struct IsReactive> { static const bool value = true; }; template struct IsReactive> { static const bool value = true; }; +template +struct IsReactive> { static const bool value = true; }; + +template +struct IsReactive> { static const bool value = true; }; + +template +struct IsReactive> { static const bool value = true; }; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// RemoveInput /////////////////////////////////////////////////////////////////////////////////////////////////// From 3ca6720022559c08c5ce4601e4ef83284481ff3b Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 29 Jul 2014 00:41:41 +0200 Subject: [PATCH 185/266] Added SetWeightHint and did some refactoring in the process: * SetWeightHint is added as a member function to all reactive types. * Moved NodeUpdateTimer to NodeBase. * Got rid of domain specific registry. Instead, unique_ptrs to observers are stored per subject. * Moved internals from Domain.h to DomainBase.h. * Added common base classes for reactive types. * Added some missing constructors & assignment operators & IsValid(). * Removed NodePtr() from public interface. Replaced by internal friend function GetNodePtr(). --- include/react/Algorithm.h | 14 +- include/react/Domain.h | 237 ++-------------- include/react/Event.h | 158 +++++++---- include/react/Observer.h | 131 +++++---- include/react/Reactor.h | 6 +- include/react/Signal.h | 171 +++++++---- include/react/common/Timing.h | 20 +- include/react/detail/DomainBase.h | 265 ++++++++++++++++++ include/react/detail/EventBase.h | 6 +- include/react/detail/ObserverBase.h | 122 ++------ include/react/detail/ReactiveBase.h | 104 +++++-- include/react/detail/ReactiveInput.h | 8 +- include/react/detail/SignalBase.h | 6 +- include/react/detail/graph/AlgorithmNodes.h | 32 +-- .../react/detail/graph/ContinuationNodes.h | 3 +- include/react/detail/graph/EventNodes.h | 23 +- include/react/detail/graph/GraphBase.h | 58 +++- include/react/detail/graph/ObserverNodes.h | 39 +-- include/react/detail/graph/ReactorNodes.h | 4 +- include/react/detail/graph/SignalNodes.h | 14 +- project/msvc/CppReact.vcxproj.filters | 3 + 21 files changed, 819 insertions(+), 605 deletions(-) create mode 100644 include/react/detail/DomainBase.h diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index 90af2917..2f0e7730 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -55,7 +55,7 @@ auto Hold(const Events& events, V&& init) return Signal( std::make_shared>( - std::forward(init), events.NodePtr())); + std::forward(init), GetNodePtr(events))); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -73,7 +73,7 @@ auto Monitor(const Signal& target) return Events( std::make_shared>( - target.NodePtr())); + GetNodePtr(target))); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -103,7 +103,7 @@ auto Iterate(const Events& events, V&& init, FIn&& func) return Signal( std::make_shared( - std::forward(init), events.NodePtr(), std::forward(func))); + std::forward(init), GetNodePtr(events), std::forward(func))); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -146,8 +146,8 @@ auto Iterate(const Events& events, V&& init, { return Signal( std::make_shared( - std::forward(MyInit), MySource.NodePtr(), - std::forward(MyFunc), deps.NodePtr() ...)); + std::forward(MyInit), GetNodePtr(MySource), + std::forward(MyFunc), GetNodePtr(deps) ...)); } const Events& MySource; @@ -176,7 +176,7 @@ auto Snapshot(const Events& trigger, const Signal& target) return Signal( std::make_shared>( - target.NodePtr(), trigger.NodePtr())); + GetNodePtr(target), GetNodePtr(trigger))); } @@ -197,7 +197,7 @@ auto Pulse(const Events& trigger, const Signal& target) return Events( std::make_shared>( - target.NodePtr(), trigger.NodePtr())); + GetNodePtr(target), GetNodePtr(trigger))); } /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/Domain.h b/include/react/Domain.h index 54896fe5..548560a2 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -14,8 +14,9 @@ #include #include -#include "react/detail/IReactiveEngine.h" +#include "react/detail/DomainBase.h" #include "react/detail/ReactiveInput.h" + #include "react/detail/graph/ContinuationNodes.h" #ifdef REACT_ENABLE_LOGGING @@ -23,11 +24,6 @@ #include "react/logging/EventRecords.h" #endif //REACT_ENABLE_LOGGING -// Include all engines for convenience -#include "react/engine/PulsecountEngine.h" -#include "react/engine/SubtreeEngine.h" -#include "react/engine/ToposortEngine.h" - /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -79,18 +75,18 @@ using REACT_IMPL::allow_merging; #endif //REACT_ENABLE_LOGGING // Domain modes -enum EDomainMode -{ - sequential, - sequential_concurrent, - parallel, - parallel_concurrent -}; +using REACT_IMPL::EDomainMode; +using REACT_IMPL::sequential; +using REACT_IMPL::sequential_concurrent; +using REACT_IMPL::parallel; +using REACT_IMPL::parallel_concurrent; // Expose enum type so aliases for engines can be declared, but don't // expose the actual enum values as they are reserved for internal use. using REACT_IMPL::EPropagationMode; +using REACT_IMPL::WeightHint; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// TransactionStatus /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -139,101 +135,38 @@ class TransactionStatus friend void AsyncTransaction(TransactionFlagsT flags, TransactionStatus& status, F&& func); }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DomainBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class DomainBase -{ -public: - using TurnT = typename TPolicy::Engine::TurnT; - - DomainBase() = delete; - - using Policy = TPolicy; - using Engine = REACT_IMPL::EngineInterface; - - /////////////////////////////////////////////////////////////////////////////////////////////// - /// Domain traits - /////////////////////////////////////////////////////////////////////////////////////////////// - static const bool uses_node_update_timer = - REACT_IMPL::NodeUpdateTimerEnabled::value; - - static const bool is_concurrent = - Policy::input_mode == REACT_IMPL::concurrent_input; - - static const bool is_parallel = - Policy::propagation_mode == REACT_IMPL::parallel_propagation; - - /////////////////////////////////////////////////////////////////////////////////////////////// - /// Aliases for reactives of this domain - /////////////////////////////////////////////////////////////////////////////////////////////// - template - using SignalT = Signal; - - template - using VarSignalT = VarSignal; - - template - using EventsT = Events; - - template - using EventSourceT = EventSource; - - using ObserverT = Observer; - - using ScopedObserverT = ScopedObserver; - - using ReactorT = Reactor; - -#ifdef REACT_ENABLE_LOGGING - /////////////////////////////////////////////////////////////////////////////////////////////// - /// Log - /////////////////////////////////////////////////////////////////////////////////////////////// - static EventLog& Log() - { - static EventLog instance; - return instance; - } -#endif //REACT_ENABLE_LOGGING -}; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Continuation /////////////////////////////////////////////////////////////////////////////////////////////////// template < - typename TSourceDomain, - typename TTargetDomain + typename D, + typename D2 > -class Continuation +class Continuation : public REACT_IMPL::ContinuationBase { - using NodePtrT = REACT_IMPL::NodeBasePtrT; +private: + using NodePtrT = REACT_IMPL::NodeBasePtrT; public: - using SourceDomainT = TSourceDomain; - using TargetDomainT = TTargetDomain; + using SourceDomainT = D; + using TargetDomainT = D2; Continuation() = default; - Continuation(const Continuation&) = delete; - Continuation& operator=(const Continuation&) = delete; Continuation(Continuation&& other) : - nodePtr_( std::move(other.nodePtr_) ) + Continuation::ContinuationBase( std::move(other) ) {} explicit Continuation(NodePtrT&& nodePtr) : - nodePtr_( std::move(nodePtr) ) - {} + Continuation::ContinuationBase( std::move(nodePtr) ) + {} Continuation& operator=(Continuation&& other) { - nodePtr_ = std::move(other.nodePtr_); + Continuation::ContinuationBase::operator=( std::move(other) ); return *this; } - -private: - NodePtrT nodePtr_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -257,7 +190,7 @@ auto MakeContinuation(TransactionFlagsT flags, const Signal& trigger, FIn&& return Continuation( std::make_shared>( - flags, trigger.NodePtr(), std::forward(func))); + flags, GetNodePtr(trigger), std::forward(func))); } template @@ -294,7 +227,7 @@ auto MakeContinuation(TransactionFlagsT flags, const Events& trigger, FIn&& return Continuation( std::make_shared>( - flags, trigger.NodePtr(), std::forward(func))); + flags, GetNodePtr(trigger), std::forward(func))); } template @@ -345,11 +278,11 @@ auto MakeContinuation(TransactionFlagsT flags, const Events& trigger, return Continuation( std::make_shared>( MyFlags, - MyTrigger.NodePtr(), - std::forward(MyFunc), deps.NodePtr() ...)); + GetNodePtr(MyTrigger), + std::forward(MyFunc), GetNodePtr(deps) ...)); } - TransactionFlagsT MyFlags; + TransactionFlagsT MyFlags; const Events& MyTrigger; FIn MyFunc; }; @@ -437,130 +370,12 @@ void AsyncTransaction(TransactionFlagsT flags, TransactionStatus& status, F&& fu /******************************************/ REACT_END /******************************************/ -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ModeSelector - Translate domain mode to individual propagation and input modes -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct ModeSelector; - -template <> -struct ModeSelector -{ - static const EInputMode input = consecutive_input; - static const EPropagationMode propagation = sequential_propagation; -}; - -template <> -struct ModeSelector -{ - static const EInputMode input = concurrent_input; - static const EPropagationMode propagation = sequential_propagation; -}; - -template <> -struct ModeSelector -{ - static const EInputMode input = consecutive_input; - static const EPropagationMode propagation = parallel_propagation; -}; - -template <> -struct ModeSelector -{ - static const EInputMode input = concurrent_input; - static const EPropagationMode propagation = parallel_propagation; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// GetDefaultEngine - Get default engine type for given propagation mode -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct GetDefaultEngine; - -template <> -struct GetDefaultEngine -{ - using Type = ToposortEngine; -}; - -template <> -struct GetDefaultEngine -{ - using Type = SubtreeEngine; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EngineTypeBuilder - Instantiate the given template engine type with mode. -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct DefaultEnginePlaceholder; - -// Concrete engine template type -template -< - EPropagationMode mode, - template class TTEngine -> -struct EngineTypeBuilder -{ - using Type = TTEngine; -}; - -// Placeholder engine type - use default engine for given mode -template -< - EPropagationMode mode -> -struct EngineTypeBuilder -{ - using Type = typename GetDefaultEngine::Type; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DomainPolicy -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - EDomainMode mode, - template class TTEngine = DefaultEnginePlaceholder -> -struct DomainPolicy -{ - static const EInputMode input_mode = ModeSelector::input; - static const EPropagationMode propagation_mode = ModeSelector::propagation; - - using Engine = typename EngineTypeBuilder::Type; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Ensure singletons are created immediately after domain declaration (TODO hax) -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class DomainInitializer -{ -public: - DomainInitializer() - { -#ifdef REACT_ENABLE_LOGGING - D::Log(); -#endif //REACT_ENABLE_LOGGING - - D::Engine::Instance(); - DomainSpecificObserverRegistry::Instance(); - DomainSpecificInputManager::Instance(); - } -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Domain definition macro /////////////////////////////////////////////////////////////////////////////////////////////////// #define REACTIVE_DOMAIN(name, ...) \ struct name : \ - public REACT::DomainBase> {}; \ + public REACT_IMPL::DomainBase> {}; \ REACT_IMPL::DomainInitializer name ## _initializer_; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/Event.h b/include/react/Event.h index fed4c33c..550ef5ac 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -42,6 +42,8 @@ class Signal; template class SignalPack; +using REACT_IMPL::WeightHint; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// MakeEventSource /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -79,7 +81,7 @@ auto Merge(const Events& arg1, const Events& ... args) return TempEvents( std::make_shared>( - arg1.NodePtr(), args.NodePtr() ...)); + GetNodePtr(arg1), GetNodePtr(args) ...)); } template @@ -105,7 +107,7 @@ auto operator|(const TLeftEvents& lhs, const TRightEvents& rhs) return TempEvents( std::make_shared>( - lhs.NodePtr(), rhs.NodePtr())); + GetNodePtr(lhs), GetNodePtr(rhs))); } template @@ -149,7 +151,7 @@ auto operator|(TempEvents&& lhs, const TRightEvents& rhs) return TempEvents( std::make_shared>( - lhs.StealOp(), rhs.NodePtr())); + lhs.StealOp(), GetNodePtr(rhs))); } template @@ -173,7 +175,7 @@ auto operator|(const TLeftEvents& lhs, TempEvents&& rhs) return TempEvents( std::make_shared>( - lhs.NodePtr(), rhs.StealOp())); + GetNodePtr(lhs), rhs.StealOp())); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -195,7 +197,7 @@ auto Filter(const Events& src, FIn&& filter) return TempEvents( std::make_shared>( - std::forward(filter), src.NodePtr())); + std::forward(filter), GetNodePtr(src))); } template @@ -246,7 +248,7 @@ auto Filter(const Events& source, const SignalPack& depPac { return Events( std::make_shared>( - MySource.NodePtr(), std::forward(MyFunc), deps.NodePtr() ...)); + GetNodePtr(MySource), std::forward(MyFunc), GetNodePtr(deps) ...)); } const Events& MySource; @@ -278,7 +280,7 @@ auto Transform(const Events& src, FIn&& func) return TempEvents( std::make_shared>( - std::forward(func), src.NodePtr())); + std::forward(func), GetNodePtr(src))); } template @@ -331,7 +333,7 @@ auto Transform(const Events& source, const SignalPack& d { return Events( std::make_shared>( - MySource.NodePtr(), std::forward(MyFunc), deps.NodePtr() ...)); + GetNodePtr(MySource), std::forward(MyFunc), GetNodePtr(deps) ...)); } const Events& MySource; @@ -351,12 +353,12 @@ template typename D, typename TInnerValue > -auto Flatten(const Signal>& node) +auto Flatten(const Signal>& outer) -> Events { return Events( std::make_shared, TInnerValue>>( - node.NodePtr(), node().NodePtr())); + GetNodePtr(outer), GetNodePtr(outer.Value()))); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -387,9 +389,6 @@ template > class Events : public REACT_IMPL::EventStreamBase { -protected: - using BaseT = REACT_IMPL::EventStreamBase; - private: using NodeT = REACT_IMPL::EventStreamNode; using NodePtrT = std::shared_ptr; @@ -397,32 +396,45 @@ class Events : public REACT_IMPL::EventStreamBase public: using ValueT = E; + // Default ctor Events() = default; + + // Copy ctor Events(const Events&) = default; - Events& operator=(const Events&) = default; + // Move ctor Events(Events&& other) : Events::EventStreamBase( std::move(other) ) {} + // Node ctor + explicit Events(NodePtrT&& nodePtr) : + Events::EventStreamBase( std::move(nodePtr) ) + {} + + // Copy assignment + Events& operator=(const Events&) = default; + + // Move assignment Events& operator=(Events&& other) { Events::EventStreamBase::operator=( std::move(other) ); return *this; } - explicit Events(NodePtrT&& nodePtr) : - Events::EventStreamBase( std::move(nodePtr) ) - {} - bool Equals(const Events& other) const { - return Events::BaseT::Equals(other); + return Events::EventStreamBase::Equals(other); } bool IsValid() const { - return Events::BaseT::IsValid(); + return Events::EventStreamBase::IsValid(); + } + + void SetWeightHint(WeightHint weight) + { + Events::EventStreamBase::SetWeightHint(weight); } auto Tokenize() const @@ -461,9 +473,6 @@ template > class Events : public REACT_IMPL::EventStreamBase> { -protected: - using BaseT = REACT_IMPL::EventStreamBase>; - private: using NodeT = REACT_IMPL::EventStreamNode>; using NodePtrT = std::shared_ptr; @@ -471,32 +480,45 @@ class Events : public REACT_IMPL::EventStreamBase using NodePtrT = std::shared_ptr; public: + // Default ctor EventSource() = default; + + // Copy ctor EventSource(const EventSource&) = default; - EventSource& operator=(const EventSource&) = default; + // Move ctor EventSource(EventSource&& other) : EventSource::Events( std::move(other) ) {} + // Node ctor + explicit EventSource(NodePtrT&& nodePtr) : + EventSource::Events( std::move(nodePtr) ) + {} + + // Copy assignemnt + EventSource& operator=(const EventSource&) = default; + + // Move assignment EventSource& operator=(EventSource&& other) { EventSource::Events::operator=( std::move(other) ); return *this; } - explicit EventSource(NodePtrT&& nodePtr) : - EventSource::Events( std::move(nodePtr) ) - {} - // Explicit emit - void Emit(const E& e) const { EventSource::BaseT::emit(e); } - void Emit(E&& e) const { EventSource::BaseT::emit(std::move(e)); } + void Emit(const E& e) const { EventSource::EventStreamBase::emit(e); } + void Emit(E&& e) const { EventSource::EventStreamBase::emit(std::move(e)); } void Emit() const { static_assert(std::is_same::value, "Can't emit on non token stream."); - EventSource::BaseT::emit(Token::value); + EventSource::EventStreamBase::emit(Token::value); } // Function object style - void operator()(const E& e) const { EventSource::BaseT::emit(e); } - void operator()(E&& e) const { EventSource::BaseT::emit(std::move(e)); } + void operator()(const E& e) const { EventSource::EventStreamBase::emit(e); } + void operator()(E&& e) const { EventSource::EventStreamBase::emit(std::move(e)); } void operator()() const { static_assert(std::is_same::value, "Can't emit on non token stream."); - EventSource::BaseT::emit(Token::value); + EventSource::EventStreamBase::emit(Token::value); } // Stream style const EventSource& operator<<(const E& e) const { - EventSource::BaseT::emit(e); + EventSource::EventStreamBase::emit(e); return *this; } const EventSource& operator<<(E&& e) const { - EventSource::BaseT::emit(std::move(e)); + EventSource::EventStreamBase::emit(std::move(e)); return *this; } }; @@ -607,34 +637,42 @@ class EventSource : public Events> using NodePtrT = std::shared_ptr; public: + // Default ctor EventSource() = default; + + // Copy ctor EventSource(const EventSource&) = default; - EventSource& operator=(const EventSource&) = default; + // Move ctor EventSource(EventSource&& other) : EventSource::Events( std::move(other) ) {} + // Node ctor + explicit EventSource(NodePtrT&& nodePtr) : + EventSource::Events( std::move(nodePtr) ) + {} + + // Copy assignment + EventSource& operator=(const EventSource&) = default; + + // Move assignment EventSource& operator=(EventSource&& other) { EventSource::Events::operator=( std::move(other) ); return *this; } - explicit EventSource(NodePtrT&& nodePtr) : - EventSource::Events( std::move(nodePtr) ) - {} - // Explicit emit - void Emit(std::reference_wrapper e) const { EventSource::BaseT::emit(e); } + void Emit(std::reference_wrapper e) const { EventSource::EventStreamBase::emit(e); } // Function object style - void operator()(std::reference_wrapper e) const { EventSource::BaseT::emit(e); } + void operator()(std::reference_wrapper e) const { EventSource::EventStreamBase::emit(e); } // Stream style const EventSource& operator<<(std::reference_wrapper e) const { - EventSource::BaseT::emit(e); + EventSource::EventStreamBase::emit(e); return *this; } }; @@ -654,19 +692,33 @@ class TempEvents : public Events using NodeT = REACT_IMPL::EventOpNode; using NodePtrT = std::shared_ptr; -public: +public: + // Default ctor TempEvents() = default; + + // Copy ctor TempEvents(const TempEvents&) = default; - TempEvents& operator=(const TempEvents&) = default; + // Move ctor TempEvents(TempEvents&& other) : TempEvents::Events( std::move(other) ) {} + // Node ctor explicit TempEvents(NodePtrT&& nodePtr) : TempEvents::Events( std::move(nodePtr) ) {} + // Copy assignment + TempEvents& operator=(const TempEvents&) = default; + + // Move assignment + TempEvents& operator=(TempEvents&& other) + { + TempEvents::EventStreamBase::operator=( std::move(other) ); + return *this; + } + TOp StealOp() { return std::move(reinterpret_cast(this->ptr_.get())->StealOp()); diff --git a/include/react/Observer.h b/include/react/Observer.h index 5bcd9957..07d76c32 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -34,6 +34,7 @@ template class Events; using REACT_IMPL::ObserverAction; +using REACT_IMPL::WeightHint; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Observer @@ -42,56 +43,70 @@ template class Observer { private: - using SubjectT = REACT_IMPL::NodeBasePtrT; - using NodeT = REACT_IMPL::IObserver; + using SubjectPtrT = std::shared_ptr>; + using NodeT = REACT_IMPL::ObserverNode; public: + // Default ctor Observer() : - nodePtr_( nullptr ) + nodePtr_( nullptr ), + subjectPtr_( nullptr ) {} - Observer(const Observer&) = delete; - Observer& operator=(const Observer&) = delete; - + // Move ctor Observer(Observer&& other) : nodePtr_( other.nodePtr_ ), - subject_( std::move(other.subject_) ) + subjectPtr_( std::move(other.subjectPtr_) ) { other.nodePtr_ = nullptr; + other.subjectPtr_.reset(); } + // Node ctor + Observer(NodeT* nodePtr, const SubjectPtrT& subjectPtr) : + nodePtr_( nodePtr ), + subjectPtr_( subjectPtr ) + {} + + // Move assignment Observer& operator=(Observer&& other) { nodePtr_ = other.nodePtr_; - subject_ = std::move(other.subject_); + subjectPtr_ = std::move(other.subjectPtr_); other.nodePtr_ = nullptr; + other.subjectPtr_.reset(); return *this; } - Observer(NodeT* nodePtr, const SubjectT& subject) : - nodePtr_( nodePtr ), - subject_( subject ) - {} + // Deleted copy ctor and assignment + Observer(const Observer&) = delete; + Observer& operator=(const Observer&) = delete; + + void Detach() + { + assert(IsValid()); + subjectPtr_->UnregisterObserver(nodePtr_); + } bool IsValid() const { return nodePtr_ != nullptr; } - void Detach() + void SetWeightHint(WeightHint weight) { - REACT_ASSERT(IsValid(), "Detach on invalid Observer."); - REACT_IMPL::DomainSpecificObserverRegistry::Instance().Unregister(nodePtr_); + assert(IsValid()); + nodePtr_->SetWeightHint(weight); } private: - // Ownership managed by registry - NodeT* nodePtr_; + // Owned by subject + NodeT* nodePtr_; // While the observer handle exists, the subject is not destroyed - SubjectT subject_; + SubjectPtrT subjectPtr_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -101,27 +116,40 @@ template class ScopedObserver { public: - ScopedObserver() = delete; - ScopedObserver(const ScopedObserver&) = delete; - ScopedObserver& operator=(const ScopedObserver&) = delete; - + // Move ctor ScopedObserver(ScopedObserver&& other) : obs_( std::move(other.obs_) ) {} + // Construct from observer + ScopedObserver(Observer&& obs) : + obs_( std::move(obs) ) + {} + + // Move assignment ScopedObserver& operator=(ScopedObserver&& other) { obs_ = std::move(other.obs_); } - ScopedObserver(Observer&& obs) : - obs_( std::move(obs) ) - {} + // Deleted default ctor, copy ctor and assignment + ScopedObserver() = delete; + ScopedObserver(const ScopedObserver&) = delete; + ScopedObserver& operator=(const ScopedObserver&) = delete; ~ScopedObserver() { - if (obs_.IsValid()) - obs_.Detach(); + obs_.Detach(); + } + + bool IsValid() const + { + return obs_.IsValid(); + } + + void SetWeightHint(WeightHint weight) + { + obs_.SetWeightHint(weight); } private: @@ -141,8 +169,8 @@ auto Observe(const Signal& subject, FIn&& func) -> Observer { using REACT_IMPL::IObserver; + using REACT_IMPL::ObserverNode; using REACT_IMPL::SignalObserverNode; - using REACT_IMPL::DomainSpecificObserverRegistry; using REACT_IMPL::AddDefaultReturnValueWrapper; using F = typename std::decay::type; @@ -157,13 +185,15 @@ auto Observe(const Signal& subject, FIn&& func) SignalObserverNode >::type; - std::unique_ptr obsPtr(new TNode( - subject.NodePtr(), std::forward(func))); - auto* rawObsPtr = DomainSpecificObserverRegistry::Instance() - .Register(std::move(obsPtr), subject.NodePtr().get()); + const auto& subjectPtr = GetNodePtr(subject); - return Observer( rawObsPtr, subject.NodePtr() ); + std::unique_ptr> nodePtr( new TNode(subjectPtr, std::forward(func)) ); + ObserverNode* rawNodePtr = nodePtr.get(); + + subjectPtr->RegisterObserver(std::move(nodePtr)); + + return Observer( rawNodePtr, subjectPtr ); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -179,8 +209,8 @@ auto Observe(const Events& subject, FIn&& func) -> Observer { using REACT_IMPL::IObserver; + using REACT_IMPL::ObserverNode; using REACT_IMPL::EventObserverNode; - using REACT_IMPL::DomainSpecificObserverRegistry; using REACT_IMPL::AddDefaultReturnValueWrapper; using F = typename std::decay::type; @@ -195,13 +225,14 @@ auto Observe(const Events& subject, FIn&& func) EventObserverNode >::type; - std::unique_ptr obsPtr(new TNode( - subject.NodePtr(), std::forward(func))); + const auto& subjectPtr = GetNodePtr(subject); + + std::unique_ptr> nodePtr( new TNode(subjectPtr, std::forward(func)) ); + ObserverNode* rawNodePtr = nodePtr.get(); - auto* rawObsPtr = DomainSpecificObserverRegistry::Instance() - .Register(std::move(obsPtr), subject.NodePtr().get()); + subjectPtr->RegisterObserver(std::move(nodePtr)); - return Observer(rawObsPtr, subject.NodePtr()); + return Observer( rawNodePtr, subjectPtr ); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -219,8 +250,8 @@ auto Observe(const Events& subject, -> Observer { using REACT_IMPL::IObserver; + using REACT_IMPL::ObserverNode; using REACT_IMPL::SyncedObserverNode; - using REACT_IMPL::DomainSpecificObserverRegistry; using REACT_IMPL::AddDefaultReturnValueWrapper; using F = typename std::decay::type; @@ -243,25 +274,27 @@ auto Observe(const Events& subject, {} auto operator()(const Signal& ... deps) - -> std::unique_ptr + -> ObserverNode* { - return std::unique_ptr( - new TNode( - MySubject.NodePtr(), std::forward(MyFunc), deps.NodePtr() ... )); + return new TNode( + GetNodePtr(MySubject), std::forward(MyFunc), GetNodePtr(deps) ... ); } const Events& MySubject; FIn MyFunc; }; - auto obsPtr = REACT_IMPL::apply( + const auto& subjectPtr = GetNodePtr(subject); + + std::unique_ptr> nodePtr( REACT_IMPL::apply( NodeBuilder_( subject, std::forward(func) ), - depPack.Data); + depPack.Data) ); + + ObserverNode* rawNodePtr = nodePtr.get(); - auto* rawObsPtr = DomainSpecificObserverRegistry::Instance() - .Register(std::move(obsPtr), subject.NodePtr().get()); + subjectPtr->RegisterObserver(std::move(nodePtr)); - return Observer(rawObsPtr, subject.NodePtr()); + return Observer( rawNodePtr, subjectPtr ); } /******************************************/ REACT_END /******************************************/ diff --git a/include/react/Reactor.h b/include/react/Reactor.h index eac2f93e..2149dcac 100644 --- a/include/react/Reactor.h +++ b/include/react/Reactor.h @@ -41,19 +41,19 @@ class Reactor template E& Await(const Events& evn) { - return node_.Await(evn.NodePtr()); + return node_.Await(GetNodePtr(evn)); } template void RepeatUntil(const Events& evn, F&& func) { - node_.RepeatUntil(evn.NodePtr(), std::forward(func)); + node_.RepeatUntil(GetNodePtr(evn), std::forward(func)); } template const S& Get(const Signal& sig) { - return node_.Get(sig.NodePtr()); + return node_.Get(GetNodePtr(sig)); } private: diff --git a/include/react/Signal.h b/include/react/Signal.h index d031a844..936874b4 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -9,6 +9,10 @@ #pragma once +#if _MSC_VER && !__INTEL_COMPILER + #pragma warning(disable : 4503) +#endif + #include "react/detail/Defs.h" #include @@ -80,7 +84,9 @@ template typename V, typename S = typename std::decay::type, class = typename std::enable_if< - ! IsReactive::value>::type + ! IsSignal::value>::type, + class = typename std::enable_if< + ! IsEvent::value>::type > auto MakeVar(V&& value) -> VarSignal @@ -157,7 +163,7 @@ auto MakeSignal(const Signal& arg, FIn&& func) { return TempSignal( std::make_shared>( - std::forward(func), arg.NodePtr())); + std::forward(func), GetNodePtr(arg))); } // Multiple args @@ -186,7 +192,7 @@ auto MakeSignal(const SignalPack& argPack, FIn&& func) { return TempSignal( std::make_shared>( - std::forward(MyFunc), args.NodePtr() ...)); + std::forward(MyFunc), GetNodePtr(args) ...)); } FIn MyFunc; @@ -223,7 +229,7 @@ auto operator op(const TSignal& arg) { \ return TempSignal( \ std::make_shared>( \ - F(), arg.NodePtr())); \ + F(), GetNodePtr(arg))); \ } \ \ template \ @@ -334,7 +340,7 @@ auto operator op(const TLeftSignal& lhs, const TRightSignal& rhs) { \ return TempSignal( \ std::make_shared>( \ - F(), lhs.NodePtr(), rhs.NodePtr())); \ + F(), GetNodePtr(lhs), GetNodePtr(rhs))); \ } \ \ template \ @@ -358,7 +364,7 @@ auto operator op(const TLeftSignal& lhs, TRightValIn&& rhs) { \ return TempSignal( \ std::make_shared>( \ - F( std::forward(rhs) ), lhs.NodePtr())); \ + F( std::forward(rhs) ), GetNodePtr(lhs))); \ } \ \ template \ @@ -382,7 +388,7 @@ auto operator op(TLeftValIn&& lhs, const TRightSignal& rhs) { \ return TempSignal( \ std::make_shared>( \ - F( std::forward(lhs) ), rhs.NodePtr())); \ + F( std::forward(lhs) ), GetNodePtr(rhs))); \ } \ template \ < \ @@ -425,7 +431,7 @@ auto operator op(TempSignal&& lhs, { \ return TempSignal( \ std::make_shared>( \ - F(), lhs.StealOp(), rhs.NodePtr())); \ + F(), lhs.StealOp(), GetNodePtr(rhs))); \ } \ \ template \ @@ -448,7 +454,7 @@ auto operator op(const TLeftSignal& lhs, TempSignal&& rhs) { \ return TempSignal( \ std::make_shared>( \ - F(), lhs.NodePtr(), rhs.StealOp())); \ + F(), GetNodePtr(lhs), rhs.StealOp())); \ } \ \ template \ @@ -587,12 +593,12 @@ template typename D, typename TInnerValue > -auto Flatten(const Signal>& node) +auto Flatten(const Signal>& outer) -> Signal { return Signal( std::make_shared, TInnerValue>>( - node.NodePtr(), node.Value().NodePtr())); + GetNodePtr(outer), GetNodePtr(outer.Value()))); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -605,9 +611,6 @@ template > class Signal : public REACT_IMPL::SignalBase { -protected: - using BaseT = REACT_IMPL::SignalBase; - private: using NodeT = REACT_IMPL::SignalNode; using NodePtrT = std::shared_ptr; @@ -615,40 +618,54 @@ class Signal : public REACT_IMPL::SignalBase public: using ValueT = S; + // Default ctor Signal() = default; + + // Copy ctor Signal(const Signal&) = default; - Signal& operator=(const Signal&) = default; + // Move ctor Signal(Signal&& other) : Signal::SignalBase( std::move(other) ) {} + // Node ctor + explicit Signal(NodePtrT&& nodePtr) : + Signal::SignalBase( std::move(nodePtr) ) + {} + + // Copy assignment + Signal& operator=(const Signal&) = default; + + // Move assignment Signal& operator=(Signal&& other) { Signal::SignalBase::operator=( std::move(other) ); return *this; } - explicit Signal(NodePtrT&& nodePtr) : - Signal::SignalBase( std::move(nodePtr) ) - {} - - const S& Value() const { return Signal::BaseT::getValue(); } - const S& operator()() const { return Signal::BaseT::getValue(); } + const S& Value() const { return Signal::SignalBase::getValue(); } + const S& operator()() const { return Signal::SignalBase::getValue(); } bool Equals(const Signal& other) const { - return Signal::BaseT::Equals(other); + return Signal::SignalBase::Equals(other); } bool IsValid() const { - return Signal::BaseT::IsValid(); + return Signal::SignalBase::IsValid(); + } + + void SetWeightHint(WeightHint weight) + { + Signal::SignalBase::SetWeightHint(weight); } S Flatten() const { - static_assert(IsReactive::value, "Flatten requires a Signal value type."); + static_assert(IsSignal::value || IsEvent::value, + "Flatten requires a Signal or Events value type."); return REACT::Flatten(*this); } }; @@ -661,9 +678,6 @@ template > class Signal : public REACT_IMPL::SignalBase> { -protected: - using BaseT = REACT_IMPL::SignalBase>; - private: using NodeT = REACT_IMPL::SignalNode>; using NodePtrT = std::shared_ptr; @@ -671,35 +685,48 @@ class Signal : public REACT_IMPL::SignalBase> public: using ValueT = S; + // Default ctor Signal() = default; + + // Copy ctor Signal(const Signal&) = default; - Signal& operator=(const Signal&) = default; + // Move ctor Signal(Signal&& other) : Signal::SignalBase( std::move(other) ) {} + // Node ctor + explicit Signal(NodePtrT&& nodePtr) : + Signal::SignalBase( std::move(nodePtr) ) + {} + + // Copy assignment + Signal& operator=(const Signal&) = default; + + // Move assignment Signal& operator=(Signal&& other) { Signal::SignalBase::operator=( std::move(other) ); return *this; } - explicit Signal(NodePtrT&& nodePtr) : - Signal::SignalBase( std::move(nodePtr) ) - {} - - const S& Value() const { return Signal::BaseT::getValue(); } - const S& operator()() const { return Signal::BaseT::getValue(); } + const S& Value() const { return Signal::SignalBase::getValue(); } + const S& operator()() const { return Signal::SignalBase::getValue(); } bool Equals(const Signal& other) const { - return Signal::BaseT::Equals(other); + return Signal::SignalBase::Equals(other); } bool IsValid() const { - return Signal::BaseT::IsValid(); + return Signal::SignalBase::IsValid(); + } + + void SetWeightHint(WeightHint weight) + { + Signal::SignalBase::SetWeightHint(weight); } }; @@ -718,50 +745,58 @@ class VarSignal : public Signal using NodePtrT = std::shared_ptr; public: + // Default ctor VarSignal() = default; + + // Copy ctor VarSignal(const VarSignal&) = default; - VarSignal& operator=(const VarSignal&) = default; + // Move ctor VarSignal(VarSignal&& other) : VarSignal::Signal( std::move(other) ) {} + // Node ctor + explicit VarSignal(NodePtrT&& nodePtr) : + VarSignal::Signal( std::move(nodePtr) ) + {} + + // Copy assignment + VarSignal& operator=(const VarSignal&) = default; + + // Move assignment VarSignal& operator=(VarSignal&& other) { - VarSignal::Signal::operator=( std::move(other) ); + VarSignal::SignalBase::operator=( std::move(other) ); return *this; } - explicit VarSignal(NodePtrT&& nodePtr) : - VarSignal::Signal( std::move(nodePtr) ) - {} - void Set(const S& newValue) const { - VarSignal::BaseT::setValue(newValue); + VarSignal::SignalBase::setValue(newValue); } void Set(S&& newValue) const { - VarSignal::BaseT::setValue(std::move(newValue)); + VarSignal::SignalBase::setValue(std::move(newValue)); } const VarSignal& operator<<=(const S& newValue) const { - VarSignal::BaseT::setValue(newValue); + VarSignal::SignalBase::setValue(newValue); return *this; } const VarSignal& operator<<=(S&& newValue) const { - VarSignal::BaseT::setValue(std::move(newValue)); + VarSignal::SignalBase::setValue(std::move(newValue)); return *this; } template void Modify(const F& func) const { - VarSignal::BaseT::modifyValue(func); + VarSignal::SignalBase::modifyValue(func); } }; @@ -780,32 +815,40 @@ class VarSignal : public Signal> public: using ValueT = S; + // Default ctor VarSignal() = default; + + // Copy ctor VarSignal(const VarSignal&) = default; - VarSignal& operator=(const VarSignal&) = default; + // Move ctor VarSignal(VarSignal&& other) : VarSignal::Signal( std::move(other) ) {} + // Node ctor + explicit VarSignal(NodePtrT&& nodePtr) : + VarSignal::Signal( std::move(nodePtr) ) + {} + + // Copy assignment + VarSignal& operator=(const VarSignal&) = default; + + // Move assignment VarSignal& operator=(VarSignal&& other) { VarSignal::Signal::operator=( std::move(other) ); return *this; } - explicit VarSignal(NodePtrT&& nodePtr) : - VarSignal::Signal( std::move(nodePtr) ) - {} - void Set(std::reference_wrapper newValue) const { - VarSignal::BaseT::setValue(newValue); + VarSignal::SignalBase::setValue(newValue); } const VarSignal& operator<<=(std::reference_wrapper newValue) const { - VarSignal::BaseT::setValue(newValue); + VarSignal::SignalBase::setValue(newValue); return *this; } }; @@ -825,25 +868,33 @@ class TempSignal : public Signal using NodeT = REACT_IMPL::SignalOpNode; using NodePtrT = std::shared_ptr; -public: +public: + // Default ctor TempSignal() = default; + + // Copy ctor TempSignal(const TempSignal&) = default; - TempSignal& operator=(const TempSignal&) = default; + // Move ctor TempSignal(TempSignal&& other) : TempSignal::Signal( std::move(other) ) {} + // Node ctor + explicit TempSignal(NodePtrT&& ptr) : + TempSignal::Signal( std::move(ptr) ) + {} + + // Copy assignment + TempSignal& operator=(const TempSignal&) = default; + + // Move assignemnt TempSignal& operator=(TempSignal&& other) { TempSignal::Signal::operator=( std::move(other) ); return *this; } - explicit TempSignal(NodePtrT&& ptr) : - TempSignal::Signal( std::move(ptr) ) - {} - TOp StealOp() { return std::move(reinterpret_cast(this->ptr_.get())->StealOp()); diff --git a/include/react/common/Timing.h b/include/react/common/Timing.h index 8c8b62a8..08f72892 100644 --- a/include/react/common/Timing.h +++ b/include/react/common/Timing.h @@ -66,6 +66,8 @@ class ConditionalTimer ScopedTimer(const ConditionalTimer&, const size_t& count); }; + void Reset(); + void ForceThresholdExceeded(bool isExceeded); bool IsThresholdExceeded() const; }; @@ -84,7 +86,9 @@ class ConditionalTimer ScopedTimer(const ConditionalTimer&, const size_t& count) {} }; - bool IsThresholdExceeded() const { return false; } + void Reset() {} + void ForceThresholdExceeded(bool isExceeded) {} + bool IsThresholdExceeded() const { return false; } }; // Enabled @@ -142,7 +146,7 @@ class ConditionalTimer durationUS.QuadPart *= 1000000; durationUS.QuadPart /= GetPerformanceFrequency().QuadPart; - parent_.isThresholdExceeded_ = durationUS.QuadPart > (threshold * count_); + parent_.isThresholdExceeded_ = (durationUS.QuadPart - (threshold * count_)) > 0; #else using std::chrono::duration_cast; using std::chrono::microseconds; @@ -168,6 +172,18 @@ class ConditionalTimer #endif } + void Reset() + { + shouldMeasure_ = true; + isThresholdExceeded_ = false; + } + + void ForceThresholdExceeded(bool isExceeded) + { + shouldMeasure_ = false; + isThresholdExceeded_ = isExceeded; + } + bool IsThresholdExceeded() const { return isThresholdExceeded_; } private: diff --git a/include/react/detail/DomainBase.h b/include/react/detail/DomainBase.h new file mode 100644 index 00000000..67c7e8ae --- /dev/null +++ b/include/react/detail/DomainBase.h @@ -0,0 +1,265 @@ + +// 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_DOMAINBASE_H_INCLUDED +#define REACT_DETAIL_DOMAINBASE_H_INCLUDED + +#pragma once + +#include +#include + +#include "react/detail/Defs.h" + +#include "react/detail/ReactiveBase.h" +#include "react/detail/IReactiveEngine.h" +#include "react/detail/graph/ContinuationNodes.h" + +// Include all engines for convenience +#include "react/engine/PulsecountEngine.h" +#include "react/engine/SubtreeEngine.h" +#include "react/engine/ToposortEngine.h" + +/*****************************************/ REACT_BEGIN /*****************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Forward declarations +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class Signal; + +template +class VarSignal; + +template +class Events; + +template +class EventSource; + +enum class Token; + +template +class Observer; + +template +class ScopedObserver; + +template +class Reactor; + +/******************************************/ REACT_END /******************************************/ + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Common types & constants +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// Domain modes +enum EDomainMode +{ + sequential, + sequential_concurrent, + parallel, + parallel_concurrent +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// DomainBase +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class DomainBase +{ +public: + using TurnT = typename TPolicy::Engine::TurnT; + + DomainBase() = delete; + + using Policy = TPolicy; + using Engine = REACT_IMPL::EngineInterface; + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// Domain traits + /////////////////////////////////////////////////////////////////////////////////////////////// + static const bool uses_node_update_timer = + REACT_IMPL::NodeUpdateTimerEnabled::value; + + static const bool is_concurrent = + Policy::input_mode == REACT_IMPL::concurrent_input; + + static const bool is_parallel = + Policy::propagation_mode == REACT_IMPL::parallel_propagation; + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// Aliases for reactives of this domain + /////////////////////////////////////////////////////////////////////////////////////////////// + template + using SignalT = Signal; + + template + using VarSignalT = VarSignal; + + template + using EventsT = Events; + + template + using EventSourceT = EventSource; + + using ObserverT = Observer; + + using ScopedObserverT = ScopedObserver; + + using ReactorT = Reactor; + +#ifdef REACT_ENABLE_LOGGING + /////////////////////////////////////////////////////////////////////////////////////////////// + /// Log + /////////////////////////////////////////////////////////////////////////////////////////////// + static EventLog& Log() + { + static EventLog instance; + return instance; + } +#endif //REACT_ENABLE_LOGGING +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ContinuationBase +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename D2 +> +class ContinuationBase : public MovableReactive> +{ +public: + ContinuationBase() = default; + + template + ContinuationBase(T&& t) : + ContinuationBase::MovableReactive( std::forward(t) ) + {} +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ModeSelector - Translate domain mode to individual propagation and input modes +/////////////////////////////////////////////////////////////////////////////////////////////////// + +template +struct ModeSelector; + +template <> +struct ModeSelector +{ + static const EInputMode input = consecutive_input; + static const EPropagationMode propagation = sequential_propagation; +}; + +template <> +struct ModeSelector +{ + static const EInputMode input = concurrent_input; + static const EPropagationMode propagation = sequential_propagation; +}; + +template <> +struct ModeSelector +{ + static const EInputMode input = consecutive_input; + static const EPropagationMode propagation = parallel_propagation; +}; + +template <> +struct ModeSelector +{ + static const EInputMode input = concurrent_input; + static const EPropagationMode propagation = parallel_propagation; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// GetDefaultEngine - Get default engine type for given propagation mode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct GetDefaultEngine; + +template <> +struct GetDefaultEngine +{ + using Type = ToposortEngine; +}; + +template <> +struct GetDefaultEngine +{ + using Type = SubtreeEngine; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EngineTypeBuilder - Instantiate the given template engine type with mode. +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct DefaultEnginePlaceholder; + +// Concrete engine template type +template +< + EPropagationMode mode, + template class TTEngine +> +struct EngineTypeBuilder +{ + using Type = TTEngine; +}; + +// Placeholder engine type - use default engine for given mode +template +< + EPropagationMode mode +> +struct EngineTypeBuilder +{ + using Type = typename GetDefaultEngine::Type; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// DomainPolicy +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + EDomainMode mode, + template class TTEngine = DefaultEnginePlaceholder +> +struct DomainPolicy +{ + static const EInputMode input_mode = ModeSelector::input; + static const EPropagationMode propagation_mode = ModeSelector::propagation; + + using Engine = typename EngineTypeBuilder::Type; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Ensure singletons are created immediately after domain declaration (TODO hax) +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class DomainInitializer +{ +public: + DomainInitializer() + { +#ifdef REACT_ENABLE_LOGGING + D::Log(); +#endif //REACT_ENABLE_LOGGING + + D::Engine::Instance(); + DomainSpecificInputManager::Instance(); + } +}; + +/****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_DOMAINBASE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/EventBase.h b/include/react/detail/EventBase.h index 907365f6..583651aa 100644 --- a/include/react/detail/EventBase.h +++ b/include/react/detail/EventBase.h @@ -27,15 +27,15 @@ template typename D, typename E > -class EventStreamBase : public ReactiveBase> +class EventStreamBase : public CopyableReactive> { public: EventStreamBase() = default; EventStreamBase(const EventStreamBase&) = default; template - explicit EventStreamBase(T&& ptr) : - EventStreamBase::ReactiveBase( std::forward(ptr) ) + EventStreamBase(T&& t) : + EventStreamBase::CopyableReactive( std::forward(t) ) {} protected: diff --git a/include/react/detail/ObserverBase.h b/include/react/detail/ObserverBase.h index ec0f987f..814e0207 100644 --- a/include/react/detail/ObserverBase.h +++ b/include/react/detail/ObserverBase.h @@ -11,145 +11,67 @@ #include "react/detail/Defs.h" -#include #include -#include +#include #include #include "IReactiveNode.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Observable; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// IObserver /////////////////////////////////////////////////////////////////////////////////////////////////// class IObserver { public: - virtual ~IObserver() = default; + virtual ~IObserver() {} + + virtual void UnregisterSelf() = 0; private: virtual void detachObserver() = 0; template - friend class ObserverRegistry; + friend class Observable; }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// ObserverRegistry +/// Observable /////////////////////////////////////////////////////////////////////////////////////////////////// -// Todo: still ugly, needs more refactoring template -class ObserverRegistry +class Observable { -private: - class Entry - { - public: - Entry(std::unique_ptr&& nodePtr, const Observable* subjectPtr) : - SubjectPtr( subjectPtr ), - nodePtr_( std::move(nodePtr) ) - {} - - Entry(const Entry&) = delete; - - Entry(Entry&& other) : - SubjectPtr( other.SubjectPtr ), - nodePtr_( std::move(other.nodePtr_) ) - {} - - const Observable* SubjectPtr; - - private: - // Manage lifetime - std::unique_ptr nodePtr_; - }; - public: - // Pass ownership of obs node to registry - IObserver* Register(std::unique_ptr&& obsPtr, const Observable* subjectPtr) - { - // Use raw ptr copy as index to find owned version of itself - auto* rawObsPtr = obsPtr.get(); - - observerMap_.emplace(rawObsPtr, Entry( std::move(obsPtr), subjectPtr )); + Observable() = default; - return rawObsPtr; + ~Observable() + { + for (const auto& p : observers_) + if (p != nullptr) + p->detachObserver(); } - void Unregister(IObserver* obsPtr) + void RegisterObserver(std::unique_ptr&& obsPtr) { - obsPtr->detachObserver(); - observerMap_.erase(obsPtr); + observers_.push_back(std::move(obsPtr)); } - void UnregisterFrom(const Observable* subjectPtr) + void UnregisterObserver(IObserver* rawObsPtr) { - auto it = observerMap_.begin(); - while (it != observerMap_.end()) + for (auto it = observers_.begin(); it != observers_.end(); ++it) { - if (it->second.SubjectPtr == subjectPtr) + if (it->get() == rawObsPtr) { - it->first->detachObserver(); - it = observerMap_.erase(it); - } - else - { - ++it; + it->get()->detachObserver(); + observers_.erase(it); + break; } } } private: - std::unordered_map observerMap_; -}; - -template -class DomainSpecificObserverRegistry -{ -public: - DomainSpecificObserverRegistry() = delete; - - static ObserverRegistry& Instance() - { - static ObserverRegistry instance; - return instance; - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Observable -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Observable -{ -public: - Observable() : - obsCount_( 0u ) - {}; - - Observable(const Observable& other) : - obsCount_( other.GetObsCount() ) - {} - - void IncObsCount() { obsCount_.fetch_add(1, std::memory_order_relaxed); } - void DecObsCount() { obsCount_.fetch_sub(1, std::memory_order_relaxed); } - uint GetObsCount() const { return obsCount_.load(std::memory_order_relaxed); } - - ~Observable() - { - if (GetObsCount() > 0) - DomainSpecificObserverRegistry::Instance().UnregisterFrom(this); - } - -private: - std::atomic obsCount_; + std::vector> observers_; }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/ReactiveBase.h b/include/react/detail/ReactiveBase.h index da44e421..d7f32124 100644 --- a/include/react/detail/ReactiveBase.h +++ b/include/react/detail/ReactiveBase.h @@ -31,56 +31,128 @@ bool Equals(const std::reference_wrapper& lhs, const std::reference_wrapper class ReactiveBase { public: using DomainT = typename TNode::DomainT; - using NodePtrT = std::shared_ptr; - + + // Default ctor ReactiveBase() = default; + + // Copy ctor ReactiveBase(const ReactiveBase&) = default; - ReactiveBase& operator=(const ReactiveBase&) = default; + // Move ctor (VS2013 doesn't default generate that yet) ReactiveBase(ReactiveBase&& other) : ptr_( std::move(other.ptr_) ) {} + // Explicit node ctor + explicit ReactiveBase(std::shared_ptr&& ptr) : + ptr_( std::move(ptr) ) + {} + + // Copy assignment + ReactiveBase& operator=(const ReactiveBase&) = default; + + // Move assignment ReactiveBase& operator=(ReactiveBase&& other) { ptr_.reset(std::move(other)); return *this; } - explicit ReactiveBase(const NodePtrT& ptr) : - ptr_( ptr ) + bool IsValid() const + { + return ptr_ != nullptr; + } + + void SetWeightHint(WeightHint weight) + { + assert(IsValid()); + ptr_->SetWeightHint(weight); + } + +protected: + std::shared_ptr ptr_; + + template + friend const std::shared_ptr& GetNodePtr(const ReactiveBase& node); +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// CopyableReactive +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class CopyableReactive : public ReactiveBase +{ +public: + CopyableReactive() = default; + + CopyableReactive(const CopyableReactive&) = default; + + CopyableReactive(CopyableReactive&& other) : + CopyableReactive::ReactiveBase( std::move(other) ) {} - explicit ReactiveBase(NodePtrT&& ptr) : - ptr_( std::move(ptr) ) + explicit CopyableReactive(std::shared_ptr&& ptr) : + CopyableReactive::ReactiveBase( std::move(ptr) ) {} - const std::shared_ptr& NodePtr() const + CopyableReactive& operator=(const CopyableReactive&) = default; + + CopyableReactive& operator=(CopyableReactive&& other) { - return ptr_; + CopyableReactive::ReactiveBase::operator=(std::move(other)); + return *this; } - bool Equals(const ReactiveBase& other) const + bool Equals(const CopyableReactive& other) const { - return ptr_ == other.ptr_; + return this->ptr_ == other.ptr_; } +}; - bool IsValid() const +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// UniqueReactiveBase +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class MovableReactive : public ReactiveBase +{ +public: + MovableReactive() = default; + + MovableReactive(MovableReactive&& other) : + MovableReactive::ReactiveBase( std::move(other) ) + {} + + explicit MovableReactive(std::shared_ptr&& ptr) : + MovableReactive::ReactiveBase( std::move(ptr) ) + {} + + MovableReactive& operator=(MovableReactive&& other) { - return ptr_ != nullptr; + MovableReactive::ReactiveBase::operator=(std::move(other)); + return *this; } -protected: - std::shared_ptr ptr_; + // Deleted copy ctor and assignment + MovableReactive(const MovableReactive&) = delete; + MovableReactive& operator=(const MovableReactive&) = delete; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// GetNodePtr +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +const std::shared_ptr& GetNodePtr(const ReactiveBase& node) +{ + return node.ptr_; +} + /****************************************/ REACT_IMPL_END /***************************************/ #endif // REACT_DETAIL_REACTIVEBASE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index 906e6e0f..fb2ceaf2 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -151,10 +151,8 @@ class ContinuationManager template void DetachQueuedObservers() { - auto& registry = DomainSpecificObserverRegistry::Instance(); - for (auto* o : detachedObservers_) - registry.Unregister(o); + o->UnregisterSelf(); detachedObservers_.clear(); } @@ -214,12 +212,10 @@ class ContinuationManager template void DetachQueuedObservers() { - auto& registry = DomainSpecificObserverRegistry::Instance(); - for (auto& v : detachedObservers_) { for (auto* o : v) - registry.Unregister(o); + o->UnregisterSelf(); v.clear(); } } diff --git a/include/react/detail/SignalBase.h b/include/react/detail/SignalBase.h index 3ba0abb8..6c41b9df 100644 --- a/include/react/detail/SignalBase.h +++ b/include/react/detail/SignalBase.h @@ -27,15 +27,15 @@ template typename D, typename S > -class SignalBase : public ReactiveBase> +class SignalBase : public CopyableReactive> { public: SignalBase() = default; SignalBase(const SignalBase&) = default; template - explicit SignalBase(T&& ptr) : - SignalBase::ReactiveBase( std::forward(ptr) ) + SignalBase(T&& t) : + SignalBase::CopyableReactive( std::forward(t) ) {} protected: diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index d3559e27..fc89aaf9 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -30,8 +30,7 @@ template typename TFunc > class IterateNode : - public SignalNode, - public UpdateTimingPolicy + public SignalNode { using Engine = typename IterateNode::Engine; @@ -90,11 +89,6 @@ class IterateNode : virtual const char* GetNodeType() const override { return "IterateNode"; } virtual int DependencyCount() const override { return 1; } - virtual bool IsHeavyweight() const override - { - return this->IsUpdateThresholdExceeded(); - } - private: std::shared_ptr> events_; @@ -112,8 +106,7 @@ template typename TFunc > class IterateByRefNode : - public SignalNode, - public UpdateTimingPolicy + public SignalNode { using Engine = typename IterateByRefNode::Engine; @@ -160,11 +153,6 @@ class IterateByRefNode : virtual const char* GetNodeType() const override { return "IterateByRefNode"; } virtual int DependencyCount() const override { return 1; } - virtual bool IsHeavyweight() const override - { - return this->IsUpdateThresholdExceeded(); - } - protected: TFunc func_; @@ -183,8 +171,7 @@ template typename ... TDepValues > class SyncedIterateNode : - public SignalNode, - public UpdateTimingPolicy + public SignalNode { using Engine = typename SyncedIterateNode::Engine; @@ -273,11 +260,6 @@ class SyncedIterateNode : virtual const char* GetNodeType() const override { return "SyncedIterateNode"; } virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } - virtual bool IsHeavyweight() const override - { - return this->IsUpdateThresholdExceeded(); - } - private: using DepHolderT = std::tuple>...>; @@ -299,8 +281,7 @@ template typename ... TDepValues > class SyncedIterateByRefNode : - public SignalNode, - public UpdateTimingPolicy + public SignalNode { using Engine = typename SyncedIterateByRefNode::Engine; @@ -383,11 +364,6 @@ class SyncedIterateByRefNode : virtual const char* GetNodeType() const override { return "SyncedIterateByRefNode"; } virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } - virtual bool IsHeavyweight() const override - { - return this->IsUpdateThresholdExceeded(); - } - private: using DepHolderT = std::tuple>...>; diff --git a/include/react/detail/graph/ContinuationNodes.h b/include/react/detail/graph/ContinuationNodes.h index 85cfa5be..ab3c3cd2 100644 --- a/include/react/detail/graph/ContinuationNodes.h +++ b/include/react/detail/graph/ContinuationNodes.h @@ -34,8 +34,7 @@ class EventStreamNode; /// ContinuationNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class ContinuationNode : - public ReactiveNode +class ContinuationNode : public NodeBase { public: ContinuationNode(TransactionFlagsT turnFlags) : diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 8c8688e2..484ed997 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -53,7 +53,7 @@ template typename E > class EventStreamNode : - public ReactiveNode, + public ObservableNode, private BufferClearAccessPolicy { public: @@ -385,8 +385,7 @@ template typename TOp > class EventOpNode : - public EventStreamNode, - public UpdateTimingPolicy + public EventStreamNode { using Engine = typename EventOpNode::Engine; @@ -441,11 +440,6 @@ class EventOpNode : virtual const char* GetNodeType() const override { return "EventOpNode"; } virtual int DependencyCount() const override { return TOp::dependency_count; } - virtual bool IsHeavyweight() const override - { - return this->IsUpdateThresholdExceeded(); - } - TOp StealOp() { REACT_ASSERT(wasOpStolen_ == false, "Op was already stolen."); @@ -514,7 +508,7 @@ class EventFlattenNode : public EventStreamNode this->SetCurrentTurn(turn, true); inner_->SetCurrentTurn(turn); - auto newInner = outer_->ValueRef().NodePtr(); + auto newInner = GetNodePtr(outer_->ValueRef()); if (newInner != inner_) { @@ -564,8 +558,7 @@ template typename ... TDepValues > class SyncedEventTransformNode : - public EventStreamNode, - public UpdateTimingPolicy + public EventStreamNode { using Engine = typename SyncedEventTransformNode::Engine; @@ -644,11 +637,6 @@ class SyncedEventTransformNode : virtual const char* GetNodeType() const override { return "SyncedEventTransformNode"; } virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } - virtual bool IsHeavyweight() const override - { - return this->IsUpdateThresholdExceeded(); - } - private: using DepHolderT = std::tuple>...>; @@ -669,8 +657,7 @@ template typename ... TDepValues > class SyncedEventFilterNode : - public EventStreamNode, - public UpdateTimingPolicy + public EventStreamNode { using Engine = typename SyncedEventFilterNode::Engine; diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index a8d1d1ad..29382e82 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -22,6 +22,16 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// UpdateTimingPolicy +/////////////////////////////////////////////////////////////////////////////////////////////////// +enum class WeightHint +{ + automatic, + light, + heavy +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// UpdateTimingPolicy /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -44,7 +54,9 @@ class UpdateTimingPolicy : {} }; - bool IsUpdateThresholdExceeded() const { return this->IsThresholdExceeded(); } + void ResetUpdateThreshold() { this->Reset(); } + void ForceUpdateThresholdExceeded(bool v) { this->ForceThresholdExceeded(v); } + bool IsUpdateThresholdExceeded() const { return this->IsThresholdExceeded(); } }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -76,7 +88,7 @@ template class NodeBase : public std::enable_shared_from_this>, public D::Policy::Engine::NodeT, - public Observable + public UpdateTimingPolicy { public: using DomainT = D; @@ -84,11 +96,33 @@ class NodeBase : using Engine = typename D::Engine; using NodeT = typename Engine::NodeT; using TurnT = typename Engine::TurnT; + + NodeBase() = default; + + // Nodes can't be copied + NodeBase(const NodeBase&) = delete; virtual bool IsInputNode() const override { return false; } virtual bool IsOutputNode() const override { return false; } virtual bool IsDynamicNode() const override { return false; } - virtual bool IsHeavyweight() const override { return false; } + + virtual bool IsHeavyweight() const override { return this->IsUpdateThresholdExceeded(); } + + void SetWeightHint(WeightHint weight) + { + switch (weight) + { + case WeightHint::heavy : + this->ForceUpdateThresholdExceeded(true); + break; + case WeightHint::light : + this->ForceUpdateThresholdExceeded(false); + break; + case WeightHint::automatic : + this->ResetUpdateThreshold(); + break; + } + } std::shared_ptr GetSharedPtr() const { @@ -100,21 +134,15 @@ template using NodeBasePtrT = std::shared_ptr>; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// ReactiveNode +/// ObservableNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename P, - typename V -> -class ReactiveNode : public NodeBase +template +class ObservableNode : + public NodeBase, + public Observable { public: - ReactiveNode() = default; - - // Reactive nodes can't be copied - ReactiveNode(const ReactiveNode&) = delete; + ObservableNode() = default; }; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index 2def3890..a2d4c1df 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -43,7 +43,7 @@ enum class ObserverAction /////////////////////////////////////////////////////////////////////////////////////////////////// template class ObserverNode : - public ReactiveNode, + public NodeBase, public IObserver { public: @@ -62,8 +62,7 @@ template typename TFunc > class SignalObserverNode : - public ObserverNode, - public UpdateTimingPolicy + public ObserverNode { using Engine = typename SignalObserverNode::Engine; @@ -75,7 +74,6 @@ class SignalObserverNode : func_( std::forward(func) ) { Engine::OnNodeCreate(*this); - subject->IncObsCount(); Engine::OnNodeAttach(*this, *subject); } @@ -116,13 +114,17 @@ class SignalObserverNode : GetObjectId(*this), turn.Id())); } + virtual void UnregisterSelf() override + { + if (auto p = subject_.lock()) + p->UnregisterObserver(this); + } + private: virtual void detachObserver() override { if (auto p = subject_.lock()) { - p->DecObsCount(); - Engine::OnNodeDetach(*this, *p); subject_.reset(); } @@ -142,8 +144,7 @@ template typename TFunc > class EventObserverNode : - public ObserverNode, - public UpdateTimingPolicy + public ObserverNode { using Engine = typename EventObserverNode::Engine; @@ -155,7 +156,6 @@ class EventObserverNode : func_( std::forward(func) ) { Engine::OnNodeCreate(*this); - subject->IncObsCount(); Engine::OnNodeAttach(*this, *subject); } @@ -202,6 +202,12 @@ class EventObserverNode : GetObjectId(*this), turn.Id())); } + virtual void UnregisterSelf() override + { + if (auto p = subject_.lock()) + p->UnregisterObserver(this); + } + private: std::weak_ptr> subject_; @@ -211,8 +217,6 @@ class EventObserverNode : { if (auto p = subject_.lock()) { - p->DecObsCount(); - Engine::OnNodeDetach(*this, *p); subject_.reset(); } @@ -230,13 +234,11 @@ template typename ... TDepValues > class SyncedObserverNode : - public ObserverNode, - public UpdateTimingPolicy + public ObserverNode { using Engine = typename SyncedObserverNode::Engine; public: - // NOTE: After upgrading to VS2013 Udpate2, using std::shared_ptr here crashes the compiler template SyncedObserverNode(const std::shared_ptr>& subject, F&& func, const std::shared_ptr>& ... deps) : @@ -246,7 +248,6 @@ class SyncedObserverNode : deps_( deps ... ) { Engine::OnNodeCreate(*this); - subject->IncObsCount(); Engine::OnNodeAttach(*this, *subject); REACT_EXPAND_PACK(Engine::OnNodeAttach(*this, *deps)); @@ -315,6 +316,12 @@ class SyncedObserverNode : GetObjectId(*this), turn.Id())); } + virtual void UnregisterSelf() override + { + if (auto p = subject_.lock()) + p->UnregisterObserver(this); + } + private: using DepHolderT = std::tuple>...>; @@ -327,8 +334,6 @@ class SyncedObserverNode : { if (auto p = subject_.lock()) { - p->DecObsCount(); - Engine::OnNodeDetach(*this, *p); apply( diff --git a/include/react/detail/graph/ReactorNodes.h b/include/react/detail/graph/ReactorNodes.h index 9a259bae..b693a5f0 100644 --- a/include/react/detail/graph/ReactorNodes.h +++ b/include/react/detail/graph/ReactorNodes.h @@ -34,7 +34,7 @@ template typename TContext > class ReactorNode : - public ReactiveNode + public NodeBase { using Engine = typename ReactorNode::Engine; @@ -46,7 +46,7 @@ class ReactorNode : template ReactorNode(F&& func) : - ReactorNode::ReactiveNode( ), + ReactorNode::NodeBase( ), func_( std::forward(func) ) { Engine::OnNodeCreate(*this); diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index 7715cff2..eb438a0c 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -32,14 +32,14 @@ template typename D, typename S > -class SignalNode : public ReactiveNode +class SignalNode : public ObservableNode { public: SignalNode() = default; template explicit SignalNode(T&& value) : - SignalNode::ReactiveNode( ), + SignalNode::ObservableNode( ), value_( std::forward(value) ) {} @@ -233,8 +233,7 @@ template typename TOp > class SignalOpNode : - public SignalNode, - public UpdateTimingPolicy + public SignalNode { using Engine = typename SignalOpNode::Engine; @@ -292,11 +291,6 @@ class SignalOpNode : virtual const char* GetNodeType() const override { return "SignalOpNode"; } virtual int DependencyCount() const override { return TOp::dependency_count; } - virtual bool IsHeavyweight() const override - { - return this->IsUpdateThresholdExceeded(); - } - TOp StealOp() { REACT_ASSERT(wasOpStolen_ == false, "Op was already stolen."); @@ -347,7 +341,7 @@ class FlattenNode : public SignalNode using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); - auto newInner = outer_->ValueRef().NodePtr(); + auto newInner = GetNodePtr(outer_->ValueRef()); if (newInner != inner_) { diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index cf59babe..7de3201b 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -147,6 +147,9 @@ Header Files\common + + Header Files\detail + From 8fa654bd3379220de29ba0b424dff8b00e22212c Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 29 Jul 2014 03:05:54 +0200 Subject: [PATCH 186/266] Added rudimentary test case for SetWeightHint. --- project/msvc/CppReactTest.vcxproj | 2 + project/msvc/CppReactTest.vcxproj.filters | 6 +++ tests/src/ParallelizationTest.cpp | 29 +++++++++++ tests/src/ParallelizationTest.h | 63 +++++++++++++++++++++++ 4 files changed, 100 insertions(+) create mode 100644 tests/src/ParallelizationTest.cpp create mode 100644 tests/src/ParallelizationTest.h diff --git a/project/msvc/CppReactTest.vcxproj b/project/msvc/CppReactTest.vcxproj index ff65e1d5..7b1cb65f 100644 --- a/project/msvc/CppReactTest.vcxproj +++ b/project/msvc/CppReactTest.vcxproj @@ -174,6 +174,7 @@ + @@ -183,6 +184,7 @@ + diff --git a/project/msvc/CppReactTest.vcxproj.filters b/project/msvc/CppReactTest.vcxproj.filters index 91914d7c..ae74e006 100644 --- a/project/msvc/CppReactTest.vcxproj.filters +++ b/project/msvc/CppReactTest.vcxproj.filters @@ -45,6 +45,9 @@ Source Files + + Source Files + @@ -68,5 +71,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/tests/src/ParallelizationTest.cpp b/tests/src/ParallelizationTest.cpp new file mode 100644 index 00000000..aa583c17 --- /dev/null +++ b/tests/src/ParallelizationTest.cpp @@ -0,0 +1,29 @@ + +// 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) + +#include "ParallelizationTest.h" +#include "TestUtil.h" + +#include "react/engine/PulsecountEngine.h" +#include "react/engine/ToposortEngine.h" +#include "react/engine/SubtreeEngine.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace { + +using namespace react; + +using P1 = DomainParams; +using P2 = DomainParams; +using P3 = DomainParams; +using P4 = DomainParams; + +INSTANTIATE_TYPED_TEST_CASE_P(SeqToposortQ, ParallelizationTest, P1); +INSTANTIATE_TYPED_TEST_CASE_P(ParToposortQ, ParallelizationTest, P2); +INSTANTIATE_TYPED_TEST_CASE_P(PulsecountQ, ParallelizationTest, P3); +INSTANTIATE_TYPED_TEST_CASE_P(SubtreeQ, ParallelizationTest, P4); + +} // ~namespace \ No newline at end of file diff --git a/tests/src/ParallelizationTest.h b/tests/src/ParallelizationTest.h new file mode 100644 index 00000000..6c5fd1d7 --- /dev/null +++ b/tests/src/ParallelizationTest.h @@ -0,0 +1,63 @@ + +// 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) + +#pragma once + +#include "gtest/gtest.h" + +#include "react/Domain.h" +#include "react/Signal.h" +#include "react/Event.h" +#include "react/Observer.h" +#include "react/Algorithm.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace { + +using namespace react; +using namespace std; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// ParallelizationTest fixture +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class ParallelizationTest : public testing::Test +{ +public: + template + class MyEngine : public TParams::template EngineT {}; + + REACTIVE_DOMAIN(MyDomain, TParams::mode, MyEngine) +}; + +TYPED_TEST_CASE_P(ParallelizationTest); + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Iterate1 test +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(ParallelizationTest, WeightHint1) +{ + using D = typename WeightHint1::MyDomain; + + auto sig = MakeVar(0); + auto evn = MakeEventSource(); + auto cont = MakeContinuation(sig, [] (int v) {}); + auto obs = Observe(evn, [] (int v) {}); + + sig.SetWeightHint(WeightHint::heavy); + evn.SetWeightHint(WeightHint::automatic); + cont.SetWeightHint(WeightHint::light); + obs.SetWeightHint(WeightHint::automatic); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +REGISTER_TYPED_TEST_CASE_P +( + ParallelizationTest, + WeightHint1 +); + +} // ~namespace From e35720f24527967fbca005ee47c9399e8c5de233 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 29 Jul 2014 14:19:26 +0200 Subject: [PATCH 187/266] Cleanup. --- include/react/detail/graph/GraphBase.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index 29382e82..38979813 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -23,7 +23,7 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// -/// UpdateTimingPolicy +/// WeightHint /////////////////////////////////////////////////////////////////////////////////////////////////// enum class WeightHint { @@ -86,7 +86,6 @@ struct DepCounter /////////////////////////////////////////////////////////////////////////////////////////////////// template class NodeBase : - public std::enable_shared_from_this>, public D::Policy::Engine::NodeT, public UpdateTimingPolicy { @@ -123,11 +122,6 @@ class NodeBase : break; } } - - std::shared_ptr GetSharedPtr() const - { - return this->shared_from_this(); - } }; template From 39b10515137dc6430d36ee723bc60374104ef7aa Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 29 Jul 2014 14:20:54 +0200 Subject: [PATCH 188/266] Added AsyncTransaction example. --- examples/src/BasicSynchronization.cpp | 165 ++++++++++++++++++ include/react/detail/ReactiveInput.h | 3 +- .../msvc/Example_BasicSynchronization.vcxproj | 2 + 3 files changed, 169 insertions(+), 1 deletion(-) diff --git a/examples/src/BasicSynchronization.cpp b/examples/src/BasicSynchronization.cpp index e27d1a93..38f05f3e 100644 --- a/examples/src/BasicSynchronization.cpp +++ b/examples/src/BasicSynchronization.cpp @@ -1,5 +1,170 @@ +// 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) +#include + +#include "tbb/tick_count.h" + +#include "react/Domain.h" +#include "react/Event.h" +#include "react/Observer.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 1 - Asynchronous transactions +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example1 +{ + using namespace react; + using namespace std; + + REACTIVE_DOMAIN(D, sequential_concurrent) + + class Sensor + { + public: + USING_REACTIVE_DOMAIN(D) + + EventSourceT Samples = MakeEventSource(); + }; + + void Run() + { + cout << "Example 1 - Asynchronous transactions" << endl; + + Sensor mySensor; + + Observe(mySensor.Samples, [] (int v) { + cout << v << std::endl; + }); + + TransactionStatus status; + + AsyncTransaction(status, [&] { + mySensor.Samples << 30 << 31 << 31 << 32; + }); + + AsyncTransaction(status, [&] { + mySensor.Samples << 40 << 41 << 51 << 62; + }); + + // Waits until both transactions are completed. + // This does not mean that both transactions are interleaved. + status.Wait(); + + cout << endl; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 2 - Transaction merging +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example2 +{ + using namespace react; + using namespace std; + + REACTIVE_DOMAIN(D, sequential_concurrent) + + class Sensor + { + public: + USING_REACTIVE_DOMAIN(D) + + EventSourceT Samples = MakeEventSource(); + }; + + const int K = 100000; + + namespace v1 + { + void Run() + { + cout << "Example 2 - Transaction merging (no merging)" << endl; + + Sensor mySensor; + int sum = 0; + + Observe(mySensor.Samples, [&] (int v) { + sum += v; + }); + + TransactionStatus status; + + cout << "Executing " << K << " async transactions..."; + + auto t0 = tbb::tick_count::now(); + + for (int i=0; i < K; i++) + { + AsyncTransaction(status, [&] { + mySensor.Samples << 3 << 4 << 2 << 1; + }); + } + + status.Wait(); + + double d = (tbb::tick_count::now() - t0).seconds(); + + cout << " done." << endl; + + cout << " Sum: " << sum << endl; + cout << " Time: " << d << endl; + + cout << endl; + } + } + + namespace v2 + { + void Run() + { + cout << "Example 2 - Transaction merging (allow merging)" << endl; + + Sensor mySensor; + int sum = 0; + + Observe(mySensor.Samples, [&] (int v) { + sum += v; + }); + + TransactionStatus status; + + cout << "Executing " << K << " async transactions..."; + + auto t0 = tbb::tick_count::now(); + + for (int i=0; i < K; i++) + { + AsyncTransaction(allow_merging, status, [&] { + mySensor.Samples << 3 << 4 << 2 << 1; + }); + } + + status.Wait(); + + double d = (tbb::tick_count::now() - t0).seconds(); + + cout << " done." << endl; + + cout << " Sum: " << sum << endl; + cout << " Time: " << d << endl; + + cout << endl; + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Run examples +/////////////////////////////////////////////////////////////////////////////////////////////////// int main() { + example1::Run(); + example2::v1::Run(); + example2::v2::Run(); + + return 0; } \ No newline at end of file diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index fb2ceaf2..dbab6b1f 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -744,7 +744,8 @@ class InputManager : if (canMerge) { uint extraCount = 0; - while (extraCount < 100 && asyncQueue_.try_pop(item)) + // Todo: Make configurable + while (extraCount < 512 && asyncQueue_.try_pop(item)) { bool canMergeNext = (item.Flags & allow_merging) != 0; if (canMergeNext) diff --git a/project/msvc/Example_BasicSynchronization.vcxproj b/project/msvc/Example_BasicSynchronization.vcxproj index 919643b3..2b2edec4 100644 --- a/project/msvc/Example_BasicSynchronization.vcxproj +++ b/project/msvc/Example_BasicSynchronization.vcxproj @@ -118,6 +118,7 @@ true true true + Console @@ -133,6 +134,7 @@ true true true + Console From d20d972731d30e73fa6ae8e51b8820167f7149e5 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 29 Jul 2014 14:21:22 +0200 Subject: [PATCH 189/266] Cleanup. --- include/react/Domain.h | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/include/react/Domain.h b/include/react/Domain.h index 548560a2..1279c3f9 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -96,19 +96,19 @@ class TransactionStatus using PtrT = REACT_IMPL::WaitingStatePtrT; public: + // Default ctor inline TransactionStatus() : statePtr_( StateT::Create() ) {} - TransactionStatus(const TransactionStatus&) = delete; - TransactionStatus& operator=(const TransactionStatus&) = delete; - + // Move ctor inline TransactionStatus(TransactionStatus&& other) : statePtr_( std::move(other.statePtr_) ) { other.statePtr_ = StateT::Create(); } + // Move assignment inline TransactionStatus& operator=(TransactionStatus&& other) { if (this != &other) @@ -119,6 +119,10 @@ class TransactionStatus return *this; } + // Deleted copy ctor & assignment + TransactionStatus(const TransactionStatus&) = delete; + TransactionStatus& operator=(const TransactionStatus&) = delete; + inline void Wait() { assert(statePtr_.Get() != nullptr); @@ -152,21 +156,29 @@ class Continuation : public REACT_IMPL::ContinuationBase using SourceDomainT = D; using TargetDomainT = D2; + // Default ctor Continuation() = default; + // Move ctor Continuation(Continuation&& other) : Continuation::ContinuationBase( std::move(other) ) {} + // Node ctor explicit Continuation(NodePtrT&& nodePtr) : Continuation::ContinuationBase( std::move(nodePtr) ) {} + // Move assignment Continuation& operator=(Continuation&& other) { Continuation::ContinuationBase::operator=( std::move(other) ); return *this; } + + // Deleted copy ctor & assignment + Continuation(const Continuation&) = delete; + Continuation& operator=(const Continuation&) = delete; }; /////////////////////////////////////////////////////////////////////////////////////////////////// From cd4801890ac75a5170c191bf776ed61e503646c3 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 29 Jul 2014 20:03:04 +0200 Subject: [PATCH 190/266] Removed images from main source tree. They will be moved to the gh-pages branch, where they belong. --- doc/media/graphs/flow_eventflatten.graphml | 149 ------ doc/media/graphs/flow_eventflatten.png | Bin 5218 -> 0 bytes doc/media/graphs/flow_filter.graphml | 110 ---- doc/media/graphs/flow_filter.png | Bin 4369 -> 0 bytes doc/media/graphs/flow_filter2.graphml | 197 ------- doc/media/graphs/flow_filter2.png | Bin 8051 -> 0 bytes doc/media/graphs/flow_flatten.graphml | 149 ------ doc/media/graphs/flow_flatten.png | Bin 4622 -> 0 bytes doc/media/graphs/flow_hold.graphml | 111 ---- doc/media/graphs/flow_hold.png | Bin 4216 -> 0 bytes doc/media/graphs/flow_iterate.graphml | 111 ---- doc/media/graphs/flow_iterate.png | Bin 4677 -> 0 bytes doc/media/graphs/flow_iterate2.graphml | 198 ------- doc/media/graphs/flow_iterate2.png | Bin 8052 -> 0 bytes doc/media/graphs/flow_makesignal.graphml | 206 -------- doc/media/graphs/flow_makesignal.png | Bin 6945 -> 0 bytes doc/media/graphs/flow_makesource.graphml | 109 ---- doc/media/graphs/flow_makesource.png | Bin 1521 -> 0 bytes doc/media/graphs/flow_makevar.graphml | 111 ---- doc/media/graphs/flow_makevar.png | Bin 1675 -> 0 bytes doc/media/graphs/flow_merge.graphml | 205 -------- doc/media/graphs/flow_merge.png | Bin 7452 -> 0 bytes doc/media/graphs/flow_monitor.graphml | 110 ---- doc/media/graphs/flow_monitor.png | Bin 3515 -> 0 bytes doc/media/graphs/flow_observe.graphml | 109 ---- doc/media/graphs/flow_observe.png | Bin 4412 -> 0 bytes doc/media/graphs/flow_observe2.graphml | 109 ---- doc/media/graphs/flow_observe2.png | Bin 3987 -> 0 bytes doc/media/graphs/flow_observe3.graphml | 196 ------- doc/media/graphs/flow_observe3.png | Bin 8143 -> 0 bytes doc/media/graphs/flow_pulse.graphml | 141 ----- doc/media/graphs/flow_pulse.png | Bin 5700 -> 0 bytes doc/media/graphs/flow_snapshot.graphml | 142 ----- doc/media/graphs/flow_snapshot.png | Bin 5731 -> 0 bytes doc/media/graphs/flow_transform.graphml | 110 ---- doc/media/graphs/flow_transform.png | Bin 4321 -> 0 bytes doc/media/graphs/flow_transform2.graphml | 197 ------- doc/media/graphs/flow_transform2.png | Bin 7915 -> 0 bytes doc/media/helloworld.graphml | 222 -------- doc/media/helloworld.png | Bin 7126 -> 0 bytes doc/media/reactives1.graphml | 178 ------- doc/media/reactives2.graphml | 358 ------------- doc/media/signals1.graphml | 178 ------- doc/media/signals1.png | Bin 3869 -> 0 bytes doc/media/signals2.graphml | 358 ------------- doc/media/signals2.png | Bin 9576 -> 0 bytes doc/media/toposort1.graphml | 579 --------------------- doc/media/toposort1.png | Bin 25492 -> 0 bytes doc/media/toposort2.graphml | 570 -------------------- doc/media/toposort2.png | Bin 10369 -> 0 bytes doc/media/toposort3.graphml | 156 ------ doc/media/toposort3.png | Bin 7912 -> 0 bytes 52 files changed, 5369 deletions(-) delete mode 100644 doc/media/graphs/flow_eventflatten.graphml delete mode 100644 doc/media/graphs/flow_eventflatten.png delete mode 100644 doc/media/graphs/flow_filter.graphml delete mode 100644 doc/media/graphs/flow_filter.png delete mode 100644 doc/media/graphs/flow_filter2.graphml delete mode 100644 doc/media/graphs/flow_filter2.png delete mode 100644 doc/media/graphs/flow_flatten.graphml delete mode 100644 doc/media/graphs/flow_flatten.png delete mode 100644 doc/media/graphs/flow_hold.graphml delete mode 100644 doc/media/graphs/flow_hold.png delete mode 100644 doc/media/graphs/flow_iterate.graphml delete mode 100644 doc/media/graphs/flow_iterate.png delete mode 100644 doc/media/graphs/flow_iterate2.graphml delete mode 100644 doc/media/graphs/flow_iterate2.png delete mode 100644 doc/media/graphs/flow_makesignal.graphml delete mode 100644 doc/media/graphs/flow_makesignal.png delete mode 100644 doc/media/graphs/flow_makesource.graphml delete mode 100644 doc/media/graphs/flow_makesource.png delete mode 100644 doc/media/graphs/flow_makevar.graphml delete mode 100644 doc/media/graphs/flow_makevar.png delete mode 100644 doc/media/graphs/flow_merge.graphml delete mode 100644 doc/media/graphs/flow_merge.png delete mode 100644 doc/media/graphs/flow_monitor.graphml delete mode 100644 doc/media/graphs/flow_monitor.png delete mode 100644 doc/media/graphs/flow_observe.graphml delete mode 100644 doc/media/graphs/flow_observe.png delete mode 100644 doc/media/graphs/flow_observe2.graphml delete mode 100644 doc/media/graphs/flow_observe2.png delete mode 100644 doc/media/graphs/flow_observe3.graphml delete mode 100644 doc/media/graphs/flow_observe3.png delete mode 100644 doc/media/graphs/flow_pulse.graphml delete mode 100644 doc/media/graphs/flow_pulse.png delete mode 100644 doc/media/graphs/flow_snapshot.graphml delete mode 100644 doc/media/graphs/flow_snapshot.png delete mode 100644 doc/media/graphs/flow_transform.graphml delete mode 100644 doc/media/graphs/flow_transform.png delete mode 100644 doc/media/graphs/flow_transform2.graphml delete mode 100644 doc/media/graphs/flow_transform2.png delete mode 100644 doc/media/helloworld.graphml delete mode 100644 doc/media/helloworld.png delete mode 100644 doc/media/reactives1.graphml delete mode 100644 doc/media/reactives2.graphml delete mode 100644 doc/media/signals1.graphml delete mode 100644 doc/media/signals1.png delete mode 100644 doc/media/signals2.graphml delete mode 100644 doc/media/signals2.png delete mode 100644 doc/media/toposort1.graphml delete mode 100644 doc/media/toposort1.png delete mode 100644 doc/media/toposort2.graphml delete mode 100644 doc/media/toposort2.png delete mode 100644 doc/media/toposort3.graphml delete mode 100644 doc/media/toposort3.png diff --git a/doc/media/graphs/flow_eventflatten.graphml b/doc/media/graphs/flow_eventflatten.graphml deleted file mode 100644 index a0385cfe..00000000 --- a/doc/media/graphs/flow_eventflatten.graphml +++ /dev/null @@ -1,149 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <E> - r - - - - - - - - - - - - - - - - - <E> - inner - - - - - - - - - - - - - - - - - <Events<E>> - - - - - - - - - outer - - - - - - - - - - - - - - - - - [e,...] - - - - - - - - - - - - [e,...] - - - - - - - - - - - - - inner - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/graphs/flow_eventflatten.png b/doc/media/graphs/flow_eventflatten.png deleted file mode 100644 index d9911717c271b1ccdcf995ef337759581c3c8012..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5218 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 gQ~&K_+J!^4j(e - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <E> - r - - - - - - - - - - - - - - - - - <E> - source - - - - - - - - - - - - - - - - - [e if func(e), ...] - - - - - - - - - - - - [e,...] - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/graphs/flow_filter.png b/doc/media/graphs/flow_filter.png deleted file mode 100644 index fd3454b2e7af195d8f27f0f7976068a422dd1cb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4369 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 diff --git a/doc/media/graphs/flow_filter2.graphml b/doc/media/graphs/flow_filter2.graphml deleted file mode 100644 index beec03af..00000000 --- a/doc/media/graphs/flow_filter2.graphml +++ /dev/null @@ -1,197 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <E> - r - - - - - - - - - - - - - - - - - <TDepValue1> - dep1 - - - - - - - - - - - - - - - - - <TDepValueN> - - - - - - - - - depN - - - - - - - - - - - - - - - - - ... - - - - - - - - - - - - - - - - - <E> - source - - - - - - - - - - - - - - - - - [e1 if func(e1,v1,...,vN), ...] - - - - - - - - - - - - v1 - - - - - - - - - - - - - vN - - - - - - - - - - - - - [e1,...] - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/graphs/flow_filter2.png b/doc/media/graphs/flow_filter2.png deleted file mode 100644 index ae73e1fa178a0016e6d2ae3d0314dd96f8daa67a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8051 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 diff --git a/doc/media/graphs/flow_flatten.graphml b/doc/media/graphs/flow_flatten.graphml deleted file mode 100644 index ace5b909..00000000 --- a/doc/media/graphs/flow_flatten.graphml +++ /dev/null @@ -1,149 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <S> - r - - - - - - - - - - - - - - - - - <S> - inner - - - - - - - - - - - - - - - - - <Signal<S>> - - - - - - - - - outer - - - - - - - - - - - - - - - - - v = x - - - - - - - - - - - - x - - - - - - - - - - - - - inner - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/graphs/flow_flatten.png b/doc/media/graphs/flow_flatten.png deleted file mode 100644 index 561430cc1887dfd24f0a8e8f86ebea7de6154c10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4622 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 F{{`srXvhEn diff --git a/doc/media/graphs/flow_hold.graphml b/doc/media/graphs/flow_hold.graphml deleted file mode 100644 index 16f1c40e..00000000 --- a/doc/media/graphs/flow_hold.graphml +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <S> - r - - - - - - - - - - - - - - - - - <E> - events - - - - - - - - - - - - - - - - - INIT: v = init -STEP: v = e - - - - - - - - - - - - [e,...] - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/graphs/flow_hold.png b/doc/media/graphs/flow_hold.png deleted file mode 100644 index 544e59cd8ae0166c985006cb12101007748548fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4216 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 diff --git a/doc/media/graphs/flow_iterate.graphml b/doc/media/graphs/flow_iterate.graphml deleted file mode 100644 index ac293f3a..00000000 --- a/doc/media/graphs/flow_iterate.graphml +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <S> - r - - - - - - - - - - - - - - - - - <E> - events - - - - - - - - - - - - - - - - - INIT: v = init -STEP: v = func(e,v) - - - - - - - - - - - - [e,...] - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/graphs/flow_iterate.png b/doc/media/graphs/flow_iterate.png deleted file mode 100644 index 5f2e08e9b166ebfdb60f894b254efed6a0511516..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4677 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <S> - r - - - - - - - - - - - - - - - - - <TDepValue1> - dep1 - - - - - - - - - - - - - - - - - <TDepValueN> - - - - - - - - - depN - - - - - - - - - - - - - - - - - ... - - - - - - - - - - - - - - - - - <E> - events - - - - - - - - - - - - - - - - - INIT: v = init -STEP: v = func(e,v,v1,...,vN) - - - - - - - - - - - - v1 - - - - - - - - - - - - - vN - - - - - - - - - - - - - [e,...] - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/graphs/flow_iterate2.png b/doc/media/graphs/flow_iterate2.png deleted file mode 100644 index 8ee567e09a1c4934fc01edb91018fbca5e9055d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8052 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 diff --git a/doc/media/graphs/flow_makesignal.graphml b/doc/media/graphs/flow_makesignal.graphml deleted file mode 100644 index 74177539..00000000 --- a/doc/media/graphs/flow_makesignal.graphml +++ /dev/null @@ -1,206 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <S> - r - - - - - - - - - - - - - - - - - <TValue1> - arg1 - - - - - - - - - - - - - - - - - <TValue2> - - - - - - - - - arg2 - - - - - - - - - - - - - - - - - <TValueN> - - - - - - - - - argN - - - - - - - - - - - - - - - - - ... - - - - - - - - - - - - - - - - - v = func(v1,v2,...,vN) - - - - - - - - - - - - v1 - - - - - - - - - - - - - v2 - - - - - - - - - - - - - vN - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/graphs/flow_makesignal.png b/doc/media/graphs/flow_makesignal.png deleted file mode 100644 index 09412e5f5ee09c9f4b8476078fbd40e246194567..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6945 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 Pf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <E> - r - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - [...] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/graphs/flow_makesource.png b/doc/media/graphs/flow_makesource.png deleted file mode 100644 index 89423d5ed5837cdb8637de68f5c74e10e8231d48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1521 zcmY*ZeLT~79RH%zQlnQVrm0mPZk0TQal>xPh7Mz7od}tSY@VWRn#Cb1C(}-Q={zKH zB-BWh$9gpiNu`=4u@2Q48l&@czv*?={qg<2-rv{v^ZI<=pQra3cQ;44+9ovs0NmNh zfee811_-lZ%Ftb9v*ZcDclFK=_Fk<1@d2|i&!eDqD!F<(^>j>jUR-qa*hzQm^5Hkz zv1o6YdO;j_OWb}&%96dDK zr_wc0;i59{iXww@uka&B?$#=G2q}B@_vE^i-CtUqj~Z;={8|TrfJHrh7gs_yivR)- z6MpARLJ*zal?T~kFYx)r=&PN=;o-|as&F%B;2Ij$5yEUeM;AmAoj%r-MurQyw!lgZDn9s6@o{J-oYUjXGT$5 zl)#x_J&R(7LqQYkazjQ+X%y2Wf1L=n&#&H0Y7e-0%8CW2u#d z(25gS9%@ZMhTw*U`GG^BagQIRTRI|Q5%C#<-uNt45vVgllGGZaVd_yfVWWqzAtFVS zs47sE6@~ta@tE6|$V2kl7s8foI@HwjW?5NTBoZlVX|FVb_1gtF9&=tNw!zn}gq-VQ zcuq3etgtc`mF4^778VXRv1g&WiS?0feH9d1P{!pj7K;@b>4pKKFg|^qy1Mk`jUX!Z z3P<2k>8+t_eO)fFp9{qmR(tIfD0L9;iePPXb5lW2nMVIF*i2apor|@QNtF^08N63&iEX_$q2c-{}amA+0xYXItH=0xEQ0G_^F2$ ztjKci1^?&aw#Sd%%e}6$aa9oMZOCObzw=#>P$x$7OxwtgO%Ss6?{e?IVcGa6_<(}RDa1}W>2Stj5YF8XV)P>&87$k>Q8)t=FCMr^y z4=LV^fWbysO zw?$soYAv@Z?@!xlNE&g7Q{w|4g0obTDjyLi7ea8<;<^zQx#Olx>$%Q0L}1xkE1?|> zoPR*9)V=SYc=dj9_bo2G8#q}b3vq>&J{NPtWqm|MQLHD3$X?m@Zx4qX_M$LRh+NRmDz-t_4X zsbS0ij-Q(hT%5z$?K-}^wCHz-ajFOI-{z|vN5FVfC>khS-UF(Z7Q}V%#knnZKFpLq zNrlpZJIt`Kv2pRO((eQ2LnY8u6s^ue{d71n0q(1kI6dZ3FPf6HyZ+EB?;##ENbMsY zG)p~xZ@EL5p=}}`7i%{s_o?D3RLRqeG^9)>Ti$^jk`L89DzN*|uZq_WE*^Q9vx~#w zFr}}KDoEQfrPD4ulZ?BG?nYWY`8E_N$E8;j3!h{>9 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <S> - r - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - v = init - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/graphs/flow_makevar.png b/doc/media/graphs/flow_makevar.png deleted file mode 100644 index 07349c52aa0f6d03ba7bbc569f8ed2236beedfd8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1675 zcmZ8idpwj`7(U1)&Cn8UrioH1m6G*!am{5Anp}#?r81Iy2<4K|D5a(v*Q5!nOu0ld zGqu>py~ebnwG!hRle?j*#%0Lt$5y-TALsnu^E=OZ-gDmPoad1D@3TV7tH}cZ&^Fc< zjsQq1A#tRfB+?fe%sBxd<7i{C$2q)>-?7K~W`ugBK*EZssd-t>b20gX23bLH@k9BO z#?@ChoV=%CF6&}wy*0FTP~*}sf8JZ9h+S)T5T*JySB_rCD004PamAF#T}ocZiGfk% zqSd;nGLz6&56_TSs#Y1)*H`vr=lEoc^~8ediOfbDB8b+v4VME6Xh4txTwn?xtAiG^^LK_4Q@+eh z*0#jP#!gR1G|3>Vy|+`JYw5U^9Bn}MEX`yxUlM{3LD@Mu^(&&`g3(bf(>5*W^Pqz= z0=6_dKO61~jdfQ9RwHQr%VE_uzZyjbm!>b4N}x2OP^e{$o)&#T;C<>J7{I8hCB1y# z(UH!TOrX3rAk7hXXFvx)3Y8MZEu~&5&Qv_{b|iSmr|~M=?gAc`y$&}u6UfX~Kbp)G z)OB9yc|0sDPHYvWj<+R=r+Z?3KxMYK<{gKfn&K&bp3r?|!AxI{t)~@qJlCmvGExb^ zW>A;+fc(e}Pm)f2cj46_+^$~7ESG$x=GtOq4tx3Si124c4I5}N@%mObcHum-VCNP+ z8^{zY=X|QNOTWnTTY)9o)NaNgdc-#6@}#*5Xzpp&{0Ih&)fmRSm^axKb49wvNoZs) zfuiEZ3J)R@-4GH2O_J##UmJ%_Mki3VM9J&Sih|M~hK_Yiw|E#PqPgIq4#wZ#UteE8 zBO{|Pjk2?yeTz&c#~)d*rM1Lzb7O_asFcZ?N-r-jS%sCkj~*dd_ucmo1PHl!<-~c) zn-n>mai-^FO!vEH_C50}ZSCzQbM*ZDO~EA-VD>Veq^%n5tSv+YD>2@_U8&D?=J%>o zQXAr7J3KzM_np_1oVoHKb^h{!!70VLv>k!HqUvfhH+3v_3x8DgCGHqXntmf0efKYh zL)Lr>+~(Vd4<8nZM7`%oByoAv%wFSXmJGVc7%2n$76(@n#KUQ)T-ziN_SdKRkrut3 zwAAJT0?m*?FDYqlZ4DO=H~07VJF<>A4a4`FhQ`LmCda$$WRM!d&o>&ll?Fd3EG+!s zc)%vc0lvW43mHEvi1;EB3V5|%v*(lK3FZ*A0i!Zww@~WSYG+xdRBzgSLXAN3n-(18 zN+R)Bf#@=Nih;Eor)mT7DUDU)GeaXkhumOU{mays?(4+(EK0ThKa!*yO zRlM24Eh|#HNohhu&^icT@*GpJfaLw+24bj>h~%@lMVhg@g;5n+5|#Y~+WxqEq`5~+ zDg-rVWFW4Xf)Lz5W~yRjY4R@)VtN{BRtBM?SKHY}w{4>za&Sb=a1(#42kT@& zKwM-|8_2x|6J0-cv8SfO=}{l2iSF*53<{mpG2Bra($x-fv*2ZJ<-}dp+aTz-<1XjU zooh(n#XRFRTI$~pnb=70f54S9H#g_=`C?2*W+=a{xwEru)LnphR!`3{C%OG)-#uCC p|Naa9_3N+J{nfqaM|Xo~m!lkSIVE+xq9UIRu(8}{QD}Db;@^*RA=Cf> diff --git a/doc/media/graphs/flow_merge.graphml b/doc/media/graphs/flow_merge.graphml deleted file mode 100644 index ea36b912..00000000 --- a/doc/media/graphs/flow_merge.graphml +++ /dev/null @@ -1,205 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <E> - r - - - - - - - - - - - - - - - - - <TValue1> - arg1 - - - - - - - - - - - - - - - - - <TValue2> - - - - - - - - - arg2 - - - - - - - - - - - - - - - - - <TValueN> - - - - - - - - - argN - - - - - - - - - - - - - - - - - ... - - - - - - - - - - - - - - - - - [e1,...,e2,...,eN,...] - - - - - - - - - - - - [e1,...] - - - - - - - - - - - - - [e2,...] - - - - - - - - - - - - - [eN,...] - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/graphs/flow_merge.png b/doc/media/graphs/flow_merge.png deleted file mode 100644 index 79f11631daab7ba7fa0d0ff72429ae9d4c2b64c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7452 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 diff --git a/doc/media/graphs/flow_monitor.graphml b/doc/media/graphs/flow_monitor.graphml deleted file mode 100644 index a5e67669..00000000 --- a/doc/media/graphs/flow_monitor.graphml +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <S> - r - - - - - - - - - - - - - - - - - <S> - target - - - - - - - - - - - - - - - - - [t] - - - - - - - - - - - - t - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/graphs/flow_monitor.png b/doc/media/graphs/flow_monitor.png deleted file mode 100644 index d4f3d67406f88c73faf81cbb5bd3159edc2580c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3515 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 bk*an;nx6rp&=WLe&`&3=&0hX^`Ir9yY8=fF diff --git a/doc/media/graphs/flow_observe.graphml b/doc/media/graphs/flow_observe.graphml deleted file mode 100644 index 66187620..00000000 --- a/doc/media/graphs/flow_observe.graphml +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - r - - - - - - - - - - - - - - - - - <E> - subject - - - - - - - - - - - - - - - - - func(e), ... - - - - - - - - - - - - [e,...] - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/graphs/flow_observe.png b/doc/media/graphs/flow_observe.png deleted file mode 100644 index de427401c7a7fe8fa5f9808903758fcda8e5af6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4412 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 W@4O{T$ber(07E^vZt+#yi2njeGJ+8R diff --git a/doc/media/graphs/flow_observe2.graphml b/doc/media/graphs/flow_observe2.graphml deleted file mode 100644 index 37013734..00000000 --- a/doc/media/graphs/flow_observe2.graphml +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - r - - - - - - - - - - - - - - - - - <S> - subject - - - - - - - - - - - - - - - - - func(s) - - - - - - - - - - - - s - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/graphs/flow_observe2.png b/doc/media/graphs/flow_observe2.png deleted file mode 100644 index 82f519c70caeff46206f998976096663cb7732cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3987 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - r - - - - - - - - - - - - - - - - - <TDepValue1> - dep1 - - - - - - - - - - - - - - - - - <TDepValueN> - - - - - - - - - depN - - - - - - - - - - - - - - - - - ... - - - - - - - - - - - - - - - - - <E> - subject - - - - - - - - - - - - - - - - - func(e,d1,...,dN), ... - - - - - - - - - - - - d1 - - - - - - - - - - - - - dN - - - - - - - - - - - - - [e,...] - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/graphs/flow_observe3.png b/doc/media/graphs/flow_observe3.png deleted file mode 100644 index ad86b97679fd95b23fa300064ad29bdb02f175fd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8143 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 LYv~e6;}8D@tAUo4 diff --git a/doc/media/graphs/flow_pulse.graphml b/doc/media/graphs/flow_pulse.graphml deleted file mode 100644 index c89f4ee3..00000000 --- a/doc/media/graphs/flow_pulse.graphml +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <S> - r - - - - - - - - - - - - - - - - - <S> - target - - - - - - - - - - - - - - - - - <E> - trigger - - - - - - - - - - - - - - - - - [t,t,...] - - - - - - - - - - - - t - - - - - - - - - - - - - [...] - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/graphs/flow_pulse.png b/doc/media/graphs/flow_pulse.png deleted file mode 100644 index b669ce4e7a2f5ad3624f66d4fe19b1dd1626a1d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5700 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 diff --git a/doc/media/graphs/flow_snapshot.graphml b/doc/media/graphs/flow_snapshot.graphml deleted file mode 100644 index daf1bc3a..00000000 --- a/doc/media/graphs/flow_snapshot.graphml +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <S> - r - - - - - - - - - - - - - - - - - <S> - target - - - - - - - - - - - - - - - - - <E> - trigger - - - - - - - - - - - - - - - - - INIT: v = t -STEP: v = t - - - - - - - - - - - - t - - - - - - - - - - - - - [...] - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/graphs/flow_snapshot.png b/doc/media/graphs/flow_snapshot.png deleted file mode 100644 index db28dfdb457766633da5dede781b59c6a2f85146..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5731 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 Sx&nsB5G{2bwUX<$VgCh%RLar- diff --git a/doc/media/graphs/flow_transform.graphml b/doc/media/graphs/flow_transform.graphml deleted file mode 100644 index 957fe523..00000000 --- a/doc/media/graphs/flow_transform.graphml +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <T> - r - - - - - - - - - - - - - - - - - <E> - source - - - - - - - - - - - - - - - - - [func(e), ...] - - - - - - - - - - - - [e,...] - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/graphs/flow_transform.png b/doc/media/graphs/flow_transform.png deleted file mode 100644 index 9e76db2e74adf639c67ce7743b1d320e363c63e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4321 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 diff --git a/doc/media/graphs/flow_transform2.graphml b/doc/media/graphs/flow_transform2.graphml deleted file mode 100644 index 85baa006..00000000 --- a/doc/media/graphs/flow_transform2.graphml +++ /dev/null @@ -1,197 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <T> - r - - - - - - - - - - - - - - - - - <TDepValue1> - dep1 - - - - - - - - - - - - - - - - - <TDepValueN> - - - - - - - - - depN - - - - - - - - - - - - - - - - - ... - - - - - - - - - - - - - - - - - <E> - source - - - - - - - - - - - - - - - - - [func(e,d1,...,dN), ...] - - - - - - - - - - - - d1 - - - - - - - - - - - - - dN - - - - - - - - - - - - - [e,...] - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/graphs/flow_transform2.png b/doc/media/graphs/flow_transform2.png deleted file mode 100644 index 0af4bdd1301b2b4207186b8a6e565a3f9855dbeb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7915 zcmZ{J2UHWxvo97vK>|vb2nZt5ix>!1dX?T=K$KpD&_WYMjEIINT|s&i_)tO*y%Ufs z5}LHoL8=tNyZGJzedoRNF6Y2WhTWaIGryUQ(A8F@qPR{$LPA2NuBN0a=8f&V*m9avpp@Yb2}!70xvn~G@;omcVoNU@P%~Rf?BUwzb zlfP6za~?0WQ&=t*AD_d^H!_s> zK%wl5^>sV12^Cg-5BJ-c*qE#h!(VlpTUl9YZ&xv4+dwW3@2;sx>%G-cQ5jCY%PWW2 z+ig94+R=ga=iVZ`zZRv6L&dGJ`|RZM3K~xb(T1dtjPM?EX=QYlXlNR_xEy@=s-8hY ze5K~*R@^b?C@m^NE|--QkIXjjuJu8)z3YGLOFeUFX@6Vz$#rEtLBM&3 zc!xu?F1oPBx(8CL1DEi66>HW^_qx8wWpp^@-i%+`Z-sOCtIGjxO6)Y9y}dZ(qIPH0 zcV6{M_sQ<=?%2(4R!i@9TxHKfyL*jpj$v7{$;b?CXNJcq8HcwQ2#5S5))B$&O`UxS zirFJ@d%MfS_jXyt%R{*?-~C=~u($6_nJ84$7U{r}&9Cp6*F7m&+y@oF!i_Llc-F}Glql4(J*v5OZFa37(7iHrLU{Y9{5Bv zRWX+5s{D$}%x4#IL1|Q`)n{R2Qe1Y4q+O1w#1os(E4%|MYhwjpb69IoUwq*T`?UMM zUmeewttTmpTIhbfQQjIa9DWTJKDgG8q^sGvMMq(SVHQ4u6UN80MA5E7(}kZtc{|Ce zssXDOoE(oZBD4fGKSp@(zkFF?RFpe4Rg>e^PbfUOXCfE*dN@bZoJz8ArPz#2KM&?% z7t_q#dlRh&F?{@Z`v`uAZpS{hqN3u*2kv5B5+oi!kfnN8RJ4Q{=XEz(v0^s>w}Go% zS<)-6Lmx`x|4D0qv({OXIwKU= zV-~-rvmvR2gM&8&3Y+9dhO;p@kh_}t2ftAiALPy)W-8$|-+BJmKOWPvi3tY5xNiIv zIg)Xw(%oIByT5wv?tm*)CJciScMflHm>3zs>#%wT2As>5RZlXq$KGDmkozNdGQ2!8 zUTlG7=Tesm4mfoQJ4*(X+&XFRp45R%VDiXlkf(q?vT7sXm30};tL?_+1aFP^v7^%U z3JPcGC}y-2!N|a%!N2j@zx7$1|L=pcY?zSHX1qvK0gIz^%BSx&mkQ=JQn(g&=)nzA z#dGV#U7Xdi^4WKttT7r4A{f2(b>i&0yjhYE%QB>uL2_#XN=r-2XZ$Jp&cwHG#LS7Q zhNp5eGPwNwPG#zs9BMK06r{umc!@QRmvBc&$j|B}S8we0?O$Sq%leO^6`r9UVtiJ= zssF~!CY;LCR(eb)ynA{ugLZfFrOEDO4?n-t1ohjT@-g{IRV3pC^K6c@ryyllC-w^+ zh>3|+iG5`hvi2Uxsrasa@Dcr{o%AFl?_vRyC{yWd|1SdsTTeqHR5+2?zv<>2!N>;^bS#v|d4M#S%BcIeiLIBuO9$#I9TX+AvyZAf>_fS!>V z{9Y6(F$sz6bZH^OpW6^oMIfe|!zo)JxpA+5&$OU}Wz#$6%sxNr+M4U=bvZsh20S)` zI}!ve0v;IM4pu2xUThrZKJp3-4D|K&RacKV1)HdQy1VDu)HNWi(vI5r#>V@~%HgL` zk7n8;8DmmzY+H?e5z8Q|MhEPw#V0sZ3kvWV#4dl75EcH&%lmG$2=}Nd$Z%IcRmBih zz3o3tz^*ifIR*Ch8lg}tM?Uzg%4D>L&?7<%8rzVkZ3}r40pA@<$3WrNYF2i>%2B6S z|5H+;jo53UmYZK(Of~7U;LIHt={VY39U2@gEGk-{Ez>qOPC$8#SGWy{vx2OC=ZA93%hst^#c~3wF^&H-~MPy=S?d$4_5OcCoQF*Bs-hkT4<=3Pc zHCdl-Kul@y$#Wd{_eW1P39=Ey(Gqj(|V_WT5C*>>)bCFJts%CqV<$HHMiml{(yMw++3Vq zmjv%og3C8YdfUSiL!X89q|@db@xg&C+(Y@LR^}MSJjpTGJ7IT@89DIe^Aw!0S^$rXT#igaW z%jg|0Eje0QeHf#v(d4iy9(-W)1~SkQX_};1eAzLi$SOX1HUoglb28p)TI zS~T9t`4tt$h`rgi$o^U{axyZ!wTOiO&yQGmO>HgC+M~a?GUha5|)%CA&^5 z5#>4%%9(E*376O`DzqXwuZ7}W_FOpI^+eo>BHtM3UX)(7gQPNRS$e+cZ63-aEbyPj z4EnY3nZ=i;aDhl95_mC4Hr=u1Q^M5Llz>IWqdHESOS`+fNpn@5oKjL2#y0>KX%B(m z9%M!RG%a_GDHq1iQRIwdMqQ+Hc=ztzL^YCI>}1DliWi<4oAKsNSE0Kuba^u!8gN@) z118KyAKc^cwcL>lF~grCb-s3^hzn zCcrmi1DnddbMstE7r!p9J1L%3&<|y0BEf_AV>-&37Na@~4%ipNjQKy?b5!f?M6W^VWqODyi`8F^83`sM(+wJakWR@E3|`h`_GcueU??etmk1bkYjiA9J|tzWnp@h(n#i;4s}H zA%E)e^^U}B;hYk*5-*(-eqlZz7r0p86!Sd%yr3{zvC4B!MOz#5J4Y#t@01i8cJRd^ z>gcE9173h=P&SaHXQQQ9_oyg2`iKXj8;u8miwgI+4(#P{_k$j;|gAWLq;L5TO>$! zYWz8vqr0(ey4Of~`d^-y>Z}ZT7q_54GOFi=JA&Sk`&rmxs*v-q%*zz2QI0&ox>qV! z9%8Bt46-IerKIsOf6pbyzYzHrCC?HHk0vLECNr}btbTKfixhUeAgo##3Z!=J?>O4h zk_jCaML&E2C%%d=5nSIGe@TY9cz-2X(X&X9_=+|76Pp}!=nNzA4Cy9UVr z&)Cv%`_WeD?Z2v_1yWM6-^lDvK#vRrtB^qHr*=e>i=DH<9CUKhZ&YY_B7d)#NPoNx zA~cGw+DQCyuZV~U{LCExnlu@;r@^wyE%fr$1sllxGq`;&aF%J4socNk%LD|JkLUZB zu#pheVx@@-lg2!cG2@j+)~shdRX-<{R<7h?;Mq$-vT3X2q3}$~n4M=#t-?Kt$SdN< zjDHsm6V;md@uEW*QSxFwa4cMyoCvf_x_|^NJ6i9th>BWzK+LRbVZY7s=R)DasuOt6|MbrGcH${n<=%wiN-!Ki z0@YKqKI_W_F@*s79N`|;p6ZOBx>!dzC1ZjRVq?pHduPYR#f6&~;GQLs0)k2aQDPO_ z-3MV)v3i72Lk%S6T;@6jvx6i~W+IK-yZCq?Kfj)BfU+JxUT6vx=D}^DH|%XX;aqa;+lcUw$cyG<=`TzNloPzF#HQ z`mV?7D6wWBChCK0gIoZV+cKv|Ag@pryk)_%*9KsHsojmgmAi2~h*{M_nl84z5t9Ow z8lqYi{F9TLTf7hh9UWf%b8@9~$UG?tGCLan^5x6lAq_!WtECO?!2N*s9xkf&eIjgh zRe*=5I_S{rnm!8*1p)&oUoEEk`q8<+A@U5aKLEOfvmJQ7?+fsG_aI2MSxt0Xd(BAIPAs~@(oHX&DuPtz5et{laNd8Oo zq2Aev5!LI$e$V07IDL!C!VgkdQn2)gHws@3Pm>!>xo%|i_uA9MNyknvic}JpmIsbn zqM(1T>cfZHN7BT8sFf9AvU2gO!8TCKa%4M+52?nW%mQKRKTnbrmVjo#>mf?5vTL5E z50W)PX+{%Nz2;+rkWi^y3QFd3aB&*3+iIWT`)dQa4eP5}FU|>*R~fUa-|hIGbaY_1 z-u96S=(7QK;cpU5E44|_|4kjC#|tqRksxt7J2Raf!d|_t)>>S1ZKw^pE$+EQ?I;<1 z-3=(HCV08VXmoS5Il##&vpK*bwYy7?ii(aMG{2~UZ3&c@S8P+t^>yvBD&$t?zU9A( zuhCN=YG!6;ywXFpwrI0qH0^~5ELR{>`VqW-ZhK|*o?GYBb3iDJ3rg(XJ<{{f@_jRl zW;Z6CILvCi^>2MyY`yH}F*!-cNJ1o0t&oP?kbIYxCMzM~_>|gKNN1U`bnUM6rw3+t z3LG7EeS8SACn0-r>5f(#|06vlgPLXy-8`|_V&*?-uWG(y~P8D0Y&RH z`&T-0BQL`>Ip=<(isdX%?H#htOayU9S=RaMU57rzG&eV24ir~Y?-xhpi6Z~hSNOI! z1w4G^v)WuL8Ek$s%SZ(8J6T$=08Py~_*EecYU214 zMUO#jV`f}dwOJ|!BPbmbi4hSIfZM@INDCAWhuc+~};Ei#56Qr=_b#LM(1(ls<*)aV{TDs-ooTXOf?u8LH#UQ`}A4Bf@mNB(G7Xi24Yd z2aJ1;oAQS%PzfTV;)jsg%r`tS33M3@O5kv~U3D+}A>_&RInJ=RPjNr}*cMKv5BTzO z)(43kF&(A|afjPB5a8O`X%-zd@A~5fR`T59or$vf-8ehQS-r}l&ai7mnMhrcNbdUW9l>-W{6l>tqW#um#Z0x>Owg z>j4IX5wxzW%E<}Or&FA5IN7&I|0*sjIy+ydbd&?YG0jnx=VH{*x8t97+KZrlOifKK zPWNzh!>AR(+z5syCgL%l%zl7L2=3{hQzddlS05NQfT^6N!&y=FEmr=vzcFcHWVE1i z1DJ>=!0Pnr>B09yx2&-V@rlvV#N=e2y$2E7pP{PA?3|plDg!6asH_wRqo0wYgGxv* za~*2O-qUkSd^%_@L6`m!W^i!OcxX3SzS{qA`?0&byN(VuuoMmFg+JdIe${*8GA5^{ z(ms5!($~M%A+vfi{|9yQ&=n!)Ir}250yGsjq}37^Ld4M1AT^uFEdy3zwZiGeyO)AU;k`#aKF?FwDuaibh>MKe;jHQMM&$Z&V3f zpm^Qqe-m|OC3tS@26qNp+EZnsbf)gabM{v%g#*%Uz5Ojq#*FOUyGVc#qS)jxcBw7R z(9d;`x?;(xSv$9LG!t*NGZ)n#Z`5XHX0j(HZ3W;mRE^4SstlwbNC^wG3%m>?rzS+y zg*1R4#Giy(ylsqsuo)&BjIDdyQpG*y)gFQfXLv)`n`5D=)2yDX9MtNd z&L7dfFP%AgdF1Dxff;RfvYn%2r$3*-T{|=W!Lc!BBe8*oy|u9~1r$vO{_gG@Z&dYp z*KjzT-a<3n^wyMVg7M@?kqPI50>R<0rnekjeP`XZ?-8G|p$PQjBZc&Q#z`OaOifKe z$Vo^rrLsPRsb(A5M=CiZ)q>$`=fv;Mg(&<<{42GcS;~zflTt`3~M;?e9_tArOc@DUc5t zmo?0xy?U0GLCb?&X8u2aG<|;DJKB##yyB92NQ5;ob*7XFgKve2`rDb-Z8^ zBbm@DK4@A{S}Ok3)XFOON8`CejljU#sakirQCq)!+DjK7nb6bI1GwZi+w0`+rp@|{s4rLpG4mlPGk3k!7dc$zpjkWYUKuF@S%vAH z!m0yB_+&;OQ1Ed&0B(_^g-q8xrt~pS`?=@l=48`_T*v!35Dcv{Jpe`ApI8*uiH64t zQ`aeyxtSV(>70Q9?T85&-Ef_dl9Fy|C{Fk#;s0Pk>X@wc`--ZsG7pDOJeWA{qJ4z9 zg}<;#fqe7kjavNSxJLuF8fWXNLBqZtp#LQ5 zTMm$&vFP`x9|C>&E40XIt+lmsxz@qey@Yq~EbD{d)_-I+q_l{C2w-%deVi7TEJVp4 z;skO&QzESl`p{LaPx_~H`UU6>eGzhr*YkK=*1PJd z)O+t}Y+@%xHx@=MO+0Hr*d^`ZXw-AHR_UJ%4!1+&nxH=>NDCw zb}AaC2T&VHwAOwN6P(Kt$I?H+f7soZ=DI%Yd!61LJO==*;Gg5^V2G1j5+auOHQE3E z4tX}>2b=W^@>U*6flrNsi+vv`j|~_9Y0fVxad&l{|9B2SXDeqH7a3L^P0h#$$j*e% z!B$o`&=Alkbc3et>ZOZbNaW^3HEsO?r)74H&#Ez)u?Cs!H`-nVO#m-&0ccbXgX%%1 zROUbJoMf@GvN}9G%*n|CEd*2xU`?K;+y|*jSAK0we&G%2!E9kLkg*?eIQ!u0sDIkI@Yq&Y+z{gFPhb%GydL3O3LG3v>Vv%>V!Z diff --git a/doc/media/helloworld.graphml b/doc/media/helloworld.graphml deleted file mode 100644 index 00b98c9f..00000000 --- a/doc/media/helloworld.graphml +++ /dev/null @@ -1,222 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bothWords - - - - - - - - - - - - - - - - - secondWord - - - - - - - - - - - - - - - - - - - - - - - - - firstWord - - - - - - - - - - - - - - - - - v = "Hello" + " " + "World - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "World" - - - - - - - - - - - - - "Hello" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/helloworld.png b/doc/media/helloworld.png deleted file mode 100644 index 3e23d09a257e0fde697f5804cdd63b1b4577b1f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7126 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - a - - - - - - - - - - - - - - - - - - b - - - - - - - - - - - - - - - - - - c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - x - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/reactives2.graphml b/doc/media/reactives2.graphml deleted file mode 100644 index dda20611..00000000 --- a/doc/media/reactives2.graphml +++ /dev/null @@ -1,358 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - a - - - - - - - - - - - - - - - - - b - - - - - - - - - - - - - - - - - c - - - - - - - - - - - - - - - - - x - - - - - - - - - - - - - - - - - 2 - - - - - - - - - - - - - - - - - 2 - - - - - - - - - - - - - - - - - 2 - - - - - - - - - - - - - - - - - 1 - - - - - - - - - - - - - - - - - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/signals1.graphml b/doc/media/signals1.graphml deleted file mode 100644 index 5f2c894a..00000000 --- a/doc/media/signals1.graphml +++ /dev/null @@ -1,178 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - a - - - - - - - - - - - - - - - - - - b - - - - - - - - - - - - - - - - - - c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - x - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/signals1.png b/doc/media/signals1.png deleted file mode 100644 index 372383bf2d5bef80f80fdfc8fa243f177c7a485d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3869 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 Hrw9K4*hpy? diff --git a/doc/media/signals2.graphml b/doc/media/signals2.graphml deleted file mode 100644 index 03a90b8a..00000000 --- a/doc/media/signals2.graphml +++ /dev/null @@ -1,358 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - a - - - - - - - - - - - - - - - - - b - - - - - - - - - - - - - - - - - c - - - - - - - - - - - - - - - - - x - - - - - - - - - - - - - - - - - 2 - - - - - - - - - - - - - - - - - 2 - - - - - - - - - - - - - - - - - 2 - - - - - - - - - - - - - - - - - 1 - - - - - - - - - - - - - - - - - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/signals2.png b/doc/media/signals2.png deleted file mode 100644 index 019f747b18dbd5860253d67e48ae8221a661a0a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9576 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 diff --git a/doc/media/toposort1.graphml b/doc/media/toposort1.graphml deleted file mode 100644 index 1cf13a90..00000000 --- a/doc/media/toposort1.graphml +++ /dev/null @@ -1,579 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - a - - - - - - - - - - - - - - - - - b - - - - - - - - - - - - - - - - - c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - x - - - - - - - - - - - - - - - - - a - - - - - - - - - - - - - - - - - b - - - - - - - - - - - - - - - - - c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - x - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - a - - - - - - - - - - - - - - - - - b - - - - - - - - - - - - - - - - - c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - x - - - - - - - - - - - - - - - - - b <<= K - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - level 0 - - - - - - - - - - - - - - - - - level 1 - - - - - - - - - - - - - - - - - level 2 - - - - - - - - - - - - - - - - - x=(a+b)*c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/toposort1.png b/doc/media/toposort1.png deleted file mode 100644 index bf7270ee9f51d7e86cb20e82814425079b0b835c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25492 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 r{3t?B_}32#*#eOt - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - task 1 - - - - - - - - - - - - - - - - - P= { - - - - - - - - - - - - - - - - - } - - - - - - - - - - - - - - - - - task 2 - - - - - - - - - - - - - - - - - task 3 - - - - - - - - - - - - - - - - - task 4 - - - - - - - - - - - - - - - - - - heavy - - - - - - - - - - - - - - - - - - light - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/toposort2.png b/doc/media/toposort2.png deleted file mode 100644 index 87f36465eaf0d199bfe8364893890938fe5b203a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10369 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 ac4_4C)HTriFTm_%WGV{kcM9YlJ^No2rQ@ss diff --git a/doc/media/toposort3.graphml b/doc/media/toposort3.graphml deleted file mode 100644 index 69529a72..00000000 --- a/doc/media/toposort3.graphml +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - op2 - - - - - - - - - - - - - - - - - op1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - delayed by op2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/media/toposort3.png b/doc/media/toposort3.png deleted file mode 100644 index d60b848a68ad6010db2027f2ecb78e3f8c2f89bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7912 zcmZvh2UJr{xAzf6=^#?1qx9aShN7T=fb=R&5Qs<#y(NHB1q7uAqzMWrRp~VdNJkKr zUP7n>hCpaZfaE(q?{mL(@49!^Iwx6^GiN3C#biP?M37(cRa(YX*EC zk&#`Xrz8iyE$#Q)lacY%-oJatB6JR25anezi6^xOJJ>aTaDFUNVPaWaHPR+$)>vV} zV#%7!_n7_CRg+s`gIZIr@mp=_sUPZenhbZ!UIz80TNn=_Zo-9zyhg*CfHFKj86rL!k^qq1F9R2n3?5t7~UBpDy0B zL&vEYt5s|x5xo56d7!F;2=mO7wfVqrV`F7zAD=&e?pf37eqVun|NQ*Cg?A}*l}?c# zuV`d$Zr&b(nMw)tepv5L_mc3@ynN>U+B~IOz3ufs#*x?zdNb7X<}+ckDUytitpi(2KJ5Ed z39?ib#exV64ZRL_HqXH5&`dRVb~=D%nG(NbPvaX~?dgd_!&%}%(_F@6ipqGO*VJ+3 zCR5)O&lsyW zL1+>TDv>kkJ>Nv-i}DTi@fpK_DI}WnuhOe--@Y`|EbZI-kmf1n70wNr_hzE|mL>_g zx$I_o2=!otwzf8b%)7p`%hX?JfVoJq@lzFBn>P@?1M~TUF6u?s1gc8D=w$=LHSote zX?k}T();av5?I)DDV8*C1&~M7w`t7wTJs!vaV!4y+$#RD%ITQ41s`@%3aE$P<+j>d zEi%U)owbD=^>thHqHRrOFJ2_#LSoujykQ8ZTeI z{Qh)tZF?d%15=@(piuf!i5L!Q`YOJD5EjU^+%FeKL5I zL5ll^5c%aF_Yos{;lW8jx5wnHEuAvYX7h){z zitT7r28PdoAe}Mvo`r>nmlsSD#MfBJ5x-_%E&puua@BY+q;v&$2g~j1ccz4=Z&8j) z09xfo-ABY!&Cc4|Ce9?`4?ak4j%Q%RT>A*Z=_=hc{`V1;jH68wPm0Dj>8ftfHhJ+6 z9#sy8piRqih3pGuUP=swNWRI$Fa z#W$GlBRn$^Q86mA`F~!_PEGYMZViPvyvfP=e$wF;9??Rg#~<7D_V!-hExIgj#5&Zh zw2ek9c#{mo`yeZ_O7uj4s`yZQWXm;PH$Nd=)Ucm#Zxw$#6n4bDsFJ-Q53CC~I;OP3_tF3{6Rw@w*|DTmNjpc zCQt-B>@r%!Gp9qG)G~zV)3V}L`P8^5Z{m+{@7-$ho-g~by|v}B`$3((Ur#y?27Pya zdLo@I@6q81MJw!^DHHQx)0IEVjYl|W$rfY9hR(QK3|s9}0W+Tq`uRXfG-t0TAAd>i z3r*@Zu*z#pxa9g91<#icW1PdoA9gTHf%FibhlYmkdf6#`ow`5=*23!GACr@AZyDZB zzjma?*G#D1Y`GPDZliu&3fDr2XBEcC3xMl#1VX5!`$q5G9%}YWcT)gdsTVQ8$zODV z>op8Mjp|J(Wl+oe_QuKi65LH!!EEi6w3VW?^m_90wZvG-$;h=d-8Z1)WSHGXZeUQ5 z?X6tv@zTTNVuCxls;a6I1-�`WvEv?^orzKK9h}2IA`LZqBr*pIZ%T3)O?U5<)GO zSnWg0MtWY2QBh6FuOWbkF)WZneVTGPUfFL!Yd%o4@PI(zETW83Cx?fO3sxvQ$%}c0 zLTn9__il%gS+V8j~pygkbW7g8;~)M=SpDP6uY{4E*_d%99;9HcI(gma&z~QSM&BT?OzDgWsKFE%A?FFu!@7v$dG-Q8VrN-9F;1PcLv7X+d)za=BX_t;9@ zDVfDcvBrE~hL&Bw0y_V^!{`|cvns7hWaQ(yTv)ru)2B~Qb{8k5WbKC@&eb7u-(+*j z)I0p()wU7UoRtl?YCM>0^i+p{u?`Psy;$Slhh~M&(x84%yn9v`Djl1cNOirqySqCb z#Xm|rEMdRZO8aS6O7zSo>tXl!^nBQCom-z!*w3;Dppe~;%|a;R91V%WK#`oSH#_2y z(X6{WJM$f32j!aQ9&ljff`#c5Pk!AA27`rb=$_SxVYRQVPDm{NV!ES&TaFp_YF>`M zfrmM=W9z}=B?a8Bz4G1dWQ0>BiKy3S=0j+lcHDWgi9$J7>Z*p0D^=AZCiv$>m=b?! zW-@TCwthT)8+!irnsI}d`*I%gtjs3_F6EFk!_o5Um+&pQs(7EgJ+4p9?Y|5Vlf;hj zpUbUV1CHqg(dEFVxoG8zf}naj%}bY|UqR^EdhqEnnowAf-Bb^Tt%{cEYVOfTEYjE@ z%Oseow~Sz);)038)`$|$X$WkUlb=W#l2(}T`Ex-hiqqZFy-y3}JA;df2&O z=d8yaxtc9$w`LuMA8}f0h9F-Q=?gH0D^&!cJHBSL%` z3n(5{%M=mY=0$Zi1v@L8M$x#1XY-l~^A1~q4;d3)UGK10j!gTnx|1bnEuW78Xu7X? z)FOcc(#maR&&3i-y0B^o+5DGO`jUcc3Ls?OJvz`fX(>i76@LL4-**LqER;pi_p{N` zKBl1QL8XN|LtH2gMFDxS2gs=@)|Y|Qb9nh8{7Y8@4px&k&(;2vriOqNnC?5%#IkQ& zT?K%t8XC#Cx9EXl-lI^ctMUyHqz1bTI21J9(~O|-t#=vj%N%kgmrv$FOBCL(chQH} z@QTm1vLf6(tU6@&gzq1DDWS01ir#a#TnbIZ59V8+BHR{b&J+R$gEWfZ1IeAMITK^U zq0$a*zve#+I(kgkIHViMHWgHEfOuY27R3r@e)i>C%epg~jg>?%Oin7eR4`&Wmc}Oc zQ%uarX)>lK$JYHGf!=|{TG)};k^4#uO1KhImFl}q)kl{ zdpy@bQP>7Md|dy-<3}Lt?q;<_j;?~WQUf?o=<p^?A-j>argdgaSz6hw7oojD?;Li`R0gg_=mm$A}#F@)E!A@AIAk&cCLe z;f(^Y2bS%u=KUC?P&bp=ZbUCe9M6K2@!8NQ=C|zhPg9Vt^Cn^=p)2bkC0zfl`&HzRF+!3# z{rMlTra3EH(+0fjOfYr1whpA68o~{WI-JmzmzN)QHy8qJ*0^;}Dt!B6xe3$pQYr1W z$3Ih>V*{6C&^(i&h8n#vr*1I>xbLxb2~FbuD{04$7o24?XurXk;Nu(&dcOG`nTzj$ zUju(CWVWtv-@C7V&SHog6#N>(RybCq`6~UDCb4BzIXX9P#B&vdi^L8I8)UqMHkW-7q{|NT)@7pMG}YLR3m!#sO^f`^zDIvq;(&J7ou{F2ay)cCj6F z`MIK27iG6W4BV;~bpj?+3_X*Gz^(K)YD-CutFdRz3uRB`|D?|~Ly8QDYsCpX{*S^p z3qrCx+N8=qmnc!!SO3 z;v_p&bGF6ru_YcdIY8fqpU~wE+N<`T(Inx={VpME72R*+aPN4}^;k|PYa=OYHwr?Q zx|5%t9=my1wht6pC`axA9!5_8E~YE+P`_ezAeB4d5Bv9}rQIdX;r$pFpX6HdROPB# zRpNjq@#>##!Oe}0$nza1gU@w#X_unT9<8r-P1vOAt!-D>1xCMtL1|X3&M-I2-BTd?c{{}n*tx7Zuko)h3e33X(yhBqsqhI1EYdfU$L{- zZL__=^6*b)5tH%F0g|xs(a}NjsIpQu`P`SN5&o&EF#rl0jvi0isP@jc>M$t(uYja#*>OG7~Yu{3Loog2xe^gjV*^Dp87a3uX;gDjqJmTvX6eTeNsItLY- zy!J}06X}j`cLhNfS|%_LE&@@5tAUpncwFZh*%O>A;T8pHVIR;W!hF_g+6=cC3g4A9 z%fsY>N6d zgYbZWpqkMPH_8I<4~C$jU?(<=gQ=!NYP=KrE6P|K3sce@FpRWXVFGeT*p z)_3SgmdhlosAyHKGQN30X%kd#F8DM)K0bOvdn*idkznW3TKX^m;W;gJscKK`;?$VN z*jtsLZ41AhN-{21?&mZT-wzKP3O#x}xt_0TXlT%D-@FzZaTMk??SiIlr%6TxY>ok9 zh8p2;^2z?+5EdDEik@%s4p6%i{c8*%eI5SIy%+UqS9V$`RETeBX@9|M^AT1uUL{Na z=;UN%C7gwN-C1wBq`@dY7VX80Ecr*FiwTZRqm0wqvpf`kKCS#?agjSk2UTTkWYqM~ z2Y5^gSt{;~66N|_?oJfP%PvY}9~r?%~% zsF9mIedZO4kR`4`g-ah?tKLW)J~@H)8PHYvBWm1Mx$lz`eO6>;HCS|^BL zx_0__c=LdMvwckcis&#OqL@gG9C#zwAzy~ z12Ger_7!?A6PPA;kiUb+;ybRepunNt{Trb5#k2*{PyT>ur|G%+`o3n1sVjsuN;%5q zz8qNX?Cj*`=I+~BiTkT(UhWbsF#5?~F0ssMq$ifeas9eT>#v5>wz7Ye%$7(Ax0}fS z$e1<3t&TOM2f1S*ig|I}Qan5&r%GJv;fIG?Kl@IV4F3w2z5EF$X0)oKD#LrjrVmy* z|EFN-FgauWmR8puNoNwY%D})7K;o{Tg7p%%FJA0+5ny^QS$p6a(8_g|ktJ@uU87P+ z))8wFgR?r7+ji%S)Vp}xwdP7W9D+be0!1?VI?n2F8 zwChiwzk*~Y7}k5Av53?6bknz8yugTNWv_3x)Htj+M1V=w^(ncJq@Ts|`9Yi65ti;P zg(z;}0ZDMLZ`~c_zeXu7X$eC5w!6J&{!@B`{fRBH0toK6m@9%2?bp>Ii|Z0154SEWnG?$N?c%7&c~SN;(tzt~w-SeH|6hOfoHadpBM zzIkKT&l%OpZMTwj=oYihE^UOcOT&b!)qIx?OZ18Dkh95$0lM1{A-QW;;umIw1_2%{IoW4+oPofDfY zC|sl{lCY(7AgSHyo!c0BhV!#T?e6+uy(N&_vQT8i31E>o%-R5u+s6Q8G4llB*2sMj zW1#yY@maSW!YOQ>WkFuts%oY+U^5Sb*m}EmhCc`a+EtA7&@kJ)EKf{K1nOQ`qSN9Fr zT21?|nPuB*D#&EUFS{8T31X&3!F?9njv!H|c^&QHM^8J%Rh|JM2rsF`<{dt}klSwK zS#e>y*8XdV{a~Z#^l%3Avu!ezSdCq?QPeMT%3SYi_}Muo%1O;%uhne$JupdzMioB_ zS}qJ1_H>2|;Z!jD> zkX%O3Y5UDSjicolhKg>xiZFwk#h>l;V^}D#-=yhQ^lKN!3bpT%R35Sph`tbh28uXX#TEB0HxGMBm zqJ!g-L8@=b1hHAt!0&V|3RF`Xlb*h(QqxeV^h`EhmnPgdh`T|Q;Ec8y5-toaNjGn? zu4O`8<;w=fCxh-o#yo56slWYc_ZcSxZwuCOy)j4gV<~smMZR&DP;$bN`L(sLVZJCT zIBzWwSqu0T$6R!i!*r1%zb2FE>9-aAUF#kGod@FJ%gy$)s>+wZ@2aAr^X^7jkCSN- ze!o-p->Giyna0XMM$SSc(Jdhu$8}6a&S=_O+*k-AW*XS$N=wnjX4XdPD+bu=sY$Xp zU)AJ0mz0KiMwBxDSxIblv}va9+r%i|@ZZICK{gVI4qw~w{0gwg9_v_%mgAP71@!I8 zFBW>jUMEQ|*YIKY2@co7dk$~W3zzzn3fldCq30;Tb(m}*SsGz8dJ=K6Xs!6ca_ABz z7rbfCNYg<9B_paCQh92y>f&M{s! z#^TkguF`&z%She1V5}X4_%OjNMlu;yD(nNO+$q}SlVcR&XHinExZxXrZ*DuQmi!X0 zqa?f)E1OeX7v)7RH`dA+?Y|})QMXdJT=kAVwf^s(xXp$63)8yi-1~+H_zhflKj6CG zaVRbL#!Lq7DWd!Lk-RiglH8<}ss3yBD(ipaugRcKF&K(5q{rJe+3FL%hYzoK;s6|u z_c$jU`POzFH`Zo-yOQU0Wj)jGOY@xgQptaE8&?eNNoypq(O!uxT8|I-uaWx&OUL!l4mNk5W4J7sdi0!S`mgD~mLxhO=MhSEt<`PsVN4Jj-8pekGe zLWG$*UD6ata+pW%S97sMu)jY(4*vh_889zbT2z+>aWg;S-paah5-fCyhZ)Fy!I-in zKYA0$?;$*|Y8h@3(vEs|cn$OZ<;rUbVLlZTFzr27SMg9&FnDuj#-?#Yt!SM8(PRNM z>jOInhYN$?Bo`vfs6f@yuvI_sONlTOl??$%1gO7Dm{ukqh=9S=Sd)h?IXgx006bLV zrN~L25cH~5v0MJa{?W_LvcYDjXlJ8RGk`|wUO2~Tg~o@075xa4u)D*UEoO_~QQs!2cr!>@`8U5Xag}^Koua4#;4W O-M?paw?^wx^#23rgiR&@ From 244fa1cb99671f876c8aa3fff9c5ed206c427175 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 29 Jul 2014 23:44:22 +0200 Subject: [PATCH 191/266] Updated readme. --- README.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a580cce6..a10f27b3 100644 --- a/README.md +++ b/README.md @@ -93,24 +93,30 @@ SignalT area = MakeSignal( return w * h; }); ``` -``` -// Signal values can be accessed imperativly +Signal values can be accessed imperatively: +```C++ cout << "area: " << area.Value() << endl; // => area: 2 -// VarSignals can be manipulated imperatively -width.Set(10); // Width changed, so area is re-calculated automatically +width.Set(10); cout << "area: " << area.Value() << endl; // => area: 20 ``` +Or, instead of using `Value()` to pull the new value, callback functions can be registered to receive notifications on a change: +```C++ +Observe(area, [] (int newValue) { + cout << "area changed: " << newValue << endl; +}); +``` + Overloaded operators for signal types allow to omit `MakeSignal` in this case for a more concise syntax: ```C++ // Lift as reactive expression - equivalent to previous example SignalT area = width * height; ``` -### Event streams and Observers +### Event streams Event streams represent flows of discrete values. They are first-class objects and can be merged, filtered, transformed or composed to more complex types: @@ -129,7 +135,7 @@ EventSourceT rightClicked = MakeEventSource(); EventsT merged = leftClicked | rightClicked; // React to events -auto obs = Observe(merged, [] (Token) { +Observe(merged, [] (Token) { cout << "clicked!" << endl; }); ``` From 1659bf4ce62c8f6f7050f04c771995e1676d51a5 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 30 Jul 2014 14:03:43 +0200 Subject: [PATCH 192/266] Continuation target domain defaults to source. --- include/react/Domain.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/react/Domain.h b/include/react/Domain.h index 1279c3f9..7fb1ef16 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -145,7 +145,7 @@ class TransactionStatus template < typename D, - typename D2 + typename D2 = D > class Continuation : public REACT_IMPL::ContinuationBase { From 6c297aa67a443f8c3db1966f35789702a1932d52 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 30 Jul 2014 14:04:17 +0200 Subject: [PATCH 193/266] Cleaned up LifeSim benchmark a bit (it's still not great). --- benchmarks/src/BenchmarkLifeSim.h | 63 ++++++++++++++++++------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/benchmarks/src/BenchmarkLifeSim.h b/benchmarks/src/BenchmarkLifeSim.h index b53856dd..1ebab386 100644 --- a/benchmarks/src/BenchmarkLifeSim.h +++ b/benchmarks/src/BenchmarkLifeSim.h @@ -67,6 +67,8 @@ class Time template class Region { + Time& theTime; + public: USING_REACTIVE_DOMAIN(D) @@ -83,21 +85,17 @@ class Region SignalT FoodPerDay = MakeSignal( theTime.Season, - [] (int season) { - return season == summer ? 20 : 10; - }); + calculateFoodPerDay); SignalT FoodOutputPerDay = MakeSignal( With(FoodPerDay,AnimalCount), - [] (int food, int count) { - return count > 0 ? food/count : 0; - }); + calculateFoodOutputPerDay); EventsT FoodOutput = Pulse(theTime.NewDay, FoodOutputPerDay); Region(Time& time, int x, int y) : - Bounds( x*10, x*10+9, y*10, y*10+9 ), - theTime( time ) + theTime( time ), + Bounds( x*10, x*10+9, y*10, y*10+9 ) {} PositionT Center() const @@ -126,7 +124,16 @@ class Region } private: - Time& theTime; + static int calculateFoodPerDay(int season) + { + return season == summer ? 20 : 10; + } + + static int calculateFoodOutputPerDay(int food, int count) + { + return count > 0 ? food/count : 0; + } + }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -138,7 +145,7 @@ class World public: USING_REACTIVE_DOMAIN(D) - vector>> Regions; + vector>> Regions; World(Time& time, int w) : w_( w ) @@ -199,14 +206,7 @@ class Animal EventsT*> RegionChanged = Monitor(NewRegion); SignalT Age = Iterate(theTime.NewDay, 0, Incrementer()); - - SignalT Health = Iterate( - FoodReceived, - 100, - [] (int food, int health) { - auto newHealth = health + food - 10; - return newHealth < 0 ? 0 : newHealth > 10000 ? 10000 : newHealth; - }); + SignalT Health = Iterate(FoodReceived, 100, calculateHealth); Animal(Time& time, World& world, Region* initRegion, unsigned seed) : theTime( time ), @@ -239,20 +239,29 @@ class Animal [this] (PositionT pos) { return theWorld.GetRegion(pos); }) + ), + regionChangeContinuation_ + ( + MakeContinuation(RegionChanged, With(CurrentRegion), + [this] (Region* newRegion, Region* oldRegion) { + oldRegion->EnterOrLeave(leave); + newRegion->EnterOrLeave(enter); + + // Change region in continuation + CurrentRegion <<= newRegion; + }) ) { initRegion->EnterOrLeave(enter); + } - Observe( - RegionChanged, - With(CurrentRegion), - [this] (Region* newRegion, Region* oldRegion) { - oldRegion->EnterOrLeave(leave); - newRegion->EnterOrLeave(enter); +private: + Continuation regionChangeContinuation_; - // Change region in continuation - CurrentRegion <<= newRegion; - }); + static int calculateHealth(int food, int health) + { + auto newHealth = health + food - 10; + return newHealth < 0 ? 0 : newHealth > 10000 ? 10000 : newHealth; } }; From 2c77bd7add90d688188ee4961f55d2893b899d52 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 30 Jul 2014 16:02:32 +0200 Subject: [PATCH 194/266] Examples cleanup. --- examples/src/BasicSynchronization.cpp | 68 ++++++++- examples/src/Main.cpp | 195 +------------------------- 2 files changed, 69 insertions(+), 194 deletions(-) diff --git a/examples/src/BasicSynchronization.cpp b/examples/src/BasicSynchronization.cpp index 38f05f3e..2e49be40 100644 --- a/examples/src/BasicSynchronization.cpp +++ b/examples/src/BasicSynchronization.cpp @@ -9,6 +9,7 @@ #include "tbb/tick_count.h" #include "react/Domain.h" +#include "react/Signal.h" #include "react/Event.h" #include "react/Observer.h" @@ -37,7 +38,7 @@ namespace example1 Sensor mySensor; Observe(mySensor.Samples, [] (int v) { - cout << v << std::endl; + cout << v << endl; }); TransactionStatus status; @@ -157,14 +158,79 @@ namespace example2 } } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Example 3 - Continuations (1) +/////////////////////////////////////////////////////////////////////////////////////////////////// +namespace example3 +{ + using namespace react; + using namespace std; + + REACTIVE_DOMAIN(D, sequential_concurrent) + + class Widget + { + public: + USING_REACTIVE_DOMAIN(D) + + VarSignalT Label1 = MakeVar(string( "Change" ));; + VarSignalT Label2 = MakeVar(string( "me!" ));; + + EventSourceT<> Reset = MakeEventSource(); + + Widget() : + resetCont_ + ( + MakeContinuation( + Reset, + [this] (Token) { + Label1 <<= string( "Change" ); + Label2 <<= string( "me!" ); + }) + ) + {} + + private: + Continuation resetCont_; + }; + + void Run() + { + cout << "Example 3 - Continuations (1)" << endl; + + Widget myWidget; + int sum = 0; + + Observe(myWidget.Label1, [&] (const string& v) { + cout << "Label 1 changed to " << v << endl;; + }); + + Observe(myWidget.Label2, [&] (const string& v) { + cout << "Label 2 changed to " << v << endl;; + }); + + myWidget.Label1 <<= "Hello"; + myWidget.Label2 <<= "world"; + + cout << "Resetting..." << endl; + + myWidget.Reset(); + + cout << endl; + } +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Run examples /////////////////////////////////////////////////////////////////////////////////////////////////// int main() { example1::Run(); + example2::v1::Run(); example2::v2::Run(); + example3::Run(); + return 0; } \ No newline at end of file diff --git a/examples/src/Main.cpp b/examples/src/Main.cpp index 1a8f1ee3..89f61f07 100644 --- a/examples/src/Main.cpp +++ b/examples/src/Main.cpp @@ -18,205 +18,14 @@ using namespace std; using namespace react; -// Defines a domain. -// Each domain represents a separate dependency graph, managed by a dedicated propagation engine. -// Reactives of different domains can not be combined. - - -void SignalExample3() -{ - REACTIVE_DOMAIN(D, sequential_concurrent) - - cout << "Signal Example 3" << endl; - - auto src = MakeVar(0); - - // Input values can be manipulated imperatively in observers. - // Inputs are implicitly thread-safe, buffered and executed in a continuation turn. - // This continuation turn is queued just like a regular turn. - // If other turns are already queued, they are executed before the continuation. - auto cont = MakeContinuation(src, [&] (int v) { - cout << "V: " << v << endl; - if (v < 10) - src <<= v+1; - }); - - src <<= 1; - - cout << endl; -} - -void SignalExample4() -{ - REACTIVE_DOMAIN(L, sequential_concurrent, ToposortEngine) - REACTIVE_DOMAIN(R, sequential_concurrent, ToposortEngine) - - cout << "Signal Example 4" << endl; - - auto srcL = MakeVar(0); - auto srcR = MakeVar(0); - - auto contL = MakeContinuation(srcL, [&] (int v) { - cout << "L->R: " << v << endl; - if (v < 10) - srcR <<= v+1; - }); - - auto contR = MakeContinuation(srcR, [&] (int v) { - cout << "R->L: " << v << endl; - if (v < 10) - srcL <<= v+1; - }); - - srcL <<= 1; - printf("End\n"); - - cout << endl; -} - -void SignalExample5() -{ - REACTIVE_DOMAIN(L, sequential_concurrent, ToposortEngine) - REACTIVE_DOMAIN(R, sequential_concurrent, ToposortEngine) - - cout << "Signal Example 5" << endl; - - auto srcL = MakeVar(0); - auto depL1 = MakeVar(0); - auto depL2 = MakeVar(0); - auto srcR = MakeVar(0); - - auto contL = MakeContinuation( - Monitor(srcL), - With(depL1, depL2), - [&] (int v, int depL1, int depL2) { - cout << "L->R: " << v << endl; - if (v < 10) - srcR <<= v+1; - }); - - auto contR = MakeContinuation( - Monitor(srcR), - [&] (int v) { - cout << "R->L: " << v << endl; - if (v < 10) - srcL <<= v+1; - }); - - srcL <<= 1; - printf("End\n"); - - cout << endl; -} - void testme() { - REACTIVE_DOMAIN(D, sequential_concurrent) - - std::vector results; - - auto f_0 = [] (int a) -> int - { - int k = 0; - for (int i = 0; i<10000; i++) - k += i; - return a + k; - }; - - auto f_n = [] (int a, int b) -> int - { - int k = 0; - for (int i=0; i<10000; i++) - k += i; - return a + b + k; - }; - - auto n1 = MakeVar(0); - auto n2 = n1 ->* f_0; - auto n3 = ((n2, n1) ->* f_n) ->* f_0; - auto n4 = n3 ->* f_0; - auto n5 = ((((n4, n3) ->* f_n), n1) ->* f_n) ->* f_0; - auto n6 = n5 ->* f_0; - auto n7 = ((n6, n5) ->* f_n) ->* f_0; - auto n8 = n7 ->* f_0; - auto n9 = ((((((n8, n7) ->* f_n), n5) ->* f_n), n1) ->* f_n) ->* f_0; - auto n10 = n9 ->* f_0; - auto n11 = ((n10, n9) ->* f_n) ->* f_0; - auto n12 = n11 ->* f_0; - auto n13 = ((((n12, n11) ->* f_n), n9) ->* f_n) ->* f_0; - auto n14 = n13 ->* f_0; - auto n15 = ((n14, n13) ->* f_n) ->* f_0; - auto n16 = n15 ->* f_0; - auto n17 = ((((((n16, n15) ->* f_n), n13) ->* f_n), n9) ->* f_n) ->* f_0; - - auto src = MakeEventSource(); - - atomic c( 0 ); - - Observe(src, [&] (int v){ - c++; - }); - - auto x0 = tbb::tick_count::now(); - - TransactionStatus st; - - for (int i=0; i<10000; i++) - { - AsyncTransaction(st, [&,i] { - n1 <<= 1+i; - }); - } - - for (int i=0; i<10000; i++) - { - AsyncTransaction(st, [&,i] { - n1 <<= 20000+i; - }); - } - - for (int i=0; i<10000; i++) - { - AsyncTransaction(st, [&,i] { - n1 <<= 100000+i; - }); - } - - st.Wait(); - - //std::thread t3([&] { - // for (int i=0; i<10000; i++) - // n1 <<= 1+i; - //}); - - //std::thread t2([&] { - // for (int i=0; i<10000; i++) - // n1 <<= 20000+i; - //}); - - //std::thread t1([&] { - // for (int i=0; i<10000; i++) - // n1 <<= 100000+i; - //}); - - //t3.join(); - //t2.join(); - //t1.join(); - //std::chrono::milliseconds dura( 10000 ); - //std::this_thread::sleep_for( dura ); - - auto x1 = tbb::tick_count::now(); - - double d = (x1 - x0).seconds(); - printf("Time %g\n", d); - printf("Updates %d\n", c.load()); - printf("n1 %d\n", n1()); + // Note: This project exists as a sandbox where I occasionally stage new examples. + // Currently it's empty. } int main() { - SignalExample3(); - SignalExample4(); testme(); #ifdef REACT_ENABLE_LOGGING From 7ab678dbb7fc8526412d2efd09c08c40bec81314 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 30 Jul 2014 16:02:42 +0200 Subject: [PATCH 195/266] Readme tweak. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index a10f27b3..7285d7f1 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,6 @@ VarSignalT a = MakeVar(1); VarSignalT b = MakeVar(2); VarSignalT c = MakeVar(3); -// Using overloaded arithmetic operators instead of MakeSignal SignalT x = (a + b) * c; ``` From 318b585cc9d04b23827f354178e6cfb60eaa6d44 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 30 Jul 2014 16:28:39 +0200 Subject: [PATCH 196/266] Example cleanup. --- examples/src/BasicSynchronization.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/src/BasicSynchronization.cpp b/examples/src/BasicSynchronization.cpp index 2e49be40..ec2be1c9 100644 --- a/examples/src/BasicSynchronization.cpp +++ b/examples/src/BasicSynchronization.cpp @@ -173,10 +173,10 @@ namespace example3 public: USING_REACTIVE_DOMAIN(D) - VarSignalT Label1 = MakeVar(string( "Change" ));; - VarSignalT Label2 = MakeVar(string( "me!" ));; + VarSignalT Label1 = MakeVar(string( "Change" )); + VarSignalT Label2 = MakeVar(string( "me!" )); - EventSourceT<> Reset = MakeEventSource(); + EventSourceT<> Reset = MakeEventSource(); Widget() : resetCont_ @@ -199,14 +199,13 @@ namespace example3 cout << "Example 3 - Continuations (1)" << endl; Widget myWidget; - int sum = 0; Observe(myWidget.Label1, [&] (const string& v) { - cout << "Label 1 changed to " << v << endl;; + cout << "Label 1 changed to " << v << endl; }); Observe(myWidget.Label2, [&] (const string& v) { - cout << "Label 2 changed to " << v << endl;; + cout << "Label 2 changed to " << v << endl; }); myWidget.Label1 <<= "Hello"; From 31856896ddbc4e64d2b10f60100969d4a52578ea Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 1 Aug 2014 19:17:11 +0200 Subject: [PATCH 197/266] Changed Subystem to Console for Release profile in some examples. --- project/msvc/Example_BasicAlgorithms.vcxproj | 1 + project/msvc/Example_BasicComposition.vcxproj | 1 + project/msvc/Example_BasicEvents.vcxproj | 1 + project/msvc/Example_BasicObservers.vcxproj | 1 + project/msvc/Example_BasicReactors.vcxproj | 1 + project/msvc/Example_BasicSignals.vcxproj | 1 + 6 files changed, 6 insertions(+) diff --git a/project/msvc/Example_BasicAlgorithms.vcxproj b/project/msvc/Example_BasicAlgorithms.vcxproj index cd4b6e9b..eeffca5a 100644 --- a/project/msvc/Example_BasicAlgorithms.vcxproj +++ b/project/msvc/Example_BasicAlgorithms.vcxproj @@ -118,6 +118,7 @@ true true true + Console diff --git a/project/msvc/Example_BasicComposition.vcxproj b/project/msvc/Example_BasicComposition.vcxproj index 8b336087..01d26c88 100644 --- a/project/msvc/Example_BasicComposition.vcxproj +++ b/project/msvc/Example_BasicComposition.vcxproj @@ -118,6 +118,7 @@ true true true + Console diff --git a/project/msvc/Example_BasicEvents.vcxproj b/project/msvc/Example_BasicEvents.vcxproj index 2e3bad5e..d5d13295 100644 --- a/project/msvc/Example_BasicEvents.vcxproj +++ b/project/msvc/Example_BasicEvents.vcxproj @@ -118,6 +118,7 @@ true true true + Console diff --git a/project/msvc/Example_BasicObservers.vcxproj b/project/msvc/Example_BasicObservers.vcxproj index f99889bf..9fa37b8c 100644 --- a/project/msvc/Example_BasicObservers.vcxproj +++ b/project/msvc/Example_BasicObservers.vcxproj @@ -118,6 +118,7 @@ true true true + Console diff --git a/project/msvc/Example_BasicReactors.vcxproj b/project/msvc/Example_BasicReactors.vcxproj index f05f8265..07701d98 100644 --- a/project/msvc/Example_BasicReactors.vcxproj +++ b/project/msvc/Example_BasicReactors.vcxproj @@ -118,6 +118,7 @@ true true true + Console diff --git a/project/msvc/Example_BasicSignals.vcxproj b/project/msvc/Example_BasicSignals.vcxproj index 653861ba..e090dd8a 100644 --- a/project/msvc/Example_BasicSignals.vcxproj +++ b/project/msvc/Example_BasicSignals.vcxproj @@ -118,6 +118,7 @@ true true true + Console From 32724f98565730f61df79a9ddf1ade97f6e21080 Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 1 Aug 2014 19:20:23 +0200 Subject: [PATCH 198/266] Fixed #2. --- examples/src/BasicAlgorithms.cpp | 35 ++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/examples/src/BasicAlgorithms.cpp b/examples/src/BasicAlgorithms.cpp index 07907c76..22f92cfb 100644 --- a/examples/src/BasicAlgorithms.cpp +++ b/examples/src/BasicAlgorithms.cpp @@ -93,7 +93,7 @@ namespace example2 } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 3 - Creating stateful signals (1) +/// Example 3 - Folding event streams into signals (1) /////////////////////////////////////////////////////////////////////////////////////////////////// namespace example3 { @@ -119,7 +119,7 @@ namespace example3 void Run() { - cout << "Example 3 - Creating stateful signals (1)" << endl; + cout << "Example 3 - Folding event streams into signals (1)" << endl; Counter myCounter; @@ -135,7 +135,7 @@ namespace example3 } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 4 - Creating stateful signals (2) +/// Example 4 - Folding event streams into signals (2) /////////////////////////////////////////////////////////////////////////////////////////////////// namespace example4 { @@ -149,23 +149,36 @@ namespace example4 public: USING_REACTIVE_DOMAIN(D) - EventSourceT Input = MakeEventSource(); + EventSourceT Input = MakeEventSource(); - SignalT Average = Iterate( + SignalT Count = Iterate( + Tokenize(Input), + 0, + [] (Token, int oldCount) { + return oldCount + 1; + }); + + SignalT Sum = Iterate( Input, 0.0f, - [] (int sample, float oldAvg) { - return (oldAvg + sample) / 2.0f; + [] (float v, float sum) { + return v + sum; + }); + + SignalT Average = MakeSignal( + With(Sum,Count), + [] (float sum, int count) { + return count != 0 ? sum / count : 0.0f; }); }; void Run() { - cout << "Example 4 - Creating stateful signals (2)" << endl; + cout << "Example 4 - Folding event streams into signals (2)" << endl; Sensor mySensor; - mySensor.Input << 10 << 5 << 10 << 8; + mySensor.Input << 10.0f << 5.0f << 10.0f << 8.0f; cout << "Average: " << mySensor.Average() << endl; // output: 3 @@ -174,7 +187,7 @@ namespace example4 } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 5 - Creating stateful signals (3) +/// Example 5 - Folding event streams into signals (3) /////////////////////////////////////////////////////////////////////////////////////////////////// namespace example5 { @@ -210,7 +223,7 @@ namespace example5 void Run() { - cout << "Example 5 - Creating stateful signals (3)" << endl; + cout << "Example 5 - Folding event streams into signals (3)" << endl; Counter myCounter; From ccd5f37f9be70ca041ad26b332ca6825b0ad097c Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 1 Aug 2014 21:08:18 +0200 Subject: [PATCH 199/266] Comment correction. --- examples/src/BasicAlgorithms.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/BasicAlgorithms.cpp b/examples/src/BasicAlgorithms.cpp index 22f92cfb..69229ee5 100644 --- a/examples/src/BasicAlgorithms.cpp +++ b/examples/src/BasicAlgorithms.cpp @@ -180,7 +180,7 @@ namespace example4 mySensor.Input << 10.0f << 5.0f << 10.0f << 8.0f; - cout << "Average: " << mySensor.Average() << endl; // output: 3 + cout << "Average: " << mySensor.Average() << endl; // output: 8.25 cout << endl; } From 4850a6a83456823901a8f4bdbbd411252fb731a1 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sat, 2 Aug 2014 17:30:16 +0200 Subject: [PATCH 200/266] Updated readme. --- README.md | 53 ++++++++++++++++++----------------------------------- 1 file changed, 18 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 7285d7f1..957014de 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,12 @@ C++React is reactive programming library for C++11. Generally speaking, it provides abstractions to handle change propagation and data processing for a push-based event model. -A more practical description is that it enables coordinated, multi-layered - and potentially parallel - execution of callbacks. +A more practical description is that it enables coordinated, multi-layered - _and potentially parallel_ - execution of callbacks. All this happens implicitly, based on declarative definitions, with guarantees regarding - _update minimality_ - nothing is re-calculated or processed unnecessarily; - _glitch freedom_ - no transiently inconsistent data sets; -- _thread safety_ - no data races for parallel execution. +- _thread safety_ - no data races for parallel execution by avoiding side effects. The core abstractions of the library are @@ -30,19 +30,17 @@ Additional features include [If you're interested in learning about C++React, have a look at its documentation.](http://schlangster.github.io/cpp.react/) -## Development +## Using the library -This library is a work-in-progress and should not be considered release quality yet. +This library is a work-in-progress. It should not be considered release quality yet and its API might still change. It is, however, in a perfectly usable state and has already received a fair amount of testing and tuning. - ### Dependencies * [Intel TBB 4.2](https://www.threadingbuildingblocks.org/) (required) * [Google test framework](https://code.google.com/p/googletest/) (optional, to compile the unit tests) * [Boost 1.55.0 C++ Libraries](http://www.boost.org/) (optional, to include Reactor.h, which requires `boost::coroutine`) - ### Compiling C++React has been tested with the following compilers: @@ -69,20 +67,21 @@ For more details, refer to the [Build instructions](https://github.com/schlangst ### Signals Signals are self-updating reactive variables. -They can be combined as expressions to create new signals, which are automatically re-calculated whenever one of their dependencies changes. +They can be combined in expressions to create new signals, which are automatically re-calculated when their dependencies change. ```C++ using namespace std; using namespace react; -// Define a reactive domain that uses single-threaded, sequential updating +// Defines a reactive domain that uses single-threaded, sequential updating REACTIVE_DOMAIN(D, sequential) -// Define aliases for types of the given domain, +// Defines aliases for types of the given domain, // e.g. using VarSignalT = VarSignal USING_REACTIVE_DOMAIN(D) // Two reactive variables that can be manipulated imperatively +// to input external changes VarSignalT width = MakeVar(1); VarSignalT height = MakeVar(2); @@ -110,7 +109,7 @@ Observe(area, [] (int newValue) { }); ``` -Overloaded operators for signal types allow to omit `MakeSignal` in this case for a more concise syntax: +Overloaded operators for signal types allow to omit `MakeSignal` for a more concise syntax: ```C++ // Lift as reactive expression - equivalent to previous example SignalT area = width * height; @@ -118,7 +117,8 @@ SignalT area = width * height; ### Event streams -Event streams represent flows of discrete values. They are first-class objects and can be merged, filtered, transformed or composed to more complex types: +Unlike signals, event streams are not centered on changing state, but represent flows of discrete values. +They are first-class objects and can be merged, filtered, transformed or composed to more complex types: ```C++ using namespace std; @@ -128,39 +128,27 @@ REACTIVE_DOMAIN(D, sequential) USING_REACTIVE_DOMAIN(D) // Two event sources -EventSourceT leftClicked = MakeEventSource(); -EventSourceT rightClicked = MakeEventSource(); +EventSourceT leftClick = MakeEventSource(); +EventSourceT rightClick = MakeEventSource(); // Merge both event streams -EventsT merged = leftClicked | rightClicked; +EventsT anyClick = leftClick | rightClick; // React to events -Observe(merged, [] (Token) { +Observe(anyClick, [] (Token) { cout << "clicked!" << endl; }); ``` ``` -rightClicked.Emit(); // => clicked! +leftClick.Emit(); // => clicked! +rightClick.Emit(); // => clicked! ``` ### Parallelism and concurrency -The change propagation is handled implicitly. -Depending on the selected concurrency policy, updates can be parallelized: - -```C++ -// Sequential updating -REACTIVE_DOMAIN(D, sequential) - -VarSignalT a = MakeVar(1); -VarSignalT b = MakeVar(2); -VarSignalT c = MakeVar(3); - -SignalT x = (a + b) * c; -``` +When enabling it through the concurrency policy, updates are automatically parallelized: ```C++ -// Parallel updating REACTIVE_DOMAIN(D, parallel) VarSignalT in = MakeVar(0); @@ -181,11 +169,6 @@ SignalT op2 = MakeSignal(in, [] (int in) SignalT out = op1 + op2; ``` -### More examples - -* [Examples](https://github.com/schlangster/cpp.react/tree/master/examples/src) -* [Test cases](https://github.com/schlangster/cpp.react/tree/master/tests/src) - ## Acknowledgements The API of C++React has been inspired by the following two research papers: From 3b1b6bc3963893a2ed07e31383bf9cfccb84b585 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 3 Aug 2014 19:12:38 +0200 Subject: [PATCH 201/266] Fixed #3. --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 957014de..5c27b6bc 100644 --- a/README.md +++ b/README.md @@ -153,17 +153,17 @@ REACTIVE_DOMAIN(D, parallel) VarSignalT in = MakeVar(0); -SignalT op1 = MakeSignal(in, [] (int in) -{ - int result = doCostlyOperation1(in); - return result; -}; - -SignalT op2 = MakeSignal(in, [] (int in) -{ - int result = doCostlyOperation2(in); - return result; -}; +SignalT op1 = MakeSignal(in, + [] (int in) { + int result = doCostlyOperation1(in); + return result; + }); + +SignalT op2 = MakeSignal(in, + [] (int in) { + int result = doCostlyOperation2(in); + return result; + }); // op1 and op2 can be re-calculated in parallel SignalT out = op1 + op2; From 7b6009eea952cf8b3d1894218ede6a2d2eb7a76b Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 7 Aug 2014 02:29:40 +0200 Subject: [PATCH 202/266] Misc additions to Util.h. --- include/react/common/Util.h | 77 +++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/include/react/common/Util.h b/include/react/common/Util.h index 35e8e771..b50c005b 100644 --- a/include/react/common/Util.h +++ b/include/react/common/Util.h @@ -102,18 +102,29 @@ struct Identity /////////////////////////////////////////////////////////////////////////////////////////////////// struct DontMove {}; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// DisableIfSame +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct DisableIfSame : + std::enable_if::type, + typename std::decay::type>::value> {}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// AddDummyArgWrapper /////////////////////////////////////////////////////////////////////////////////////////////////// template struct AddDummyArgWrapper { - // Dummy int to make sure it calls the right ctor - template - AddDummyArgWrapper(int, FIn&& func) : MyFunc( std::forward(func) ) {} - AddDummyArgWrapper(const AddDummyArgWrapper& other) = default; - AddDummyArgWrapper(AddDummyArgWrapper&& other) : MyFunc( std::move(other.MyFunc) ) {} + + AddDummyArgWrapper(AddDummyArgWrapper&& other) : + MyFunc( std::move(other.MyFunc) ) + {} + + template ::type> + explicit AddDummyArgWrapper(FIn&& func) : MyFunc( std::forward(func) ) {} TRet operator()(TArg, TDepValues& ... args) { @@ -126,12 +137,15 @@ struct AddDummyArgWrapper template struct AddDummyArgWrapper { - // Dummy int to make sure it calls the right ctor - template - AddDummyArgWrapper(int, FIn&& func) : MyFunc( std::forward(func) ) {} - +public: AddDummyArgWrapper(const AddDummyArgWrapper& other) = default; - AddDummyArgWrapper(AddDummyArgWrapper&& other) : MyFunc( std::move(other.MyFunc) ) {} + + AddDummyArgWrapper(AddDummyArgWrapper&& other) : + MyFunc( std::move(other.MyFunc) ) + {} + + template ::type> + explicit AddDummyArgWrapper(FIn&& func) : MyFunc( std::forward(func) ) {} void operator()(TArg, TDepValues& ... args) { @@ -152,8 +166,20 @@ template > struct AddDefaultReturnValueWrapper { - template - AddDefaultReturnValueWrapper(FIn&& func) : MyFunc( std::forward(func) ) {} + AddDefaultReturnValueWrapper(const AddDefaultReturnValueWrapper&) = default; + + AddDefaultReturnValueWrapper(AddDefaultReturnValueWrapper&& other) : + MyFunc( std::move(other.MyFunc) ) + {} + + template + < + typename FIn, + class = typename DisableIfSame::type + > + explicit AddDefaultReturnValueWrapper(FIn&& func) : + MyFunc( std::forward(func) ) + {} template TRet operator()(TArgs&& ... args) @@ -165,6 +191,33 @@ struct AddDefaultReturnValueWrapper F MyFunc; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// IsCallableWith +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename F, + typename TRet, + typename ... TArgs +> +class IsCallableWith +{ +private: + using NoT = char[1]; + using YesT = char[2]; + + template + static YesT& check( + decltype( static_cast( + (std::declval())(std::declval() ...))) *); + + template + static NoT& check(...); + +public: + enum { value = sizeof(check(nullptr)) == sizeof(YesT) }; +}; + /****************************************/ REACT_IMPL_END /***************************************/ // Expand args by wrapping them in a dummy function From 12d72a5d4978aba346ee0961a02c25fa15479d00 Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 7 Aug 2014 02:47:50 +0200 Subject: [PATCH 203/266] Added a new range-based signature option for event callbacks. Instead of passing a function `func(E)` that is called for every event on the stream, EventRange allows to iterate them manually. This allows client code to add node-level parallelization with parallel_for or parallel_reduce. It's supported by existing Observe and MakeContinuation, and also by a new operation `Process`, which is a more generic form of Transform/Filter. The signature of process callbacks is `void(EventRange range, EventInserter out)`. out is a back_insert_iterator. --- include/react/Domain.h | 26 ++- include/react/Event.h | 66 ++++++ include/react/Observer.h | 60 +++-- .../react/detail/graph/ContinuationNodes.h | 45 +++- include/react/detail/graph/EventNodes.h | 217 +++++++++++++++--- include/react/detail/graph/GraphBase.h | 24 ++ include/react/detail/graph/ObserverNodes.h | 74 +++--- 7 files changed, 415 insertions(+), 97 deletions(-) diff --git a/include/react/Domain.h b/include/react/Domain.h index 7fb1ef16..ac049438 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -235,10 +235,21 @@ auto MakeContinuation(TransactionFlagsT flags, const Events& trigger, FIn&& "MakeContinuation requires support for concurrent input to target domain."); using REACT_IMPL::EventContinuationNode; + using REACT_IMPL::AddContinuationRangeWrapper; + using REACT_IMPL::IsCallableWith; + using REACT_IMPL::EventRange; + using F = typename std::decay::type; + using WrapperT = + typename std::conditional< + IsCallableWith>::value, + F, + AddContinuationRangeWrapper + >::type; + return Continuation( - std::make_shared>( + std::make_shared>( flags, GetNodePtr(trigger), std::forward(func))); } @@ -274,8 +285,19 @@ auto MakeContinuation(TransactionFlagsT flags, const Events& trigger, "MakeContinuation requires support for concurrent input to target domain."); using REACT_IMPL::SyncedContinuationNode; + using REACT_IMPL::AddContinuationRangeWrapper; + using REACT_IMPL::IsCallableWith; + using REACT_IMPL::EventRange; + using F = typename std::decay::type; + using WrapperT = + typename std::conditional< + IsCallableWith, TDepValues ...>::value, + F, + AddContinuationRangeWrapper + >::type; + struct NodeBuilder_ { NodeBuilder_(TransactionFlagsT flags, const Events& trigger, FIn&& func) : @@ -288,7 +310,7 @@ auto MakeContinuation(TransactionFlagsT flags, const Events& trigger, -> Continuation { return Continuation( - std::make_shared>( + std::make_shared>( MyFlags, GetNodePtr(MyTrigger), std::forward(MyFunc), GetNodePtr(deps) ...)); diff --git a/include/react/Event.h b/include/react/Event.h index 550ef5ac..e3f70f3e 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -345,6 +345,72 @@ auto Transform(const Events& source, const SignalPack& d depPack.Data); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Process +/////////////////////////////////////////////////////////////////////////////////////////////////// +using REACT_IMPL::EventRange; +using REACT_IMPL::EventInserter; + +template +< + typename TOut, + typename D, + typename TIn, + typename FIn, + typename F = typename std::decay::type +> +auto Process(const Events& src, FIn&& func) + -> Events +{ + using REACT_IMPL::EventProcessingNode; + + return Events( + std::make_shared>( + GetNodePtr(src), std::forward(func))); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Process - Synced +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename TOut, + typename D, + typename TIn, + typename FIn, + typename ... TDepValues +> +auto Process(const Events& source, const SignalPack& depPack, FIn&& func) + -> Events +{ + using REACT_IMPL::SyncedEventProcessingNode; + + using F = typename std::decay::type; + + struct NodeBuilder_ + { + NodeBuilder_(const Events& source, FIn&& func) : + MySource( source ), + MyFunc( std::forward(func) ) + {} + + auto operator()(const Signal& ... deps) + -> Events + { + return Events( + std::make_shared>( + GetNodePtr(MySource), std::forward(MyFunc), GetNodePtr(deps) ...)); + } + + const Events& MySource; + FIn MyFunc; + }; + + return REACT_IMPL::apply( + NodeBuilder_( source, std::forward(func) ), + depPack.Data); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Flatten /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/Observer.h b/include/react/Observer.h index 07d76c32..9b50fc6a 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -185,7 +185,6 @@ auto Observe(const Signal& subject, FIn&& func) SignalObserverNode >::type; - const auto& subjectPtr = GetNodePtr(subject); std::unique_ptr> nodePtr( new TNode(subjectPtr, std::forward(func)) ); @@ -212,18 +211,29 @@ auto Observe(const Events& subject, FIn&& func) using REACT_IMPL::ObserverNode; using REACT_IMPL::EventObserverNode; using REACT_IMPL::AddDefaultReturnValueWrapper; + using REACT_IMPL::AddObserverRangeWrapper; + using REACT_IMPL::IsCallableWith; + using REACT_IMPL::EventRange; using F = typename std::decay::type; - using R = typename std::result_of::type; - using WrapperT = AddDefaultReturnValueWrapper; - // If return value of passed function is void, add ObserverAction::next as - // default return value. - using TNode = typename std::conditional< - std::is_same::value, - EventObserverNode, - EventObserverNode - >::type; + using WrapperT = + typename std::conditional< + IsCallableWith>::value, + F, + typename std::conditional< + IsCallableWith::value, + AddObserverRangeWrapper, + typename std::conditional< + IsCallableWith>::value, + AddDefaultReturnValueWrapper, + AddObserverRangeWrapper> + >::type + >::type + >::type; + + using TNode = EventObserverNode; const auto& subjectPtr = GetNodePtr(subject); @@ -253,18 +263,30 @@ auto Observe(const Events& subject, using REACT_IMPL::ObserverNode; using REACT_IMPL::SyncedObserverNode; using REACT_IMPL::AddDefaultReturnValueWrapper; + using REACT_IMPL::AddObserverRangeWrapper; + using REACT_IMPL::IsCallableWith; + using REACT_IMPL::EventRange; using F = typename std::decay::type; - using R = typename std::result_of::type; - using WrapperT = AddDefaultReturnValueWrapper; - // If return value of passed function is void, add ObserverAction::next as - // default return value. - using TNode = typename std::conditional< - std::is_same::value, - SyncedObserverNode, - SyncedObserverNode - >::type; + using WrapperT = + typename std::conditional< + IsCallableWith, TDepValues ...>::value, + F, + typename std::conditional< + IsCallableWith::value, + AddObserverRangeWrapper, + typename std::conditional< + IsCallableWith, TDepValues ...>::value, + AddDefaultReturnValueWrapper, + AddObserverRangeWrapper, + TDepValues...> + >::type + >::type + >::type; + + using TNode = SyncedObserverNode; struct NodeBuilder_ { diff --git a/include/react/detail/graph/ContinuationNodes.h b/include/react/detail/graph/ContinuationNodes.h index ab3c3cd2..c841d23f 100644 --- a/include/react/detail/graph/ContinuationNodes.h +++ b/include/react/detail/graph/ContinuationNodes.h @@ -30,6 +30,36 @@ class SignalNode; template class EventStreamNode; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// AddContinuationRangeWrapper +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct AddContinuationRangeWrapper +{ + AddContinuationRangeWrapper(const AddContinuationRangeWrapper& other) = default; + + AddContinuationRangeWrapper(AddContinuationRangeWrapper&& other) : + MyFunc( std::move(other.MyFunc) ) + {} + + template + < + typename FIn, + class = typename DisableIfSame::type + > + explicit AddContinuationRangeWrapper(FIn&& func) : + MyFunc( std::forward(func) ) + {} + + void operator()(EventRange range, const TArgs& ... args) + { + for (const auto& e : range) + MyFunc(e, args ...); + } + + F MyFunc; +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// ContinuationNode /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -171,13 +201,10 @@ class EventContinuationNode : public ContinuationNode // Copy events and func [storedFunc,storedEvents] () mutable { - for (const auto& e : storedEvents) - storedFunc(e); + storedFunc(EventRange( storedEvents )); } ); - TransactionFuncT cont2 = [] { return; }; - DomainSpecificInputManager::Instance() .StoreContinuation( DomainSpecificInputManager::Instance(), @@ -291,10 +318,12 @@ class SyncedContinuationNode : public ContinuationNode // Copy events, func, value tuple (note: 2x copy) [storedFunc,storedEvents,storedValues] () mutable { - for (const auto& e : storedEvents) - { - apply(EvalFunctor_( e, storedFunc ), storedValues); - } + apply( + [&storedFunc,&storedEvents] (const TDepValues& ... vals) + { + storedFunc(EventRange( storedEvents ), vals ...); + }, + storedValues); } }; diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 484ed997..bc4eb4b3 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -547,7 +547,7 @@ class EventFlattenNode : public EventStreamNode }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// SycnedEventTransformNode +/// SyncedEventTransformNode /////////////////////////////////////////////////////////////////////////////////////////////////// template < @@ -590,22 +590,6 @@ class SyncedEventTransformNode : virtual void Tick(void* turnPtr) override { - struct EvalFunctor_ - { - EvalFunctor_(const TIn& e, TFunc& f) : - MyEvent( e ), - MyFunc( f ) - {} - - TOut operator()(const std::shared_ptr>& ... args) - { - return MyFunc(MyEvent, args->ValueRef() ...); - } - - const TIn& MyEvent; - TFunc& MyFunc; - }; - using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -617,12 +601,20 @@ class SyncedEventTransformNode : REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); + // Don't time if there is nothing to do + if (! source_->Events().empty()) {// timer using TimerT = typename SyncedEventTransformNode::ScopedUpdateTimer; TimerT scopedTimer( *this, source_->Events().size() ); for (const auto& e : source_->Events()) - this->events_.push_back(apply(EvalFunctor_( e, func_ ), deps_)); + this->events_.push_back(apply( + [this, &e] (const std::shared_ptr>& ... args) + { + return func_(e, args->ValueRef() ...); + }, + deps_)); + }// ~timer REACT_LOG(D::Log().template Append( @@ -647,7 +639,7 @@ class SyncedEventTransformNode : }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// SycnedEventFilterNode +/// SyncedEventFilterNode /////////////////////////////////////////////////////////////////////////////////////////////////// template < @@ -689,22 +681,6 @@ class SyncedEventFilterNode : virtual void Tick(void* turnPtr) override { - struct EvalFunctor_ - { - EvalFunctor_(const E& e, TFunc& f) : - MyEvent( e ), - MyFilter( f ) - {} - - bool operator()(const std::shared_ptr>& ... args) - { - return MyFilter(MyEvent, args->ValueRef() ...); - } - - const E& MyEvent; - TFunc& MyFilter; - }; - using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -716,12 +692,19 @@ class SyncedEventFilterNode : REACT_LOG(D::Log().template Append( GetObjectId(*this), turn.Id())); + // Don't time if there is nothing to do + if (! source_->Events().empty()) {// timer using TimerT = typename SyncedEventFilterNode::ScopedUpdateTimer; TimerT scopedTimer( *this, source_->Events().size() ); for (const auto& e : source_->Events()) - if (apply(EvalFunctor_( e, filter_ ), deps_)) + if (apply( + [this, &e] (const std::shared_ptr>& ... args) + { + return filter_(e, args->ValueRef() ...); + }, + deps_)) this->events_.push_back(e); }// ~timer @@ -751,6 +734,168 @@ class SyncedEventFilterNode : DepHolderT deps_; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventProcessingNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename TIn, + typename TOut, + typename TFunc +> +class EventProcessingNode : + public EventStreamNode +{ + using Engine = typename EventProcessingNode::Engine; + +public: + template + EventProcessingNode(const std::shared_ptr>& source, F&& func) : + EventProcessingNode::EventStreamNode( ), + source_( source ), + func_( std::forward(func) ) + { + Engine::OnNodeCreate(*this); + Engine::OnNodeAttach(*this, *source); + } + + ~EventProcessingNode() + { + Engine::OnNodeDetach(*this, *source_); + 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())); + + {// timer + using TimerT = typename EventProcessingNode::ScopedUpdateTimer; + TimerT scopedTimer( *this, source_->Events().size() ); + + func_( + EventRange( source_->Events() ), + std::back_inserter(this->events_)); + }// ~timer + + 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 "EventProcessingNode"; } + virtual int DependencyCount() const override { return 1; } + +private: + std::shared_ptr> source_; + + TFunc func_; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SyncedEventProcessingNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename TIn, + typename TOut, + typename TFunc, + typename ... TDepValues +> +class SyncedEventProcessingNode : + public EventStreamNode +{ + using Engine = typename SyncedEventProcessingNode::Engine; + +public: + template + SyncedEventProcessingNode(const std::shared_ptr>& source, F&& func, + const std::shared_ptr>& ... deps) : + SyncedEventProcessingNode::EventStreamNode( ), + source_( source ), + func_( std::forward(func) ), + deps_( deps ... ) + { + Engine::OnNodeCreate(*this); + Engine::OnNodeAttach(*this, *source); + REACT_EXPAND_PACK(Engine::OnNodeAttach(*this, *deps)); + } + + ~SyncedEventProcessingNode() + { + Engine::OnNodeDetach(*this, *source_); + + apply( + DetachFunctor>...>( *this ), + deps_); + + Engine::OnNodeDestroy(*this); + } + + virtual void Tick(void* turnPtr) override + { + using TurnT = typename D::Engine::TurnT; + TurnT& turn = *reinterpret_cast(turnPtr); + + this->SetCurrentTurn(turn, true); + // Update of this node could be triggered from deps, + // so make sure source doesnt contain events from last turn + source_->SetCurrentTurn(turn); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + + // Don't time if there is nothing to do + if (! source_->Events().empty()) + {// timer + using TimerT = typename SyncedEventProcessingNode::ScopedUpdateTimer; + TimerT scopedTimer( *this, source_->Events().size() ); + + apply( + [this] (const std::shared_ptr>& ... args) + { + func_( + EventRange( source_->Events() ), + std::back_inserter(this->events_)); + }, + deps_); + + }// ~timer + + 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 "SycnedEventProcessingNode"; } + virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } + +private: + using DepHolderT = std::tuple>...>; + + std::shared_ptr> source_; + + TFunc func_; + DepHolderT deps_; +}; + /****************************************/ REACT_IMPL_END /***************************************/ #endif // REACT_DETAIL_GRAPH_EVENTNODES_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index 38979813..9ce6a9ef 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -245,6 +245,30 @@ class ReactiveOpBase DepHolderT deps_; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Iterators for event processing +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class EventRange +{ +public: + using const_iterator = typename std::vector::const_iterator; + + EventRange(const EventRange&) = default; + + const_iterator begin() const { return data_.begin(); } + const_iterator end() const { return data_.end(); } + + explicit EventRange(const std::vector& data) : + data_( data ) + {} + +private: + const std::vector& data_; +}; + +template +using EventInserter = std::back_insert_iterator>; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index a2d4c1df..351a54e5 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -38,6 +38,39 @@ enum class ObserverAction stop_and_detach }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// AddObserverRangeWrapper +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct AddObserverRangeWrapper +{ + AddObserverRangeWrapper(const AddObserverRangeWrapper& other) = default; + + AddObserverRangeWrapper(AddObserverRangeWrapper&& other) : + MyFunc( std::move(other.MyFunc) ) + {} + + template + < + typename FIn, + class = typename DisableIfSame::type + > + explicit AddObserverRangeWrapper(FIn&& func) : + MyFunc( std::forward(func) ) + {} + + ObserverAction operator()(EventRange range, const TArgs& ... args) + { + for (const auto& e : range) + if (MyFunc(e, args ...) == ObserverAction::stop_and_detach) + return ObserverAction::stop_and_detach; + + return ObserverAction::next; + } + + F MyFunc; +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// ObserverNode /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -183,15 +216,9 @@ class EventObserverNode : {// timer using TimerT = typename EventObserverNode::ScopedUpdateTimer; TimerT scopedTimer( *this, p->Events().size() ); - - for (const auto& e : p->Events()) - { - if (func_(e) == ObserverAction::stop_and_detach) - { - shouldDetach = true; - break; - } - } + + shouldDetach = func_(EventRange( p->Events() )) == ObserverAction::stop_and_detach; + }// ~timer if (shouldDetach) @@ -263,22 +290,6 @@ class SyncedObserverNode : virtual void Tick(void* turnPtr) override { - struct EvalFunctor_ - { - EvalFunctor_(const E& e, TFunc& f) : - MyEvent( e ), - MyFunc( f ) - {} - - ObserverAction operator()(const std::shared_ptr>& ... args) - { - return MyFunc(MyEvent, args->ValueRef() ...); - } - - const E& MyEvent; - TFunc& MyFunc; - }; - using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -297,14 +308,13 @@ class SyncedObserverNode : using TimerT = typename SyncedObserverNode::ScopedUpdateTimer; TimerT scopedTimer( *this, p->Events().size() ); - for (const auto& e : p->Events()) - { - if (apply(EvalFunctor_( e, func_ ), deps_) == ObserverAction::stop_and_detach) + shouldDetach = apply( + [this, &p] (const std::shared_ptr>& ... args) { - shouldDetach = true; - break; - } - } + return func_(EventRange( p->Events() ), args->ValueRef() ...); + }, + deps_) == ObserverAction::stop_and_detach; + }// ~timer } From 301202026c6cd67314b7ca1f1e60858c5a3804fc Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 7 Aug 2014 13:58:06 +0200 Subject: [PATCH 204/266] Improved static_assert error messages. --- include/react/Domain.h | 36 +++++++++++++++++++++++++++--------- include/react/Observer.h | 38 +++++++++++++++++++++++++++----------- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/include/react/Domain.h b/include/react/Domain.h index ac049438..8bbf6f00 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -195,7 +195,7 @@ auto MakeContinuation(TransactionFlagsT flags, const Signal& trigger, FIn&& -> Continuation { static_assert(DOut::is_concurrent, - "MakeContinuation requires support for concurrent input to target domain."); + "MakeContinuation: Target domain does not support concurrent input."); using REACT_IMPL::SignalContinuationNode; using F = typename std::decay::type; @@ -232,7 +232,7 @@ auto MakeContinuation(TransactionFlagsT flags, const Events& trigger, FIn&& -> Continuation { static_assert(DOut::is_concurrent, - "MakeContinuation requires support for concurrent input to target domain."); + "MakeContinuation: Target domain does not support concurrent input."); using REACT_IMPL::EventContinuationNode; using REACT_IMPL::AddContinuationRangeWrapper; @@ -245,9 +245,16 @@ auto MakeContinuation(TransactionFlagsT flags, const Events& trigger, FIn&& typename std::conditional< IsCallableWith>::value, F, - AddContinuationRangeWrapper + typename std::conditional< + IsCallableWith::value, + AddContinuationRangeWrapper, + void + >::type >::type; + static_assert(! std::is_same::value, + "MakeContinuation: Passed function does not match any of the supported signatures."); + return Continuation( std::make_shared>( flags, GetNodePtr(trigger), std::forward(func))); @@ -282,7 +289,7 @@ auto MakeContinuation(TransactionFlagsT flags, const Events& trigger, -> Continuation { static_assert(DOut::is_concurrent, - "MakeContinuation requires support for concurrent input to target domain."); + "MakeContinuation: Target domain does not support concurrent input."); using REACT_IMPL::SyncedContinuationNode; using REACT_IMPL::AddContinuationRangeWrapper; @@ -295,9 +302,16 @@ auto MakeContinuation(TransactionFlagsT flags, const Events& trigger, typename std::conditional< IsCallableWith, TDepValues ...>::value, F, - AddContinuationRangeWrapper + typename std::conditional< + IsCallableWith::value, + AddContinuationRangeWrapper, + void + >::type >::type; + static_assert(! std::is_same::value, + "MakeContinuation: Passed function does not match any of the supported signatures."); + struct NodeBuilder_ { NodeBuilder_(TransactionFlagsT flags, const Events& trigger, FIn&& func) : @@ -364,7 +378,8 @@ void DoTransaction(TransactionFlagsT flags, F&& func) template void AsyncTransaction(F&& func) { - static_assert(D::is_concurrent, "AsyncTransaction requires concurrent domain."); + static_assert(D::is_concurrent, + "AsyncTransaction: Domain does not support concurrent input."); using REACT_IMPL::DomainSpecificInputManager; DomainSpecificInputManager::Instance() @@ -374,7 +389,8 @@ void AsyncTransaction(F&& func) template void AsyncTransaction(TransactionFlagsT flags, F&& func) { - static_assert(D::is_concurrent, "AsyncTransaction requires concurrent domain."); + static_assert(D::is_concurrent, + "AsyncTransaction: Domain does not support concurrent input."); using REACT_IMPL::DomainSpecificInputManager; DomainSpecificInputManager::Instance() @@ -384,7 +400,8 @@ void AsyncTransaction(TransactionFlagsT flags, F&& func) template void AsyncTransaction(TransactionStatus& status, F&& func) { - static_assert(D::is_concurrent, "AsyncTransaction requires concurrent domain."); + static_assert(D::is_concurrent, + "AsyncTransaction: Domain does not support concurrent input."); using REACT_IMPL::DomainSpecificInputManager; @@ -395,7 +412,8 @@ void AsyncTransaction(TransactionStatus& status, F&& func) template void AsyncTransaction(TransactionFlagsT flags, TransactionStatus& status, F&& func) { - static_assert(D::is_concurrent, "AsyncTransaction requires concurrent domain."); + static_assert(D::is_concurrent, + "AsyncTransaction: Domain does not support concurrent input."); using REACT_IMPL::DomainSpecificInputManager; DomainSpecificInputManager::Instance() diff --git a/include/react/Observer.h b/include/react/Observer.h index 9b50fc6a..45fd78a1 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -179,7 +179,7 @@ auto Observe(const Signal& subject, FIn&& func) // If return value of passed function is void, add ObserverAction::next as // default return value. - using TNode = typename std::conditional< + using NodeT = typename std::conditional< std::is_same::value, SignalObserverNode, SignalObserverNode @@ -187,7 +187,7 @@ auto Observe(const Signal& subject, FIn&& func) const auto& subjectPtr = GetNodePtr(subject); - std::unique_ptr> nodePtr( new TNode(subjectPtr, std::forward(func)) ); + std::unique_ptr> nodePtr( new NodeT(subjectPtr, std::forward(func)) ); ObserverNode* rawNodePtr = nodePtr.get(); subjectPtr->RegisterObserver(std::move(nodePtr)); @@ -227,17 +227,25 @@ auto Observe(const Events& subject, FIn&& func) typename std::conditional< IsCallableWith>::value, AddDefaultReturnValueWrapper, - AddObserverRangeWrapper> + typename std::conditional< + IsCallableWith::value, + AddObserverRangeWrapper>, + void + >::type >::type >::type >::type; + + static_assert( + ! std::is_same::value, + "Observe: Passed function does not match any of the supported signatures."); - using TNode = EventObserverNode; + using NodeT = EventObserverNode; const auto& subjectPtr = GetNodePtr(subject); - std::unique_ptr> nodePtr( new TNode(subjectPtr, std::forward(func)) ); + std::unique_ptr> nodePtr( new NodeT(subjectPtr, std::forward(func)) ); ObserverNode* rawNodePtr = nodePtr.get(); subjectPtr->RegisterObserver(std::move(nodePtr)); @@ -279,14 +287,22 @@ auto Observe(const Events& subject, typename std::conditional< IsCallableWith, TDepValues ...>::value, AddDefaultReturnValueWrapper, - AddObserverRangeWrapper, - TDepValues...> + typename std::conditional< + IsCallableWith::value, + AddObserverRangeWrapper, + TDepValues...>, + void + >::type >::type >::type >::type; - using TNode = SyncedObserverNode; + static_assert( + ! std::is_same::value, + "Observe: Passed function does not match any of the supported signatures."); + + using NodeT = SyncedObserverNode; struct NodeBuilder_ { @@ -298,7 +314,7 @@ auto Observe(const Events& subject, auto operator()(const Signal& ... deps) -> ObserverNode* { - return new TNode( + return new NodeT( GetNodePtr(MySubject), std::forward(MyFunc), GetNodePtr(deps) ... ); } From 794835380609f3c5c54b306e69eb432e77a741dd Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 7 Aug 2014 13:58:46 +0200 Subject: [PATCH 205/266] Added some convenience functions to EventRange. --- include/react/detail/graph/GraphBase.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index 9ce6a9ef..2adf8160 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -253,12 +253,16 @@ class EventRange { public: using const_iterator = typename std::vector::const_iterator; + using size_type = typename std::vector::size_type; EventRange(const EventRange&) = default; const_iterator begin() const { return data_.begin(); } const_iterator end() const { return data_.end(); } + size_type Size() const { return data_.size(); } + bool IsEmpty() const { return data_.empty(); } + explicit EventRange(const std::vector& data) : data_( data ) {} From 3ff8cdcba524692536e0096ae25604f73228ad47 Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 7 Aug 2014 13:59:29 +0200 Subject: [PATCH 206/266] Added EventRange support to Iterate. --- include/react/Algorithm.h | 78 +++++++++--- include/react/detail/graph/AlgorithmNodes.h | 124 ++++++++++++-------- 2 files changed, 138 insertions(+), 64 deletions(-) diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index 2f0e7730..7caed812 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -77,7 +77,7 @@ auto Monitor(const Signal& target) } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Iterate - Iteratively combines signal value with values from event stream +/// Iterate - Iteratively combines signal value with values from event stream (aka Fold) /////////////////////////////////////////////////////////////////////////////////////////////////// template < @@ -92,17 +92,38 @@ auto Iterate(const Events& events, V&& init, FIn&& func) { using REACT_IMPL::IterateNode; using REACT_IMPL::IterateByRefNode; + using REACT_IMPL::AddIterateRangeWrapper; + using REACT_IMPL::AddIterateByRefRangeWrapper; + using REACT_IMPL::IsCallableWith; + using REACT_IMPL::EventRange; using F = typename std::decay::type; - using R = typename std::result_of::type; - using TNode = typename std::conditional< - std::is_same::value, - IterateByRefNode, - IterateNode - >::type; + + using NodeT = + typename std::conditional< + IsCallableWith,S>::value, + IterateNode, + typename std::conditional< + IsCallableWith::value, + IterateNode>, + typename std::conditional< + IsCallableWith, S&>::value, + IterateByRefNode, + typename std::conditional< + IsCallableWith::value, + IterateByRefNode>, + void + >::type + >::type + >::type + >::type; + + static_assert( + ! std::is_same::value, + "Iterate: Passed function does not match any of the supported signatures."); return Signal( - std::make_shared( + std::make_shared( std::forward(init), GetNodePtr(events), std::forward(func))); } @@ -124,14 +145,41 @@ auto Iterate(const Events& events, V&& init, { using REACT_IMPL::SyncedIterateNode; using REACT_IMPL::SyncedIterateByRefNode; + using REACT_IMPL::AddIterateRangeWrapper; + using REACT_IMPL::AddIterateByRefRangeWrapper; + using REACT_IMPL::IsCallableWith; + using REACT_IMPL::EventRange; using F = typename std::decay::type; - using R = typename std::result_of::type; - using TNode = typename std::conditional< - std::is_same::value, - SyncedIterateByRefNode, - SyncedIterateNode - >::type; + + using NodeT = + typename std::conditional< + IsCallableWith,S,TDepValues ...>::value, + SyncedIterateNode, + typename std::conditional< + IsCallableWith::value, + SyncedIterateNode, + TDepValues ...>, + typename std::conditional< + IsCallableWith,S&,TDepValues ...>::value, + SyncedIterateByRefNode, + typename std::conditional< + IsCallableWith::value, + SyncedIterateByRefNode, + TDepValues ...>, + void + >::type + >::type + >::type + >::type; + + static_assert( + ! std::is_same::value, + "Iterate: Passed function does not match any of the supported signatures."); + + //static_assert(NodeT::dummy_error, "DUMP MY TYPE" ); struct NodeBuilder_ { @@ -145,7 +193,7 @@ auto Iterate(const Events& events, V&& init, -> Signal { return Signal( - std::make_shared( + std::make_shared( std::forward(MyInit), GetNodePtr(MySource), std::forward(MyFunc), GetNodePtr(deps) ...)); } diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index fc89aaf9..3c061c68 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -19,6 +19,65 @@ /***************************************/ 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 /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -65,10 +124,7 @@ class IterateNode : using TimerT = typename IterateNode::ScopedUpdateTimer; TimerT scopedTimer( *this, events_->Events().size() ); - S newValue = this->value_; - - for (const auto& e : events_->Events()) - newValue = func_(e, newValue); + S newValue = func_(EventRange( events_->Events() ), this->value_); if (! Equals(newValue, this->value_)) { @@ -139,8 +195,8 @@ class IterateByRefNode : using TimerT = typename IterateByRefNode::ScopedUpdateTimer; TimerT scopedTimer( *this, events_->Events().size() ); - for (const auto& e : events_->Events()) - func_(e, this->value_); + func_(EventRange( events_->Events() ), this->value_); + }// ~timer REACT_LOG(D::Log().template Append( @@ -203,24 +259,6 @@ class SyncedIterateNode : virtual void Tick(void* turnPtr) override { - struct EvalFunctor_ - { - EvalFunctor_(const E& e, const S& v, TFunc& f) : - MyEvent( e ), - MyValue( v ), - MyFunc( f ) - {} - - S operator()(const std::shared_ptr>& ... args) - { - return MyFunc(MyEvent, MyValue, args->ValueRef() ...); - } - - const E& MyEvent; - const S& MyValue; - TFunc& MyFunc; - }; - using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -235,11 +273,13 @@ class SyncedIterateNode : {// timer using TimerT = typename SyncedIterateNode::ScopedUpdateTimer; TimerT scopedTimer( *this, events_->Events().size() ); - - S newValue = this->value_; - - for (const auto& e : events_->Events()) - newValue = apply(EvalFunctor_( e, std::move(newValue), func_ ), deps_); + + S newValue = apply( + [this] (const std::shared_ptr>& ... args) + { + return func_(EventRange( events_->Events() ), this->value_, args->ValueRef() ...); + }, + deps_); if (! Equals(newValue, this->value_)) { @@ -313,24 +353,6 @@ class SyncedIterateByRefNode : virtual void Tick(void* turnPtr) override { - struct EvalFunctor_ - { - EvalFunctor_(const E& e, S& v, TFunc& f) : - MyEvent( e ), - MyValue( v ), - MyFunc( f ) - {} - - void operator()(const std::shared_ptr>& ... args) - { - MyFunc(MyEvent, MyValue, args->ValueRef() ...); - } - - const E& MyEvent; - S& MyValue; - TFunc& MyFunc; - }; - using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -346,8 +368,12 @@ class SyncedIterateByRefNode : using TimerT = typename SyncedIterateByRefNode::ScopedUpdateTimer; TimerT scopedTimer( *this, events_->Events().size() ); - for (const auto& e : events_->Events()) - apply(EvalFunctor_( e, this->value_, func_ ), deps_); + apply( + [this] (const std::shared_ptr>& ... args) + { + func_(EventRange( events_->Events() ), this->value_, args->ValueRef() ...); + }, + deps_); changed = true; }// ~timer From 33ba9c3c283d5334d1fdba3bd4fcf300a76610e7 Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 7 Aug 2014 14:00:08 +0200 Subject: [PATCH 207/266] Added test cases for EventRange Iterate. --- tests/src/OperationsTest.h | 222 +++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) diff --git a/tests/src/OperationsTest.h b/tests/src/OperationsTest.h index 87c54f35..a10167c8 100644 --- a/tests/src/OperationsTest.h +++ b/tests/src/OperationsTest.h @@ -586,6 +586,226 @@ TYPED_TEST_P(OperationsTest, SyncedIterate2) } } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SyncedIterate3 test (event range) +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(OperationsTest, SyncedIterate3) +{ + using D = typename SyncedIterate3::MyDomain; + + auto in1 = MakeVar(1); + auto in2 = MakeVar(1); + + auto op1 = in1 + in2; + auto op2 = (in1 + in2) * 10; + + auto src1 = MakeEventSource(); + auto src2 = MakeEventSource(); + + auto out1 = Iterate( + src1, + make_tuple(0,0), + With(op1,op2), + [] (EventRange range, const tuple& t, int op1, int op2) { + return make_tuple( + get<0>(t) + (op1 * range.Size()), + get<1>(t) + (op2 * range.Size())); + }); + + auto out2 = Iterate( + src2, + make_tuple(0,0,0), + With(op1,op2), + [] (EventRange range, const tuple& t, int op1, int op2) { + int sum = 0; + for (const auto& e : range) + sum += e; + + return make_tuple( + get<0>(t) + sum, + get<1>(t) + (op1 * range.Size()), + get<2>(t) + (op2 * range.Size())); + }); + + int obsCount1 = 0; + int obsCount2 = 0; + + { + auto obs1 = Observe(out1, [&] (const tuple& t) { + ++obsCount1; + + ASSERT_EQ(get<0>(t), 33); + ASSERT_EQ(get<1>(t), 330); + }); + + auto obs2 = Observe(out2, [&] (const tuple& t) { + ++obsCount2; + + ASSERT_EQ(get<0>(t), 42); + ASSERT_EQ(get<1>(t), 33); + ASSERT_EQ(get<2>(t), 330); + }); + + in1 <<= 22; + in2 <<= 11; + + src1.Emit(); + src2.Emit(42); + + ASSERT_EQ(obsCount1, 1); + ASSERT_EQ(obsCount2, 1); + + obs1.Detach(); + obs2.Detach(); + } + + { + auto obs1 = Observe(out1, [&] (const tuple& t) { + ++obsCount1; + + ASSERT_EQ(get<0>(t), 33 + 330); + ASSERT_EQ(get<1>(t), 330 + 3300); + }); + + auto obs2 = Observe(out2, [&] (const tuple& t) { + ++obsCount2; + + ASSERT_EQ(get<0>(t), 42 + 420); + ASSERT_EQ(get<1>(t), 33 + 330); + ASSERT_EQ(get<2>(t), 330 + 3300); + }); + + in1 <<= 220; + in2 <<= 110; + + src1.Emit(); + src2.Emit(420); + + ASSERT_EQ(obsCount1, 2); + ASSERT_EQ(obsCount2, 2); + + obs1.Detach(); + obs2.Detach(); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SyncedIterate4 test (event range, by ref) +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(OperationsTest, SyncedIterate4) +{ + using D = typename SyncedIterate4::MyDomain; + + auto in1 = MakeVar(1); + auto in2 = MakeVar(1); + + auto op1 = in1 + in2; + auto op2 = (in1 + in2) * 10; + + auto src1 = MakeEventSource(); + auto src2 = MakeEventSource(); + + auto out1 = Iterate( + src1, + vector{}, + With(op1,op2), + [] (EventRange range, vector& v, int op1, int op2) -> void { + for (const auto& e : range) + { + v.push_back(op1); + v.push_back(op2); + } + }); + + auto out2 = Iterate( + src2, + vector{}, + With(op1,op2), + [] (EventRange range, vector& v, int op1, int op2) -> void { + for (const auto& e : range) + { + v.push_back(e); + v.push_back(op1); + v.push_back(op2); + } + }); + + int obsCount1 = 0; + int obsCount2 = 0; + + { + auto obs1 = Observe(out1, [&] (const vector& v) { + ++obsCount1; + + ASSERT_EQ(v.size(), 2); + + ASSERT_EQ(v[0], 33); + ASSERT_EQ(v[1], 330); + }); + + auto obs2 = Observe(out2, [&] (const vector& v) { + ++obsCount2; + + ASSERT_EQ(v.size(), 3); + + ASSERT_EQ(v[0], 42); + ASSERT_EQ(v[1], 33); + ASSERT_EQ(v[2], 330); + }); + + in1 <<= 22; + in2 <<= 11; + + src1.Emit(); + src2.Emit(42); + + ASSERT_EQ(obsCount1, 1); + ASSERT_EQ(obsCount2, 1); + + obs1.Detach(); + obs2.Detach(); + } + + { + auto obs1 = Observe(out1, [&] (const vector& v) { + ++obsCount1; + + ASSERT_EQ(v.size(), 4); + + ASSERT_EQ(v[0], 33); + ASSERT_EQ(v[1], 330); + ASSERT_EQ(v[2], 330); + ASSERT_EQ(v[3], 3300); + }); + + auto obs2 = Observe(out2, [&] (const vector& v) { + ++obsCount2; + + ASSERT_EQ(v.size(), 6); + + ASSERT_EQ(v[0], 42); + ASSERT_EQ(v[1], 33); + ASSERT_EQ(v[2], 330); + + ASSERT_EQ(v[3], 420); + ASSERT_EQ(v[4], 330); + ASSERT_EQ(v[5], 3300); + }); + + in1 <<= 220; + in2 <<= 110; + + src1.Emit(); + src2.Emit(420); + + ASSERT_EQ(obsCount1, 2); + ASSERT_EQ(obsCount2, 2); + + obs1.Detach(); + obs2.Detach(); + } +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventFilter1 /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -688,6 +908,8 @@ REGISTER_TYPED_TEST_CASE_P SyncedTransform1, SyncedIterate1, SyncedIterate2, + SyncedIterate3, + SyncedIterate4, SyncedEventFilter1, SyncedEventTransform1 ); From eadf7f370850b474d02a0f84f007beee761efb97 Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 8 Aug 2014 01:17:23 +0200 Subject: [PATCH 208/266] IsCallableWith had problems with function pointers on MSVC. Using a different SFINAE method fixed it. --- include/react/common/Util.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/include/react/common/Util.h b/include/react/common/Util.h index b50c005b..39741ed7 100644 --- a/include/react/common/Util.h +++ b/include/react/common/Util.h @@ -206,10 +206,14 @@ class IsCallableWith using NoT = char[1]; using YesT = char[2]; - template - static YesT& check( - decltype( static_cast( - (std::declval())(std::declval() ...))) *); + template + < + typename U, + class = decltype( + static_cast( + (std::declval())(std::declval() ...))) + > + static YesT& check(void*); template static NoT& check(...); From c3ec931be5390913a567cb10248f22aee46a63f5 Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 8 Aug 2014 01:45:46 +0200 Subject: [PATCH 209/266] Added test cases Process. --- tests/src/EventStreamTest.h | 53 ++++++++++++++++++++++++++++++++- tests/src/OperationsTest.h | 58 +++++++++++++++++++++++++++++++++++-- 2 files changed, 108 insertions(+), 3 deletions(-) diff --git a/tests/src/EventStreamTest.h b/tests/src/EventStreamTest.h index 55a67124..39e2479d 100644 --- a/tests/src/EventStreamTest.h +++ b/tests/src/EventStreamTest.h @@ -269,6 +269,56 @@ TYPED_TEST_P(EventStreamTest, EventTransform) ASSERT_TRUE(std::find(results.begin(), results.end(), "HELLO VORLD") != results.end()); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventProcess test +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(EventStreamTest, EventProcess) +{ + using D = typename EventProcess::MyDomain; + + std::vector results; + + auto in1 = MakeEventSource(); + auto in2 = MakeEventSource(); + + auto merged = Merge(in1, in2); + int callCount = 0; + + auto processed = Process(merged, + [&] (EventRange range, EventInserter out) + { + for (const auto& e : range) + { + *out = 0.1f * e; + *out = 1.5f * e; + } + + callCount++; + }); + + Observe(processed, + [&] (float s) + { + results.push_back(s); + }); + + DoTransaction([&] { + in1 << 10 << 20; + }); + + in2 << 30; + + ASSERT_EQ(results.size(), 6); + ASSERT_EQ(callCount, 2); + + ASSERT_EQ(results[0], 1.0f); + ASSERT_EQ(results[1], 15.0f); + ASSERT_EQ(results[2], 2.0f); + ASSERT_EQ(results[3], 30.0f); + ASSERT_EQ(results[4], 3.0f); + ASSERT_EQ(results[5], 45.0f); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// REGISTER_TYPED_TEST_CASE_P ( @@ -278,7 +328,8 @@ REGISTER_TYPED_TEST_CASE_P EventMerge2, EventMerge3, EventFilter, - EventTransform + EventTransform, + EventProcess ); } // ~namespace diff --git a/tests/src/OperationsTest.h b/tests/src/OperationsTest.h index a10167c8..77e8c399 100644 --- a/tests/src/OperationsTest.h +++ b/tests/src/OperationsTest.h @@ -847,7 +847,7 @@ TYPED_TEST_P(OperationsTest, SyncedEventFilter1) } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventTransform1 +/// SyncedEventTransform1 /////////////////////////////////////////////////////////////////////////////////////////////////// TYPED_TEST_P(OperationsTest, SyncedEventTransform1) { @@ -892,6 +892,59 @@ TYPED_TEST_P(OperationsTest, SyncedEventTransform1) ASSERT_TRUE(std::find(results.begin(), results.end(), "HELLO VORLD, Alice Anderson") != results.end()); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SyncedEventProcess1 +/////////////////////////////////////////////////////////////////////////////////////////////////// +TYPED_TEST_P(OperationsTest, SyncedEventProcess1) +{ + using D = typename SyncedEventProcess1::MyDomain; + + std::vector results; + + auto in1 = MakeEventSource(); + auto in2 = MakeEventSource(); + + auto mult = MakeVar(10); + + auto merged = Merge(in1, in2); + int callCount = 0; + + auto processed = Process(merged, + With(mult), + [&] (EventRange range, EventInserter out, int mult) + { + for (const auto& e : range) + { + *out = 0.1f * e * mult; + *out = 1.5f * e * mult; + } + + callCount++; + }); + + Observe(processed, + [&] (float s) + { + results.push_back(s); + }); + + DoTransaction([&] { + in1 << 10 << 20; + }); + + in2 << 30; + + ASSERT_EQ(results.size(), 6); + ASSERT_EQ(callCount, 2); + + ASSERT_EQ(results[0], 10.0f); + ASSERT_EQ(results[1], 150.0f); + ASSERT_EQ(results[2], 20.0f); + ASSERT_EQ(results[3], 300.0f); + ASSERT_EQ(results[4], 30.0f); + ASSERT_EQ(results[5], 450.0f); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// REGISTER_TYPED_TEST_CASE_P ( @@ -911,7 +964,8 @@ REGISTER_TYPED_TEST_CASE_P SyncedIterate3, SyncedIterate4, SyncedEventFilter1, - SyncedEventTransform1 + SyncedEventTransform1, + SyncedEventProcess1 ); } // ~namespace From 2acc6ece1d36aa8a44effb8efe843a81f4175b89 Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 8 Aug 2014 01:46:38 +0200 Subject: [PATCH 210/266] Fixed synced Process + cleanup. --- include/react/detail/graph/AlgorithmNodes.h | 2 +- include/react/detail/graph/EventNodes.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index 3c061c68..454fdfbb 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -124,7 +124,7 @@ class IterateNode : using TimerT = typename IterateNode::ScopedUpdateTimer; TimerT scopedTimer( *this, events_->Events().size() ); - S newValue = func_(EventRange( events_->Events() ), this->value_); + S newValue = func_(EventRange( events_->Events() ), this->value_); if (! Equals(newValue, this->value_)) { diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index bc4eb4b3..499cda24 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -783,6 +783,7 @@ class EventProcessingNode : func_( EventRange( source_->Events() ), std::back_inserter(this->events_)); + }// ~timer REACT_LOG(D::Log().template Append( @@ -869,7 +870,8 @@ class SyncedEventProcessingNode : { func_( EventRange( source_->Events() ), - std::back_inserter(this->events_)); + std::back_inserter(this->events_), + args->ValueRef() ...); }, deps_); From c29d50e7128a8052705285d6e3fe963190ce9f1a Mon Sep 17 00:00:00 2001 From: schlangster Date: Sat, 9 Aug 2014 14:38:20 +0200 Subject: [PATCH 211/266] Async queue no longer uses a dedicated worker thread. Instead, a task is started as soon as there are items in the queue and stopped once it's empty. --- include/react/detail/ReactiveInput.h | 177 ++++++++++++++++++++++----- 1 file changed, 147 insertions(+), 30 deletions(-) diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index dbab6b1f..3589d8c7 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -19,10 +19,10 @@ #include #include #include -#include #include #include +#include "tbb/task.h" #include "tbb/concurrent_queue.h" #include "tbb/enumerable_thread_specific.h" #include "tbb/queuing_mutex.h" @@ -39,6 +39,9 @@ struct IInputNode; class IObserver; +template +class InputManager; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Common types & constants /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -478,37 +481,142 @@ class TransactionQueue }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// InputManager +/// AsyncWorker /////////////////////////////////////////////////////////////////////////////////////////////////// +struct AsyncItem +{ + TransactionFlagsT Flags; + WaitingStatePtrT WaitingStatePtr; + TransactionFuncT Func; +}; + +// Interface +template +class AsyncWorker +{ +public: + AsyncWorker(InputManager& mgr); + + void PushItem(AsyncItem&& item); + + void PopItem(AsyncItem& item); + bool TryPopItem(AsyncItem& item); + + bool IncrementItemCount(size_t n); + bool DecrementItemCount(size_t n); + + void Start(); +}; + +// Disabled template -class InputManager : - public IContinuationTarget +struct AsyncWorker { -private: - struct AsyncItem +public: + AsyncWorker(InputManager& mgr) + {} + + void PushItem(AsyncItem&& item) { assert(false); } + + void PopItem(AsyncItem& item) { assert(false); } + bool TryPopItem(AsyncItem& item) { assert(false); return false; } + + bool IncrementItemCount(size_t n) { assert(false); return false; } + bool DecrementItemCount(size_t n) { assert(false); return false; } + + void Start() { assert(false); } +}; + +// Enabled +template +struct AsyncWorker +{ + using DataT = tbb::concurrent_bounded_queue; + + class WorkerTask : public tbb::task { - TransactionFlagsT Flags; - WaitingStatePtrT WaitingStatePtr; - TransactionFuncT Func; + public: + WorkerTask(InputManager& mgr) : + mgr_( mgr ) + {} + + tbb::task* execute() + { + mgr_.processAsyncQueue(); + return nullptr; + } + + private: + InputManager& mgr_; }; - using AsyncQueueT = tbb::concurrent_bounded_queue; +public: + AsyncWorker(InputManager& mgr) : + mgr_( mgr ) + {} + + void PushItem(AsyncItem&& item) + { + data_.push(std::move(item)); + } + void PopItem(AsyncItem& item) + { + data_.pop(item); + } + + bool TryPopItem(AsyncItem& item) + { + return data_.try_pop(item); + } + + bool IncrementItemCount(size_t n) + { + return count_.fetch_add(n, std::memory_order_relaxed) == 0; + } + + bool DecrementItemCount(size_t n) + { + return count_.fetch_sub(n, std::memory_order_relaxed) == n; + } + + void Start() + { + tbb::task::enqueue(*new(tbb::task::allocate_root()) WorkerTask(mgr_)); + } + +private: + DataT data_; + std::atomic count_{ 0 }; + + InputManager& mgr_; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// InputManager +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class InputManager : + public IContinuationTarget +{ +private: // Select between thread-safe and non thread-safe implementations using TransactionQueueT = TransactionQueue; + using QueueEntryT = typename TransactionQueueT::QueueEntry; + using ContinuationManagerT = ContinuationManager; + using AsyncWorkerT = AsyncWorker; - using QueueEntryT = typename TransactionQueueT::QueueEntry; + template + friend class AsyncWorker; public: using TurnT = typename D::TurnT; using Engine = typename D::Engine; InputManager() : - asyncWorker_( [this] { processAsyncQueue(); } ) - { - asyncWorker_.detach(); - } + asyncWorker_(*this) + {} template void DoTransaction(TransactionFlagsT flags, F&& func) @@ -556,7 +664,10 @@ class InputManager : if (waitingStatePtr != nullptr) waitingStatePtr->IncWaitCount(); - asyncQueue_.push(AsyncItem{ flags, waitingStatePtr, std::forward(func) } ); + asyncWorker_.PushItem(AsyncItem{ flags, waitingStatePtr, std::forward(func) }); + + if (asyncWorker_.IncrementItemCount(1)) + asyncWorker_.Start(); } template @@ -702,11 +813,20 @@ class InputManager : while (true) { - // Blocks if queue is empty + size_t popCount = 0; + if (!skipPop) - asyncQueue_.pop(item); + { + // Blocks if queue is empty. + // This should never happen, + // and if (maybe due to some memory access internals), only briefly + asyncWorker_.PopItem(item); + popCount++; + } else + { skipPop = false; + } // First try to merge to an existing synchronous item in the queue bool canMerge = (item.Flags & allow_merging) != 0; @@ -745,8 +865,10 @@ class InputManager : { uint extraCount = 0; // Todo: Make configurable - while (extraCount < 512 && asyncQueue_.try_pop(item)) + while (extraCount < 1024 && asyncWorker_.TryPopItem(item)) { + ++popCount; + bool canMergeNext = (item.Flags & allow_merging) != 0; if (canMergeNext) { @@ -786,10 +908,6 @@ class InputManager : continuationManager_.template DetachQueuedObservers(); - //printf("1\n"); - //for (const auto& p : waitingStatePtrs) - // printf("%08X\n", p.Get()); - // Has continuations? If so, status ptrs have to be passed on to // continuation transactions if (continuationManager_.HasContinuations()) @@ -800,10 +918,6 @@ class InputManager : // More than 1 waiting state -> create collection from vector if (waitingStatePtrs.size() > 1) { - /* printf("2\n"); - for (const auto& p : waitingStatePtrs) - printf("%08X\n", p.Get());*/ - WaitingStatePtrT p ( SharedWaitingStateCollection::Create(std::move(waitingStatePtrs)) @@ -839,14 +953,17 @@ class InputManager : } waitingStatePtrs.clear(); + + // Stop this task if the number of items has just been decremented zero. + // A new task will be started by the thread that increments the item count from zero. + if (asyncWorker_.DecrementItemCount(popCount)) + break; } } TransactionQueueT transactionQueue_; ContinuationManagerT continuationManager_; - - AsyncQueueT asyncQueue_; - std::thread asyncWorker_; + AsyncWorkerT asyncWorker_; std::atomic nextTurnId_{ 0 }; From 4538dd90f3f90dee8643d63124225779fbc99947 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 10 Aug 2014 00:26:52 +0200 Subject: [PATCH 212/266] Added event operation: Join. Joins two streams A,B to tuple. Values are buffered for each input slot and emitted once a tuple is complete. --- include/react/Event.h | 24 ++++- include/react/detail/graph/EventNodes.h | 129 ++++++++++++++++++++++++ 2 files changed, 151 insertions(+), 2 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index e3f70f3e..777935e9 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -60,7 +60,6 @@ auto MakeEventSource() /////////////////////////////////////////////////////////////////////////////////////////////////// /// Merge /////////////////////////////////////////////////////////////////////////////////////////////////// -// Note: Default template arguments are in forward declaration template < typename D, @@ -77,7 +76,7 @@ auto Merge(const Events& arg1, const Events& ... args) using REACT_IMPL::EventOpNode; static_assert(sizeof...(TArgs) > 0, - "react::Merge requires at least 2 arguments."); + "Merge: 2+ arguments are required."); return TempEvents( std::make_shared>( @@ -427,6 +426,27 @@ auto Flatten(const Signal>& outer) GetNodePtr(outer), GetNodePtr(outer.Value()))); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Join +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename ... TArgs +> +auto Join(const Events& ... args) + -> Events> +{ + using REACT_IMPL::EventJoinNode; + + static_assert(sizeof...(TArgs) > 1, + "Join: 2+ arguments are required."); + + return Events>( + std::make_shared>( + GetNodePtr(args) ...)); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Token /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 499cda24..850b36c2 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -898,6 +898,135 @@ class SyncedEventProcessingNode : DepHolderT deps_; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventJoinNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename D, + typename ... TValues +> +class EventJoinNode : + public EventStreamNode> +{ + using Engine = typename EventJoinNode::Engine; + using TurnT = typename Engine::TurnT; + +public: + EventJoinNode(const std::shared_ptr>& ... sources) : + EventJoinNode::EventStreamNode( ), + slots_( sources ... ) + { + Engine::OnNodeCreate(*this); + REACT_EXPAND_PACK(Engine::OnNodeAttach(*this, *sources)); + } + + ~EventJoinNode() + { + apply( + [this] (Slot& ... slots) { + REACT_EXPAND_PACK(Engine::OnNodeDetach(*this, *slots.Source)); + }, + slots_); + + Engine::OnNodeDestroy(*this); + } + + virtual void Tick(void* turnPtr) override + { + TurnT& turn = *reinterpret_cast(turnPtr); + + this->SetCurrentTurn(turn, true); + + REACT_LOG(D::Log().template Append( + GetObjectId(*this), turn.Id())); + + // Don't time if there is nothing to do + {// timer + size_t count = 0; + using TimerT = typename EventJoinNode::ScopedUpdateTimer; + TimerT scopedTimer( *this, count ); + + // Move events into buffers + apply( + [this, &turn] (Slot& ... slots) { + REACT_EXPAND_PACK(fetchBuffer(turn, slots)); + }, + slots_); + + while (true) + { + bool isReady = true; + + // All slots ready? + apply( + [this,&isReady] (Slot& ... slots) { + // Todo: combine return values instead + REACT_EXPAND_PACK(checkSlot(slots, isReady)); + }, + slots_); + + if (!isReady) + break; + + // Pop values from buffers and emit tuple + apply( + [this] (Slot& ... slots) { + this->events_.emplace_back(slots.Buffer.front() ...); + REACT_EXPAND_PACK(slots.Buffer.pop_front()); + }, + slots_); + } + + count = this->events_.size(); + + }// ~timer + + 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 "EventJoinNode"; } + virtual int DependencyCount() const override { return sizeof...(TValues); } + +private: + template + struct Slot + { + Slot(const std::shared_ptr>& src) : + Source( src ) + {} + + std::shared_ptr> Source; + std::deque Buffer; + }; + + template + static void fetchBuffer(TurnT& turn, Slot& slot) + { + slot.Source->SetCurrentTurn(turn); + + slot.Buffer.insert( + slot.Buffer.end(), + slot.Source->Events().begin(), + slot.Source->Events().end()); + } + + template + static void checkSlot(Slot& slot, bool& isReady) + { + auto t = isReady && !slot.Buffer.empty(); + isReady = t; + } + + std::tuple...> slots_; +}; + /****************************************/ REACT_IMPL_END /***************************************/ #endif // REACT_DETAIL_GRAPH_EVENTNODES_H_INCLUDED \ No newline at end of file From bad1c7940f329e23b935b1f40ef1d2e76068cd51 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 12 Jan 2015 17:54:34 +0100 Subject: [PATCH 213/266] Fixed duplicated symbols for domain initializer. (Thanks to Dominik for reporting the bug.) --- include/react/Domain.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/include/react/Domain.h b/include/react/Domain.h index 8bbf6f00..e27c2514 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -428,7 +428,16 @@ void AsyncTransaction(TransactionFlagsT flags, TransactionStatus& status, F&& fu #define REACTIVE_DOMAIN(name, ...) \ struct name : \ public REACT_IMPL::DomainBase> {}; \ - REACT_IMPL::DomainInitializer name ## _initializer_; + static REACT_IMPL::DomainInitializer name ## _initializer_; + +/* + A brief reminder why the domain initializer is here: + Each domain has a couple of singletons (debug log, engine, input manager) which are + currently implemented as meyer singletons. From what I understand, these are thread-safe + in C++11, but not all compilers implement that yet. That's why a static initializer has + been added to make sure singleton creation happens before any multi-threaded access. + This implemenation is obviously inconsequential. + */ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Define type aliases for given domain From 412127d0da37f3fafe4d17c07dd51509b737b86e Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 12 Jan 2015 17:55:04 +0100 Subject: [PATCH 214/266] Cleanup/refactoring. --- include/react/Event.h | 2 +- include/react/Signal.h | 4 ++-- include/react/TypeTraits.h | 32 ++++++++++++++++++++++---- include/react/detail/graph/GraphBase.h | 6 ++++- tests/src/EventStreamTest.h | 2 +- tests/src/OperationsTest.h | 2 +- 6 files changed, 38 insertions(+), 10 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index 777935e9..2c286809 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -348,7 +348,7 @@ auto Transform(const Events& source, const SignalPack& d /// Process /////////////////////////////////////////////////////////////////////////////////////////////////// using REACT_IMPL::EventRange; -using REACT_IMPL::EventInserter; +using REACT_IMPL::EventEmitter; template < diff --git a/include/react/Signal.h b/include/react/Signal.h index 936874b4..7f211392 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -933,7 +933,7 @@ bool Equals(const Signal& lhs, const Signal& rhs) REACT_IMPL::Identity::Type::ValueT& r) \ { \ using T = decltype(r.name); \ - using S = REACT_MSVC_NO_TYPENAME REACT::RemoveInput::Type; \ + using S = REACT_MSVC_NO_TYPENAME REACT::DecayInput::Type; \ return static_cast(r.name); \ })) @@ -946,7 +946,7 @@ bool Equals(const Signal& lhs, const Signal& rhs) { \ assert(r != nullptr); \ using T = decltype(r->name); \ - using S = REACT_MSVC_NO_TYPENAME REACT::RemoveInput::Type; \ + using S = REACT_MSVC_NO_TYPENAME REACT::DecayInput::Type; \ return static_cast(r->name); \ })) diff --git a/include/react/TypeTraits.h b/include/react/TypeTraits.h index 7141ac14..c6b6b7dd 100644 --- a/include/react/TypeTraits.h +++ b/include/react/TypeTraits.h @@ -94,6 +94,30 @@ struct IsContinuation { static const bool value = false; }; template struct IsContinuation> { static const bool value = true; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// IsObservable +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct IsObservable { static const bool value = false; }; + +template +struct IsObservable> { static const bool value = true; }; + +template +struct IsObservable> { static const bool value = true; }; + +template +struct IsObservable> { static const bool value = true; }; + +template +struct IsObservable> { static const bool value = true; }; + +template +struct IsObservable> { static const bool value = true; }; + +template +struct IsObservable> { static const bool value = true; }; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// IsReactive /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -128,16 +152,16 @@ template struct IsReactive> { static const bool value = true; }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// RemoveInput +/// DecayInput /////////////////////////////////////////////////////////////////////////////////////////////////// template -struct RemoveInput { using Type = T; }; +struct DecayInput { using Type = T; }; template -struct RemoveInput> { using Type = Signal; }; +struct DecayInput> { using Type = Signal; }; template -struct RemoveInput> { using Type = Events; }; +struct DecayInput> { using Type = Events; }; /******************************************/ REACT_END /******************************************/ diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index 2adf8160..b7d79d27 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -255,8 +255,12 @@ class EventRange using const_iterator = typename std::vector::const_iterator; using size_type = typename std::vector::size_type; + // Copy ctor EventRange(const EventRange&) = default; + // Copy assignment + EventRange& operator=(const EventRange&) = default; + const_iterator begin() const { return data_.begin(); } const_iterator end() const { return data_.end(); } @@ -272,7 +276,7 @@ class EventRange }; template -using EventInserter = std::back_insert_iterator>; +using EventEmitter = std::back_insert_iterator>; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/tests/src/EventStreamTest.h b/tests/src/EventStreamTest.h index 39e2479d..bb2efce4 100644 --- a/tests/src/EventStreamTest.h +++ b/tests/src/EventStreamTest.h @@ -285,7 +285,7 @@ TYPED_TEST_P(EventStreamTest, EventProcess) int callCount = 0; auto processed = Process(merged, - [&] (EventRange range, EventInserter out) + [&] (EventRange range, EventEmitter out) { for (const auto& e : range) { diff --git a/tests/src/OperationsTest.h b/tests/src/OperationsTest.h index 77e8c399..5ef9cc3d 100644 --- a/tests/src/OperationsTest.h +++ b/tests/src/OperationsTest.h @@ -911,7 +911,7 @@ TYPED_TEST_P(OperationsTest, SyncedEventProcess1) auto processed = Process(merged, With(mult), - [&] (EventRange range, EventInserter out, int mult) + [&] (EventRange range, EventEmitter out, int mult) { for (const auto& e : range) { From 2f1cb0ac85a3aa4fd8c908832634b85e68aa20fb Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 16 Feb 2015 20:12:28 +0100 Subject: [PATCH 215/266] Fixed #8. --- include/react/detail/IReactiveEngine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/react/detail/IReactiveEngine.h b/include/react/detail/IReactiveEngine.h index 33aaf076..9a422cb8 100644 --- a/include/react/detail/IReactiveEngine.h +++ b/include/react/detail/IReactiveEngine.h @@ -72,7 +72,7 @@ struct EngineInterface static void OnTurnAdmissionStart(TurnT& turn) { - Instance().OnTurnAdmissionEnd(turn); + Instance().OnTurnAdmissionStart(turn); } static void OnTurnAdmissionEnd(TurnT& turn) From 1f6ddb75ccba2304d7fdfeb36e398fc276d7b89b Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 23 Mar 2015 11:44:12 +0100 Subject: [PATCH 216/266] Fixed #9 --- include/react/common/Timing.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/react/common/Timing.h b/include/react/common/Timing.h index 08f72892..3775661d 100644 --- a/include/react/common/Timing.h +++ b/include/react/common/Timing.h @@ -189,7 +189,11 @@ class ConditionalTimer private: // Only measure once bool shouldMeasure_ = true; - bool isThresholdExceeded_ = false; + + // Until we have measured, assume the threshold is exceeded. + // The cost of initially not parallelizing what should be parallelized is much higher + // than for the other way around. + bool isThresholdExceeded_ = true; }; /****************************************/ REACT_IMPL_END /***************************************/ From 0ca831161895a04d7965144ddc35d16f3b3c1f24 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 12 Jun 2016 15:23:43 +0200 Subject: [PATCH 217/266] Update to VS2015. --- project/msvc/CppReact.vcxproj | 10 +++++----- project/msvc/CppReactBenchmark.vcxproj | 10 +++++----- project/msvc/CppReactExample.vcxproj | 10 +++++----- project/msvc/CppReactTest.vcxproj | 10 +++++----- project/msvc/Example_BasicAlgorithms.vcxproj | 10 +++++----- project/msvc/Example_BasicComposition.vcxproj | 10 +++++----- project/msvc/Example_BasicEvents.vcxproj | 10 +++++----- project/msvc/Example_BasicObservers.vcxproj | 10 +++++----- project/msvc/Example_BasicReactors.vcxproj | 10 +++++----- project/msvc/Example_BasicSignals.vcxproj | 10 +++++----- project/msvc/Example_BasicSynchronization.vcxproj | 10 +++++----- 11 files changed, 55 insertions(+), 55 deletions(-) diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index 16ae1f97..d9782c0d 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -26,26 +26,26 @@ StaticLibrary true - v120 + v140 MultiByte StaticLibrary true - v120 + v140 MultiByte StaticLibrary false - v120 + v140 true MultiByte StaticLibrary false - v120 + v140 true MultiByte diff --git a/project/msvc/CppReactBenchmark.vcxproj b/project/msvc/CppReactBenchmark.vcxproj index 7613239a..a57e4b20 100644 --- a/project/msvc/CppReactBenchmark.vcxproj +++ b/project/msvc/CppReactBenchmark.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -26,26 +26,26 @@ Application true - v120 + v140 MultiByte Application true - v120 + v140 MultiByte Application false - v120 + v140 true MultiByte Application false - v120 + v140 true MultiByte diff --git a/project/msvc/CppReactExample.vcxproj b/project/msvc/CppReactExample.vcxproj index 323c777f..34a714e6 100644 --- a/project/msvc/CppReactExample.vcxproj +++ b/project/msvc/CppReactExample.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -27,26 +27,26 @@ Application true - v120 + v140 MultiByte Application true - v120 + v140 MultiByte Application false - v120 + v140 true MultiByte Application false - v120 + v140 true MultiByte diff --git a/project/msvc/CppReactTest.vcxproj b/project/msvc/CppReactTest.vcxproj index 7b1cb65f..5c2878a4 100644 --- a/project/msvc/CppReactTest.vcxproj +++ b/project/msvc/CppReactTest.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -26,26 +26,26 @@ Application true - v120 + v140 MultiByte Application true - v120 + v140 MultiByte Application false - v120 + v140 true MultiByte Application false - v120 + v140 true MultiByte diff --git a/project/msvc/Example_BasicAlgorithms.vcxproj b/project/msvc/Example_BasicAlgorithms.vcxproj index eeffca5a..f77233e6 100644 --- a/project/msvc/Example_BasicAlgorithms.vcxproj +++ b/project/msvc/Example_BasicAlgorithms.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -26,26 +26,26 @@ Application true - v120 + v140 MultiByte Application true - v120 + v140 MultiByte Application false - v120 + v140 true MultiByte Application false - v120 + v140 true MultiByte diff --git a/project/msvc/Example_BasicComposition.vcxproj b/project/msvc/Example_BasicComposition.vcxproj index 01d26c88..43101fb2 100644 --- a/project/msvc/Example_BasicComposition.vcxproj +++ b/project/msvc/Example_BasicComposition.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -26,26 +26,26 @@ Application true - v120 + v140 MultiByte Application true - v120 + v140 MultiByte Application false - v120 + v140 true MultiByte Application false - v120 + v140 true MultiByte diff --git a/project/msvc/Example_BasicEvents.vcxproj b/project/msvc/Example_BasicEvents.vcxproj index d5d13295..07370926 100644 --- a/project/msvc/Example_BasicEvents.vcxproj +++ b/project/msvc/Example_BasicEvents.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -26,26 +26,26 @@ Application true - v120 + v140 MultiByte Application true - v120 + v140 MultiByte Application false - v120 + v140 true MultiByte Application false - v120 + v140 true MultiByte diff --git a/project/msvc/Example_BasicObservers.vcxproj b/project/msvc/Example_BasicObservers.vcxproj index 9fa37b8c..7e27da42 100644 --- a/project/msvc/Example_BasicObservers.vcxproj +++ b/project/msvc/Example_BasicObservers.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -26,26 +26,26 @@ Application true - v120 + v140 MultiByte Application true - v120 + v140 MultiByte Application false - v120 + v140 true MultiByte Application false - v120 + v140 true MultiByte diff --git a/project/msvc/Example_BasicReactors.vcxproj b/project/msvc/Example_BasicReactors.vcxproj index 07701d98..ba7b1dad 100644 --- a/project/msvc/Example_BasicReactors.vcxproj +++ b/project/msvc/Example_BasicReactors.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -26,26 +26,26 @@ Application true - v120 + v140 MultiByte Application true - v120 + v140 MultiByte Application false - v120 + v140 true MultiByte Application false - v120 + v140 true MultiByte diff --git a/project/msvc/Example_BasicSignals.vcxproj b/project/msvc/Example_BasicSignals.vcxproj index e090dd8a..556b2ad0 100644 --- a/project/msvc/Example_BasicSignals.vcxproj +++ b/project/msvc/Example_BasicSignals.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -26,26 +26,26 @@ Application true - v120 + v140 MultiByte Application true - v120 + v140 MultiByte Application false - v120 + v140 true MultiByte Application false - v120 + v140 true MultiByte diff --git a/project/msvc/Example_BasicSynchronization.vcxproj b/project/msvc/Example_BasicSynchronization.vcxproj index 2b2edec4..faa0ccfb 100644 --- a/project/msvc/Example_BasicSynchronization.vcxproj +++ b/project/msvc/Example_BasicSynchronization.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -26,26 +26,26 @@ Application true - v120 + v140 MultiByte Application true - v120 + v140 MultiByte Application false - v120 + v140 true MultiByte Application false - v120 + v140 true MultiByte From 872d6d34a5eacac966ac5bb1058f05c0d4d5e0ec Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 12 Jun 2016 15:24:33 +0200 Subject: [PATCH 218/266] WIP refactor --- benchmarks/src/Main.cpp | 15 +- include/react/Event.h | 495 ++--------------- include/react/Observer.h | 8 +- include/react/Signal.h | 652 +---------------------- include/react/TypeTraits.h | 113 ++-- include/react/detail/graph/SignalNodes.h | 39 +- 6 files changed, 135 insertions(+), 1187 deletions(-) diff --git a/benchmarks/src/Main.cpp b/benchmarks/src/Main.cpp index eb47f33f..00284776 100644 --- a/benchmarks/src/Main.cpp +++ b/benchmarks/src/Main.cpp @@ -37,6 +37,11 @@ REACTIVE_DOMAIN(ToposortDomain, parallel, ToposortEngine) REACTIVE_DOMAIN(PulsecountDomain, parallel, PulsecountEngine) REACTIVE_DOMAIN(SubtreeDomain, parallel, SubtreeEngine) +REACTIVE_DOMAIN(ToposortSTDomainConc, sequential_concurrent, ToposortEngine) +REACTIVE_DOMAIN(ToposortDomainConc, parallel_concurrent, ToposortEngine) +REACTIVE_DOMAIN(PulsecountDomainConc, parallel_concurrent, PulsecountEngine) +REACTIVE_DOMAIN(SubtreeDomainConc, parallel_concurrent, SubtreeEngine) + void runBenchmarkGrid(std::ostream& out) { RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(20, 10000), @@ -130,7 +135,7 @@ void runBenchmarkLifeSim(std::ostream& out) // SourceSetDomain, PulsecountDomain); RUN_BENCHMARK(out, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 15, 10000), - ToposortSTDomain, ToposortDomain, PulsecountDomain); + ToposortSTDomainConc, ToposortDomainConc, PulsecountDomainConc); //RUN_BENCHMARK(out, 3, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 50, 100), // PulsecountDomain, PulsecountDomain); @@ -216,9 +221,9 @@ void debugBenchmarks() void profileBenchmark() { - RUN_BENCHMARK(std::cout, 1, Benchmark_Grid, BenchmarkParams_Grid(30, 10000), - ToposortSTDomain); - //ToposortSTDomain, ToposortDomain, PulsecountDomain, SubtreeDomain); + RUN_BENCHMARK(std::cout, 3, Benchmark_Grid, BenchmarkParams_Grid(100, 10000), + //SubtreeDomain); + ToposortSTDomain, ToposortDomain, PulsecountDomain, SubtreeDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_Grid, BenchmarkParams_Grid(30, 10000), //SourceSetDomain); @@ -228,7 +233,7 @@ void profileBenchmark() //ToposortSTDomain, ToposortDomain, PulsecountDomain, SubtreeDomain); //RUN_BENCHMARK(std::cout, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 15, 10000), - //ToposortSTDomain); + //ToposortSTDomainConc); } } // ~anonymous namespace diff --git a/include/react/Event.h b/include/react/Event.h index 2c286809..8ca754c2 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -25,36 +25,28 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class Events; -template +template class EventSource; -template -class TempEvents; - enum class Token; -template +template class Signal; -template -class SignalPack; - using REACT_IMPL::WeightHint; /////////////////////////////////////////////////////////////////////////////////////////////////// /// MakeEventSource /////////////////////////////////////////////////////////////////////////////////////////////////// -template -auto MakeEventSource() - -> EventSource +template +auto MakeEventSource() -> EventSource { using REACT_IMPL::EventSourceNode; - return EventSource( - std::make_shared>()); + return EventSource(std::make_shared>()); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -62,174 +54,49 @@ auto MakeEventSource() /////////////////////////////////////////////////////////////////////////////////////////////////// template < - typename D, typename TArg1, typename ... TArgs, - typename E = TArg1, - typename TOp = REACT_IMPL::EventMergeOp, - REACT_IMPL::EventStreamNodePtrT ...> + typename E = TArg1 > -auto Merge(const Events& arg1, const Events& ... args) - -> TempEvents +auto Merge(const Events& arg1, const Events& ... args) -> Events { using REACT_IMPL::EventOpNode; - static_assert(sizeof...(TArgs) > 0, - "Merge: 2+ arguments are required."); + static_assert(sizeof...(TArgs) > 0, "Merge: 2+ arguments are required."); - return TempEvents( - std::make_shared>( + return Events( + std::make_shared>( GetNodePtr(arg1), GetNodePtr(args) ...)); } -template -< - typename TLeftEvents, - typename TRightEvents, - typename D = typename TLeftEvents::DomainT, - typename TLeftVal = typename TLeftEvents::ValueT, - typename TRightVal = typename TRightEvents::ValueT, - typename E = TLeftVal, - typename TOp = REACT_IMPL::EventMergeOp, - REACT_IMPL::EventStreamNodePtrT>, - class = typename std::enable_if< - IsEvent::value>::type, - class = typename std::enable_if< - IsEvent::value>::type -> -auto operator|(const TLeftEvents& lhs, const TRightEvents& rhs) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - GetNodePtr(lhs), GetNodePtr(rhs))); -} - -template -< - typename D, - typename TLeftVal, - typename TLeftOp, - typename TRightVal, - typename TRightOp, - typename E = TLeftVal, - typename TOp = REACT_IMPL::EventMergeOp -> -auto operator|(TempEvents&& lhs, TempEvents&& rhs) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - lhs.StealOp(), rhs.StealOp())); -} - -template -< - typename D, - typename TLeftVal, - typename TLeftOp, - typename TRightEvents, - typename TRightVal = typename TRightEvents::ValueT, - typename E = TLeftVal, - typename TOp = REACT_IMPL::EventMergeOp>, - class = typename std::enable_if< - IsEvent::value>::type -> -auto operator|(TempEvents&& lhs, const TRightEvents& rhs) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - lhs.StealOp(), GetNodePtr(rhs))); -} - -template -< - typename TLeftEvents, - typename D, - typename TRightVal, - typename TRightOp, - typename TLeftVal = typename TLeftEvents::ValueT, - typename E = TLeftVal, - typename TOp = REACT_IMPL::EventMergeOp, - TRightOp>, - class = typename std::enable_if< - IsEvent::value>::type -> -auto operator|(const TLeftEvents& lhs, TempEvents&& rhs) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - GetNodePtr(lhs), rhs.StealOp())); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Filter /////////////////////////////////////////////////////////////////////////////////////////////////// template < - typename D, typename E, typename FIn, - typename F = typename std::decay::type, - typename TOp = REACT_IMPL::EventFilterOp> + typename F = typename std::decay::type > -auto Filter(const Events& src, FIn&& filter) - -> TempEvents +auto Filter(const Events& src, FIn&& filter) -> Events { using REACT_IMPL::EventOpNode; - return TempEvents( - std::make_shared>( + return Events( + std::make_shared>( std::forward(filter), GetNodePtr(src))); } -template -< - typename D, - typename E, - typename TOpIn, - typename FIn, - typename F = typename std::decay::type, - typename TOpOut = REACT_IMPL::EventFilterOp -> -auto Filter(TempEvents&& src, FIn&& filter) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - std::forward(filter), src.StealOp())); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Filter - Synced /////////////////////////////////////////////////////////////////////////////////////////////////// template < - typename D, typename E, typename FIn, typename ... TDepValues > -auto Filter(const Events& source, const SignalPack& depPack, FIn&& func) - -> Events +auto Filter(const Events& source, const SignalPack& depPack, FIn&& func) -> Events { using REACT_IMPL::SyncedEventFilterNode; @@ -237,20 +104,20 @@ auto Filter(const Events& source, const SignalPack& depPac struct NodeBuilder_ { - NodeBuilder_(const Events& source, FIn&& func) : + NodeBuilder_(const Events& source, FIn&& func) : MySource( source ), MyFunc( std::forward(func) ) {} auto operator()(const Signal& ... deps) - -> Events + -> Events { - return Events( + return Events( std::make_shared>( GetNodePtr(MySource), std::forward(MyFunc), GetNodePtr(deps) ...)); } - const Events& MySource; + const Events& MySource; FIn MyFunc; }; @@ -264,57 +131,31 @@ auto Filter(const Events& source, const SignalPack& depPac /////////////////////////////////////////////////////////////////////////////////////////////////// template < - typename D, typename TIn, typename FIn, typename F = typename std::decay::type, - typename TOut = typename std::result_of::type, - typename TOp = REACT_IMPL::EventTransformOp> + typename TOut = typename std::result_of::type > -auto Transform(const Events& src, FIn&& func) - -> TempEvents +auto Transform(const Events& src, FIn&& func) -> Events { using REACT_IMPL::EventOpNode; - return TempEvents( - std::make_shared>( + return Events( + std::make_shared>( std::forward(func), GetNodePtr(src))); } -template -< - typename D, - typename TIn, - typename TOpIn, - typename FIn, - typename F = typename std::decay::type, - typename TOut = typename std::result_of::type, - typename TOpOut = REACT_IMPL::EventTransformOp -> -auto Transform(TempEvents&& src, FIn&& func) - -> TempEvents -{ - using REACT_IMPL::EventOpNode; - - return TempEvents( - std::make_shared>( - std::forward(func), src.StealOp())); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Transform - Synced /////////////////////////////////////////////////////////////////////////////////////////////////// template < - typename D, typename TIn, typename FIn, typename ... TDepValues, typename TOut = typename std::result_of::type > -auto Transform(const Events& source, const SignalPack& depPack, FIn&& func) - -> Events +auto Transform(const Events& source, FIn&& func, const Signal& ... deps) -> Events { using REACT_IMPL::SyncedEventTransformNode; @@ -322,20 +163,20 @@ auto Transform(const Events& source, const SignalPack& d struct NodeBuilder_ { - NodeBuilder_(const Events& source, FIn&& func) : + NodeBuilder_(const Events& source, FIn&& func) : MySource( source ), MyFunc( std::forward(func) ) {} auto operator()(const Signal& ... deps) - -> Events + -> Events { - return Events( + return Events( std::make_shared>( GetNodePtr(MySource), std::forward(MyFunc), GetNodePtr(deps) ...)); } - const Events& MySource; + const Events& MySource; FIn MyFunc; }; @@ -353,17 +194,15 @@ using REACT_IMPL::EventEmitter; template < typename TOut, - typename D, typename TIn, typename FIn, typename F = typename std::decay::type > -auto Process(const Events& src, FIn&& func) - -> Events +auto Process(const Events& src, FIn&& func) -> Events { using REACT_IMPL::EventProcessingNode; - return Events( + return Events( std::make_shared>( GetNodePtr(src), std::forward(func))); } @@ -374,13 +213,11 @@ auto Process(const Events& src, FIn&& func) template < typename TOut, - typename D, typename TIn, typename FIn, typename ... TDepValues > -auto Process(const Events& source, const SignalPack& depPack, FIn&& func) - -> Events +auto Process(const Events& source, const SignalPack& depPack, FIn&& func) -> Events { using REACT_IMPL::SyncedEventProcessingNode; @@ -388,20 +225,20 @@ auto Process(const Events& source, const SignalPack& dep struct NodeBuilder_ { - NodeBuilder_(const Events& source, FIn&& func) : + NodeBuilder_(const Events& source, FIn&& func) : MySource( source ), MyFunc( std::forward(func) ) {} auto operator()(const Signal& ... deps) - -> Events + -> Events { - return Events( + return Events( std::make_shared>( GetNodePtr(MySource), std::forward(MyFunc), GetNodePtr(deps) ...)); } - const Events& MySource; + const Events& MySource; FIn MyFunc; }; @@ -413,37 +250,26 @@ auto Process(const Events& source, const SignalPack& dep /////////////////////////////////////////////////////////////////////////////////////////////////// /// Flatten /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TInnerValue -> -auto Flatten(const Signal>& outer) - -> Events +template +auto Flatten(const Signal>& outer) -> Events { - return Events( - std::make_shared, TInnerValue>>( + return Events( + std::make_shared, TInnerValue>>( GetNodePtr(outer), GetNodePtr(outer.Value()))); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Join /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename ... TArgs -> -auto Join(const Events& ... args) - -> Events> +template +auto Join(const Events& ... args) -> Events> { using REACT_IMPL::EventJoinNode; - static_assert(sizeof...(TArgs) > 1, - "Join: 2+ arguments are required."); + static_assert(sizeof...(TArgs) > 1, "Join: 2+ arguments are required."); - return Events>( - std::make_shared>( + return Events< std::tuple>( + std::make_shared>( GetNodePtr(args) ...)); } @@ -459,8 +285,7 @@ struct Tokenizer }; template -auto Tokenize(TEvents&& source) - -> decltype(Transform(source, Tokenizer{})) +auto Tokenize(TEvents&& source) -> decltype(Transform(source, Tokenizer{})) { return Transform(source, Tokenizer{}); } @@ -468,15 +293,11 @@ auto Tokenize(TEvents&& source) /////////////////////////////////////////////////////////////////////////////////////////////////// /// Events /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E = Token -> -class Events : public REACT_IMPL::EventStreamBase +template +class Events : public REACT_IMPL::EventStreamBase { private: - using NodeT = REACT_IMPL::EventStreamNode; + using NodeT = REACT_IMPL::EventStreamNode; using NodePtrT = std::shared_ptr; public: @@ -551,99 +372,11 @@ class Events : public REACT_IMPL::EventStreamBase } }; -// Specialize for references -template -< - typename D, - typename E -> -class Events : public REACT_IMPL::EventStreamBase> -{ -private: - using NodeT = REACT_IMPL::EventStreamNode>; - using NodePtrT = std::shared_ptr; - -public: - using ValueT = E; - - // Default ctor - Events() = default; - - // Copy ctor - Events(const Events&) = default; - - // Move ctor - Events(Events&& other) : - Events::EventStreamBase( std::move(other) ) - {} - - // Node ctor - explicit Events(NodePtrT&& nodePtr) : - Events::EventStreamBase( std::move(nodePtr) ) - {} - - // Copy assignment - Events& operator=(const Events&) = default; - - // Move assignment - Events& operator=(Events&& other) - { - Events::EventStreamBase::operator=( std::move(other) ); - return *this; - } - - bool Equals(const Events& other) const - { - return Events::EventStreamBase::Equals(other); - } - - bool IsValid() const - { - return Events::EventStreamBase::IsValid(); - } - - void SetWeightHint(WeightHint weight) - { - Events::EventStreamBase::SetWeightHint(weight); - } - - auto Tokenize() const - -> decltype(REACT::Tokenize(std::declval())) - { - return REACT::Tokenize(*this); - } - - template - auto Merge(TArgs&& ... args) - -> decltype(REACT::Merge(std::declval(), std::forward(args) ...)) - { - return REACT::Merge(*this, std::forward(args) ...); - } - - template - auto Filter(F&& f) const - -> decltype(REACT::Filter(std::declval(), std::forward(f))) - { - return REACT::Filter(*this, std::forward(f)); - } - - template - auto Transform(F&& f) const - -> decltype(REACT::Transform(std::declval(), std::forward(f))) - { - return REACT::Transform(*this, std::forward(f)); - } -}; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventSource /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E = Token -> -class EventSource : public Events +template +class EventSource : public Events { private: using NodeT = REACT_IMPL::EventSourceNode; @@ -710,134 +443,12 @@ class EventSource : public Events } }; -// Specialize for references -template -< - typename D, - typename E -> -class EventSource : public Events> -{ -private: - using NodeT = REACT_IMPL::EventSourceNode>; - using NodePtrT = std::shared_ptr; - -public: - // Default ctor - EventSource() = default; - - // Copy ctor - EventSource(const EventSource&) = default; - - // Move ctor - EventSource(EventSource&& other) : - EventSource::Events( std::move(other) ) - {} - - // Node ctor - explicit EventSource(NodePtrT&& nodePtr) : - EventSource::Events( std::move(nodePtr) ) - {} - - // Copy assignment - EventSource& operator=(const EventSource&) = default; - - // Move assignment - EventSource& operator=(EventSource&& other) - { - EventSource::Events::operator=( std::move(other) ); - return *this; - } - - // Explicit emit - void Emit(std::reference_wrapper e) const { EventSource::EventStreamBase::emit(e); } - - // Function object style - void operator()(std::reference_wrapper e) const { EventSource::EventStreamBase::emit(e); } - - // Stream style - const EventSource& operator<<(std::reference_wrapper e) const - { - EventSource::EventStreamBase::emit(e); - return *this; - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// TempEvents -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E, - typename TOp -> -class TempEvents : public Events -{ -protected: - using NodeT = REACT_IMPL::EventOpNode; - using NodePtrT = std::shared_ptr; - -public: - // Default ctor - TempEvents() = default; - - // Copy ctor - TempEvents(const TempEvents&) = default; - - // Move ctor - TempEvents(TempEvents&& other) : - TempEvents::Events( std::move(other) ) - {} - - // Node ctor - explicit TempEvents(NodePtrT&& nodePtr) : - TempEvents::Events( std::move(nodePtr) ) - {} - - // Copy assignment - TempEvents& operator=(const TempEvents&) = default; - - // Move assignment - TempEvents& operator=(TempEvents&& other) - { - TempEvents::EventStreamBase::operator=( std::move(other) ); - return *this; - } - - TOp StealOp() - { - return std::move(reinterpret_cast(this->ptr_.get())->StealOp()); - } - - template - auto Merge(TArgs&& ... args) - -> decltype(REACT::Merge(std::declval(), std::forward(args) ...)) - { - return REACT::Merge(*this, std::forward(args) ...); - } - - template - auto Filter(F&& f) const - -> decltype(REACT::Filter(std::declval(), std::forward(f))) - { - return REACT::Filter(*this, std::forward(f)); - } - - template - auto Transform(F&& f) const - -> decltype(REACT::Transform(std::declval(), std::forward(f))) - { - return REACT::Transform(*this, std::forward(f)); - } -}; - /******************************************/ REACT_END /******************************************/ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -template -bool Equals(const Events& lhs, const Events& rhs) +template +bool Equals(const Events& lhs, const Events& rhs) { return lhs.Equals(rhs); } diff --git a/include/react/Observer.h b/include/react/Observer.h index 45fd78a1..c9d986dc 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -24,13 +24,10 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class Signal; -template -class SignalPack; - -template +template class Events; using REACT_IMPL::ObserverAction; @@ -39,7 +36,6 @@ using REACT_IMPL::WeightHint; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Observer /////////////////////////////////////////////////////////////////////////////////////////////////// -template class Observer { private: diff --git a/include/react/Signal.h b/include/react/Signal.h index 7f211392..ac5c6030 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -29,83 +29,34 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class Signal; -template +template class VarSignal; -template -class TempSignal; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalPack - Wraps several nodes in a tuple. Create with comma operator. -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename ... TValues -> -class SignalPack -{ -public: - SignalPack(const Signal& ... deps) : - Data( std::tie(deps ...) ) - {} - - template - SignalPack(const SignalPack& curArgs, const Signal& newArg) : - Data( std::tuple_cat(curArgs.Data, std::tie(newArg)) ) - {} - - std::tuple& ...> Data; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// With - Utility function to create a SignalPack -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename ... TValues -> -auto With(const Signal& ... deps) - -> SignalPack -{ - return SignalPack(deps ...); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// MakeVar /////////////////////////////////////////////////////////////////////////////////////////////////// template < - typename D, typename V, typename S = typename std::decay::type, - class = typename std::enable_if< - ! IsSignal::value>::type, - class = typename std::enable_if< - ! IsEvent::value>::type + class = typename std::enable_if::value>::type, + class = typename std::enable_if::value>::type > -auto MakeVar(V&& value) - -> VarSignal +auto MakeVar(V&& value) -> VarSignal { - return VarSignal( - std::make_shared>( + return VarSignal( + std::make_shared>( std::forward(value))); } -template -< - typename D, - typename S -> -auto MakeVar(std::reference_wrapper value) - -> VarSignal +template +auto MakeVar(std::reference_wrapper value) -> VarSignal { - return VarSignal( - std::make_shared>>(value)); + return VarSignal( + std::make_shared>>(value)); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -113,15 +64,13 @@ auto MakeVar(std::reference_wrapper value) /////////////////////////////////////////////////////////////////////////////////////////////////// template < - typename D, typename V, typename S = typename std::decay::type, typename TInner = typename S::ValueT, - class = typename std::enable_if< - IsSignal::value>::type + class = typename std::enable_if::value>::type > auto MakeVar(V&& value) - -> VarSignal> + -> VarSignal> { return VarSignal>( std::make_shared>>( @@ -130,7 +79,7 @@ auto MakeVar(V&& value) template < - typename D, + typename V, typename S = typename std::decay::type, typename TInner = typename S::ValueT, @@ -151,7 +100,7 @@ auto MakeVar(V&& value) // Single arg template < - typename D, + typename TValue, typename FIn, typename F = typename std::decay::type, @@ -169,7 +118,7 @@ auto MakeSignal(const Signal& arg, FIn&& func) // Multiple args template < - typename D, + typename ... TValues, typename FIn, typename F = typename std::decay::type, @@ -203,396 +152,10 @@ auto MakeSignal(const SignalPack& argPack, FIn&& func) argPack.Data); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Unary operators -/////////////////////////////////////////////////////////////////////////////////////////////////// -#define REACT_DECLARE_OP(op,name) \ -template \ -struct name ## OpFunctor \ -{ \ - T operator()(const T& v) const { return op v; } \ -}; \ - \ -template \ -< \ - typename TSignal, \ - typename D = typename TSignal::DomainT, \ - typename TVal = typename TSignal::ValueT, \ - class = typename std::enable_if< \ - IsSignal::value>::type, \ - typename F = name ## OpFunctor, \ - typename S = typename std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp> \ -> \ -auto operator op(const TSignal& arg) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(), GetNodePtr(arg))); \ -} \ - \ -template \ -< \ - typename D, \ - typename TVal, \ - typename TOpIn, \ - typename F = name ## OpFunctor, \ - typename S = typename std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp \ -> \ -auto operator op(TempSignal&& arg) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(), arg.StealOp())); \ -} - -REACT_DECLARE_OP(+, UnaryPlus) -REACT_DECLARE_OP(-, UnaryMinus) -REACT_DECLARE_OP(!, LogicalNegation) -REACT_DECLARE_OP(~, BitwiseComplement) -REACT_DECLARE_OP(++, Increment) -REACT_DECLARE_OP(--, Decrement) - -#undef REACT_DECLARE_OP - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Binary operators -/////////////////////////////////////////////////////////////////////////////////////////////////// -#define REACT_DECLARE_OP(op,name) \ -template \ -struct name ## OpFunctor \ -{ \ - auto operator()(const L& lhs, const R& rhs) const \ - -> decltype(std::declval() op std::declval()) \ - { \ - return lhs op rhs; \ - } \ -}; \ - \ -template \ -struct name ## OpRFunctor \ -{ \ - name ## OpRFunctor(name ## OpRFunctor&& other) : \ - LeftVal( std::move(other.LeftVal) ) \ - {} \ - \ - template \ - name ## OpRFunctor(T&& val) : \ - LeftVal( std::forward(val) ) \ - {} \ - \ - name ## OpRFunctor(const name ## OpRFunctor& other) = delete; \ - \ - auto operator()(const R& rhs) const \ - -> decltype(std::declval() op std::declval()) \ - { \ - return LeftVal op rhs; \ - } \ - \ - L LeftVal; \ -}; \ - \ -template \ -struct name ## OpLFunctor \ -{ \ - name ## OpLFunctor(name ## OpLFunctor&& other) : \ - RightVal( std::move(other.RightVal) ) \ - {} \ - \ - template \ - name ## OpLFunctor(T&& val) : \ - RightVal( std::forward(val) ) \ - {} \ - \ - name ## OpLFunctor(const name ## OpLFunctor& other) = delete; \ - \ - auto operator()(const L& lhs) const \ - -> decltype(std::declval() op std::declval()) \ - { \ - return lhs op RightVal; \ - } \ - \ - R RightVal; \ -}; \ - \ -template \ -< \ - typename TLeftSignal, \ - typename TRightSignal, \ - typename D = typename TLeftSignal::DomainT, \ - typename TLeftVal = typename TLeftSignal::ValueT, \ - typename TRightVal = typename TRightSignal::ValueT, \ - class = typename std::enable_if< \ - IsSignal::value>::type, \ - class = typename std::enable_if< \ - IsSignal::value>::type, \ - typename F = name ## OpFunctor, \ - typename S = typename std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp, \ - REACT_IMPL::SignalNodePtrT> \ -> \ -auto operator op(const TLeftSignal& lhs, const TRightSignal& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(), GetNodePtr(lhs), GetNodePtr(rhs))); \ -} \ - \ -template \ -< \ - typename TLeftSignal, \ - typename TRightValIn, \ - typename D = typename TLeftSignal::DomainT, \ - typename TLeftVal = typename TLeftSignal::ValueT, \ - typename TRightVal = typename std::decay::type, \ - class = typename std::enable_if< \ - IsSignal::value>::type, \ - class = typename std::enable_if< \ - ! IsSignal::value>::type, \ - typename F = name ## OpLFunctor, \ - typename S = typename std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp> \ -> \ -auto operator op(const TLeftSignal& lhs, TRightValIn&& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F( std::forward(rhs) ), GetNodePtr(lhs))); \ -} \ - \ -template \ -< \ - typename TLeftValIn, \ - typename TRightSignal, \ - typename D = typename TRightSignal::DomainT, \ - typename TLeftVal = typename std::decay::type, \ - typename TRightVal = typename TRightSignal::ValueT, \ - class = typename std::enable_if< \ - ! IsSignal::value>::type, \ - class = typename std::enable_if< \ - IsSignal::value>::type, \ - typename F = name ## OpRFunctor, \ - typename S = typename std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp> \ -> \ -auto operator op(TLeftValIn&& lhs, const TRightSignal& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F( std::forward(lhs) ), GetNodePtr(rhs))); \ -} \ -template \ -< \ - typename D, \ - typename TLeftVal, \ - typename TLeftOp, \ - typename TRightVal, \ - typename TRightOp, \ - typename F = name ## OpFunctor, \ - typename S = typename std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp \ -> \ -auto operator op(TempSignal&& lhs, \ - TempSignal&& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(), lhs.StealOp(), rhs.StealOp())); \ -} \ - \ -template \ -< \ - typename D, \ - typename TLeftVal, \ - typename TLeftOp, \ - typename TRightSignal, \ - typename TRightVal = typename TRightSignal::ValueT, \ - class = typename std::enable_if< \ - IsSignal::value>::type, \ - typename F = name ## OpFunctor, \ - typename S = typename std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp> \ -> \ -auto operator op(TempSignal&& lhs, \ - const TRightSignal& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(), lhs.StealOp(), GetNodePtr(rhs))); \ -} \ - \ -template \ -< \ - typename TLeftSignal, \ - typename D, \ - typename TRightVal, \ - typename TRightOp, \ - typename TLeftVal = typename TLeftSignal::ValueT, \ - class = typename std::enable_if< \ - IsSignal::value>::type, \ - typename F = name ## OpFunctor, \ - typename S = typename std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp, \ - TRightOp> \ -> \ -auto operator op(const TLeftSignal& lhs, TempSignal&& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F(), GetNodePtr(lhs), rhs.StealOp())); \ -} \ - \ -template \ -< \ - typename D, \ - typename TLeftVal, \ - typename TLeftOp, \ - typename TRightValIn, \ - typename TRightVal = typename std::decay::type, \ - class = typename std::enable_if< \ - ! IsSignal::value>::type, \ - typename F = name ## OpLFunctor, \ - typename S = typename std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp \ -> \ -auto operator op(TempSignal&& lhs, TRightValIn&& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F( std::forward(rhs) ), lhs.StealOp())); \ -} \ - \ -template \ -< \ - typename TLeftValIn, \ - typename D, \ - typename TRightVal, \ - typename TRightOp, \ - typename TLeftVal = typename std::decay::type, \ - class = typename std::enable_if< \ - ! IsSignal::value>::type, \ - typename F = name ## OpRFunctor, \ - typename S = typename std::result_of::type, \ - typename TOp = REACT_IMPL::FunctionOp \ -> \ -auto operator op(TLeftValIn&& lhs, TempSignal&& rhs) \ - -> TempSignal \ -{ \ - return TempSignal( \ - std::make_shared>( \ - F( std::forward(lhs) ), rhs.StealOp())); \ -} - -REACT_DECLARE_OP(+, Addition) -REACT_DECLARE_OP(-, Subtraction) -REACT_DECLARE_OP(*, Multiplication) -REACT_DECLARE_OP(/, Division) -REACT_DECLARE_OP(%, Modulo) - -REACT_DECLARE_OP(==, Equal) -REACT_DECLARE_OP(!=, NotEqual) -REACT_DECLARE_OP(<, Less) -REACT_DECLARE_OP(<=, LessEqual) -REACT_DECLARE_OP(>, Greater) -REACT_DECLARE_OP(>=, GreaterEqual) - -REACT_DECLARE_OP(&&, LogicalAnd) -REACT_DECLARE_OP(||, LogicalOr) - -REACT_DECLARE_OP(&, BitwiseAnd) -REACT_DECLARE_OP(|, BitwiseOr) -REACT_DECLARE_OP(^, BitwiseXor) -//REACT_DECLARE_OP(<<, BitwiseLeftShift); // MSVC: Internal compiler error -//REACT_DECLARE_OP(>>, BitwiseRightShift); - -#undef REACT_DECLARE_OP - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Comma operator overload to create signal pack from 2 signals. -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TLeftVal, - typename TRightVal -> -auto operator,(const Signal& a, const Signal& b) - -> SignalPack -{ - return SignalPack(a, b); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Comma operator overload to append node to existing signal pack. -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename ... TCurValues, - typename TAppendValue -> -auto operator,(const SignalPack& cur, const Signal& append) - -> SignalPack -{ - return SignalPack(cur, append); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// operator->* overload to connect signals to a function and return the resulting signal. -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Single arg -template -< - typename D, - typename F, - template class TSignal, - typename TValue, - class = typename std::enable_if< - IsSignal>::value>::type -> -auto operator->*(const TSignal& arg, F&& func) - -> Signal::type> -{ - return REACT::MakeSignal(arg, std::forward(func)); -} - -// Multiple args -template -< - typename D, - typename F, - typename ... TValues -> -auto operator->*(const SignalPack& argPack, F&& func) - -> Signal::type> -{ - return REACT::MakeSignal(argPack, std::forward(func)); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Flatten /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TInnerValue -> +template auto Flatten(const Signal>& outer) -> Signal { @@ -604,11 +167,7 @@ auto Flatten(const Signal>& outer) /////////////////////////////////////////////////////////////////////////////////////////////////// /// Signal /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> +template class Signal : public REACT_IMPL::SignalBase { private: @@ -670,78 +229,14 @@ class Signal : public REACT_IMPL::SignalBase } }; -// Specialize for references -template -< - typename D, - typename S -> -class Signal : public REACT_IMPL::SignalBase> -{ -private: - using NodeT = REACT_IMPL::SignalNode>; - using NodePtrT = std::shared_ptr; - -public: - using ValueT = S; - - // Default ctor - Signal() = default; - - // Copy ctor - Signal(const Signal&) = default; - - // Move ctor - Signal(Signal&& other) : - Signal::SignalBase( std::move(other) ) - {} - - // Node ctor - explicit Signal(NodePtrT&& nodePtr) : - Signal::SignalBase( std::move(nodePtr) ) - {} - - // Copy assignment - Signal& operator=(const Signal&) = default; - - // Move assignment - Signal& operator=(Signal&& other) - { - Signal::SignalBase::operator=( std::move(other) ); - return *this; - } - - const S& Value() const { return Signal::SignalBase::getValue(); } - const S& operator()() const { return Signal::SignalBase::getValue(); } - - bool Equals(const Signal& other) const - { - return Signal::SignalBase::Equals(other); - } - - bool IsValid() const - { - return Signal::SignalBase::IsValid(); - } - - void SetWeightHint(WeightHint weight) - { - Signal::SignalBase::SetWeightHint(weight); - } -}; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// VarSignal /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -class VarSignal : public Signal +template +class VarSignal : public Signal { private: - using NodeT = REACT_IMPL::VarNode; + using NodeT = REACT_IMPL::VarNode; using NodePtrT = std::shared_ptr; public: @@ -800,113 +295,12 @@ class VarSignal : public Signal } }; -// Specialize for references -template -< - typename D, - typename S -> -class VarSignal : public Signal> -{ -private: - using NodeT = REACT_IMPL::VarNode>; - using NodePtrT = std::shared_ptr; - -public: - using ValueT = S; - - // Default ctor - VarSignal() = default; - - // Copy ctor - VarSignal(const VarSignal&) = default; - - // Move ctor - VarSignal(VarSignal&& other) : - VarSignal::Signal( std::move(other) ) - {} - - // Node ctor - explicit VarSignal(NodePtrT&& nodePtr) : - VarSignal::Signal( std::move(nodePtr) ) - {} - - // Copy assignment - VarSignal& operator=(const VarSignal&) = default; - - // Move assignment - VarSignal& operator=(VarSignal&& other) - { - VarSignal::Signal::operator=( std::move(other) ); - return *this; - } - - void Set(std::reference_wrapper newValue) const - { - VarSignal::SignalBase::setValue(newValue); - } - - const VarSignal& operator<<=(std::reference_wrapper newValue) const - { - VarSignal::SignalBase::setValue(newValue); - return *this; - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// TempSignal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename TOp -> -class TempSignal : public Signal -{ -private: - using NodeT = REACT_IMPL::SignalOpNode; - using NodePtrT = std::shared_ptr; - -public: - // Default ctor - TempSignal() = default; - - // Copy ctor - TempSignal(const TempSignal&) = default; - - // Move ctor - TempSignal(TempSignal&& other) : - TempSignal::Signal( std::move(other) ) - {} - - // Node ctor - explicit TempSignal(NodePtrT&& ptr) : - TempSignal::Signal( std::move(ptr) ) - {} - - // Copy assignment - TempSignal& operator=(const TempSignal&) = default; - - // Move assignemnt - TempSignal& operator=(TempSignal&& other) - { - TempSignal::Signal::operator=( std::move(other) ); - return *this; - } - - TOp StealOp() - { - return std::move(reinterpret_cast(this->ptr_.get())->StealOp()); - } -}; - /******************************************/ REACT_END /******************************************/ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -template -bool Equals(const Signal& lhs, const Signal& rhs) +template +bool Equals(const Signal& lhs, const Signal& rhs) { return lhs.Equals(rhs); } diff --git a/include/react/TypeTraits.h b/include/react/TypeTraits.h index c6b6b7dd..bd0ac99c 100644 --- a/include/react/TypeTraits.h +++ b/include/react/TypeTraits.h @@ -16,47 +16,35 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class Signal; -template +template class VarSignal; -template -class TempSignal; - -template +template class Events; -template +template class EventSource; -template -class TempEvents; - template class Observer; -template -class ScopedObserver; - -template -class Continuation; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// IsSignal /////////////////////////////////////////////////////////////////////////////////////////////////// template struct IsSignal { static const bool value = false; }; -template -struct IsSignal> { static const bool value = true; }; +template +struct IsSignal> { static const bool value = true; }; -template -struct IsSignal> { static const bool value = true; }; +template +struct IsSignal> { static const bool value = true; }; -template -struct IsSignal> { static const bool value = true; }; +template +constexpr bool IsSignalType = IsSignal::value; /////////////////////////////////////////////////////////////////////////////////////////////////// /// IsEvent @@ -64,14 +52,11 @@ struct IsSignal> { static const bool value = true; }; template struct IsEvent { static const bool value = false; }; -template -struct IsEvent> { static const bool value = true; }; - -template -struct IsEvent> { static const bool value = true; }; +template +struct IsEvent> { static const bool value = true; }; -template -struct IsEvent> { static const bool value = true; }; +template +struct IsEvent> { static const bool value = true; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// IsObserver @@ -82,41 +67,23 @@ struct IsObserver { static const bool value = false; }; template struct IsObserver> { static const bool value = true; }; -template -struct IsObserver> { static const bool value = true; }; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IsContinuation -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct IsContinuation { static const bool value = false; }; - -template -struct IsContinuation> { static const bool value = true; }; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// IsObservable /////////////////////////////////////////////////////////////////////////////////////////////////// template struct IsObservable { static const bool value = false; }; -template -struct IsObservable> { static const bool value = true; }; - -template -struct IsObservable> { static const bool value = true; }; - -template -struct IsObservable> { static const bool value = true; }; +template +struct IsObservable> { static const bool value = true; }; -template -struct IsObservable> { static const bool value = true; }; +template +struct IsObservable> { static const bool value = true; }; -template -struct IsObservable> { static const bool value = true; }; +template +struct IsObservable> { static const bool value = true; }; -template -struct IsObservable> { static const bool value = true; }; +template +struct IsObservable> { static const bool value = true; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// IsReactive @@ -124,44 +91,32 @@ struct IsObservable> { static const bool value = true; }; template struct IsReactive { static const bool value = false; }; -template -struct IsReactive> { static const bool value = true; }; - -template -struct IsReactive> { static const bool value = true; }; - -template -struct IsReactive> { static const bool value = true; }; +template +struct IsReactive> { static const bool value = true; }; -template -struct IsReactive> { static const bool value = true; }; +template +struct IsReactive> { static const bool value = true; }; -template -struct IsReactive> { static const bool value = true; }; +template +struct IsReactive> { static const bool value = true; }; -template -struct IsReactive> { static const bool value = true; }; +template +struct IsReactive> { static const bool value = true; }; template struct IsReactive> { static const bool value = true; }; -template -struct IsReactive> { static const bool value = true; }; - -template -struct IsReactive> { static const bool value = true; }; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// DecayInput /////////////////////////////////////////////////////////////////////////////////////////////////// template struct DecayInput { using Type = T; }; -template -struct DecayInput> { using Type = Signal; }; +template +struct DecayInput> { using Type = Signal; }; -template -struct DecayInput> { using Type = Events; }; +template +struct DecayInput> { using Type = Events; }; /******************************************/ REACT_END /******************************************/ diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index eb438a0c..64cccaee 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -21,18 +21,14 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template bool Equals(const L& lhs, const R& rhs); /////////////////////////////////////////////////////////////////////////////////////////////////// /// SignalNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -class SignalNode : public ObservableNode +template +class SignalNode : public ObservableNode { public: SignalNode() = default; @@ -52,19 +48,15 @@ class SignalNode : public ObservableNode S value_; }; -template -using SignalNodePtrT = std::shared_ptr>; +template +using SignalNodePtrT = std::shared_ptr>; /////////////////////////////////////////////////////////////////////////////////////////////////// /// VarNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> +template class VarNode : - public SignalNode, + public SignalNode, public IInputNode { using Engine = typename VarNode::Engine; @@ -228,12 +220,10 @@ class FunctionOp : public ReactiveOpBase /////////////////////////////////////////////////////////////////////////////////////////////////// template < - typename D, typename S, typename TOp > -class SignalOpNode : - public SignalNode +class SignalOpNode : public SignalNode { using Engine = typename SignalOpNode::Engine; @@ -261,8 +251,7 @@ class SignalOpNode : using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); + REACT_LOG(D::Log().template Append(GetObjectId(*this), turn.Id())); bool changed = false; @@ -279,8 +268,7 @@ class SignalOpNode : } }// ~timer - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); + REACT_LOG(D::Log().template Append(GetObjectId(*this), turn.Id())); if (changed) Engine::OnNodePulse(*this, turn); @@ -309,11 +297,10 @@ class SignalOpNode : /////////////////////////////////////////////////////////////////////////////////////////////////// template < - typename D, typename TOuter, typename TInner > -class FlattenNode : public SignalNode +class FlattenNode : public SignalNode { using Engine = typename FlattenNode::Engine; @@ -377,8 +364,8 @@ class FlattenNode : public SignalNode virtual int DependencyCount() const override { return 2; } private: - std::shared_ptr> outer_; - std::shared_ptr> inner_; + std::shared_ptr> outer_; + std::shared_ptr> inner_; }; /****************************************/ REACT_IMPL_END /***************************************/ From f9ca5bbb1915619da1ba2594cf60ce2fc1ccdcf5 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 25 Jul 2016 18:19:34 +0200 Subject: [PATCH 219/266] Refactor WIP. --- benchmarks/src/BenchmarkBase.h | 2 +- benchmarks/src/BenchmarkFanout.h | 2 +- benchmarks/src/BenchmarkGrid.h | 2 +- benchmarks/src/BenchmarkLifeSim.h | 2 +- benchmarks/src/BenchmarkRandom.h | 2 +- benchmarks/src/BenchmarkSequence.h | 2 +- benchmarks/src/Main.cpp | 2 +- examples/src/BasicAlgorithms.cpp | 2 +- examples/src/BasicComposition.cpp | 2 +- examples/src/BasicEvents.cpp | 2 +- examples/src/BasicObservers.cpp | 2 +- examples/src/BasicReactors.cpp | 2 +- examples/src/BasicSignals.cpp | 2 +- examples/src/BasicSynchronization.cpp | 2 +- include/react/Algorithm.h | 2 +- include/react/Domain.h | 246 +----------------- include/react/Event.h | 99 +++---- include/react/Observer.h | 2 +- include/react/Reactor.h | 2 +- include/react/Signal.h | 169 +++--------- include/react/TypeTraits.h | 45 +--- include/react/common/Concurrency.h | 2 +- include/react/common/Containers.h | 2 +- include/react/common/RefCounting.h | 2 +- include/react/common/SourceIdSet.h | 2 +- include/react/common/Timing.h | 2 +- include/react/common/TopoQueue.h | 2 +- include/react/common/Types.h | 2 +- include/react/common/Util.h | 2 +- include/react/detail/Defs.h | 2 +- include/react/detail/DomainBase.h | 2 +- include/react/detail/EngineBase.h | 2 +- include/react/detail/EventBase.h | 10 +- include/react/detail/IReactiveEngine.h | 133 ++-------- include/react/detail/IReactiveNode.h | 20 +- include/react/detail/ObserverBase.h | 2 +- include/react/detail/ReactiveBase.h | 2 +- include/react/detail/ReactiveInput.h | 2 +- include/react/detail/SignalBase.h | 2 +- include/react/detail/graph/AlgorithmNodes.h | 18 +- .../react/detail/graph/ContinuationNodes.h | 8 +- include/react/detail/graph/EventNodes.h | 131 +++++++--- include/react/detail/graph/GraphBase.h | 2 +- include/react/detail/graph/ObserverNodes.h | 8 +- include/react/detail/graph/ReactorNodes.h | 4 +- include/react/detail/graph/SignalNodes.h | 8 +- include/react/engine/PulsecountEngine.h | 2 +- include/react/engine/SubtreeEngine.h | 2 +- include/react/engine/ToposortEngine.h | 2 +- include/react/logging/EventLog.h | 2 +- include/react/logging/EventRecords.h | 2 +- include/react/logging/Logging.h | 2 +- src/engine/PulsecountEngine.cpp | 2 +- src/engine/SubtreeEngine.cpp | 2 +- src/engine/ToposortEngine.cpp | 2 +- src/logging/EventLog.cpp | 2 +- src/logging/EventRecords.cpp | 2 +- tests/src/EventStreamTest.cpp | 2 +- tests/src/EventStreamTest.h | 2 +- tests/src/EventStreamTestQ.cpp | 2 +- tests/src/MoveTest.cpp | 2 +- tests/src/MoveTest.h | 2 +- tests/src/ObserverTest.cpp | 2 +- tests/src/ObserverTest.h | 2 +- tests/src/ObserverTestQ.cpp | 2 +- tests/src/OperationsTest.cpp | 2 +- tests/src/OperationsTest.h | 2 +- tests/src/OperationsTestQ.cpp | 2 +- tests/src/ParallelizationTest.cpp | 2 +- tests/src/ParallelizationTest.h | 2 +- tests/src/SignalTest.cpp | 2 +- tests/src/SignalTest.h | 2 +- tests/src/SignalTestQ.cpp | 2 +- tests/src/TestUtil.h | 2 +- tests/src/TransactionTest.cpp | 2 +- tests/src/TransactionTest.h | 2 +- 76 files changed, 295 insertions(+), 730 deletions(-) diff --git a/benchmarks/src/BenchmarkBase.h b/benchmarks/src/BenchmarkBase.h index deaaf931..4b65306a 100644 --- a/benchmarks/src/BenchmarkBase.h +++ b/benchmarks/src/BenchmarkBase.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/benchmarks/src/BenchmarkFanout.h b/benchmarks/src/BenchmarkFanout.h index bc2c8b3e..6feaf567 100644 --- a/benchmarks/src/BenchmarkFanout.h +++ b/benchmarks/src/BenchmarkFanout.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/benchmarks/src/BenchmarkGrid.h b/benchmarks/src/BenchmarkGrid.h index 551f3db8..d719afaa 100644 --- a/benchmarks/src/BenchmarkGrid.h +++ b/benchmarks/src/BenchmarkGrid.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/benchmarks/src/BenchmarkLifeSim.h b/benchmarks/src/BenchmarkLifeSim.h index 1ebab386..39acc2df 100644 --- a/benchmarks/src/BenchmarkLifeSim.h +++ b/benchmarks/src/BenchmarkLifeSim.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/benchmarks/src/BenchmarkRandom.h b/benchmarks/src/BenchmarkRandom.h index 63cbd996..bf3ba9d1 100644 --- a/benchmarks/src/BenchmarkRandom.h +++ b/benchmarks/src/BenchmarkRandom.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/benchmarks/src/BenchmarkSequence.h b/benchmarks/src/BenchmarkSequence.h index f30d3064..5e96d3c7 100644 --- a/benchmarks/src/BenchmarkSequence.h +++ b/benchmarks/src/BenchmarkSequence.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/benchmarks/src/Main.cpp b/benchmarks/src/Main.cpp index 00284776..93a79d78 100644 --- a/benchmarks/src/Main.cpp +++ b/benchmarks/src/Main.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/examples/src/BasicAlgorithms.cpp b/examples/src/BasicAlgorithms.cpp index 69229ee5..dd20b7bc 100644 --- a/examples/src/BasicAlgorithms.cpp +++ b/examples/src/BasicAlgorithms.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/examples/src/BasicComposition.cpp b/examples/src/BasicComposition.cpp index da6075f5..d5354bae 100644 --- a/examples/src/BasicComposition.cpp +++ b/examples/src/BasicComposition.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/examples/src/BasicEvents.cpp b/examples/src/BasicEvents.cpp index 656a141b..0db396c3 100644 --- a/examples/src/BasicEvents.cpp +++ b/examples/src/BasicEvents.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/examples/src/BasicObservers.cpp b/examples/src/BasicObservers.cpp index ba5b3420..2c1b911e 100644 --- a/examples/src/BasicObservers.cpp +++ b/examples/src/BasicObservers.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/examples/src/BasicReactors.cpp b/examples/src/BasicReactors.cpp index 1e30cc1c..534c1346 100644 --- a/examples/src/BasicReactors.cpp +++ b/examples/src/BasicReactors.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/examples/src/BasicSignals.cpp b/examples/src/BasicSignals.cpp index 03707949..a5a82445 100644 --- a/examples/src/BasicSignals.cpp +++ b/examples/src/BasicSignals.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/examples/src/BasicSynchronization.cpp b/examples/src/BasicSynchronization.cpp index ec2be1c9..4a7f6529 100644 --- a/examples/src/BasicSynchronization.cpp +++ b/examples/src/BasicSynchronization.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index 7caed812..edfca68e 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/Domain.h b/include/react/Domain.h index e27c2514..b1fae362 100644 --- a/include/react/Domain.h +++ b/include/react/Domain.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) @@ -29,38 +29,22 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class Signal; -template +template class VarSignal; -template -class TempSignal; - -template +template class Events; -template +template class EventSource; -template -class TempEvents; - enum class Token; -template class Observer; -template -class ScopedObserver; - -template -class Reactor; - -template -class SignalPack; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Common types & constants /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -102,14 +86,14 @@ class TransactionStatus {} // Move ctor - inline TransactionStatus(TransactionStatus&& other) : + TransactionStatus(TransactionStatus&& other) : statePtr_( std::move(other.statePtr_) ) { other.statePtr_ = StateT::Create(); } // Move assignment - inline TransactionStatus& operator=(TransactionStatus&& other) + TransactionStatus& operator=(TransactionStatus&& other) { if (this != &other) { @@ -139,222 +123,6 @@ class TransactionStatus friend void AsyncTransaction(TransactionFlagsT flags, TransactionStatus& status, F&& func); }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Continuation -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename D2 = D -> -class Continuation : public REACT_IMPL::ContinuationBase -{ -private: - using NodePtrT = REACT_IMPL::NodeBasePtrT; - -public: - using SourceDomainT = D; - using TargetDomainT = D2; - - // Default ctor - Continuation() = default; - - // Move ctor - Continuation(Continuation&& other) : - Continuation::ContinuationBase( std::move(other) ) - {} - - // Node ctor - explicit Continuation(NodePtrT&& nodePtr) : - Continuation::ContinuationBase( std::move(nodePtr) ) - {} - - // Move assignment - Continuation& operator=(Continuation&& other) - { - Continuation::ContinuationBase::operator=( std::move(other) ); - return *this; - } - - // Deleted copy ctor & assignment - Continuation(const Continuation&) = delete; - Continuation& operator=(const Continuation&) = delete; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeContinuation - Signals -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename DOut = D, - typename S, - typename FIn -> -auto MakeContinuation(TransactionFlagsT flags, const Signal& trigger, FIn&& func) - -> Continuation -{ - static_assert(DOut::is_concurrent, - "MakeContinuation: Target domain does not support concurrent input."); - - using REACT_IMPL::SignalContinuationNode; - using F = typename std::decay::type; - - return Continuation( - std::make_shared>( - flags, GetNodePtr(trigger), std::forward(func))); -} - -template -< - typename D, - typename DOut = D, - typename S, - typename FIn -> -auto MakeContinuation(const Signal& trigger, FIn&& func) - -> Continuation -{ - return MakeContinuation(0, trigger, std::forward(func)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeContinuation - Events -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename DOut = D, - typename E, - typename FIn -> -auto MakeContinuation(TransactionFlagsT flags, const Events& trigger, FIn&& func) - -> Continuation -{ - static_assert(DOut::is_concurrent, - "MakeContinuation: Target domain does not support concurrent input."); - - using REACT_IMPL::EventContinuationNode; - using REACT_IMPL::AddContinuationRangeWrapper; - using REACT_IMPL::IsCallableWith; - using REACT_IMPL::EventRange; - - using F = typename std::decay::type; - - using WrapperT = - typename std::conditional< - IsCallableWith>::value, - F, - typename std::conditional< - IsCallableWith::value, - AddContinuationRangeWrapper, - void - >::type - >::type; - - static_assert(! std::is_same::value, - "MakeContinuation: Passed function does not match any of the supported signatures."); - - return Continuation( - std::make_shared>( - flags, GetNodePtr(trigger), std::forward(func))); -} - -template -< - typename D, - typename DOut = D, - typename E, - typename FIn -> -auto MakeContinuation(const Events& trigger, FIn&& func) - -> Continuation -{ - return MakeContinuation(0, trigger, std::forward(func)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeContinuation - Synced -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename DOut = D, - typename E, - typename FIn, - typename ... TDepValues -> -auto MakeContinuation(TransactionFlagsT flags, const Events& trigger, - const SignalPack& depPack, FIn&& func) - -> Continuation -{ - static_assert(DOut::is_concurrent, - "MakeContinuation: Target domain does not support concurrent input."); - - using REACT_IMPL::SyncedContinuationNode; - using REACT_IMPL::AddContinuationRangeWrapper; - using REACT_IMPL::IsCallableWith; - using REACT_IMPL::EventRange; - - using F = typename std::decay::type; - - using WrapperT = - typename std::conditional< - IsCallableWith, TDepValues ...>::value, - F, - typename std::conditional< - IsCallableWith::value, - AddContinuationRangeWrapper, - void - >::type - >::type; - - static_assert(! std::is_same::value, - "MakeContinuation: Passed function does not match any of the supported signatures."); - - struct NodeBuilder_ - { - NodeBuilder_(TransactionFlagsT flags, const Events& trigger, FIn&& func) : - MyFlags( flags ), - MyTrigger( trigger ), - MyFunc( std::forward(func) ) - {} - - auto operator()(const Signal& ... deps) - -> Continuation - { - return Continuation( - std::make_shared>( - MyFlags, - GetNodePtr(MyTrigger), - std::forward(MyFunc), GetNodePtr(deps) ...)); - } - - TransactionFlagsT MyFlags; - const Events& MyTrigger; - FIn MyFunc; - }; - - return REACT_IMPL::apply( - NodeBuilder_( flags, trigger, std::forward(func) ), - depPack.Data); -} - -template -< - typename D, - typename DOut = D, - typename E, - typename FIn, - typename ... TDepValues -> -auto MakeContinuation(const Events& trigger, - const SignalPack& depPack, FIn&& func) - -> Continuation -{ - return MakeContinuation(0, trigger, depPack, std::forward(func)); -} - /////////////////////////////////////////////////////////////////////////////////////////////// /// DoTransaction /////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/Event.h b/include/react/Event.h index 8ca754c2..599c2ad6 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) @@ -25,30 +25,19 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Events; +template +class EventStream; -template +template class EventSource; enum class Token; -template +template class Signal; using REACT_IMPL::WeightHint; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeEventSource -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -auto MakeEventSource() -> EventSource -{ - using REACT_IMPL::EventSourceNode; - - return EventSource(std::make_shared>()); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Merge /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -284,24 +273,24 @@ struct Tokenizer Token operator()(const T&) const { return Token::value; } }; -template -auto Tokenize(TEvents&& source) -> decltype(Transform(source, Tokenizer{})) +template +auto Tokenize(T&& source) -> decltype(auto) { - return Transform(source, Tokenizer{}); + return Transform(source, Tokenizer{ }); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Events /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Events : public REACT_IMPL::EventStreamBase +template +class Events : public REACT_IMPL::EventStreamBase { private: - using NodeT = REACT_IMPL::EventStreamNode; - using NodePtrT = std::shared_ptr; + using NodeType = REACT_IMPL::EventStreamNode; + using NodePtrType = std::shared_ptr; public: - using ValueT = E; + using ValueType = T; // Default ctor Events() = default; @@ -310,24 +299,16 @@ class Events : public REACT_IMPL::EventStreamBase Events(const Events&) = default; // Move ctor - Events(Events&& other) : - Events::EventStreamBase( std::move(other) ) - {} + Events(Events&& other) = default; // Node ctor - explicit Events(NodePtrT&& nodePtr) : - Events::EventStreamBase( std::move(nodePtr) ) - {} + explicit Events(NodePtrT&& nodePtr) = default; // Copy assignment Events& operator=(const Events&) = default; // Move assignment - Events& operator=(Events&& other) - { - Events::EventStreamBase::operator=( std::move(other) ); - return *this; - } + Events& operator=(Events&& other) = default; bool Equals(const Events& other) const { @@ -344,43 +325,47 @@ class Events : public REACT_IMPL::EventStreamBase Events::EventStreamBase::SetWeightHint(weight); } - auto Tokenize() const - -> decltype(REACT::Tokenize(std::declval())) + auto Tokenize() const -> decltype(auto) { return REACT::Tokenize(*this); } template - auto Merge(TArgs&& ... args) const - -> decltype(REACT::Merge(std::declval(), std::forward(args) ...)) + auto Merge(TArgs&& ... args) const -> decltype(auto) { return REACT::Merge(*this, std::forward(args) ...); } template - auto Filter(F&& f) const - -> decltype(REACT::Filter(std::declval(), std::forward(f))) + auto Filter(F&& f) const -> decltype(auto) { return REACT::Filter(*this, std::forward(f)); } template - auto Transform(F&& f) const - -> decltype(REACT::Transform(std::declval(), std::forward(f))) + auto Transform(F&& f) const -> decltype(auto) { return REACT::Transform(*this, std::forward(f)); } + + template + static auto Create() -> EventSource + { + using REACT_IMPL::EventSourceNode; + + return EventSource(std::make_shared>()); + } }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventSource /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EventSource : public Events +template +class EventSource : public Events { private: - using NodeT = REACT_IMPL::EventSourceNode; - using NodePtrT = std::shared_ptr; + using NodeType = REACT_IMPL::EventSourceNode; + using NodePtrType = std::shared_ptr; public: // Default ctor @@ -390,9 +375,7 @@ class EventSource : public Events EventSource(const EventSource&) = default; // Move ctor - EventSource(EventSource&& other) : - EventSource::Events( std::move(other) ) - {} + EventSource(EventSource&& other) = default; // Node ctor explicit EventSource(NodePtrT&& nodePtr) : @@ -403,15 +386,11 @@ class EventSource : public Events EventSource& operator=(const EventSource&) = default; // Move assignment - EventSource& operator=(EventSource&& other) - { - EventSource::Events::operator=( std::move(other) ); - return *this; - } + EventSource& operator=(EventSource&& other) = default; // Explicit emit - void Emit(const E& e) const { EventSource::EventStreamBase::emit(e); } - void Emit(E&& e) const { EventSource::EventStreamBase::emit(std::move(e)); } + void Emit(const T& e) const { EventSource::EventStreamBase::emit(e); } + void Emit(T&& e) const { EventSource::EventStreamBase::emit(std::move(e)); } void Emit() const { @@ -425,18 +404,18 @@ class EventSource : public Events void operator()() const { - static_assert(std::is_same::value, "Can't emit on non token stream."); + static_assert(std::is_same::value, "Can't emit on non token stream."); EventSource::EventStreamBase::emit(Token::value); } // Stream style - const EventSource& operator<<(const E& e) const + const EventSource& operator<<(const T& e) const { EventSource::EventStreamBase::emit(e); return *this; } - const EventSource& operator<<(E&& e) const + const EventSource& operator<<(T&& e) const { EventSource::EventStreamBase::emit(std::move(e)); return *this; diff --git a/include/react/Observer.h b/include/react/Observer.h index c9d986dc..17e72ebf 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/Reactor.h b/include/react/Reactor.h index 2149dcac..92d3b006 100644 --- a/include/react/Reactor.h +++ b/include/react/Reactor.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/Signal.h b/include/react/Signal.h index ac5c6030..4bf28460 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) @@ -29,129 +29,12 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class Signal; -template +template class VarSignal; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeVar -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename V, - typename S = typename std::decay::type, - class = typename std::enable_if::value>::type, - class = typename std::enable_if::value>::type -> -auto MakeVar(V&& value) -> VarSignal -{ - return VarSignal( - std::make_shared>( - std::forward(value))); -} - -template -auto MakeVar(std::reference_wrapper value) -> VarSignal -{ - return VarSignal( - std::make_shared>>(value)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeVar (higher order reactives) -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename V, - typename S = typename std::decay::type, - typename TInner = typename S::ValueT, - class = typename std::enable_if::value>::type -> -auto MakeVar(V&& value) - -> VarSignal> -{ - return VarSignal>( - std::make_shared>>( - std::forward(value))); -} - -template -< - - typename V, - typename S = typename std::decay::type, - typename TInner = typename S::ValueT, - class = typename std::enable_if< - IsEvent::value>::type -> -auto MakeVar(V&& value) - -> VarSignal> -{ - return VarSignal>( - std::make_shared>>( - std::forward(value))); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// MakeSignal -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Single arg -template -< - - typename TValue, - typename FIn, - typename F = typename std::decay::type, - typename S = typename std::result_of::type, - typename TOp = REACT_IMPL::FunctionOp> -> -auto MakeSignal(const Signal& arg, FIn&& func) - -> TempSignal -{ - return TempSignal( - std::make_shared>( - std::forward(func), GetNodePtr(arg))); -} - -// Multiple args -template -< - - typename ... TValues, - typename FIn, - typename F = typename std::decay::type, - typename S = typename std::result_of::type, - typename TOp = REACT_IMPL::FunctionOp ...> -> -auto MakeSignal(const SignalPack& argPack, FIn&& func) - -> TempSignal -{ - using REACT_IMPL::SignalOpNode; - - struct NodeBuilder_ - { - NodeBuilder_(FIn&& func) : - MyFunc( std::forward(func) ) - {} - - auto operator()(const Signal& ... args) - -> TempSignal - { - return TempSignal( - std::make_shared>( - std::forward(MyFunc), GetNodePtr(args) ...)); - } - - FIn MyFunc; - }; - - return REACT_IMPL::apply( - NodeBuilder_( std::forward(func) ), - argPack.Data); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Flatten /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -167,15 +50,15 @@ auto Flatten(const Signal>& outer) /////////////////////////////////////////////////////////////////////////////////////////////////// /// Signal /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Signal : public REACT_IMPL::SignalBase +template +class Signal : public REACT_IMPL::SignalBase { private: - using NodeT = REACT_IMPL::SignalNode; - using NodePtrT = std::shared_ptr; + using NodeType = REACT_IMPL::SignalNode; + using NodePtrType = std::shared_ptr; public: - using ValueT = S; + using ValueType = T; // Default ctor Signal() = default; @@ -184,12 +67,10 @@ class Signal : public REACT_IMPL::SignalBase Signal(const Signal&) = default; // Move ctor - Signal(Signal&& other) : - Signal::SignalBase( std::move(other) ) - {} + Signal(Signal&& other) = default; // Node ctor - explicit Signal(NodePtrT&& nodePtr) : + explicit Signal(NodePtrType&& nodePtr) : Signal::SignalBase( std::move(nodePtr) ) {} @@ -197,13 +78,9 @@ class Signal : public REACT_IMPL::SignalBase Signal& operator=(const Signal&) = default; // Move assignment - Signal& operator=(Signal&& other) - { - Signal::SignalBase::operator=( std::move(other) ); - return *this; - } + Signal& operator=(Signal&& other) = default; - const S& Value() const { return Signal::SignalBase::getValue(); } + const S& Get() const { return Signal::SignalBase::getValue(); } const S& operator()() const { return Signal::SignalBase::getValue(); } bool Equals(const Signal& other) const @@ -227,6 +104,19 @@ class Signal : public REACT_IMPL::SignalBase "Flatten requires a Signal or Events value type."); return REACT::Flatten(*this); } + + template + < + typename ... Ts, + typename FIn, + typename F = typename std::decay::type + > + static auto Create(FIn&& func, const Signal& ... args) -> Signal + { + return Signal( + std::make_shared>( + std::forward(func), GetNodePtr(args) ...)); + } }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -293,6 +183,15 @@ class VarSignal : public Signal { VarSignal::SignalBase::modifyValue(func); } + + /// Create + template + static auto Create(V&& value) -> VarSignal + { + return VarSignal( + std::make_shared>( + std::forward(value))); + } }; /******************************************/ REACT_END /******************************************/ diff --git a/include/react/TypeTraits.h b/include/react/TypeTraits.h index bd0ac99c..97c12654 100644 --- a/include/react/TypeTraits.h +++ b/include/react/TypeTraits.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) @@ -16,19 +16,18 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class Signal; -template +template class VarSignal; -template +template class Events; -template +template class EventSource; -template class Observer; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -43,9 +42,6 @@ struct IsSignal> { static const bool value = true; }; template struct IsSignal> { static const bool value = true; }; -template -constexpr bool IsSignalType = IsSignal::value; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// IsEvent /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -58,33 +54,6 @@ struct IsEvent> { static const bool value = true; }; template struct IsEvent> { static const bool value = true; }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IsObserver -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct IsObserver { static const bool value = false; }; - -template -struct IsObserver> { static const bool value = true; }; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IsObservable -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct IsObservable { static const bool value = false; }; - -template -struct IsObservable> { static const bool value = true; }; - -template -struct IsObservable> { static const bool value = true; }; - -template -struct IsObservable> { static const bool value = true; }; - -template -struct IsObservable> { static const bool value = true; }; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// IsReactive /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -103,8 +72,8 @@ struct IsReactive> { static const bool value = true; }; template struct IsReactive> { static const bool value = true; }; -template -struct IsReactive> { static const bool value = true; }; +template <> +struct IsReactive { static const bool value = true; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// DecayInput diff --git a/include/react/common/Concurrency.h b/include/react/common/Concurrency.h index e27fd9ba..0a5c350c 100644 --- a/include/react/common/Concurrency.h +++ b/include/react/common/Concurrency.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/common/Containers.h b/include/react/common/Containers.h index 9b77de06..1839bd46 100644 --- a/include/react/common/Containers.h +++ b/include/react/common/Containers.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/common/RefCounting.h b/include/react/common/RefCounting.h index 3a84dbb4..f2cd518a 100644 --- a/include/react/common/RefCounting.h +++ b/include/react/common/RefCounting.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/common/SourceIdSet.h b/include/react/common/SourceIdSet.h index 1cf89210..b1a3eeb0 100644 --- a/include/react/common/SourceIdSet.h +++ b/include/react/common/SourceIdSet.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/common/Timing.h b/include/react/common/Timing.h index 3775661d..84c58006 100644 --- a/include/react/common/Timing.h +++ b/include/react/common/Timing.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/common/TopoQueue.h b/include/react/common/TopoQueue.h index 8084d419..b86885ff 100644 --- a/include/react/common/TopoQueue.h +++ b/include/react/common/TopoQueue.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/common/Types.h b/include/react/common/Types.h index 5d747eee..0545f858 100644 --- a/include/react/common/Types.h +++ b/include/react/common/Types.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/common/Util.h b/include/react/common/Util.h index 39741ed7..19fe74f4 100644 --- a/include/react/common/Util.h +++ b/include/react/common/Util.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/detail/Defs.h b/include/react/detail/Defs.h index 477ad6b4..5dd2f91f 100644 --- a/include/react/detail/Defs.h +++ b/include/react/detail/Defs.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/detail/DomainBase.h b/include/react/detail/DomainBase.h index 67c7e8ae..7327874b 100644 --- a/include/react/detail/DomainBase.h +++ b/include/react/detail/DomainBase.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/detail/EngineBase.h b/include/react/detail/EngineBase.h index 7b9af9de..501c9c26 100644 --- a/include/react/detail/EngineBase.h +++ b/include/react/detail/EngineBase.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/detail/EventBase.h b/include/react/detail/EventBase.h index 583651aa..bf518860 100644 --- a/include/react/detail/EventBase.h +++ b/include/react/detail/EventBase.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) @@ -22,12 +22,8 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventStreamBase /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E -> -class EventStreamBase : public CopyableReactive> +template +class EventStreamBase : public ReactiveBase> { public: EventStreamBase() = default; diff --git a/include/react/detail/IReactiveEngine.h b/include/react/detail/IReactiveEngine.h index 9a422cb8..b4e0b83c 100644 --- a/include/react/detail/IReactiveEngine.h +++ b/include/react/detail/IReactiveEngine.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) @@ -21,132 +21,29 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// IReactiveEngine /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename TNode, - typename TTurn -> struct IReactiveEngine { - using NodeT = TNode; - using TurnT = TTurn; + using NodeId = uint; + using TurnId = uint; - void OnTurnAdmissionStart(TurnT& turn) {} - void OnTurnAdmissionEnd(TurnT& turn) {} + void OnTurnAdmissionStart(TurnId& turn) {} + void OnTurnAdmissionEnd(TurnId& turn) {} - void OnInputChange(NodeT& node, TurnT& turn) {} + void OnInputChange(NodeId& node, TurnId& turn) {} - void Propagate(TurnT& turn) {} + void Propagate(TurnId& turn) {} - void OnNodeCreate(NodeT& node) {} - void OnNodeDestroy(NodeT& node) {} + void OnNodeCreate(NodeId& node) {} + void OnNodeDestroy(NodeId& node) {} - void OnNodeAttach(NodeT& node, NodeT& parent) {} - void OnNodeDetach(NodeT& node, NodeT& parent) {} + void OnNodeAttach(NodeId node, NodeId parent) {} + void OnNodeDetach(NodeId node, NodeId parent) {} - void OnNodePulse(NodeT& node, TurnT& turn) {} - void OnNodeIdlePulse(NodeT& node, TurnT& turn) {} + void OnNodePulse(NodeId& node, TurnId& turn) {} + void OnNodeIdlePulse(NodeId& node, TurnId& turn) {} - void OnDynamicNodeAttach(NodeT& node, NodeT& parent, TurnT& turn) {} - void OnDynamicNodeDetach(NodeT& node, NodeT& parent, TurnT& turn) {} -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EngineInterface - Static wrapper for IReactiveEngine -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TEngine -> -struct EngineInterface -{ - using NodeT = typename TEngine::NodeT; - using TurnT = typename TEngine::TurnT; - - static TEngine& Instance() - { - static TEngine engine; - return engine; - } - - static void OnTurnAdmissionStart(TurnT& turn) - { - Instance().OnTurnAdmissionStart(turn); - } - - static void OnTurnAdmissionEnd(TurnT& turn) - { - Instance().OnTurnAdmissionEnd(turn); - } - - static void OnInputChange(NodeT& node, TurnT& turn) - { - REACT_LOG(D::Log().template Append( - GetObjectId(node), turn.Id())); - Instance().OnInputChange(node, turn); - } - - static void Propagate(TurnT& turn) - { - Instance().Propagate(turn); - } - - static void OnNodeCreate(NodeT& node) - { - REACT_LOG(D::Log().template Append( - GetObjectId(node), node.GetNodeType())); - Instance().OnNodeCreate(node); - } - - static void OnNodeDestroy(NodeT& node) - { - REACT_LOG(D::Log().template Append( - GetObjectId(node))); - Instance().OnNodeDestroy(node); - } - - static void OnNodeAttach(NodeT& node, NodeT& parent) - { - REACT_LOG(D::Log().template Append( - GetObjectId(node), GetObjectId(parent))); - Instance().OnNodeAttach(node, parent); - } - - static void OnNodeDetach(NodeT& node, NodeT& parent) - { - REACT_LOG(D::Log().template Append( - GetObjectId(node), GetObjectId(parent))); - Instance().OnNodeDetach(node, parent); - } - - static void OnNodePulse(NodeT& node, TurnT& turn) - { - REACT_LOG(D::Log().template Append( - GetObjectId(node), turn.Id())); - Instance().OnNodePulse(node, turn); - } - - static void OnNodeIdlePulse(NodeT& node, TurnT& turn) - { - REACT_LOG(D::Log().template Append( - GetObjectId(node), turn.Id())); - Instance().OnNodeIdlePulse(node, turn); - } - - static void OnDynamicNodeAttach(NodeT& node, NodeT& parent, TurnT& turn) - { - REACT_LOG(D::Log().template Append( - GetObjectId(node), GetObjectId(parent), turn.Id())); - Instance().OnDynamicNodeAttach(node, parent, turn); - } - - static void OnDynamicNodeDetach(NodeT& node, NodeT& parent, TurnT& turn) - { - REACT_LOG(D::Log().template Append( - GetObjectId(node), GetObjectId(parent), turn.Id())); - Instance().OnDynamicNodeDetach(node, parent, turn); - } + void OnDynamicNodeAttach(NodeId& node, NodeId& parent, TurnId& turn) {} + void OnDynamicNodeDetach(NodeId& node, NodeId& parent, TurnId& turn) {} }; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/IReactiveNode.h b/include/react/detail/IReactiveNode.h index b4d2e79c..34967834 100644 --- a/include/react/detail/IReactiveNode.h +++ b/include/react/detail/IReactiveNode.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) @@ -13,6 +13,12 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ +enum class EUpdateResult +{ + UNCHANGED, + CHANGED +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// IReactiveNode /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -25,24 +31,24 @@ struct IReactiveNode // Note: Could get rid of this ugly ptr by adding a template parameter to the interface // But that would mean all engine nodes need that template parameter too - so rather cast - virtual void Tick(void* turnPtr) = 0; + virtual EUpdateResult Update() = 0; /// Input nodes can be manipulated externally. - virtual bool IsInputNode() const = 0; + virtual bool IsInputNode() const = 0; /// Output nodes can't have any successors. - virtual bool IsOutputNode() const = 0; + virtual bool IsOutputNode() const = 0; /// Dynamic nodes may change in topology as a result of tick. - virtual bool IsDynamicNode() const = 0; + virtual bool IsDynamicNode() const = 0; // Number of predecessors. // This information is statically available at compile time on the graph layer, // so the engine does not have to calculate it again. - virtual int DependencyCount() const = 0; + virtual int DependencyCount() const = 0; // Heavyweight nodes are worth parallelizing. - virtual bool IsHeavyweight() const = 0; + virtual bool IsHeavyweight() const = 0; }; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/ObserverBase.h b/include/react/detail/ObserverBase.h index 814e0207..73f637fd 100644 --- a/include/react/detail/ObserverBase.h +++ b/include/react/detail/ObserverBase.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/detail/ReactiveBase.h b/include/react/detail/ReactiveBase.h index d7f32124..12dafd82 100644 --- a/include/react/detail/ReactiveBase.h +++ b/include/react/detail/ReactiveBase.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index 3589d8c7..ffcbcd6b 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/detail/SignalBase.h b/include/react/detail/SignalBase.h index 6c41b9df..9b497136 100644 --- a/include/react/detail/SignalBase.h +++ b/include/react/detail/SignalBase.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index 454fdfbb..eb158c4b 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) @@ -110,7 +110,7 @@ class IterateNode : Engine::OnNodeDestroy(*this); } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -183,7 +183,7 @@ class IterateByRefNode : Engine::OnNodeDestroy(*this); } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -257,7 +257,7 @@ class SyncedIterateNode : Engine::OnNodeDestroy(*this); } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -351,7 +351,7 @@ class SyncedIterateByRefNode : Engine::OnNodeDestroy(*this); } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -429,7 +429,7 @@ class HoldNode : public SignalNode virtual const char* GetNodeType() const override { return "HoldNode"; } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -497,7 +497,7 @@ class SnapshotNode : public SignalNode Engine::OnNodeDestroy(*this); } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -564,7 +564,7 @@ class MonitorNode : public EventStreamNode Engine::OnNodeDestroy(*this); } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -624,7 +624,7 @@ class PulseNode : public EventStreamNode Engine::OnNodeDestroy(*this); } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { typedef typename D::Engine::TurnT TurnT; TurnT& turn = *reinterpret_cast(turnPtr); diff --git a/include/react/detail/graph/ContinuationNodes.h b/include/react/detail/graph/ContinuationNodes.h index c841d23f..6d494453 100644 --- a/include/react/detail/graph/ContinuationNodes.h +++ b/include/react/detail/graph/ContinuationNodes.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) @@ -111,7 +111,7 @@ class SignalContinuationNode : public ContinuationNode virtual const char* GetNodeType() const override { return "SignalContinuationNode"; } virtual int DependencyCount() const override { return 1; } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { #ifdef REACT_ENABLE_LOGGING using TurnT = typename D::Engine::TurnT; @@ -183,7 +183,7 @@ class EventContinuationNode : public ContinuationNode virtual const char* GetNodeType() const override { return "EventContinuationNode"; } virtual int DependencyCount() const override { return 1; } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { #ifdef REACT_ENABLE_LOGGING using TurnT = typename D::Engine::TurnT; @@ -293,7 +293,7 @@ class SyncedContinuationNode : public ContinuationNode virtual const char* GetNodeType() const override { return "SyncedContinuationNode"; } virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 850b36c2..f6c750cc 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) @@ -27,7 +27,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class SignalNode; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -47,19 +47,11 @@ struct BufferClearAccessPolicy : /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventStreamNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E -> -class EventStreamNode : - public ObservableNode, - private BufferClearAccessPolicy +template +class EventStreamNode : public ObservableNode { public: - using DataT = std::vector; - using EngineT = typename D::Engine; - using TurnT = typename EngineT::TurnT; + using StorageType = std::vector; EventStreamNode() = default; @@ -75,10 +67,14 @@ class EventStreamNode : }); } - DataT& Events() { return events_; } + StorageType& Events() + { return events_; } + + const StorageType& Events() const + { return events_; } protected: - DataT events_; + StorageType events_; private: uint curTurnId_ { (std::numeric_limits::max)() }; @@ -90,17 +86,11 @@ using EventStreamNodePtrT = std::shared_ptr>; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventSourceNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E -> +template class EventSourceNode : - public EventStreamNode, + public EventStreamNode, public IInputNode { - using Engine = typename EventSourceNode::Engine; - public: EventSourceNode() : EventSourceNode::EventStreamNode{ } @@ -117,7 +107,7 @@ class EventSourceNode : virtual bool IsInputNode() const override { return true; } virtual int DependencyCount() const override { return 0; } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { REACT_ASSERT(false, "Ticked EventSourceNode\n"); } @@ -157,25 +147,86 @@ class EventSourceNode : bool changedFlag_ = false; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventOpNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +< + typename T, + typename ... Ds +> +class EventMergeNode : public EventStreamNode +{ +public: + template + EventMergeNode(Us&& ... args) : + EventOpNode::EventStreamNode( ), + op_( std::forward(args) ... ) + { + Engine::OnNodeCreate(*this); + op_.template Attach(*this); + } + + ~EventOpNode() + { + if (!wasOpStolen_) + op_.template Detach(*this); + Engine::OnNodeDestroy(*this); + } + + virtual EUpdateResult Update() override + { + using TurnT = typename D::Engine::TurnT; + TurnT& turn = *reinterpret_cast(turnPtr); + + this->SetCurrentTurn(turn, true); + + {// timer + size_t count = 0; + using TimerT = typename EventOpNode::ScopedUpdateTimer; + TimerT scopedTimer( *this, count ); + + op_.Collect(turn, EventCollector( this->events_ )); + + // Note: Count was passed by reference, so we can still change before the dtor + // of the scoped timer is called + count = this->events_.size(); + }// ~timer + + if (! this->events_.empty()) + return EUpdateResult::CHANGED; + else + return EUpdateResult::UNCHANGED; + } + + virtual const char* GetNodeType() const override + { return "EventOpNode"; } + + virtual int DependencyCount() const override + { return TOp::dependency_count; } + +private: + TOp op_; + bool wasOpStolen_ = false; +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventMergeOp /////////////////////////////////////////////////////////////////////////////////////////////////// template < - typename E, - typename ... TDeps + typename T, + typename ... Ds > -class EventMergeOp : public ReactiveOpBase +class EventMergeOp : public ReactiveOpBase { public: - template - EventMergeOp(TDepsIn&& ... deps) : - EventMergeOp::ReactiveOpBase(DontMove(), std::forward(deps) ...) + template + EventMergeOp(Us&& ... deps) : + EventMergeOp::ReactiveOpBase(DontMove(), std::forward(deps) ...) {} - EventMergeOp(EventMergeOp&& other) : - EventMergeOp::ReactiveOpBase( std::move(other) ) - {} + EventMergeOp(EventMergeOp&& other) = default; template void Collect(const TTurn& turn, const TCollector& collector) const @@ -406,7 +457,7 @@ class EventOpNode : Engine::OnNodeDestroy(*this); } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -500,7 +551,7 @@ class EventFlattenNode : public EventStreamNode virtual bool IsDynamicNode() const override { return true; } virtual int DependencyCount() const override { return 2; } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { typedef typename D::Engine::TurnT TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -588,7 +639,7 @@ class SyncedEventTransformNode : Engine::OnNodeDestroy(*this); } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -679,7 +730,7 @@ class SyncedEventFilterNode : Engine::OnNodeDestroy(*this); } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -766,7 +817,7 @@ class EventProcessingNode : Engine::OnNodeDestroy(*this); } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -846,7 +897,7 @@ class SyncedEventProcessingNode : Engine::OnNodeDestroy(*this); } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -932,7 +983,7 @@ class EventJoinNode : Engine::OnNodeDestroy(*this); } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { TurnT& turn = *reinterpret_cast(turnPtr); diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index b7d79d27..66993500 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index 351a54e5..b597e828 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) @@ -118,7 +118,7 @@ class SignalObserverNode : virtual const char* GetNodeType() const override { return "SignalObserverNode"; } virtual int DependencyCount() const override { return 1; } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { #ifdef REACT_ENABLE_LOGGING using TurnT = typename D::Engine::TurnT; @@ -200,7 +200,7 @@ class EventObserverNode : virtual const char* GetNodeType() const override { return "EventObserverNode"; } virtual int DependencyCount() const override { return 1; } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { #ifdef REACT_ENABLE_LOGGING using TurnT = typename D::Engine::TurnT; @@ -288,7 +288,7 @@ class SyncedObserverNode : virtual const char* GetNodeType() const override { return "SyncedObserverNode"; } virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); diff --git a/include/react/detail/graph/ReactorNodes.h b/include/react/detail/graph/ReactorNodes.h index b693a5f0..c85cdcc0 100644 --- a/include/react/detail/graph/ReactorNodes.h +++ b/include/react/detail/graph/ReactorNodes.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) @@ -88,7 +88,7 @@ class ReactorNode : virtual bool IsDynamicNode() const override { return true; } virtual bool IsOutputNode() const override { return true; } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { turnPtr_ = reinterpret_cast(turnPtr); diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index 64cccaee..9ea74832 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) @@ -79,7 +79,7 @@ class VarNode : virtual bool IsInputNode() const override { return true; } virtual int DependencyCount() const override { return 0; } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { REACT_ASSERT(false, "Ticked VarNode\n"); } @@ -246,7 +246,7 @@ class SignalOpNode : public SignalNode Engine::OnNodeDestroy(*this); } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); @@ -323,7 +323,7 @@ class FlattenNode : public SignalNode Engine::OnNodeDestroy(*this); } - virtual void Tick(void* turnPtr) override + virtual void Update(void* turnPtr) override { using TurnT = typename D::Engine::TurnT; TurnT& turn = *reinterpret_cast(turnPtr); diff --git a/include/react/engine/PulsecountEngine.h b/include/react/engine/PulsecountEngine.h index ed813e54..95308f3c 100644 --- a/include/react/engine/PulsecountEngine.h +++ b/include/react/engine/PulsecountEngine.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/engine/SubtreeEngine.h b/include/react/engine/SubtreeEngine.h index 9a84d7cb..cfda49d1 100644 --- a/include/react/engine/SubtreeEngine.h +++ b/include/react/engine/SubtreeEngine.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/engine/ToposortEngine.h b/include/react/engine/ToposortEngine.h index 9f8f693c..331ecd11 100644 --- a/include/react/engine/ToposortEngine.h +++ b/include/react/engine/ToposortEngine.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/logging/EventLog.h b/include/react/logging/EventLog.h index e9622c7b..6b94277f 100644 --- a/include/react/logging/EventLog.h +++ b/include/react/logging/EventLog.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/logging/EventRecords.h b/include/react/logging/EventRecords.h index 4ec91cbe..ea2359f5 100644 --- a/include/react/logging/EventRecords.h +++ b/include/react/logging/EventRecords.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/include/react/logging/Logging.h b/include/react/logging/Logging.h index c5546883..3f0aacd2 100644 --- a/include/react/logging/Logging.h +++ b/include/react/logging/Logging.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/src/engine/PulsecountEngine.cpp b/src/engine/PulsecountEngine.cpp index 1e74bd1f..5fa3a844 100644 --- a/src/engine/PulsecountEngine.cpp +++ b/src/engine/PulsecountEngine.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/src/engine/SubtreeEngine.cpp b/src/engine/SubtreeEngine.cpp index 9189c697..c71d6e0f 100644 --- a/src/engine/SubtreeEngine.cpp +++ b/src/engine/SubtreeEngine.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/src/engine/ToposortEngine.cpp b/src/engine/ToposortEngine.cpp index 3d746452..54612191 100644 --- a/src/engine/ToposortEngine.cpp +++ b/src/engine/ToposortEngine.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/src/logging/EventLog.cpp b/src/logging/EventLog.cpp index a22601e5..dcd8f627 100644 --- a/src/logging/EventLog.cpp +++ b/src/logging/EventLog.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/src/logging/EventRecords.cpp b/src/logging/EventRecords.cpp index 84ced447..3145c43a 100644 --- a/src/logging/EventRecords.cpp +++ b/src/logging/EventRecords.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/tests/src/EventStreamTest.cpp b/tests/src/EventStreamTest.cpp index 3714b371..6801c5e9 100644 --- a/tests/src/EventStreamTest.cpp +++ b/tests/src/EventStreamTest.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/tests/src/EventStreamTest.h b/tests/src/EventStreamTest.h index bb2efce4..e33653a0 100644 --- a/tests/src/EventStreamTest.h +++ b/tests/src/EventStreamTest.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/tests/src/EventStreamTestQ.cpp b/tests/src/EventStreamTestQ.cpp index 767a3dad..d61330d1 100644 --- a/tests/src/EventStreamTestQ.cpp +++ b/tests/src/EventStreamTestQ.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/tests/src/MoveTest.cpp b/tests/src/MoveTest.cpp index 81f09f31..c5963745 100644 --- a/tests/src/MoveTest.cpp +++ b/tests/src/MoveTest.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/tests/src/MoveTest.h b/tests/src/MoveTest.h index 46e1d39d..41eb5810 100644 --- a/tests/src/MoveTest.h +++ b/tests/src/MoveTest.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/tests/src/ObserverTest.cpp b/tests/src/ObserverTest.cpp index 87cb46eb..ae331812 100644 --- a/tests/src/ObserverTest.cpp +++ b/tests/src/ObserverTest.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/tests/src/ObserverTest.h b/tests/src/ObserverTest.h index cc07f44c..af5ac834 100644 --- a/tests/src/ObserverTest.h +++ b/tests/src/ObserverTest.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/tests/src/ObserverTestQ.cpp b/tests/src/ObserverTestQ.cpp index 68449272..1e36c088 100644 --- a/tests/src/ObserverTestQ.cpp +++ b/tests/src/ObserverTestQ.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/tests/src/OperationsTest.cpp b/tests/src/OperationsTest.cpp index 72257431..cc3b576e 100644 --- a/tests/src/OperationsTest.cpp +++ b/tests/src/OperationsTest.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/tests/src/OperationsTest.h b/tests/src/OperationsTest.h index 5ef9cc3d..1af27694 100644 --- a/tests/src/OperationsTest.h +++ b/tests/src/OperationsTest.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/tests/src/OperationsTestQ.cpp b/tests/src/OperationsTestQ.cpp index 5da10bfa..83e8a7ab 100644 --- a/tests/src/OperationsTestQ.cpp +++ b/tests/src/OperationsTestQ.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/tests/src/ParallelizationTest.cpp b/tests/src/ParallelizationTest.cpp index aa583c17..9ef6d156 100644 --- a/tests/src/ParallelizationTest.cpp +++ b/tests/src/ParallelizationTest.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/tests/src/ParallelizationTest.h b/tests/src/ParallelizationTest.h index 6c5fd1d7..43a99f05 100644 --- a/tests/src/ParallelizationTest.h +++ b/tests/src/ParallelizationTest.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/tests/src/SignalTest.cpp b/tests/src/SignalTest.cpp index d19239bd..44a68828 100644 --- a/tests/src/SignalTest.cpp +++ b/tests/src/SignalTest.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/tests/src/SignalTest.h b/tests/src/SignalTest.h index 8d7a34e2..b20eabe8 100644 --- a/tests/src/SignalTest.h +++ b/tests/src/SignalTest.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/tests/src/SignalTestQ.cpp b/tests/src/SignalTestQ.cpp index 6e93c730..b6bacde1 100644 --- a/tests/src/SignalTestQ.cpp +++ b/tests/src/SignalTestQ.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/tests/src/TestUtil.h b/tests/src/TestUtil.h index dd00efb8..a6cc5306 100644 --- a/tests/src/TestUtil.h +++ b/tests/src/TestUtil.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/tests/src/TransactionTest.cpp b/tests/src/TransactionTest.cpp index 61a51e11..487f7f5b 100644 --- a/tests/src/TransactionTest.cpp +++ b/tests/src/TransactionTest.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) diff --git a/tests/src/TransactionTest.h b/tests/src/TransactionTest.h index 74fc7b6c..a332de90 100644 --- a/tests/src/TransactionTest.h +++ b/tests/src/TransactionTest.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2014. +// Copyright Sebastian Jeckel 2016. // 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) From 46dee49a66e80f1909f5646b598200160205e6f3 Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 28 Jul 2016 23:50:00 +0200 Subject: [PATCH 220/266] Refactoring WIP. --- include/react/Algorithm.h | 103 +- .../{IReactiveEngine.h => IReactiveGroup.h} | 31 +- include/react/detail/IReactiveNode.h | 9 +- include/react/detail/graph/EventNodes.h | 1025 +++++------------ include/react/detail/graph/GraphBase.h | 74 +- include/react/detail/graph/SignalNodes.h | 6 +- project/msvc/CppReact.vcxproj | 5 +- project/msvc/CppReact.vcxproj.filters | 15 +- 8 files changed, 408 insertions(+), 860 deletions(-) rename include/react/detail/{IReactiveEngine.h => IReactiveGroup.h} (68%) diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index edfca68e..b6da68af 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -22,57 +22,43 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class Signal; -template +template class VarSignal; -template +template class Events; -template +template class EventSource; enum class Token; -template -class SignalPack; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Hold - Hold the most recent event in a signal /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename V, - typename T = typename std::decay::type -> -auto Hold(const Events& events, V&& init) - -> Signal +template +auto Hold(const Events& events, V&& init) -> Signal { using REACT_IMPL::HoldNode; - return Signal( - std::make_shared>( + return Signal( + std::make_shared>( std::forward(init), GetNodePtr(events))); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Monitor - Emits value changes of target signal /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -auto Monitor(const Signal& target) - -> Events +template +auto Monitor(const Signal& target) -> Events { using REACT_IMPL::MonitorNode; - return Events( - std::make_shared>( + return Events( + std::make_shared>( GetNodePtr(target))); } @@ -81,14 +67,13 @@ auto Monitor(const Signal& target) /////////////////////////////////////////////////////////////////////////////////////////////////// template < - typename D, + typename S, typename E, typename V, - typename FIn, - typename S = typename std::decay::type + typename F, + > -auto Iterate(const Events& events, V&& init, FIn&& func) - -> Signal +auto Iterate(const Events& events, V&& init, F&& func) -> Signal { using REACT_IMPL::IterateNode; using REACT_IMPL::IterateByRefNode; @@ -97,7 +82,7 @@ auto Iterate(const Events& events, V&& init, FIn&& func) using REACT_IMPL::IsCallableWith; using REACT_IMPL::EventRange; - using F = typename std::decay::type; + using TFunc = typename std::decay::type; using NodeT = typename std::conditional< @@ -122,7 +107,7 @@ auto Iterate(const Events& events, V&& init, FIn&& func) ! std::is_same::value, "Iterate: Passed function does not match any of the supported signatures."); - return Signal( + return Signal( std::make_shared( std::forward(init), GetNodePtr(events), std::forward(func))); } @@ -132,16 +117,13 @@ auto Iterate(const Events& events, V&& init, FIn&& func) /////////////////////////////////////////////////////////////////////////////////////////////////// template < - typename D, + typename S, typename E, typename V, typename FIn, - typename ... TDepValues, - typename S = typename std::decay::type + typename ... TDepValues > -auto Iterate(const Events& events, V&& init, - const SignalPack& depPack, FIn&& func) - -> Signal +auto Iterate(const Events& events, V&& init, const SignalPack& depPack, FIn&& func) -> Signal { using REACT_IMPL::SyncedIterateNode; using REACT_IMPL::SyncedIterateByRefNode; @@ -211,19 +193,13 @@ auto Iterate(const Events& events, V&& init, /////////////////////////////////////////////////////////////////////////////////////////////////// /// Snapshot - Sets signal value to value of other signal when event is received /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename E -> -auto Snapshot(const Events& trigger, const Signal& target) - -> Signal +template +auto Snapshot(const Events& trigger, const Signal& target) -> Signal { using REACT_IMPL::SnapshotNode; - return Signal( - std::make_shared>( + return Signal( + std::make_shared>( GetNodePtr(target), GetNodePtr(trigger))); } @@ -232,32 +208,21 @@ auto Snapshot(const Events& trigger, const Signal& target) /////////////////////////////////////////////////////////////////////////////////////////////////// /// Pulse - Emits value of target signal when event is received /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename E -> -auto Pulse(const Events& trigger, const Signal& target) - -> Events +template +auto Pulse(const Events& trigger, const Signal& target) -> Events { using REACT_IMPL::PulseNode; - return Events( - std::make_shared>( + return Events( + std::make_shared>( GetNodePtr(target), GetNodePtr(trigger))); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Changed - Emits token when target signal was changed /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -auto Changed(const Signal& target) - -> Events +template +auto Changed(const Signal& target) -> Events { return Monitor(target).Tokenize(); } @@ -267,12 +232,10 @@ auto Changed(const Signal& target) /////////////////////////////////////////////////////////////////////////////////////////////////// template < - typename D, + typename T, typename V, - typename S = typename std::decay::type > -auto ChangedTo(const Signal& target, V&& value) - -> Events +auto ChangedTo(const Signal& target, V&& value) -> Events { return Monitor(target) .Filter([=] (const S& v) { return v == value; }) diff --git a/include/react/detail/IReactiveEngine.h b/include/react/detail/IReactiveGroup.h similarity index 68% rename from include/react/detail/IReactiveEngine.h rename to include/react/detail/IReactiveGroup.h index b4e0b83c..b86a389c 100644 --- a/include/react/detail/IReactiveEngine.h +++ b/include/react/detail/IReactiveGroup.h @@ -18,32 +18,31 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ +using NodeId = uint; +using TurnId = uint; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// IReactiveEngine /////////////////////////////////////////////////////////////////////////////////////////////////// -struct IReactiveEngine +struct IReactiveGroup { - using NodeId = uint; - using TurnId = uint; - - void OnTurnAdmissionStart(TurnId& turn) {} - void OnTurnAdmissionEnd(TurnId& turn) {} + virtual ~IReactiveGroup() = 0; - void OnInputChange(NodeId& node, TurnId& turn) {} + virtual void OnTurnAdmissionStart(TurnId turn) = 0; + virtual void OnTurnAdmissionEnd(TurnId turn) = 0; - void Propagate(TurnId& turn) {} + virtual void OnInputChange(NodeId node, TurnId turn) = 0; - void OnNodeCreate(NodeId& node) {} - void OnNodeDestroy(NodeId& node) {} + virtual void Propagate(TurnId turn) = 0; - void OnNodeAttach(NodeId node, NodeId parent) {} - void OnNodeDetach(NodeId node, NodeId parent) {} + virtual NodeId OnNodeCreate() = 0; + virtual void OnNodeDestroy(NodeId node) = 0; - void OnNodePulse(NodeId& node, TurnId& turn) {} - void OnNodeIdlePulse(NodeId& node, TurnId& turn) {} + virtual void OnNodeAttach(NodeId node, NodeId parent) = 0; + virtual void OnNodeDetach(NodeId node, NodeId parent) = 0; - void OnDynamicNodeAttach(NodeId& node, NodeId& parent, TurnId& turn) {} - void OnDynamicNodeDetach(NodeId& node, NodeId& parent, TurnId& turn) {} + virtual void OnDynamicNodeAttach(NodeId node, NodeId parent, TurnId turn) = 0; + virtual void OnDynamicNodeDetach(NodeId node, NodeId parent, TurnId turn) = 0; }; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/IReactiveNode.h b/include/react/detail/IReactiveNode.h index 34967834..68fe08be 100644 --- a/include/react/detail/IReactiveNode.h +++ b/include/react/detail/IReactiveNode.h @@ -13,10 +13,11 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -enum class EUpdateResult +enum class UpdateResult { - UNCHANGED, - CHANGED + unchanged, + changed, + shifted }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -31,7 +32,7 @@ struct IReactiveNode // Note: Could get rid of this ugly ptr by adding a template parameter to the interface // But that would mean all engine nodes need that template parameter too - so rather cast - virtual EUpdateResult Update() = 0; + virtual UpdateResult Update() = 0; /// Input nodes can be manipulated externally. virtual bool IsInputNode() const = 0; diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index f6c750cc..00e35cbf 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -30,30 +30,23 @@ template class SignalNode; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// BufferClearAccessPolicy -/// -/// Provides thread safe access to clear event buffer if parallel updating is enabled. -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Note: Weird design due to empty base class optimization -template -struct BufferClearAccessPolicy : - private ConditionalCriticalSection -{ - template - void AccessBufferForClearing(const F& f) { this->Access(f); } -}; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventStreamNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class EventStreamNode : public ObservableNode +class EventStreamNode : public NodeBase { public: using StorageType = std::vector; - EventStreamNode() = default; + EventStreamNode(IReactiveGroup* group) : NodeBase( group ) + { } + + EventStreamNode(NodeBase&&) = default; + EventStreamNode& operator=(NodeBase&&) = default; + + EventStreamNode(const NodeBase&) = delete; + EventStreamNode& operator=(const NodeBase&) = delete; void SetCurrentTurn(const TurnT& turn, bool forceUpdate = false, bool noClear = false) { @@ -74,43 +67,36 @@ class EventStreamNode : public ObservableNode { return events_; } protected: - StorageType events_; + StorageType events_; private: - uint curTurnId_ { (std::numeric_limits::max)() }; + uint curTurnId_ { (std::numeric_limits::max)() }; }; -template -using EventStreamNodePtrT = std::shared_ptr>; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventSourceNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EventSourceNode : - public EventStreamNode, - public IInputNode +template +class EventSourceNode : public EventStreamNode, public IInputNode { public: - EventSourceNode() : - EventSourceNode::EventStreamNode{ } - { - Engine::OnNodeCreate(*this); - } + EventSourceNode(IReactiveGroup* group) : EventSourceNode::EventStreamNode( group ) + { this->RegisterMe(); } ~EventSourceNode() - { - Engine::OnNodeDestroy(*this); - } + { this->UnregisterMe(); } + + virtual const char* GetNodeType() const override + { return "EventSource"; } - virtual const char* GetNodeType() const override { return "EventSourceNode"; } - virtual bool IsInputNode() const override { return true; } - virtual int DependencyCount() const override { return 0; } + virtual bool IsInputNode() const override + { return true; } + + virtual int DependencyCount() const override + { return 0; } virtual void Update(void* turnPtr) override - { - REACT_ASSERT(false, "Ticked EventSourceNode\n"); - } + { REACT_ASSERT(false, "Updated EventSourceNode\n"); } template void AddInput(V&& v) @@ -148,416 +134,189 @@ class EventSourceNode : }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventOpNode +/// EventMergeNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename T, - typename ... Ds -> -class EventMergeNode : public EventStreamNode +template +class EventMergeNode : public EventStreamNode { public: - template - EventMergeNode(Us&& ... args) : - EventOpNode::EventStreamNode( ), - op_( std::forward(args) ... ) + EventMergeNode(IReactiveGroup* group, const std::shared_ptr>& ... deps) : + EventMergeNode::EventStreamNode( group ), + depHolder_( deps ... ) { - Engine::OnNodeCreate(*this); - op_.template Attach(*this); + this->RegisterMe(); + REACT_EXPAND_PACK(this->AttachToMe(deps->GetNodeId())); } - ~EventOpNode() + ~EventMergeNode() { - if (!wasOpStolen_) - op_.template Detach(*this); - Engine::OnNodeDestroy(*this); + apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(this->DetachFromMe(deps->GetNodeId())); }, depHolder_); + this->UnregisterMe(); } - virtual EUpdateResult Update() override + virtual UpdateResult Update() override { - using TurnT = typename D::Engine::TurnT; - TurnT& turn = *reinterpret_cast(turnPtr); - - this->SetCurrentTurn(turn, true); - - {// timer - size_t count = 0; - using TimerT = typename EventOpNode::ScopedUpdateTimer; - TimerT scopedTimer( *this, count ); + // this->SetCurrentTurn(turn, true); - op_.Collect(turn, EventCollector( this->events_ )); + apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(MergeFromDep(deps)); }, depHolder_); - // Note: Count was passed by reference, so we can still change before the dtor - // of the scoped timer is called - count = this->events_.size(); - }// ~timer - - if (! this->events_.empty()) - return EUpdateResult::CHANGED; + if (! this->Events().empty()) + return UpdateResult::changed; else - return EUpdateResult::UNCHANGED; + return UpdateResult::unchanged; } virtual const char* GetNodeType() const override - { return "EventOpNode"; } + { return "EventMerge"; } virtual int DependencyCount() const override - { return TOp::dependency_count; } + { return sizeof...(Es); } private: - TOp op_; - bool wasOpStolen_ = false; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventMergeOp -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename T, - typename ... Ds -> -class EventMergeOp : public ReactiveOpBase -{ -public: - template - EventMergeOp(Us&& ... deps) : - EventMergeOp::ReactiveOpBase(DontMove(), std::forward(deps) ...) - {} - - EventMergeOp(EventMergeOp&& other) = default; - - template - void Collect(const TTurn& turn, const TCollector& collector) const + template + void MergeFromDep(const std::shared_ptr& other) { - apply(CollectFunctor( turn, collector ), this->deps_); + //arg->SetCurrentTurn(turn); + this->Events().insert(this->Events().end(), other->Events().begin(), other->Events().end()); } - template - void CollectRec(const TFunctor& functor) const - { - apply(reinterpret_cast&>(functor), this->deps_); - } - -private: - template - struct CollectFunctor - { - CollectFunctor(const TTurn& turn, const TCollector& collector) : - MyTurn( turn ), - MyCollector( collector ) - {} - - void operator()(const TDeps& ... deps) const - { - REACT_EXPAND_PACK(collect(deps)); - } - - template - void collect(const T& op) const - { - op.template CollectRec(*this); - } - - template - void collect(const std::shared_ptr& depPtr) const - { - depPtr->SetCurrentTurn(MyTurn); - - for (const auto& v : depPtr->Events()) - MyCollector(v); - } - - const TTurn& MyTurn; - const TCollector& MyCollector; - }; + std::tuple ...> depHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventFilterOp +/// EventTransformNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename E, - typename TFilter, - typename TDep -> -class EventFilterOp : public ReactiveOpBase +template +class EventTransformNode : public EventStreamNode { public: - template - EventFilterOp(TFilterIn&& filter, TDepIn&& dep) : - EventFilterOp::ReactiveOpBase{ DontMove(), std::forward(dep) }, - filter_( std::forward(filter) ) - {} - - EventFilterOp(EventFilterOp&& other) : - EventFilterOp::ReactiveOpBase{ std::move(other) }, - filter_( std::move(other.filter_) ) - {} - - template - void Collect(const TTurn& turn, const TCollector& collector) const + template + EventTransformNode(IReactiveGroup* group, const std::shared_ptr>& dep, U&& func) : + EventTransformNode::EventStreamNode( group ), + dep_( dep ), + func_( std::forward(func) ) { - collectImpl(turn, FilteredEventCollector{ filter_, collector }, getDep()); + this->RegisterMe(); + this->AttachToMe(dep->GetNodeId()); } - template - void CollectRec(const TFunctor& functor) const + ~EventTransformNode() { - // Can't recycle functor because MyFunc needs replacing - Collect(functor.MyTurn, functor.MyCollector); + this->DetachFromMe(dep_->GetNodeId()); + this->UnregisterMe(); } -private: - const TDep& getDep() const { return std::get<0>(this->deps_); } - - template - struct FilteredEventCollector - { - FilteredEventCollector(const TFilter& filter, const TCollector& collector) : - MyFilter( filter ), - MyCollector( collector ) - {} - - void operator()(const E& e) const - { - // Accepted? - if (MyFilter(e)) - MyCollector(e); - } - - const TFilter& MyFilter; - const TCollector& MyCollector; // The wrapped collector - }; - - template - static void collectImpl(const TTurn& turn, const TCollector& collector, const T& op) + virtual UpdateResult Update() override { - op.Collect(turn, collector); - } + // this->SetCurrentTurn(turn, true); - template - static void collectImpl(const TTurn& turn, const TCollector& collector, - const std::shared_ptr& depPtr) - { - depPtr->SetCurrentTurn(turn); + for (const auto& v : dep_->Events()) + this->Events().push_back(func_(v)); - for (const auto& v : depPtr->Events()) - collector(v); + if (! this->Events().empty()) + return UpdateResult::changed; + else + return UpdateResult::unchanged; } - TFilter filter_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventTransformOp -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Todo: Refactor code duplication -template -< - typename E, - typename TFunc, - typename TDep -> -class EventTransformOp : public ReactiveOpBase -{ -public: - template - EventTransformOp(TFuncIn&& func, TDepIn&& dep) : - EventTransformOp::ReactiveOpBase( DontMove(), std::forward(dep) ), - func_( std::forward(func) ) - {} - - EventTransformOp(EventTransformOp&& other) : - EventTransformOp::ReactiveOpBase( std::move(other) ), - func_( std::move(other.func_) ) - {} - - template - void Collect(const TTurn& turn, const TCollector& collector) const - { - collectImpl(turn, TransformEventCollector( func_, collector ), getDep()); - } + virtual const char* GetNodeType() const override + { return "EventTransform"; } - template - void CollectRec(const TFunctor& functor) const - { - // Can't recycle functor because MyFunc needs replacing - Collect(functor.MyTurn, functor.MyCollector); - } + virtual int DependencyCount() const override + { return 1; } private: - const TDep& getDep() const { return std::get<0>(this->deps_); } - - template - struct TransformEventCollector - { - TransformEventCollector(const TFunc& func, const TTarget& target) : - MyFunc( func ), - MyTarget( target ) - {} - - void operator()(const E& e) const - { - MyTarget(MyFunc(e)); - } + std::shared_ptr dep_; - const TFunc& MyFunc; - const TTarget& MyTarget; - }; - - template - static void collectImpl(const TTurn& turn, const TCollector& collector, const T& op) - { - op.Collect(turn, collector); - } - - template - static void collectImpl(const TTurn& turn, const TCollector& collector, const std::shared_ptr& depPtr) - { - depPtr->SetCurrentTurn(turn); - - for (const auto& v : depPtr->Events()) - collector(v); - } - - TFunc func_; + F func_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventOpNode +/// EventFilterNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E, - typename TOp -> -class EventOpNode : - public EventStreamNode +template +class EventFilterNode : public EventStreamNode { - using Engine = typename EventOpNode::Engine; - public: - template - EventOpNode(TArgs&& ... args) : - EventOpNode::EventStreamNode( ), - op_( std::forward(args) ... ) + template + EventFilterNode(IReactiveGroup* group, const std::shared_ptr>& dep, U&& pred) : + EventFilterNode::EventStreamNode( group ), + dep_( dep ), + pred_( std::forward(pred) ) { - Engine::OnNodeCreate(*this); - op_.template Attach(*this); + this->RegisterMe(); + this->AttachToMe(dep->GetNodeId()); } - ~EventOpNode() + ~EventFilterNode() { - if (!wasOpStolen_) - op_.template Detach(*this); - Engine::OnNodeDestroy(*this); + this->DetachFromMe(dep_->GetNodeId()); + this->UnregisterMe(); } - virtual void Update(void* turnPtr) override + virtual UpdateResult Update() override { - using TurnT = typename D::Engine::TurnT; - TurnT& turn = *reinterpret_cast(turnPtr); - - this->SetCurrentTurn(turn, true); + // this->SetCurrentTurn(turn, true); - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); + for (const auto& v : dep_->Events()) + if (pred_(v)) + this->Events().push_back(v); - {// timer - size_t count = 0; - using TimerT = typename EventOpNode::ScopedUpdateTimer; - TimerT scopedTimer( *this, count ); - - op_.Collect(turn, EventCollector( this->events_ )); - - // Note: Count was passed by reference, so we can still change before the dtor - // of the scoped timer is called - count = this->events_.size(); - }// ~timer - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - if (! this->events_.empty()) - Engine::OnNodePulse(*this, turn); + if (! this->Events().empty()) + return UpdateResult::changed; else - Engine::OnNodeIdlePulse(*this, turn); + return UpdateResult::unchanged; } - virtual const char* GetNodeType() const override { return "EventOpNode"; } - virtual int DependencyCount() const override { return TOp::dependency_count; } + virtual const char* GetNodeType() const override + { return "EventFilter"; } - TOp StealOp() - { - REACT_ASSERT(wasOpStolen_ == false, "Op was already stolen."); - wasOpStolen_ = true; - op_.template Detach(*this); - return std::move(op_); - } + virtual int DependencyCount() const override + { return 1; } private: - struct EventCollector - { - using DataT = typename EventOpNode::DataT; - - EventCollector(DataT& events) : MyEvents( events ) {} - - void operator()(const E& e) const { MyEvents.push_back(e); } + std::shared_ptr dep_; - DataT& MyEvents; - }; - - TOp op_; - bool wasOpStolen_ = false; + F pred_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventFlattenNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TOuter, - typename TInner -> -class EventFlattenNode : public EventStreamNode +template +class EventFlattenNode : public EventStreamNode { - using Engine = typename EventFlattenNode::Engine; - public: - EventFlattenNode(const std::shared_ptr>& outer, - const std::shared_ptr>& inner) : - EventFlattenNode::EventStreamNode( ), + EventFlattenNode(IReactiveGroup* group, const std::shared_ptr>& outer, const std::shared_ptr>& inner) : + EventFlattenNode::EventStreamNode( group ), outer_( outer ), inner_( inner ) { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *outer_); - Engine::OnNodeAttach(*this, *inner_); + this->RegisterMe(); + this->AttachToMe(outer->GetNodeId()); + this->AttachToMe(inner->GetNodeId()); } ~EventFlattenNode() { - Engine::OnNodeDetach(*this, *outer_); - Engine::OnNodeDetach(*this, *inner_); - Engine::OnNodeDestroy(*this); + this->DetachFromMe(inner->GetNodeId()); + this->DetachFromMe(outer->GetNodeId()); + this->UnregisterMe(); } - virtual const char* GetNodeType() const override { return "EventFlattenNode"; } - virtual bool IsDynamicNode() const override { return true; } - virtual int DependencyCount() const override { return 2; } + virtual const char* GetNodeType() const override + { return "EventFlatten"; } - virtual void Update(void* turnPtr) override - { - typedef typename D::Engine::TurnT TurnT; - TurnT& turn = *reinterpret_cast(turnPtr); + virtual bool IsDynamicNode() const override + { return true; } - this->SetCurrentTurn(turn, true); - inner_->SetCurrentTurn(turn); + virtual int DependencyCount() const override + { return 2; } + + virtual UpdateResult Update() override + { + // this->SetCurrentTurn(turn, true); + // inner_->SetCurrentTurn(turn); auto newInner = GetNodePtr(outer_->ValueRef()); @@ -569,285 +328,180 @@ class EventFlattenNode : public EventStreamNode auto oldInner = inner_; inner_ = newInner; - Engine::OnDynamicNodeDetach(*this, *oldInner, turn); - Engine::OnDynamicNodeAttach(*this, *newInner, turn); + this->DynamicDetachFromMe(oldInner->GetNodeId(), 0); + this->DynamicAttachToMe(newInner->GetNodeId(), 0); - return; + return UpdateResult::shifted; } - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); + this->Events().insert(this->Events().end(), inner_->Events().begin(), inner_->Events().end()); - this->events_.insert( - this->events_.end(), - inner_->Events().begin(), - inner_->Events().end()); - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - if (this->events_.size() > 0) - Engine::OnNodePulse(*this, turn); + if (! this->Events().empty()) + return UpdateResult::changed; else - Engine::OnNodeIdlePulse(*this, turn); + return UpdateResult::unchanged; } private: - std::shared_ptr> outer_; - std::shared_ptr> inner_; + std::shared_ptr> outer_; + std::shared_ptr> inner_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// SyncedEventTransformNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TIn, - typename TOut, - typename TFunc, - typename ... TDepValues -> -class SyncedEventTransformNode : - public EventStreamNode +template +class SyncedEventTransformNode : public EventStreamNode { - using Engine = typename SyncedEventTransformNode::Engine; - public: - template - SyncedEventTransformNode(const std::shared_ptr>& source, F&& func, - const std::shared_ptr>& ... deps) : - SyncedEventTransformNode::EventStreamNode( ), - source_( source ), - func_( std::forward(func) ), - deps_( deps ... ) + template + SyncedEventTransformNode(IReactiveGroup* group, const std::shared_ptr>& dep, U&& func, const std::shared_ptr>& ... syncs) : + SyncedEventTransformNode::EventStreamNode( group ), + dep_( dep ), + func_( std::forward(func) ), + syncs_( syncs ... ) { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *source); - REACT_EXPAND_PACK(Engine::OnNodeAttach(*this, *deps)); + this->RegisterMe(); + this->AttachToMe(dep->GetNodeId()); + REACT_EXPAND_PACK(this->AttachToMe(syncs->GetNodeId())); } ~SyncedEventTransformNode() { - Engine::OnNodeDetach(*this, *source_); - - apply( - DetachFunctor>...>( *this ), - deps_); - - Engine::OnNodeDestroy(*this); + apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(syncs->GetNodeId())); }, syncHolder_); + this->DetachFromMe(dep_->GetNodeId()); + this->UnregisterMe(); } - virtual void Update(void* turnPtr) override + virtual UpdateResult Update() override { - using TurnT = typename D::Engine::TurnT; - TurnT& turn = *reinterpret_cast(turnPtr); + //this->SetCurrentTurn(turn, true); - this->SetCurrentTurn(turn, true); // Update of this node could be triggered from deps, // so make sure source doesnt contain events from last turn - source_->SetCurrentTurn(turn); - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - // Don't time if there is nothing to do - if (! source_->Events().empty()) - {// timer - using TimerT = typename SyncedEventTransformNode::ScopedUpdateTimer; - TimerT scopedTimer( *this, source_->Events().size() ); - - for (const auto& e : source_->Events()) - this->events_.push_back(apply( - [this, &e] (const std::shared_ptr>& ... args) - { - return func_(e, args->ValueRef() ...); - }, - deps_)); - - }// ~timer - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - if (! this->events_.empty()) - Engine::OnNodePulse(*this, turn); + // source_->SetCurrentTurn(turn); + + for (const auto& e : dep_->Events()) + this->Events().push_back(apply([this, &e] (const auto& ... syncs) { return this->func_(e, syncs->ValueRef() ...); }, syncHolder_)); + + if (! this->Events().empty()) + return UpdateResult::changed; else - Engine::OnNodeIdlePulse(*this, turn); + return UpdateResult::unchanged; } - virtual const char* GetNodeType() const override { return "SyncedEventTransformNode"; } - virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } + virtual const char* GetNodeType() const override + { return "SyncedEventTransform"; } + + virtual int DependencyCount() const override + { return 1 + sizeof...(TSyncs); } private: - using DepHolderT = std::tuple>...>; + std::shared_ptr> dep_; - std::shared_ptr> source_; + F func_; - TFunc func_; - DepHolderT deps_; + std::tuple>...> syncHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// SyncedEventFilterNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E, - typename TFunc, - typename ... TDepValues -> -class SyncedEventFilterNode : - public EventStreamNode +template +class SyncedEventFilterNode : public EventStreamNode { - using Engine = typename SyncedEventFilterNode::Engine; - public: - template - SyncedEventFilterNode(const std::shared_ptr>& source, F&& filter, - const std::shared_ptr>& ... deps) : - SyncedEventFilterNode::EventStreamNode( ), - source_( source ), - filter_( std::forward(filter) ), - deps_(deps ... ) + template + SyncedEventFilterNode(IReactiveGroup* group, const std::shared_ptr>& dep, U&& pred, const std::shared_ptr>& ... syncs) : + SyncedEventFilterNode::EventStreamNode( group ), + dep_( dep ), + pred_( std::forward(pred) ), + syncs_( syncs ... ) { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *source); - REACT_EXPAND_PACK(Engine::OnNodeAttach(*this, *deps)); + this->RegisterMe(); + this->AttachToMe(dep->GetNodeId()); + REACT_EXPAND_PACK(this->AttachToMe(syncs->GetNodeId())); } ~SyncedEventFilterNode() { - Engine::OnNodeDetach(*this, *source_); - - apply( - DetachFunctor>...>( *this ), - deps_); - - Engine::OnNodeDestroy(*this); + apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(syncs->GetNodeId())); }, syncHolder_); + this->DetachFromMe(dep_->GetNodeId()); + this->UnregisterMe(); } - virtual void Update(void* turnPtr) override + virtual UpdateResult Update() override { - using TurnT = typename D::Engine::TurnT; - TurnT& turn = *reinterpret_cast(turnPtr); + // this->SetCurrentTurn(turn, true); - this->SetCurrentTurn(turn, true); // Update of this node could be triggered from deps, // so make sure source doesnt contain events from last turn - source_->SetCurrentTurn(turn); - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - // Don't time if there is nothing to do - if (! source_->Events().empty()) - {// timer - using TimerT = typename SyncedEventFilterNode::ScopedUpdateTimer; - TimerT scopedTimer( *this, source_->Events().size() ); - - for (const auto& e : source_->Events()) - if (apply( - [this, &e] (const std::shared_ptr>& ... args) - { - return filter_(e, args->ValueRef() ...); - }, - deps_)) - this->events_.push_back(e); - }// ~timer - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - if (! this->events_.empty()) - Engine::OnNodePulse(*this, turn); + //source_->SetCurrentTurn(turn); + + for (const auto& e : dep_->Events()) + if (apply([this, &e] (const auto& ... syncs) { return this->func_(e, syncs->ValueRef() ...); }, syncHolder_)) + this->Events().push_back(e); + + if (! this->Events().empty()) + return UpdateResult::changed; else - Engine::OnNodeIdlePulse(*this, turn); + return UpdateResult::unchanged; } - virtual const char* GetNodeType() const override { return "SyncedEventFilterNode"; } - virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } + virtual const char* GetNodeType() const override + { return "SyncedEventFilter"; } - virtual bool IsHeavyweight() const override - { - return this->IsUpdateThresholdExceeded(); - } + virtual int DependencyCount() const override + { return 1 + sizeof...(TSyncs); } private: - using DepHolderT = std::tuple>...>; + std::shared_ptr> dep_; - std::shared_ptr> source_; + F pred_; - TFunc filter_; - DepHolderT deps_; + std::tuple>...> syncHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventProcessingNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TIn, - typename TOut, - typename TFunc -> -class EventProcessingNode : - public EventStreamNode +template +class EventProcessingNode : public EventStreamNode { - using Engine = typename EventProcessingNode::Engine; - public: - template - EventProcessingNode(const std::shared_ptr>& source, F&& func) : - EventProcessingNode::EventStreamNode( ), - source_( source ), - func_( std::forward(func) ) + template + EventProcessingNode(IReactiveGroup* group, const std::shared_ptr>& dep, U&& func) : + EventProcessingNode::EventStreamNode( group ), + dep_( dep ), + func_( std::forward(func) ) { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *source); + this->RegisterMe(); + this->AttachToMe(dep->GetNodeId()); } ~EventProcessingNode() { - Engine::OnNodeDetach(*this, *source_); - Engine::OnNodeDestroy(*this); + this->DetachFromMe(dep_->GetNodeId()); + this->UnregisterMe(); } - virtual void Update(void* turnPtr) override + virtual UpdateResult Update() 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())); - - {// timer - using TimerT = typename EventProcessingNode::ScopedUpdateTimer; - TimerT scopedTimer( *this, source_->Events().size() ); - - func_( - EventRange( source_->Events() ), - std::back_inserter(this->events_)); - - }// ~timer + //this->SetCurrentTurn(turn, true); - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); + func_(EventRange( source_->Events() ), std::back_inserter(this->Events())); - if (! this->events_.empty()) - Engine::OnNodePulse(*this, turn); + if (! this->Events().empty()) + return UpdateResult::changed; else - Engine::OnNodeIdlePulse(*this, turn); + return UpdateResult::unchanged; } - virtual const char* GetNodeType() const override { return "EventProcessingNode"; } - virtual int DependencyCount() const override { return 1; } + virtual const char* GetNodeType() const override + { return "EventProcessing"; } + + virtual int DependencyCount() const override + { return 1; } private: std::shared_ptr> source_; @@ -858,224 +512,155 @@ class EventProcessingNode : /////////////////////////////////////////////////////////////////////////////////////////////////// /// SyncedEventProcessingNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TIn, - typename TOut, - typename TFunc, - typename ... TDepValues -> -class SyncedEventProcessingNode : - public EventStreamNode +template +class SyncedEventProcessingNode : public EventStreamNode { - using Engine = typename SyncedEventProcessingNode::Engine; - public: - template - SyncedEventProcessingNode(const std::shared_ptr>& source, F&& func, - const std::shared_ptr>& ... deps) : - SyncedEventProcessingNode::EventStreamNode( ), - source_( source ), - func_( std::forward(func) ), - deps_( deps ... ) + template + SyncedEventProcessingNode(IReactiveGroup* group, const std::shared_ptr>& dep, U&& func, const std::shared_ptr>& ... syncs) : + SyncedEventProcessingNode::EventStreamNode( group ), + dep_( dep ), + func_( std::forward(func) ), + syncs_( syncs ... ) { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *source); - REACT_EXPAND_PACK(Engine::OnNodeAttach(*this, *deps)); + this->RegisterMe(); + this->AttachToMe(dep->GetNodeId()); + REACT_EXPAND_PACK(this->AttachToMe(syncs->GetNodeId())); } ~SyncedEventProcessingNode() { - Engine::OnNodeDetach(*this, *source_); - - apply( - DetachFunctor>...>( *this ), - deps_); - - Engine::OnNodeDestroy(*this); + apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(syncs->GetNodeId())); }, syncHolder_); + this->DetachFromMe(dep_->GetNodeId()); + this->UnregisterMe(); } - virtual void Update(void* turnPtr) override + virtual UpdateResult Update() override { - using TurnT = typename D::Engine::TurnT; - TurnT& turn = *reinterpret_cast(turnPtr); - - this->SetCurrentTurn(turn, true); + //this->SetCurrentTurn(turn, true); // Update of this node could be triggered from deps, // so make sure source doesnt contain events from last turn - source_->SetCurrentTurn(turn); + //source_->SetCurrentTurn(turn); - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - // Don't time if there is nothing to do - if (! source_->Events().empty()) - {// timer - using TimerT = typename SyncedEventProcessingNode::ScopedUpdateTimer; - TimerT scopedTimer( *this, source_->Events().size() ); - - apply( - [this] (const std::shared_ptr>& ... args) - { - func_( - EventRange( source_->Events() ), - std::back_inserter(this->events_), - args->ValueRef() ...); - }, - deps_); - - }// ~timer - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); + apply( + [this] (const auto& ... syncs) + { + func_(EventRange( source_->Events() ), std::back_inserter(this->Events()), syncs->ValueRef() ...); + }, + syncHolder_); - if (! this->events_.empty()) - Engine::OnNodePulse(*this, turn); + if (! this->Events().empty()) + return UpdateResult::changed; else - Engine::OnNodeIdlePulse(*this, turn); + return UpdateResult::unchanged; } - virtual const char* GetNodeType() const override { return "SycnedEventProcessingNode"; } - virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } + virtual const char* GetNodeType() const override + { return "SycnedEventProcessing"; } + + virtual int DependencyCount() const override + { return 1 + sizeof...(TSyncs); } private: - using DepHolderT = std::tuple>...>; + std::shared_ptr> dep_; - std::shared_ptr> source_; + F func_; - TFunc func_; - DepHolderT deps_; + std::tuple>...> syncHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventJoinNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename ... TValues -> -class EventJoinNode : - public EventStreamNode> +template +class EventJoinNode : public EventStreamNode> { - using Engine = typename EventJoinNode::Engine; - using TurnT = typename Engine::TurnT; - public: - EventJoinNode(const std::shared_ptr>& ... sources) : - EventJoinNode::EventStreamNode( ), - slots_( sources ... ) + EventJoinNode(IReactiveGroup* group, const std::shared_ptr>& ... deps) : + EventJoinNode::EventStreamNode( group ), + slots_( deps ... ) { - Engine::OnNodeCreate(*this); - REACT_EXPAND_PACK(Engine::OnNodeAttach(*this, *sources)); + this->RegisterMe(); + REACT_EXPAND_PACK(this->AttachToMe(deps->GetNodeId())); } ~EventJoinNode() { - apply( - [this] (Slot& ... slots) { - REACT_EXPAND_PACK(Engine::OnNodeDetach(*this, *slots.Source)); - }, - slots_); - - Engine::OnNodeDestroy(*this); + apply([this] (const auto& ... slots) { REACT_EXPAND_PACK(this->DetachFromMe(slots.source->GetNodeId())); }, slots_); + this->UnregisterMe(); } - virtual void Update(void* turnPtr) override + virtual UpdateResult Update() override { - TurnT& turn = *reinterpret_cast(turnPtr); + //this->SetCurrentTurn(turn, true); - this->SetCurrentTurn(turn, true); + // Move events into buffers + apply([this, &turn] (Slot& ... slots) { REACT_EXPAND_PACK(FetchBuffer(turn, slots)); }, slots_); - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - // Don't time if there is nothing to do - {// timer - size_t count = 0; - using TimerT = typename EventJoinNode::ScopedUpdateTimer; - TimerT scopedTimer( *this, count ); + while (true) + { + bool isReady = true; - // Move events into buffers + // All slots ready? apply( - [this, &turn] (Slot& ... slots) { - REACT_EXPAND_PACK(fetchBuffer(turn, slots)); + [this,&isReady] (Slot& ... slots) { + // Todo: combine return values instead + REACT_EXPAND_PACK(CheckSlot(slots, isReady)); }, slots_); - while (true) - { - bool isReady = true; - - // All slots ready? - apply( - [this,&isReady] (Slot& ... slots) { - // Todo: combine return values instead - REACT_EXPAND_PACK(checkSlot(slots, isReady)); - }, - slots_); - - if (!isReady) - break; - - // Pop values from buffers and emit tuple - apply( - [this] (Slot& ... slots) { - this->events_.emplace_back(slots.Buffer.front() ...); - REACT_EXPAND_PACK(slots.Buffer.pop_front()); - }, - slots_); - } - - count = this->events_.size(); - - }// ~timer + if (!isReady) + break; - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); + // Pop values from buffers and emit tuple + apply( + [this] (Slot& ... slots) + { + this->Events().emplace_back(slots.buffer.front() ...); + REACT_EXPAND_PACK(slots.buffer.pop_front()); + }, + slots_); + } - if (! this->events_.empty()) - Engine::OnNodePulse(*this, turn); + if (! this->Events().empty()) + return UpdateResult::changed; else - Engine::OnNodeIdlePulse(*this, turn); + return UpdateResult::unchanged; } - virtual const char* GetNodeType() const override { return "EventJoinNode"; } - virtual int DependencyCount() const override { return sizeof...(TValues); } + virtual const char* GetNodeType() const override + { return "EventJoin"; } + + virtual int DependencyCount() const override + { return sizeof...(Ts); } private: - template + template struct Slot { - Slot(const std::shared_ptr>& src) : - Source( src ) - {} + Slot(const std::shared_ptr>& src) : + source( src ) + { } - std::shared_ptr> Source; - std::deque Buffer; + std::shared_ptr> source; + std::deque buffer; }; - template - static void fetchBuffer(TurnT& turn, Slot& slot) + template + static void FetchBuffer(TurnT& turn, Slot& slot) { - slot.Source->SetCurrentTurn(turn); - - slot.Buffer.insert( - slot.Buffer.end(), - slot.Source->Events().begin(), - slot.Source->Events().end()); + //slot.Source->SetCurrentTurn(turn); + slot.buffer.insert(slot.buffer.end(), slot.source->Events().begin(), slot.source->Events().end()); } template - static void checkSlot(Slot& slot, bool& isReady) + static void CheckSlot(Slot& slot, bool& isReady) { - auto t = isReady && !slot.Buffer.empty(); + bool t = isReady && !slot.buffer.empty(); isReady = t; } - std::tuple...> slots_; + std::tuple...> slots_; }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index 66993500..404108d0 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -17,7 +17,8 @@ #include "react/common/Util.h" #include "react/common/Timing.h" #include "react/common/Types.h" -#include "react/detail/IReactiveEngine.h" +#include "react/detail/IReactiveGroup.h" +#include "react/detail/IReactiveNode.h" #include "react/detail/ObserverBase.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -84,28 +85,28 @@ struct DepCounter /////////////////////////////////////////////////////////////////////////////////////////////////// /// NodeBase /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class NodeBase : - public D::Policy::Engine::NodeT, - public UpdateTimingPolicy +class NodeBase : public IReactiveNode { public: - using DomainT = D; - using Policy = typename D::Policy; - using Engine = typename D::Engine; - using NodeT = typename Engine::NodeT; - using TurnT = typename Engine::TurnT; + NodeBase(IReactiveGroup* group) : group_( group ) + { } - NodeBase() = default; - - // Nodes can't be copied + NodeBase(NodeBase&&) = delete; + NodeBase& operator=(NodeBase&&) = delete; NodeBase(const NodeBase&) = delete; + NodeBase& operator=(const NodeBase&) = delete; - virtual bool IsInputNode() const override { return false; } - virtual bool IsOutputNode() const override { return false; } - virtual bool IsDynamicNode() const override { return false; } + virtual bool IsInputNode() const override + { return false; } + + virtual bool IsOutputNode() const override + { return false; } + + virtual bool IsDynamicNode() const override + { return false; } - virtual bool IsHeavyweight() const override { return this->IsUpdateThresholdExceeded(); } + virtual bool IsHeavyweight() const override + { return false; } void SetWeightHint(WeightHint weight) { @@ -122,27 +123,38 @@ class NodeBase : break; } } -}; -template -using NodeBasePtrT = std::shared_ptr>; + NodeId GetNodeId() const + { return nodeId_; } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ObservableNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ObservableNode : - public NodeBase, - public Observable -{ -public: - ObservableNode() = default; +protected: + void RegisterMe() + { nodeId_ = group_->RegisterNode(); } + + void UnregisterMe() + { group_->UnregisterNode(nodeId_); } + + void AttachToMe(NodeId otherNodeId) + { group_->OnNodeAttach(nodeId_, otherNodeId); } + + void DetachFromMe(NodeId otherNodeId) + { group_->OnNodeDetach(nodeId_, otherNodeId); } + + void DynamicAttachToMe(NodeId otherNodeId, TurnId turnId) + { group_->OnDynamicNodeAttach(nodeId_, otherNodeId, turnId); } + + void DynamicDetachFromMe(NodeId otherNodeId, TurnId turnId) + { group_->OnDynamicNodeDetach(nodeId_, otherNodeId, turnId); } + +private: + NodeId nodeId_; + IReactiveGroup* group_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Attach/detach helper functors /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template struct AttachFunctor { AttachFunctor(TNode& node) : MyNode( node ) {} diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index 9ea74832..8f9424dc 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -27,14 +27,14 @@ bool Equals(const L& lhs, const R& rhs); /////////////////////////////////////////////////////////////////////////////////////////////////// /// SignalNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class SignalNode : public ObservableNode { public: SignalNode() = default; - template - explicit SignalNode(T&& value) : + template + explicit SignalNode(U&& value) : SignalNode::ObservableNode( ), value_( std::forward(value) ) {} diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index d9782c0d..f2e9c00c 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -159,7 +159,6 @@ - @@ -167,9 +166,8 @@ - - + @@ -189,7 +187,6 @@ - diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index 7de3201b..2b7a6008 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -57,9 +57,6 @@ Header Files\common - - Header Files - Header Files\engine @@ -69,9 +66,6 @@ Header Files\detail - - Header Files\detail - Header Files\detail @@ -81,9 +75,6 @@ Header Files\detail\graph - - Header Files\detail\graph - Header Files\detail\graph @@ -141,15 +132,15 @@ Header Files\engine - - Header Files\detail\graph - Header Files\common Header Files\detail + + Header Files\detail + From d13b3f47f8ebfcbd8062ecbfdd51db60796b885d Mon Sep 17 00:00:00 2001 From: schlangster Date: Thu, 4 Aug 2016 16:16:05 +0200 Subject: [PATCH 221/266] Major redesign WIP. --- examples/src/BasicSignals.cpp | 37 +- include/react/API.h | 118 ++++ include/react/Algorithm.h | 22 +- include/react/Event.h | 465 ++++++++------- include/react/{Domain.h => Group.h} | 210 ++++--- include/react/Observer.h | 6 +- include/react/Signal.h | 321 ++++++---- include/react/TypeTraits.h | 92 --- include/react/common/Containers.h | 148 +++++ include/react/common/TopoQueue.h | 20 +- include/react/common/Util.h | 52 +- include/react/detail/Defs.h | 13 - include/react/detail/DomainBase.h | 265 --------- include/react/detail/EventBase.h | 49 -- include/react/detail/IReactiveGraph.h | 100 ++++ include/react/detail/IReactiveGroup.h | 64 -- include/react/detail/IReactiveNode.h | 67 --- include/react/detail/ReactiveBase.h | 158 ----- include/react/detail/ReactiveInput.h | 12 +- include/react/detail/SignalBase.h | 65 -- include/react/detail/graph/AlgorithmNodes.h | 553 +++++++----------- .../react/detail/graph/ContinuationNodes.h | 351 ----------- include/react/detail/graph/EventNodes.h | 418 ++++--------- include/react/detail/graph/GraphBase.h | 238 +------- include/react/detail/graph/ObserverNodes.h | 338 +++-------- include/react/detail/graph/PropagationMT.h | 0 include/react/detail/graph/PropagationST.h | 284 +++++++++ include/react/detail/graph/ReactorNodes.h | 259 -------- include/react/detail/graph/SignalNodes.h | 346 ++++------- include/react/engine/PulsecountEngine.h | 6 +- include/react/engine/SubtreeEngine.h | 6 +- include/react/engine/ToposortEngine.h | 6 +- project/msvc/CppReact.vcxproj | 19 +- project/msvc/CppReact.vcxproj.filters | 81 +-- src/engine/PulsecountEngine.cpp | 6 +- src/engine/SubtreeEngine.cpp | 6 +- src/engine/ToposortEngine.cpp | 6 +- 37 files changed, 1920 insertions(+), 3287 deletions(-) create mode 100644 include/react/API.h rename include/react/{Domain.h => Group.h} (52%) delete mode 100644 include/react/TypeTraits.h delete mode 100644 include/react/detail/DomainBase.h delete mode 100644 include/react/detail/EventBase.h create mode 100644 include/react/detail/IReactiveGraph.h delete mode 100644 include/react/detail/IReactiveGroup.h delete mode 100644 include/react/detail/IReactiveNode.h delete mode 100644 include/react/detail/ReactiveBase.h delete mode 100644 include/react/detail/SignalBase.h delete mode 100644 include/react/detail/graph/ContinuationNodes.h create mode 100644 include/react/detail/graph/PropagationMT.h create mode 100644 include/react/detail/graph/PropagationST.h delete mode 100644 include/react/detail/graph/ReactorNodes.h diff --git a/examples/src/BasicSignals.cpp b/examples/src/BasicSignals.cpp index a5a82445..7f6943f2 100644 --- a/examples/src/BasicSignals.cpp +++ b/examples/src/BasicSignals.cpp @@ -11,10 +11,8 @@ #include #include -#include "react/Domain.h" #include "react/Signal.h" -#include "react/Observer.h" - +/* /////////////////////////////////////////////////////////////////////////////////////////////////// /// Example 1 - Hello world /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -26,7 +24,7 @@ namespace example1 // Defines a domain. // Each domain represents a separate dependency graph, managed by a dedicated propagation engine. // Reactives of different domains can not be combined. - REACTIVE_DOMAIN(D, sequential) + ReactiveGroup // Define type aliases for the given domain in this namespace. // Now we can use VarSignalT instead of D::VarSignalT. @@ -41,8 +39,8 @@ namespace example1 namespace v1 { // The two words - VarSignalT firstWord = MakeVar(string("Change")); - VarSignalT secondWord = MakeVar(string("me!")); + VarSignalT firstWord( string("Change") ); + VarSignalT secondWord( string("me!") ); SignalT bothWords = MakeSignal(With(firstWord,secondWord), concatFunc); @@ -388,5 +386,32 @@ int main() example5::v2::Run(); example5::v3::Run(); + return 0; +} + +*/ + +using namespace react; + +int main() +{ + auto group = ReactiveGroup( ); + + auto sig1 = VarSignal( 1, group ); + auto sig2 = VarSignal( 2, group ); + + sig1.Set(1); + sig1 <<= 1; + + sig2.Modify([] (int& value) { value = 3; }); + + group.DoTransaction( + [&] + { + sig1 <<= 2; + }); + + auto sig3 = Signal( [] (auto a, auto b) { return a + b; }, sig1, sig2 ); + return 0; } \ No newline at end of file diff --git a/include/react/API.h b/include/react/API.h new file mode 100644 index 00000000..b445b74c --- /dev/null +++ b/include/react/API.h @@ -0,0 +1,118 @@ + +// Copyright Sebastian Jeckel 2016. +// 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_API_H_INCLUDED +#define REACT_API_H_INCLUDED + +#pragma once + +#include "react/detail/Defs.h" +#include "react/common/Util.h" + +/*****************************************/ REACT_BEGIN /*****************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// API constants +/////////////////////////////////////////////////////////////////////////////////////////////////// +enum OwnershipPolicy +{ + unique, + shared +}; + +enum ThreadingPolicy +{ + sequential, + concurrent +}; + +enum class WeightHint +{ + automatic, + light, + heavy +}; + +enum class TransactionFlags +{ + none = 1 << 0, + allow_merging = 1 << 1 +}; + +REACT_DEFINE_BITMASK_OPERATORS(TransactionFlags) + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// API types +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// Groups +template +class ReactiveGroup; + +// Signals +template +class SignalBase; + +template +class VarSignalBase; + +template +class Signal; + +template +class VarSignal; + +// Events +template +class EventBase; + +template +class EventSourceBase; + +template +class Event; + +template +class EventSource; + +enum class Token; + +// Observers +class Observer; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Traits +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct IsSignal { static const bool value = false; }; + +template +struct IsSignal> { static const bool value = true; }; + +template +struct IsSignal> { static const bool value = true; }; + +template +struct IsEvent { static const bool value = false; }; + +template +struct IsEvent> { static const bool value = true; }; + +template +struct IsEvent> { static const bool value = true; }; + +template +struct AsNonInputNode { using type = T; }; + +template +struct AsNonInputNode> { using type = Signal; }; + +template +struct AsNonInputNode> { using type = Event; }; + +/******************************************/ REACT_END /******************************************/ + +#endif // REACT_TYPETRAITS_H_INCLUDED \ No newline at end of file diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index b6da68af..e2a1f3a9 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -15,32 +15,16 @@ #include #include +#include "react/API.h" #include "react/detail/graph/AlgorithmNodes.h" /*****************************************/ REACT_BEGIN /*****************************************/ -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Signal; - -template -class VarSignal; - -template -class Events; - -template -class EventSource; - -enum class Token; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Hold - Hold the most recent event in a signal /////////////////////////////////////////////////////////////////////////////////////////////////// -template -auto Hold(const Events& events, V&& init) -> Signal +template +auto Hold(const Events& events, U&& init) -> Signal { using REACT_IMPL::HoldNode; diff --git a/include/react/Event.h b/include/react/Event.h index 599c2ad6..5af37093 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -10,72 +10,287 @@ #pragma once #include "react/detail/Defs.h" +#include "react/API.h" +#include "react/Group.h" #include #include #include -#include "react/Observer.h" -#include "react/TypeTraits.h" -#include "react/common/Util.h" -#include "react/detail/EventBase.h" +#include "react/detail/graph/EventNodes.h" /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations +/// Events +/////////////////////////////////////////////////////////////////////////////////////////////////// + +template +class EventBase +{ +private: + using NodeType = REACT_IMPL::EventStreamNode; + +public: + EventBase() = default; + + EventBase(const EventBase&) = default; + EventBase& operator=(const EventBase&) = default; + + EventBase(EventBase&&) = default; + EventBase& operator=(EventBase&&) = default; + + ~EventBase() = default; + + // Node ctor + explicit EventBase(std::shared_ptr&& nodePtr) : + nodePtr_( std::move(nodePtr) ) + { } + + auto Tokenize() const -> decltype(auto) + { return REACT::Tokenize(*this); } + + /*template + auto Merge(Us&& ... deps) const -> decltype(auto) + { return REACT::Merge(*this, std::forward(deps) ...); } + + template + auto Filter(F&& pred) const -> decltype(auto) + { return REACT::Filter(std::forward(pred), *this); } + + template + auto Transform(F&& pred) const -> decltype(auto) + { return REACT::Transform(*this, std::forward(f)); }*/ + +protected: + auto NodePtr() -> std::shared_ptr& + { return nodePtr_; } + + auto NodePtr() const -> const std::shared_ptr& + { return nodePtr_; } + + template + auto CreateProcessingNode(FIn&& func, const EventBase& dep) -> decltype(auto) + { + using F = typename std::decay::type; + using ProcessingNodeType = REACT_IMPL::EventProcessingNode; + + return std::make_shared( + REACT_IMPL::PrivateNodeInterface::GraphPtr(dep), + REACT_IMPL::PrivateNodeInterface::NodePtr(dep), + std::forward(func)); + } + +private: + std::shared_ptr nodePtr_; + + friend struct REACT_IMPL::PrivateNodeInterface; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventSource +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class EventSourceBase : public EventBase +{ +private: + using NodeType = REACT_IMPL::EventSourceNode; + +public: + using EventBase::EventBase; + + EventSourceBase() = default; + + EventSourceBase(const EventSourceBase&) = default; + EventSourceBase& operator=(const EventSourceBase&) = default; + + EventSourceBase(EventSourceBase&& other) = default; + EventSourceBase& operator=(EventSourceBase&& other) = default; + + template + explicit EventSourceBase(const TGroup& group) : + EventSourceBase::EventBase( std::make_shared(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) + { } + + void Emit(const T& value) + { EmitValue(value); } + + void Emit(T&& value) + { EmitValue(std::move(value)); } + + template ::value>::type> + void Emit() + { EmitValue(Token::value); } + + EventSourceBase& operator<<(const T& value) + { EmitValue(e); return *this; } + + EventSourceBase& operator<<(T&& value) + { EmitValue(std::move(value)); return *this; } + +private: + template + void EmitValue(U&& value) + { + using REACT_IMPL::NodeId; + using REACT_IMPL::IReactiveGraph; + + NodeType* castedPtr = static_cast(this->NodePtr().get()); + + NodeId nodeId = castedPtr->GetNodeId(); + auto& graphPtr = NodePtr()->GraphPtr(); + graphPtr->AddInput(nodeId, [castedPtr, &value] { castedPtr->EmitValue(std::forward(value)); }); + } +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Event /////////////////////////////////////////////////////////////////////////////////////////////////// template -class EventStream; +class Event : public EventBase +{ +public: + using EventBase::EventBase; + + using ValueType = T; + + Event() = delete; + + Event(const Event&) = delete; + Event& operator=(const Event&) = delete; + + Event(Event&&) = default; + Event& operator=(Event&&) = default; + + template + Event(F&& func, const EventBase& dep) : + Event::EventBase( CreateProcessingNode(std::forward(func), dep) ) + { } +}; template -class EventSource; +class Event : public EventBase +{ +public: + using EventBase::EventBase; + + using ValueType = T; + + Event() = delete; + + Event(const Event&) = default; + Event& operator=(const Event&) = default; + + Event(Event&&) = default; + Event& operator=(Event&&) = default; + + Event(Event&& other) : + Event::EventBase( std::move(other) ) + { } + + Event& operator=(Event&& other) + { Event::EventBase::operator=(std::move(other)); return *this; } + + template + Event(F&& func, const EventBase& dep) : + Event::EventBase( CreateProcessingNode(std::forward(func), dep) ) + { } +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventSource +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class EventSource : public EventSourceBase +{ +public: + using EventSourceBase::EventSourceBase; + + using ValueType = T; + + EventSource() = delete; -enum class Token; + EventSource(const EventSource&) = delete; + EventSource& operator=(const EventSource&) = delete; + + EventSource(EventSource&&) = default; + EventSource& operator=(EventSource&&) = default; +}; template -class Signal; +class EventSource : public EventSourceBase +{ +public: + using EventSourceBase::EventSourceBase; + + using ValueType = T; + + EventSource() = delete; + + EventSource(const EventSource&) = default; + EventSource& operator=(const EventSource&) = default; -using REACT_IMPL::WeightHint; + EventSource(EventSource&&) = default; + EventSource& operator=(EventSource&&) = default; + + EventSource(EventSource&& other) : + EventSource::EventSourceBase( std::move(other) ) + { } + + EventSource& operator=(EventSource&& other) + { EventSource::EventSourceBase::operator=(std::move(other)); return *this; } +}; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Merge /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename TArg1, - typename ... TArgs, - typename E = TArg1 -> -auto Merge(const Events& arg1, const Events& ... args) -> Events +template +auto Merge(const EventBase& dep1, const EventBase& ... deps) -> decltype(auto) { - using REACT_IMPL::EventOpNode; + using REACT_IMPL::EventMergeNode; + using REACT_IMPL::GetCheckedGraphPtr; + using REACT_IMPL::PrivateNodeInterface; + + static_assert(sizeof...(Us) > 0, "Merge: 2+ arguments are required."); - static_assert(sizeof...(TArgs) > 0, "Merge: 2+ arguments are required."); + // If supplied, use merge type, otherwise default to common type. + using E = typename std::conditional< + std::is_same::value, + typename std::common_type::type, + T>::type; - return Events( - std::make_shared>( - GetNodePtr(arg1), GetNodePtr(args) ...)); + const auto& graphPtr = GetCheckedGraphPtr(dep1, deps ...); + + return Event( + std::make_shared>(graphPtr, PrivateNodeInterface::NodePtr(dep1), PrivateNodeInterface::NodePtr(deps) ...)); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Filter /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename E, - typename FIn, - typename F = typename std::decay::type -> -auto Filter(const Events& src, FIn&& filter) -> Events +template +auto Filter(F&& pred, const EventBase& dep) -> Event { - using REACT_IMPL::EventOpNode; + auto filterFunc = [capturedPred = std::forward(pred)] (EventRange inRange, EventSink out) + { std::copy_if(inRange.begin(), inRange.end(), out, capturedPred); }; - return Events( - std::make_shared>( - std::forward(filter), GetNodePtr(src))); + return Event(std::move(filterFunc), dep); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Transform +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +auto Transform(F&& op, const EventBase& dep) -> Event +{ + auto transformFunc = [capturedPred = std::forward(op)] (EventRange inRange, EventSink out) + { std::transform(inRange.begin(), inRange.end(), out, pred); }; + + return Event(std::move(transformFunc), dep); +} + +#if 0 + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Filter - Synced /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -115,24 +330,7 @@ auto Filter(const Events& source, const SignalPack& depPack, F depPack.Data); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Transform -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename TIn, - typename FIn, - typename F = typename std::decay::type, - typename TOut = typename std::result_of::type -> -auto Transform(const Events& src, FIn&& func) -> Events -{ - using REACT_IMPL::EventOpNode; - return Events( - std::make_shared>( - std::forward(func), GetNodePtr(src))); -} /////////////////////////////////////////////////////////////////////////////////////////////////// /// Transform - Synced @@ -174,28 +372,6 @@ auto Transform(const Events& source, FIn&& func, const Signal& depPack.Data); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Process -/////////////////////////////////////////////////////////////////////////////////////////////////// -using REACT_IMPL::EventRange; -using REACT_IMPL::EventEmitter; - -template -< - typename TOut, - typename TIn, - typename FIn, - typename F = typename std::decay::type -> -auto Process(const Events& src, FIn&& func) -> Events -{ - using REACT_IMPL::EventProcessingNode; - - return Events( - std::make_shared>( - GetNodePtr(src), std::forward(func))); -} - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Process - Synced /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -279,155 +455,14 @@ auto Tokenize(T&& source) -> decltype(auto) return Transform(source, Tokenizer{ }); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Events -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Events : public REACT_IMPL::EventStreamBase -{ -private: - using NodeType = REACT_IMPL::EventStreamNode; - using NodePtrType = std::shared_ptr; - -public: - using ValueType = T; - - // Default ctor - Events() = default; - - // Copy ctor - Events(const Events&) = default; - - // Move ctor - Events(Events&& other) = default; - - // Node ctor - explicit Events(NodePtrT&& nodePtr) = default; - - // Copy assignment - Events& operator=(const Events&) = default; - - // Move assignment - Events& operator=(Events&& other) = default; - - bool Equals(const Events& other) const - { - return Events::EventStreamBase::Equals(other); - } - - bool IsValid() const - { - return Events::EventStreamBase::IsValid(); - } - - void SetWeightHint(WeightHint weight) - { - Events::EventStreamBase::SetWeightHint(weight); - } - - auto Tokenize() const -> decltype(auto) - { - return REACT::Tokenize(*this); - } - - template - auto Merge(TArgs&& ... args) const -> decltype(auto) - { - return REACT::Merge(*this, std::forward(args) ...); - } - - template - auto Filter(F&& f) const -> decltype(auto) - { - return REACT::Filter(*this, std::forward(f)); - } - - template - auto Transform(F&& f) const -> decltype(auto) - { - return REACT::Transform(*this, std::forward(f)); - } - - template - static auto Create() -> EventSource - { - using REACT_IMPL::EventSourceNode; - - return EventSource(std::make_shared>()); - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventSource -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EventSource : public Events -{ -private: - using NodeType = REACT_IMPL::EventSourceNode; - using NodePtrType = std::shared_ptr; - -public: - // Default ctor - EventSource() = default; - - // Copy ctor - EventSource(const EventSource&) = default; - - // Move ctor - EventSource(EventSource&& other) = default; - - // Node ctor - explicit EventSource(NodePtrT&& nodePtr) : - EventSource::Events( std::move(nodePtr) ) - {} - - // Copy assignemnt - EventSource& operator=(const EventSource&) = default; - - // Move assignment - EventSource& operator=(EventSource&& other) = default; - - // Explicit emit - void Emit(const T& e) const { EventSource::EventStreamBase::emit(e); } - void Emit(T&& e) const { EventSource::EventStreamBase::emit(std::move(e)); } - - void Emit() const - { - static_assert(std::is_same::value, "Can't emit on non token stream."); - EventSource::EventStreamBase::emit(Token::value); - } - - // Function object style - void operator()(const E& e) const { EventSource::EventStreamBase::emit(e); } - void operator()(E&& e) const { EventSource::EventStreamBase::emit(std::move(e)); } - - void operator()() const - { - static_assert(std::is_same::value, "Can't emit on non token stream."); - EventSource::EventStreamBase::emit(Token::value); - } - - // Stream style - const EventSource& operator<<(const T& e) const - { - EventSource::EventStreamBase::emit(e); - return *this; - } - - const EventSource& operator<<(T&& e) const - { - EventSource::EventStreamBase::emit(std::move(e)); - return *this; - } -}; +#endif /******************************************/ REACT_END /******************************************/ /***************************************/ REACT_IMPL_BEGIN /**************************************/ template -bool Equals(const Events& lhs, const Events& rhs) +bool Equals(const EventBase& lhs, const EventBase& rhs) { return lhs.Equals(rhs); } diff --git a/include/react/Domain.h b/include/react/Group.h similarity index 52% rename from include/react/Domain.h rename to include/react/Group.h index b1fae362..cf58e72a 100644 --- a/include/react/Domain.h +++ b/include/react/Group.h @@ -14,62 +14,25 @@ #include #include -#include "react/detail/DomainBase.h" -#include "react/detail/ReactiveInput.h" +#include "react/API.h" +#include "react/Signal.h" +//#include "react/Event.h" -#include "react/detail/graph/ContinuationNodes.h" +#include "react/detail/IReactiveGraph.h" +#include "react/detail/graph/PropagationST.h" -#ifdef REACT_ENABLE_LOGGING - #include "react/logging/EventLog.h" - #include "react/logging/EventRecords.h" -#endif //REACT_ENABLE_LOGGING +/***************************************/ REACT_IMPL_BEGIN /**************************************/ -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Signal; - -template -class VarSignal; - -template -class Events; - -template -class EventSource; - -enum class Token; +struct PrivateReactiveGroupInterface; +struct PrivateConcurrentReactiveGroupInterface; -class Observer; +/****************************************/ REACT_IMPL_END /***************************************/ -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Common types & constants -/////////////////////////////////////////////////////////////////////////////////////////////////// -using REACT_IMPL::TransactionFlagsT; - -// ETransactionFlags -using REACT_IMPL::ETransactionFlags; -using REACT_IMPL::allow_merging; - -#ifdef REACT_ENABLE_LOGGING - using REACT_IMPL::EventLog; -#endif //REACT_ENABLE_LOGGING +/*****************************************/ REACT_BEGIN /*****************************************/ -// Domain modes -using REACT_IMPL::EDomainMode; -using REACT_IMPL::sequential; -using REACT_IMPL::sequential_concurrent; -using REACT_IMPL::parallel; -using REACT_IMPL::parallel_concurrent; -// Expose enum type so aliases for engines can be declared, but don't -// expose the actual enum values as they are reserved for internal use. -using REACT_IMPL::EPropagationMode; -using REACT_IMPL::WeightHint; +#if 0 /////////////////////////////////////////////////////////////////////////////////////////////////// /// TransactionStatus @@ -134,7 +97,7 @@ void DoTransaction(F&& func) } template -void DoTransaction(TransactionFlagsT flags, F&& func) +void DoTransaction(TransactionFlags flags, F&& func) { using REACT_IMPL::DomainSpecificInputManager; DomainSpecificInputManager::Instance().DoTransaction(flags, std::forward(func)); @@ -155,7 +118,7 @@ void AsyncTransaction(F&& func) } template -void AsyncTransaction(TransactionFlagsT flags, F&& func) +void AsyncTransaction(TransactionFlags flags, F&& func) { static_assert(D::is_concurrent, "AsyncTransaction: Domain does not support concurrent input."); @@ -188,45 +151,124 @@ void AsyncTransaction(TransactionFlagsT flags, TransactionStatus& status, F&& fu .AsyncTransaction(flags, status.statePtr_, std::forward(func)); } -/******************************************/ REACT_END /******************************************/ +#endif /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Domain definition macro +/// ReactiveGroupBase /////////////////////////////////////////////////////////////////////////////////////////////////// -#define REACTIVE_DOMAIN(name, ...) \ - struct name : \ - public REACT_IMPL::DomainBase> {}; \ - static REACT_IMPL::DomainInitializer name ## _initializer_; - -/* - A brief reminder why the domain initializer is here: - Each domain has a couple of singletons (debug log, engine, input manager) which are - currently implemented as meyer singletons. From what I understand, these are thread-safe - in C++11, but not all compilers implement that yet. That's why a static initializer has - been added to make sure singleton creation happens before any multi-threaded access. - This implemenation is obviously inconsequential. - */ +class ReactiveGroupBase +{ + using GraphType = REACT_IMPL::SingleThreadedGraph; + +public: + ReactiveGroupBase() : + graphPtr_( std::make_shared() ) + { } + + ReactiveGroupBase(const ReactiveGroupBase&) = default; + ReactiveGroupBase& operator=(const ReactiveGroupBase&) = default; + + ReactiveGroupBase(ReactiveGroupBase&& other) = default; + ReactiveGroupBase& operator=(ReactiveGroupBase&& other) = default; + + ~ReactiveGroupBase() = default; + + template + void DoTransaction(F&& func) + { DoTransaction(TransactionFlags::none, std::forward(func)); } + + template + void DoTransaction(TransactionFlags flags, F&& func) + { graphPtr_->DoTransaction(flags, std::forward(func)); } + +protected: + auto GraphPtr() -> std::shared_ptr& + { return graphPtr_; } + + auto GraphPtr() const -> const std::shared_ptr& + { return graphPtr_; } + +private: + std::shared_ptr graphPtr_; + + friend struct REACT_IMPL::PrivateReactiveGroupInterface; +}; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Define type aliases for given domain +/// Signal /////////////////////////////////////////////////////////////////////////////////////////////////// -#define USING_REACTIVE_DOMAIN(name) \ - template \ - using SignalT = Signal; \ - \ - template \ - using VarSignalT = VarSignal; \ - \ - template \ - using EventsT = Events; \ - \ - template \ - using EventSourceT = EventSource; \ - \ - using ObserverT = Observer; \ - \ - using ScopedObserverT = ScopedObserver; \ - \ - using ReactorT = Reactor; +template <> +class ReactiveGroup : public ReactiveGroupBase +{ +public: + ReactiveGroup() = default; + + ReactiveGroup(const ReactiveGroup&) = delete; + ReactiveGroup& operator=(const ReactiveGroup&) = delete; + + ReactiveGroup(ReactiveGroup&& other) = default; + ReactiveGroup& operator=(ReactiveGroup&& other) = default; +}; + +template <> +class ReactiveGroup : public ReactiveGroupBase +{ +public: + ReactiveGroup() = default; + + ReactiveGroup(const ReactiveGroup&) = default; + ReactiveGroup& operator=(const ReactiveGroup&) = default; + + ReactiveGroup(ReactiveGroup&& other) = default; + ReactiveGroup& operator=(ReactiveGroup&& other) = default; +}; + +/******************************************/ REACT_END /******************************************/ + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +struct PrivateNodeInterface +{ + template + static auto NodePtr(const TBase& base) -> const std::shared_ptr& + { return base.NodePtr(); } + + template + static auto NodePtr(TBase& base) -> std::shared_ptr& + { return base.NodePtr(); } + + template + static auto GraphPtr(const TBase& base) -> const std::shared_ptr& + { return base.NodePtr()->GraphPtr(); } + + template + static auto GraphPtr(TBase& base) -> std::shared_ptr& + { return base.NodePtr()->GraphPtr(); } +}; + +struct PrivateReactiveGroupInterface +{ + static auto GraphPtr(const ReactiveGroupBase& group) -> const std::shared_ptr& + { return group.GraphPtr(); } + + static auto GraphPtr(ReactiveGroupBase& group) -> std::shared_ptr& + { return group.GraphPtr(); } +}; + +template +static auto GetCheckedGraphPtr(const TBase1& dep1, const TBases& ... deps) -> const std::shared_ptr& +{ + const std::shared_ptr& graphPtr1 = PrivateNodeInterface::GraphPtr(dep1); + + auto rawGraphPtrs = { PrivateNodeInterface::GraphPtr(deps).get() ... }; + + bool isSameGraphForAllDeps = std::all_of(rawGraphPtrs.begin(), rawGraphPtrs.end(), [&] (IReactiveGraph* p) { return p == graphPtr1.get(); }); + + REACT_ASSERT(isSameGraphForAllDeps, "All dependencies must belong to the same group."); + + return graphPtr1; +} + +/****************************************/ REACT_IMPL_END /***************************************/ #endif // REACT_DOMAIN_H_INCLUDED \ No newline at end of file diff --git a/include/react/Observer.h b/include/react/Observer.h index 17e72ebf..705d4ee9 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -4,6 +4,8 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#if 0 + #ifndef REACT_OBSERVER_H_INCLUDED #define REACT_OBSERVER_H_INCLUDED @@ -333,4 +335,6 @@ auto Observe(const Events& subject, /******************************************/ REACT_END /******************************************/ -#endif // REACT_OBSERVER_H_INCLUDED \ No newline at end of file +#endif // REACT_OBSERVER_H_INCLUDED + +#endif \ No newline at end of file diff --git a/include/react/Signal.h b/include/react/Signal.h index 4bf28460..ea60ce3a 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -14,192 +14,267 @@ #endif #include "react/detail/Defs.h" +#include "react/API.h" +#include "react/Group.h" #include #include #include #include -#include "react/Observer.h" -#include "react/TypeTraits.h" -#include "react/detail/SignalBase.h" +#include "react/detail/graph/SignalNodes.h" /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations +/// SignalBase /////////////////////////////////////////////////////////////////////////////////////////////////// template -class Signal; +class SignalBase +{ +private: + using NodeType = REACT_IMPL::SignalNode; -template -class VarSignal; +public: + SignalBase() = default; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Flatten -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -auto Flatten(const Signal>& outer) - -> Signal -{ - return Signal( - std::make_shared, TInnerValue>>( - GetNodePtr(outer), GetNodePtr(outer.Value()))); -} + SignalBase(const SignalBase&) = default; + SignalBase& operator=(const SignalBase&) = default; + + SignalBase(SignalBase&&) = default; + SignalBase& operator=(SignalBase&&) = default; + + ~SignalBase() = default; + + // Internal node ctor + explicit SignalBase(std::shared_ptr&& nodePtr) : + nodePtr_( std::move(nodePtr) ) + { } + + const T& Value() const + { return nodePtr_->Value(); } + +protected: + auto NodePtr() -> std::shared_ptr& + { return nodePtr_; } + + auto NodePtr() const -> const std::shared_ptr& + { return nodePtr_; } + + template + auto CreateFuncNode(FIn&& func, const SignalBase& dep1, const SignalBase& ... deps) -> decltype(auto) + { + using F = typename std::decay::type; + using FuncNodeType = REACT_IMPL::SignalFuncNode; + + return std::make_shared( + REACT_IMPL::GetCheckedGraphPtr(dep1, deps ...), + std::forward(func), + REACT_IMPL::PrivateNodeInterface::NodePtr(dep1), REACT_IMPL::PrivateNodeInterface::NodePtr(deps) ...); + } + +private: + std::shared_ptr nodePtr_; + + friend struct REACT_IMPL::PrivateNodeInterface; +}; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Signal +/// VarSignalBase /////////////////////////////////////////////////////////////////////////////////////////////////// template -class Signal : public REACT_IMPL::SignalBase +class VarSignalBase : public SignalBase { -private: - using NodeType = REACT_IMPL::SignalNode; - using NodePtrType = std::shared_ptr; - public: - using ValueType = T; + using SignalBase::SignalBase; - // Default ctor - Signal() = default; + VarSignalBase() = default; - // Copy ctor - Signal(const Signal&) = default; + VarSignalBase(const VarSignalBase&) = default; + VarSignalBase& operator=(const VarSignalBase&) = default; - // Move ctor - Signal(Signal&& other) = default; + VarSignalBase(VarSignalBase&&) = default; + VarSignalBase& operator=(VarSignalBase&&) = default; - // Node ctor - explicit Signal(NodePtrType&& nodePtr) : - Signal::SignalBase( std::move(nodePtr) ) - {} + void Set(const T& newValue) + { SetValue(newValue); } - // Copy assignment - Signal& operator=(const Signal&) = default; + void Set(T&& newValue) + { SetValue(std::move(newValue)); } - // Move assignment - Signal& operator=(Signal&& other) = default; + void operator<<=(const T& newValue) + { SetValue(newValue); } - const S& Get() const { return Signal::SignalBase::getValue(); } - const S& operator()() const { return Signal::SignalBase::getValue(); } + void operator<<=(T&& newValue) + { SetValue(std::move(newValue)); } - bool Equals(const Signal& other) const - { - return Signal::SignalBase::Equals(other); - } + template + void Modify(const F& func) + { ModifyValue(func); } - bool IsValid() const +protected: + template + auto CreateVarNode(U&& value, const TGroup& group) -> decltype(auto) { - return Signal::SignalBase::IsValid(); + using VarNodeType = REACT_IMPL::VarSignalNode; + return std::make_shared(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(value)); } - void SetWeightHint(WeightHint weight) +private: + template + void SetValue(U&& newValue) { - Signal::SignalBase::SetWeightHint(weight); - } + using REACT_IMPL::NodeId; + using REACT_IMPL::IReactiveGraph; + using VarNodeType = REACT_IMPL::VarSignalNode; - S Flatten() const - { - static_assert(IsSignal::value || IsEvent::value, - "Flatten requires a Signal or Events value type."); - return REACT::Flatten(*this); + VarNodeType* castedPtr = static_cast(this->NodePtr().get()); + + NodeId nodeId = castedPtr->GetNodeId(); + auto& graphPtr = NodePtr()->GraphPtr(); + graphPtr->AddInput(nodeId, [castedPtr, &newValue] { castedPtr->SetValue(std::forward(newValue)); }); } - template - < - typename ... Ts, - typename FIn, - typename F = typename std::decay::type - > - static auto Create(FIn&& func, const Signal& ... args) -> Signal + template + void ModifyValue(const F& func) { - return Signal( - std::make_shared>( - std::forward(func), GetNodePtr(args) ...)); + using REACT_IMPL::NodeId; + using REACT_IMPL::IReactiveGraph; + using VarNodeType = REACT_IMPL::VarSignalNode; + + VarNodeType* castedPtr = static_cast(this->NodePtr().get()); + + NodeId nodeId = castedPtr->GetNodeId(); + auto& graphPtr = castedPtr->GraphPtr(); + graphPtr->AddInput(nodeId, [castedPtr, &func] { castedPtr->ModifyValue(func); }); } }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// VarSignal +/// Signal /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class VarSignal : public Signal +template +class Signal : public SignalBase { -private: - using NodeT = REACT_IMPL::VarNode; - using NodePtrT = std::shared_ptr; +public: + using SignalBase::SignalBase; + + using ValueType = T; + + Signal() = delete; + + Signal(const Signal&) = delete; + Signal& operator=(const Signal&) = delete; + + Signal(Signal&&) = default; + Signal& operator=(Signal&&) = default; + + template + Signal(F&& func, const SignalBase& ... deps) : + SignalBase( CreateFuncNode(std::forward(func), deps ...) ) + { } +}; + +template +class Signal : public SignalBase +{ +public: + using SignalBase::SignalBase; + + using ValueType = T; + + Signal() = delete; + + Signal(const Signal&) = default; + Signal& operator=(const Signal&) = default; + + Signal(Signal&&) = default; + Signal& operator=(Signal&&) = default; + + Signal(Signal&& other) : + Signal::SignalBase( std::move(other) ) + { } + + Signal& operator=(Signal&& other) + { Signal::SignalBase::operator=(std::move(other)); return *this; } + + template + Signal(F&& func, const SignalBase& ... deps) : + Signal::SignalBase( CreateFuncNode(std::forward(func), deps ...) ) + { } +}; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Signal +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class VarSignal : public VarSignalBase +{ public: - // Default ctor - VarSignal() = default; + using VarSignalBase::VarSignalBase; - // Copy ctor - VarSignal(const VarSignal&) = default; + using ValueType = T; - // Move ctor - VarSignal(VarSignal&& other) : - VarSignal::Signal( std::move(other) ) - {} + VarSignal() = delete; - // Node ctor - explicit VarSignal(NodePtrT&& nodePtr) : - VarSignal::Signal( std::move(nodePtr) ) - {} + VarSignal(const VarSignal&) = delete; + VarSignal& operator=(const VarSignal&) = delete; - // Copy assignment - VarSignal& operator=(const VarSignal&) = default; + VarSignal(VarSignal&&) = default; + VarSignal& operator=(VarSignal&&) = default; - // Move assignment - VarSignal& operator=(VarSignal&& other) - { - VarSignal::SignalBase::operator=( std::move(other) ); - return *this; - } + template + VarSignal(U&& value, const TGroup& group) : + VarSignal::VarSignalBase( CreateVarNode(std::forward(value), group) ) + { } +}; - void Set(const S& newValue) const - { - VarSignal::SignalBase::setValue(newValue); - } +template +class VarSignal : public VarSignalBase +{ +public: + using VarSignalBase::VarSignalBase; - void Set(S&& newValue) const - { - VarSignal::SignalBase::setValue(std::move(newValue)); - } + using ValueType = T; - const VarSignal& operator<<=(const S& newValue) const - { - VarSignal::SignalBase::setValue(newValue); - return *this; - } + VarSignal() = delete; - const VarSignal& operator<<=(S&& newValue) const - { - VarSignal::SignalBase::setValue(std::move(newValue)); - return *this; - } + VarSignal(const VarSignal&) = default; + VarSignal& operator=(const VarSignal&) = default; - template - void Modify(const F& func) const - { - VarSignal::SignalBase::modifyValue(func); - } + VarSignal(VarSignal&&) = default; + VarSignal& operator=(VarSignal&&) = default; - /// Create - template - static auto Create(V&& value) -> VarSignal - { - return VarSignal( - std::make_shared>( - std::forward(value))); - } + VarSignal(VarSignal&& other) : + VarSignal::VarSignalBase( std::move(other) ) + { } + + VarSignal& operator=(VarSignal&& other) + { VarSignal::SignalBase::operator=(std::move(other)); return *this; } + + template + VarSignal(U&& value, const TGroup& group) : + VarSignal::VarSignalBase( CreateVarNode(std::forward(value), group) ) + { } }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Flatten +/////////////////////////////////////////////////////////////////////////////////////////////////// +/*template +auto Flatten(const SignalBase>& outer) -> Signal +{ + return Signal( + std::make_shared, TInner>>( + GetNodePtr(outer), GetNodePtr(outer.Value()))); +}*/ + /******************************************/ REACT_END /******************************************/ /***************************************/ REACT_IMPL_BEGIN /**************************************/ template -bool Equals(const Signal& lhs, const Signal& rhs) +bool Equals(const SignalBase& lhs, const SignalBase& rhs) { return lhs.Equals(rhs); } diff --git a/include/react/TypeTraits.h b/include/react/TypeTraits.h deleted file mode 100644 index 97c12654..00000000 --- a/include/react/TypeTraits.h +++ /dev/null @@ -1,92 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_TYPETRAITS_H_INCLUDED -#define REACT_TYPETRAITS_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Signal; - -template -class VarSignal; - -template -class Events; - -template -class EventSource; - -class Observer; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IsSignal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct IsSignal { static const bool value = false; }; - -template -struct IsSignal> { static const bool value = true; }; - -template -struct IsSignal> { static const bool value = true; }; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IsEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct IsEvent { static const bool value = false; }; - -template -struct IsEvent> { static const bool value = true; }; - -template -struct IsEvent> { static const bool value = true; }; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IsReactive -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct IsReactive { static const bool value = false; }; - -template -struct IsReactive> { static const bool value = true; }; - -template -struct IsReactive> { static const bool value = true; }; - -template -struct IsReactive> { static const bool value = true; }; - -template -struct IsReactive> { static const bool value = true; }; - -template <> -struct IsReactive { static const bool value = true; }; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DecayInput -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct DecayInput { using Type = T; }; - -template -struct DecayInput> { using Type = Signal; }; - -template -struct DecayInput> { using Type = Events; }; - -/******************************************/ REACT_END /******************************************/ - -#endif // REACT_TYPETRAITS_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/Containers.h b/include/react/common/Containers.h index 1839bd46..a03257db 100644 --- a/include/react/common/Containers.h +++ b/include/react/common/Containers.h @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -40,6 +41,153 @@ class EnumFlags FlagsT flags_ = 0; }; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// IndexMap +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class IndexMap +{ + static const size_t initial_capacity = 8; + static const size_t grow_factor = 2; + +public: + using ValueType = T; + + IndexMap() = default; + + IndexMap(const IndexMap&) = default; + IndexMap& operator=(const IndexMap&) = default; + + IndexMap(IndexMap&&) = default; + IndexMap& operator=(IndexMap&&) = default; + + T& operator[](size_t index) + { return data_[index]; } + + const T& operator[](size_t index) const + { return data_[index]; } + + size_t Insert(T value) + { + if (IsAtFullCapacity()) + { + Grow(); + return InsertAtBack(std::move(value)); + } + else if (HasFreeIndices()) + { + return InsertAtFreeIndex(std::move(value)); + } + else + { + return InsertAtBack(std::move(value)); + } + } + + void Remove(size_t index) + { + for (size_t index : freeIndices_) + data_[index].~T(); + --size_; + freeIndices_.push_back(index); + } + + void Clear() + { + // Sort free indexes so we can remove check for them in linear time + std::sort(freeIndices_.begin(), freeIndices_.end()); + + const size_t totalSize = size_ + freeIndices_.size(); + size_t i = 0; + + // Skip over free indices + for (auto freeIndex : freeIndices_) + { + for (; i < totalSize; ++i) + { + if (i == freeIndex) + break; + else + data_[i].~T(); + } + } + + // Rest + for (; i < totalSize; ++i) + data_[i].~T(); + + size_ = 0; + freeIndices_.clear(); + } + + void Reset() + { + Clear(); + + capacity_ = 0; + delete[] data_; + + freeIndices_.shrink_to_fit(); + } + + ~IndexMap() + { Reset(); } + +private: + bool IsAtFullCapacity() const + { return capacity_ == size_; } + + bool HasFreeIndices() const + { return !freeIndices_.empty(); } + + size_t CalcNextCapacity() const + { return capacity_ == 0? initial_capacity : capacity_ * grow_factor; } + + void Grow() + { + // Allocate new storage + size_t newCapacity = CalcNextCapacity(); + T* newData = new T[newCapacity]; + + // Move data to new storage + for (size_t i = 0; i < size_; ++i) + { + new (&newData[i]) T(std::move(data_[i])); + data_[i].~T(); + } + + delete[] data_; + + // Use new storage + data_ = newData; + capacity_ = newCapacity; + } + + size_t InsertAtBack(T&& value) + { + new (&data_[size_]) T(std::move(value)); + return size_++; + } + + size_t InsertAtFreeIndex(T&& value) + { + size_t nextFreeIndex = freeIndices_.back(); + freeIndices_.pop_back(); + + new (&data_[nextFreeIndex]) T(std::move(value)); + ++size_; + + return nextFreeIndex; + } + + T* data_ = nullptr; + size_t size_ = 0; + size_t capacity_ = 0; + + std::vector freeIndices_; +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// NodeVector /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/common/TopoQueue.h b/include/react/common/TopoQueue.h index b86885ff..091d116f 100644 --- a/include/react/common/TopoQueue.h +++ b/include/react/common/TopoQueue.h @@ -25,7 +25,16 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// TopoQueue - Sequential /////////////////////////////////////////////////////////////////////////////////////////////////// -template + +template +int GetNodeLevel(const T& node) + { return node.level; } + +template +int GetNodeLevel(const T* node) + { return node->level; } + +template class TopoQueue { private: @@ -39,14 +48,9 @@ class TopoQueue TopoQueue() = default; TopoQueue(const TopoQueue&) = default; - template - TopoQueue(FIn&& levelFunc) : - levelFunc_( std::forward(levelFunc) ) - {} - void Push(const T& value) { - queueData_.emplace_back(value, levelFunc_(value)); + queueData_.emplace_back(value, GetNodeLevel(value)); } bool FetchNext() @@ -64,7 +68,7 @@ class TopoQueue auto p = std::partition( queueData_.begin(), queueData_.end(), - LevelCompFunctor{ minLevel_ }); + [minLevel_] (const Entry& e) { return minLevel_ != e.level; }); // Reserve once to avoid multiple re-allocations nextData_.reserve(std::distance(p, queueData_.end())); diff --git a/include/react/common/Util.h b/include/react/common/Util.h index 19fe74f4..741cd61d 100644 --- a/include/react/common/Util.h +++ b/include/react/common/Util.h @@ -17,6 +17,12 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ +template +struct MakeVoid { using type = void;}; + +template +using VoidType = typename MakeVoid::type; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Unpack tuple - see /// http://stackoverflow.com/questions/687490/how-do-i-expand-a-tuple-into-variadic-template-functions-arguments @@ -25,8 +31,7 @@ template struct Apply { template - static inline auto apply(F && f, T && t, A &&... a) - -> decltype(Apply::apply(std::forward(f), std::forward(t), std::get(std::forward(t)), std::forward(a)...)) + static auto apply(F&& f, T&& t, A&&... a) -> decltype(auto) { return Apply::apply(std::forward(f), std::forward(t), std::get(std::forward(t)), std::forward(a)...); } @@ -36,26 +41,22 @@ template<> struct Apply<0> { template - static inline auto apply(F && f, T &&, A &&... a) - -> decltype(std::forward(f)(std::forward(a)...)) + static auto apply(F&& f, T&&, A&&... a) -> decltype(auto) { return std::forward(f)(std::forward(a)...); } }; template -inline auto apply(F && f, T && t) - -> decltype(Apply::type>::value>::apply(std::forward(f), std::forward(t))) +inline auto apply(F&& f, T&& t) -> decltype(auto) { - return Apply::type>::value> - ::apply(std::forward(f), std::forward(t)); + return Apply::type>::value>::apply(std::forward(f), std::forward(t)); } template struct ApplyMemberFn { template - static inline auto apply(O obj, F f, T && t, A &&... a) - -> decltype(ApplyMemberFn::apply(obj, f, std::forward(t), std::get(std::forward(t)), std::forward(a)...)) + static inline auto apply(O obj, F f, T && t, A &&... a) -> decltype(auto) { return ApplyMemberFn::apply(obj, f, std::forward(t), std::get(std::forward(t)), std::forward(a)...); } @@ -194,12 +195,7 @@ struct AddDefaultReturnValueWrapper /////////////////////////////////////////////////////////////////////////////////////////////////// /// IsCallableWith /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename F, - typename TRet, - typename ... TArgs -> +template class IsCallableWith { private: @@ -209,9 +205,7 @@ class IsCallableWith template < typename U, - class = decltype( - static_cast( - (std::declval())(std::declval() ...))) + typename = decltype(static_cast((std::declval())(std::declval() ...))) > static YesT& check(void*); @@ -219,7 +213,7 @@ class IsCallableWith static NoT& check(...); public: - enum { value = sizeof(check(nullptr)) == sizeof(YesT) }; + static const bool value = sizeof(check(nullptr)) == sizeof(YesT); }; /****************************************/ REACT_IMPL_END /***************************************/ @@ -228,4 +222,22 @@ class IsCallableWith // Use comma operator to replace potential void return value with 0 #define REACT_EXPAND_PACK(...) REACT_IMPL::pass((__VA_ARGS__ , 0) ...) +#define REACT_DEFINE_BITMASK_OPERATORS(t) \ + inline t operator|(t lhs, t rhs) \ + { return static_cast(static_cast::type>(lhs) | static_cast::type>(rhs)); } \ + inline t operator&(t lhs, t rhs) \ + { return static_cast(static_cast::type>(lhs) & static_cast::type>(rhs)); } \ + inline t operator^(t lhs, t rhs) \ + { return static_cast(static_cast::type>(lhs) ^ static_cast::type>(rhs)); } \ + inline t operator~(t rhs) \ + { return static_cast(~static_cast::type>(rhs)); } \ + inline t& operator|=(t& lhs, t rhs) \ + { lhs = static_cast(static_cast::type>(lhs) | static_cast::type>(rhs)); return lhs; } \ + inline t& operator&=(t& lhs, t rhs) \ + { lhs = static_cast(static_cast::type>(lhs) & static_cast::type>(rhs)); return lhs; } \ + inline t& operator^=(t& lhs, t rhs) \ + { lhs = static_cast(static_cast::type>(lhs) ^ static_cast::type>(rhs)); return lhs; } + +// Bitmask helper + #endif // REACT_COMMON_UTIL_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/Defs.h b/include/react/detail/Defs.h index 5dd2f91f..24f4cd97 100644 --- a/include/react/detail/Defs.h +++ b/include/react/detail/Defs.h @@ -21,23 +21,10 @@ #define REACT_IMPL_END REACT_END } #define REACT_IMPL REACT ::impl -#ifdef _DEBUG -#define REACT_MESSAGE(...) printf(__VA_ARGS__) -#else -#define REACT_MESSAGE -#endif - // Assert with message #define REACT_ASSERT(condition, ...) for (; !(condition); assert(condition)) printf(__VA_ARGS__) #define REACT_ERROR(...) REACT_ASSERT(false, __VA_ARGS__) -// Logging -#ifdef REACT_ENABLE_LOGGING - #define REACT_LOG(...) __VA_ARGS__ -#else - #define REACT_LOG(...) -#endif - // Thread local storage #if _WIN32 || _WIN64 // MSVC diff --git a/include/react/detail/DomainBase.h b/include/react/detail/DomainBase.h deleted file mode 100644 index 7327874b..00000000 --- a/include/react/detail/DomainBase.h +++ /dev/null @@ -1,265 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_DOMAINBASE_H_INCLUDED -#define REACT_DETAIL_DOMAINBASE_H_INCLUDED - -#pragma once - -#include -#include - -#include "react/detail/Defs.h" - -#include "react/detail/ReactiveBase.h" -#include "react/detail/IReactiveEngine.h" -#include "react/detail/graph/ContinuationNodes.h" - -// Include all engines for convenience -#include "react/engine/PulsecountEngine.h" -#include "react/engine/SubtreeEngine.h" -#include "react/engine/ToposortEngine.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Signal; - -template -class VarSignal; - -template -class Events; - -template -class EventSource; - -enum class Token; - -template -class Observer; - -template -class ScopedObserver; - -template -class Reactor; - -/******************************************/ REACT_END /******************************************/ - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Common types & constants -/////////////////////////////////////////////////////////////////////////////////////////////////// - -// Domain modes -enum EDomainMode -{ - sequential, - sequential_concurrent, - parallel, - parallel_concurrent -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DomainBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class DomainBase -{ -public: - using TurnT = typename TPolicy::Engine::TurnT; - - DomainBase() = delete; - - using Policy = TPolicy; - using Engine = REACT_IMPL::EngineInterface; - - /////////////////////////////////////////////////////////////////////////////////////////////// - /// Domain traits - /////////////////////////////////////////////////////////////////////////////////////////////// - static const bool uses_node_update_timer = - REACT_IMPL::NodeUpdateTimerEnabled::value; - - static const bool is_concurrent = - Policy::input_mode == REACT_IMPL::concurrent_input; - - static const bool is_parallel = - Policy::propagation_mode == REACT_IMPL::parallel_propagation; - - /////////////////////////////////////////////////////////////////////////////////////////////// - /// Aliases for reactives of this domain - /////////////////////////////////////////////////////////////////////////////////////////////// - template - using SignalT = Signal; - - template - using VarSignalT = VarSignal; - - template - using EventsT = Events; - - template - using EventSourceT = EventSource; - - using ObserverT = Observer; - - using ScopedObserverT = ScopedObserver; - - using ReactorT = Reactor; - -#ifdef REACT_ENABLE_LOGGING - /////////////////////////////////////////////////////////////////////////////////////////////// - /// Log - /////////////////////////////////////////////////////////////////////////////////////////////// - static EventLog& Log() - { - static EventLog instance; - return instance; - } -#endif //REACT_ENABLE_LOGGING -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ContinuationBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename D2 -> -class ContinuationBase : public MovableReactive> -{ -public: - ContinuationBase() = default; - - template - ContinuationBase(T&& t) : - ContinuationBase::MovableReactive( std::forward(t) ) - {} -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ModeSelector - Translate domain mode to individual propagation and input modes -/////////////////////////////////////////////////////////////////////////////////////////////////// - -template -struct ModeSelector; - -template <> -struct ModeSelector -{ - static const EInputMode input = consecutive_input; - static const EPropagationMode propagation = sequential_propagation; -}; - -template <> -struct ModeSelector -{ - static const EInputMode input = concurrent_input; - static const EPropagationMode propagation = sequential_propagation; -}; - -template <> -struct ModeSelector -{ - static const EInputMode input = consecutive_input; - static const EPropagationMode propagation = parallel_propagation; -}; - -template <> -struct ModeSelector -{ - static const EInputMode input = concurrent_input; - static const EPropagationMode propagation = parallel_propagation; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// GetDefaultEngine - Get default engine type for given propagation mode -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct GetDefaultEngine; - -template <> -struct GetDefaultEngine -{ - using Type = ToposortEngine; -}; - -template <> -struct GetDefaultEngine -{ - using Type = SubtreeEngine; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EngineTypeBuilder - Instantiate the given template engine type with mode. -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct DefaultEnginePlaceholder; - -// Concrete engine template type -template -< - EPropagationMode mode, - template class TTEngine -> -struct EngineTypeBuilder -{ - using Type = TTEngine; -}; - -// Placeholder engine type - use default engine for given mode -template -< - EPropagationMode mode -> -struct EngineTypeBuilder -{ - using Type = typename GetDefaultEngine::Type; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DomainPolicy -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - EDomainMode mode, - template class TTEngine = DefaultEnginePlaceholder -> -struct DomainPolicy -{ - static const EInputMode input_mode = ModeSelector::input; - static const EPropagationMode propagation_mode = ModeSelector::propagation; - - using Engine = typename EngineTypeBuilder::Type; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Ensure singletons are created immediately after domain declaration (TODO hax) -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class DomainInitializer -{ -public: - DomainInitializer() - { -#ifdef REACT_ENABLE_LOGGING - D::Log(); -#endif //REACT_ENABLE_LOGGING - - D::Engine::Instance(); - DomainSpecificInputManager::Instance(); - } -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_DETAIL_DOMAINBASE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/EventBase.h b/include/react/detail/EventBase.h deleted file mode 100644 index bf518860..00000000 --- a/include/react/detail/EventBase.h +++ /dev/null @@ -1,49 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_EVENTBASE_H_INCLUDED -#define REACT_DETAIL_EVENTBASE_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include - -#include "react/detail/ReactiveBase.h" -#include "react/detail/ReactiveInput.h" -#include "react/detail/graph/EventNodes.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventStreamBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EventStreamBase : public ReactiveBase> -{ -public: - EventStreamBase() = default; - EventStreamBase(const EventStreamBase&) = default; - - template - EventStreamBase(T&& t) : - EventStreamBase::CopyableReactive( std::forward(t) ) - {} - -protected: - template - void emit(T&& e) const - { - DomainSpecificInputManager::Instance().AddInput( - *reinterpret_cast*>(this->ptr_.get()), - std::forward(e)); - } -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_DETAIL_EVENTBASE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/IReactiveGraph.h b/include/react/detail/IReactiveGraph.h new file mode 100644 index 00000000..ddb29689 --- /dev/null +++ b/include/react/detail/IReactiveGraph.h @@ -0,0 +1,100 @@ + +// Copyright Sebastian Jeckel 2016. +// 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_IREACTIVEENGINE_H_INCLUDED +#define REACT_DETAIL_IREACTIVEENGINE_H_INCLUDED + +#pragma once + +#include "react/detail/Defs.h" + +#include +#include +#include + +#include "react/API.h" +#include "react/common/Types.h" +#include "react/common/Util.h" + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Definitions +/////////////////////////////////////////////////////////////////////////////////////////////////// +using NodeId = size_t; +using TurnId = size_t; + +static NodeId invalid_node_id = (std::numeric_limits::max)(); +static TurnId invalid_turn_id = (std::numeric_limits::max)(); + +enum class UpdateResult +{ + unchanged, + changed, + shifted +}; + +struct IReactiveGraph; +struct IReactiveNode; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// IReactiveGraph +/////////////////////////////////////////////////////////////////////////////////////////////////// +struct IReactiveGraph +{ + virtual ~IReactiveGraph() = default; + + virtual NodeId RegisterNode(IReactiveNode* nodePtr) = 0; + virtual void UnregisterNode(NodeId node) = 0; + + virtual void OnNodeAttach(NodeId nodeId, NodeId parentId) = 0; + virtual void OnNodeDetach(NodeId nodeId, NodeId parentId) = 0; + + virtual void OnDynamicNodeAttach(NodeId nodeId, NodeId parentId, TurnId turn) = 0; + virtual void OnDynamicNodeDetach(NodeId nodeId, NodeId parentId, TurnId turn) = 0; + + virtual void AddInput(NodeId nodeId, std::function inputCallback) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// IReactiveNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +struct IReactiveNode +{ + virtual ~IReactiveNode() = default; + + /// Returns unique type identifier + virtual const char* GetNodeType() const = 0; + + // Note: Could get rid of this ugly ptr by adding a template parameter to the interface + // But that would mean all engine nodes need that template parameter too - so rather cast + virtual UpdateResult Update(TurnId turnId) = 0; + + /// Input nodes can be manipulated externally. + virtual bool IsInputNode() const = 0; + + /// Output nodes can't have any successors. + virtual bool IsOutputNode() const = 0; + + /// Dynamic nodes may change in topology as a result of tick. + virtual bool IsDynamicNode() const = 0; + + // Number of predecessors. + virtual int GetDependencyCount() const = 0; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EPropagationMode +/////////////////////////////////////////////////////////////////////////////////////////////////// +enum EPropagationMode +{ + sequential_propagation, + parallel_propagation +}; + +/****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_IREACTIVEENGINE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/IReactiveGroup.h b/include/react/detail/IReactiveGroup.h deleted file mode 100644 index b86a389c..00000000 --- a/include/react/detail/IReactiveGroup.h +++ /dev/null @@ -1,64 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_IREACTIVEENGINE_H_INCLUDED -#define REACT_DETAIL_IREACTIVEENGINE_H_INCLUDED - -#pragma once - -#include -#include - -#include "react/detail/Defs.h" -#include "react/common/Types.h" -#include "react/logging/EventRecords.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -using NodeId = uint; -using TurnId = uint; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IReactiveEngine -/////////////////////////////////////////////////////////////////////////////////////////////////// -struct IReactiveGroup -{ - virtual ~IReactiveGroup() = 0; - - virtual void OnTurnAdmissionStart(TurnId turn) = 0; - virtual void OnTurnAdmissionEnd(TurnId turn) = 0; - - virtual void OnInputChange(NodeId node, TurnId turn) = 0; - - virtual void Propagate(TurnId turn) = 0; - - virtual NodeId OnNodeCreate() = 0; - virtual void OnNodeDestroy(NodeId node) = 0; - - virtual void OnNodeAttach(NodeId node, NodeId parent) = 0; - virtual void OnNodeDetach(NodeId node, NodeId parent) = 0; - - virtual void OnDynamicNodeAttach(NodeId node, NodeId parent, TurnId turn) = 0; - virtual void OnDynamicNodeDetach(NodeId node, NodeId parent, TurnId turn) = 0; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Engine traits -/////////////////////////////////////////////////////////////////////////////////////////////////// -template struct NodeUpdateTimerEnabled : std::false_type {}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EPropagationMode -/////////////////////////////////////////////////////////////////////////////////////////////////// -enum EPropagationMode -{ - sequential_propagation, - parallel_propagation -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_DETAIL_IREACTIVEENGINE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/IReactiveNode.h b/include/react/detail/IReactiveNode.h deleted file mode 100644 index 68fe08be..00000000 --- a/include/react/detail/IReactiveNode.h +++ /dev/null @@ -1,67 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_IREACTIVENODE_H_INCLUDED -#define REACT_DETAIL_IREACTIVENODE_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -enum class UpdateResult -{ - unchanged, - changed, - shifted -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IReactiveNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -struct IReactiveNode -{ - virtual ~IReactiveNode() = default; - - /// Returns unique type identifier - virtual const char* GetNodeType() const = 0; - - // Note: Could get rid of this ugly ptr by adding a template parameter to the interface - // But that would mean all engine nodes need that template parameter too - so rather cast - virtual UpdateResult Update() = 0; - - /// Input nodes can be manipulated externally. - virtual bool IsInputNode() const = 0; - - /// Output nodes can't have any successors. - virtual bool IsOutputNode() const = 0; - - /// Dynamic nodes may change in topology as a result of tick. - virtual bool IsDynamicNode() const = 0; - - // Number of predecessors. - // This information is statically available at compile time on the graph layer, - // so the engine does not have to calculate it again. - virtual int DependencyCount() const = 0; - - // Heavyweight nodes are worth parallelizing. - virtual bool IsHeavyweight() const = 0; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IInputNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -struct IInputNode -{ - virtual ~IInputNode() = default; - - virtual bool ApplyInput(void* turnPtr) = 0; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif //REACT_DETAIL_IREACTIVENODE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/ReactiveBase.h b/include/react/detail/ReactiveBase.h deleted file mode 100644 index 12dafd82..00000000 --- a/include/react/detail/ReactiveBase.h +++ /dev/null @@ -1,158 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_REACTIVEBASE_H_INCLUDED -#define REACT_DETAIL_REACTIVEBASE_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include - -#include "react/detail/graph/GraphBase.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -template -bool Equals(const L& lhs, const R& rhs) -{ - return lhs == rhs; -} - -template -bool Equals(const std::reference_wrapper& lhs, const std::reference_wrapper& rhs) -{ - return lhs.get() == rhs.get(); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ReactiveBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ReactiveBase -{ -public: - using DomainT = typename TNode::DomainT; - - // Default ctor - ReactiveBase() = default; - - // Copy ctor - ReactiveBase(const ReactiveBase&) = default; - - // Move ctor (VS2013 doesn't default generate that yet) - ReactiveBase(ReactiveBase&& other) : - ptr_( std::move(other.ptr_) ) - {} - - // Explicit node ctor - explicit ReactiveBase(std::shared_ptr&& ptr) : - ptr_( std::move(ptr) ) - {} - - // Copy assignment - ReactiveBase& operator=(const ReactiveBase&) = default; - - // Move assignment - ReactiveBase& operator=(ReactiveBase&& other) - { - ptr_.reset(std::move(other)); - return *this; - } - - bool IsValid() const - { - return ptr_ != nullptr; - } - - void SetWeightHint(WeightHint weight) - { - assert(IsValid()); - ptr_->SetWeightHint(weight); - } - -protected: - std::shared_ptr ptr_; - - template - friend const std::shared_ptr& GetNodePtr(const ReactiveBase& node); -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// CopyableReactive -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class CopyableReactive : public ReactiveBase -{ -public: - CopyableReactive() = default; - - CopyableReactive(const CopyableReactive&) = default; - - CopyableReactive(CopyableReactive&& other) : - CopyableReactive::ReactiveBase( std::move(other) ) - {} - - explicit CopyableReactive(std::shared_ptr&& ptr) : - CopyableReactive::ReactiveBase( std::move(ptr) ) - {} - - CopyableReactive& operator=(const CopyableReactive&) = default; - - CopyableReactive& operator=(CopyableReactive&& other) - { - CopyableReactive::ReactiveBase::operator=(std::move(other)); - return *this; - } - - bool Equals(const CopyableReactive& other) const - { - return this->ptr_ == other.ptr_; - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// UniqueReactiveBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class MovableReactive : public ReactiveBase -{ -public: - MovableReactive() = default; - - MovableReactive(MovableReactive&& other) : - MovableReactive::ReactiveBase( std::move(other) ) - {} - - explicit MovableReactive(std::shared_ptr&& ptr) : - MovableReactive::ReactiveBase( std::move(ptr) ) - {} - - MovableReactive& operator=(MovableReactive&& other) - { - MovableReactive::ReactiveBase::operator=(std::move(other)); - return *this; - } - - // Deleted copy ctor and assignment - MovableReactive(const MovableReactive&) = delete; - MovableReactive& operator=(const MovableReactive&) = delete; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// GetNodePtr -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -const std::shared_ptr& GetNodePtr(const ReactiveBase& node) -{ - return node.ptr_; -} - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_DETAIL_REACTIVEBASE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index ffcbcd6b..dd2d101c 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -4,6 +4,8 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#if 0 + #ifndef REACT_DETAIL_REACTIVEINPUT_H_INCLUDED #define REACT_DETAIL_REACTIVEINPUT_H_INCLUDED @@ -36,17 +38,11 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// -struct IInputNode; class IObserver; -template -class InputManager; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Common types & constants /////////////////////////////////////////////////////////////////////////////////////////////////// -using TurnIdT = uint; -using TransactionFuncT = std::function; enum ETransactionFlags { @@ -985,4 +981,6 @@ class DomainSpecificInputManager /****************************************/ REACT_IMPL_END /***************************************/ -#endif // REACT_DETAIL_REACTIVEINPUT_H_INCLUDED \ No newline at end of file +#endif // REACT_DETAIL_REACTIVEINPUT_H_INCLUDED + +#endif \ No newline at end of file diff --git a/include/react/detail/SignalBase.h b/include/react/detail/SignalBase.h deleted file mode 100644 index 9b497136..00000000 --- a/include/react/detail/SignalBase.h +++ /dev/null @@ -1,65 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_SIGNALBASE_H_INCLUDED -#define REACT_DETAIL_SIGNALBASE_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include - -#include "react/detail/ReactiveBase.h" -#include "react/detail/ReactiveInput.h" -#include "react/detail/graph/SignalNodes.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -class SignalBase : public CopyableReactive> -{ -public: - SignalBase() = default; - SignalBase(const SignalBase&) = default; - - template - SignalBase(T&& t) : - SignalBase::CopyableReactive( std::forward(t) ) - {} - -protected: - const S& getValue() const - { - return this->ptr_->ValueRef(); - } - - template - void setValue(T&& newValue) const - { - DomainSpecificInputManager::Instance().AddInput( - *reinterpret_cast*>(this->ptr_.get()), - std::forward(newValue)); - } - - template - void modifyValue(const F& func) const - { - DomainSpecificInputManager::Instance().ModifyInput( - *reinterpret_cast*>(this->ptr_.get()), func); - } -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_DETAIL_SIGNALBASE_H_INCLUDED diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index eb158c4b..4c4fdc35 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -81,578 +81,419 @@ struct AddIterateByRefRangeWrapper /////////////////////////////////////////////////////////////////////////////////////////////////// /// IterateNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename E, - typename TFunc -> -class IterateNode : - public SignalNode +template +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) ), + template + IterateNode(const std::shared_ptr& graphPtr, U&& init, const std::shared_ptr>& events, V&& func) : + IterateNode::SignalNode( graphPtr, std::forward(init) ), events_( events ), - func_( std::forward(func) ) + func_( std::forward(func) ) { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *events); + this->RegisterMe(); + this->AttachToMe(events->GetNodeId()); } ~IterateNode() { - Engine::OnNodeDetach(*this, *events_); - Engine::OnNodeDestroy(*this); + this->DetachFromMe(events_->GetNodeId()); + this->UnregisterMe(); } - virtual void Update(void* turnPtr) override + virtual UpdateResult Update(TurnId turnId) 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())); + S newValue = func_(EventRange( events_->Events() ), this->Value()); - if (changed) - Engine::OnNodePulse(*this, turn); + if (! Equals(newValue, this->Value())) + { + this->Value() = std::move(newValue); + return UpdateResult::changed; + } else - Engine::OnNodeIdlePulse(*this, turn); + { + return UpdateResult::unchanged; + } } - virtual const char* GetNodeType() const override { return "IterateNode"; } - virtual int DependencyCount() const override { return 1; } + virtual const char* GetNodeType() const override + { return "Iterate"; } + + virtual int GetDependencyCount() const override + { return 1; } private: - std::shared_ptr> events_; + std::shared_ptr> events_; - TFunc func_; + F func_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// IterateByRefNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename E, - typename TFunc -> -class IterateByRefNode : - public SignalNode +template +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) ), + template + IterateByRefNode(const std::shared_ptr& graphPtr, SIn&& init, const std::shared_ptr>& events, FIn&& func) : + IterateByRefNode::SignalNode( graphPtr, std::forward(init) ), + func_( std::forward(func) ), events_( events ) { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *events); + this->RegisterMe(); + this->AttachToMe(events->GetNodeId()); } ~IterateByRefNode() { - Engine::OnNodeDetach(*this, *events_); - Engine::OnNodeDestroy(*this); + this->DetachFromMe(events_->GetNodeId()); + this->UnregisterMe(); } - virtual void Update(void* turnPtr) override + virtual UpdateResult Update(TurnId turnId) 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())); + func_(EventRange( events_->Events() ), this->Value()); // Always assume change - Engine::OnNodePulse(*this, turn); + return UpdateResult::changed; } - virtual const char* GetNodeType() const override { return "IterateByRefNode"; } - virtual int DependencyCount() const override { return 1; } + virtual const char* GetNodeType() const override + { return "IterateByRefNode"; } + + virtual int GetDependencyCount() const override + { return 1; } protected: - TFunc func_; + F func_; - std::shared_ptr> events_; + std::shared_ptr> events_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// SyncedIterateNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename E, - typename TFunc, - typename ... TDepValues -> -class SyncedIterateNode : - public SignalNode +template +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) ), + template + SyncedIterateNode(const std::shared_ptr& graphPtr, SIn&& init, const std::shared_ptr>& events, FIn&& func, const std::shared_ptr>& ... syncs) : + SyncedIterateNode::SignalNode( graphPtr, std::forward(init) ), events_( events ), - func_( std::forward(func) ), - deps_( deps ... ) + func_( std::forward(func) ), + syncHolder_( syncs ... ) { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *events); - REACT_EXPAND_PACK(Engine::OnNodeAttach(*this, *deps)); + this->RegisterMe(); + this->AttachToMe(dep->GetNodeId()); + REACT_EXPAND_PACK(this->AttachToMe(syncs->GetNodeId())); } ~SyncedIterateNode() { - Engine::OnNodeDetach(*this, *events_); - - apply( - DetachFunctor>...>( *this ), - deps_); - - Engine::OnNodeDestroy(*this); + apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(syncs->GetNodeId())); }, syncHolder_); + this->DetachFromMe(dep_->GetNodeId()); + this->UnregisterMe(); } - virtual void Update(void* turnPtr) override + virtual UpdateResult Update(TurnId turnId) 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_)) + S newValue = apply( + [this] (const auto& ... syncs) { - changed = true; - this->value_ = std::move(newValue); - } - }// ~timer - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); + return func_(EventRange( events_->Events() ), this->Value(), syncs->Value() ...); + }, + syncHolder_); - if (changed) - Engine::OnNodePulse(*this, turn); + if (! Equals(newValue, this->Value())) + { + this->Value() = std::move(newValue); + return UpdateResult::changed; + } else - Engine::OnNodeIdlePulse(*this, turn); + { + return UpdateResult::unchanged; + } } - virtual const char* GetNodeType() const override { return "SyncedIterateNode"; } - virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } + virtual const char* GetNodeType() const override + { return "SyncedIterate"; } -private: - using DepHolderT = std::tuple>...>; + virtual int GetDependencyCount() const override + { return 1 + sizeof...(TSyncs); } - std::shared_ptr> events_; +private: + std::shared_ptr> events_; - TFunc func_; - DepHolderT deps_; + F func_; + + std::tuple>...> syncHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// SyncedIterateByRefNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename E, - typename TFunc, - typename ... TDepValues -> -class SyncedIterateByRefNode : - public SignalNode +template +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) ), + template + SyncedIterateByRefNode(const std::shared_ptr& graphPtr, SIn&& init, const std::shared_ptr>& events, FIn&& func, const std::shared_ptr>& ... syncs) : + SyncedIterateByRefNode::SignalNode( graphPtr, std::forward(init) ), events_( events ), - func_( std::forward(func) ), - deps_( deps ... ) + func_( std::forward(func) ), + syncHolder_( syncs ... ) { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *events); - REACT_EXPAND_PACK(Engine::OnNodeAttach(*this, *deps)); + this->RegisterMe(); + this->AttachToMe(dep->GetNodeId()); + REACT_EXPAND_PACK(this->AttachToMe(syncs->GetNodeId())); } ~SyncedIterateByRefNode() { - Engine::OnNodeDetach(*this, *events_); - - apply( - DetachFunctor>...>( *this ), - deps_); - - Engine::OnNodeDestroy(*this); + apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(syncs->GetNodeId())); }, syncHolder_); + this->DetachFromMe(dep_->GetNodeId()); + this->UnregisterMe(); } - virtual void Update(void* turnPtr) override + virtual UpdateResult Update(TurnId turnId) 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) + [this] (const auto& ... args) { - func_(EventRange( events_->Events() ), this->value_, args->ValueRef() ...); + func_(EventRange( events_->Events() ), this->Value(), args->Value() ...); }, deps_); - changed = true; - }// ~timer - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - if (changed) - Engine::OnNodePulse(*this, turn); + return UpdateResult::changed; + } else - Engine::OnNodeIdlePulse(*this, turn); + { + return UpdateResult::unchanged; + } } - virtual const char* GetNodeType() const override { return "SyncedIterateByRefNode"; } - virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } + virtual const char* GetNodeType() const override + { return "SyncedIterateByRef"; } -private: - using DepHolderT = std::tuple>...>; + virtual int GetDependencyCount() const override + { return 1 + sizeof...(TSyncs); } - std::shared_ptr> events_; +private: + std::shared_ptr> events_; - TFunc func_; - DepHolderT deps_; + F func_; + + std::tuple>...> syncHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// HoldNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S -> -class HoldNode : public SignalNode +template +class HoldNode : public SignalNode { - using Engine = typename HoldNode::Engine; - public: - template - HoldNode(T&& init, const std::shared_ptr>& events) : - HoldNode::SignalNode( std::forward(init) ), + template + HoldNode(const std::shared_ptr& graphPtr, TIn&& init, const std::shared_ptr>& events) : + HoldNode::SignalNode( graphPtr, std::forward(init) ), events_( events ) { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *events_); + this->RegisterMe(); + this->AttachToMe(events->GetNodeId()); } ~HoldNode() { - Engine::OnNodeDetach(*this, *events_); - Engine::OnNodeDestroy(*this); + this->DetachFromMe(events_->GetNodeId()); + this->UnregisterMe(); } - virtual const char* GetNodeType() const override { return "HoldNode"; } + virtual const char* GetNodeType() const override + { return "HoldNode"; } - virtual void Update(void* turnPtr) override + virtual UpdateResult Update(TurnId turnId) 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_)) + if (! Equals(newValue, this->Value())) { changed = true; - this->value_ = newValue; + this->Value() = newValue; } } - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - if (changed) - Engine::OnNodePulse(*this, turn); + return UpdateResult::changed; else - Engine::OnNodeIdlePulse(*this, turn); + return UpdateResult::unchanged; } - virtual int DependencyCount() const override { return 1; } + virtual int GetDependencyCount() const override + { return 1; } private: - const std::shared_ptr> events_; + const std::shared_ptr> events_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// SnapshotNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename E -> -class SnapshotNode : public SignalNode +template +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() ), + SnapshotNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& target, const std::shared_ptr>& trigger) : + SnapshotNode::SignalNode( graphPtr, target->Value() ), target_( target ), trigger_( trigger ) { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *target_); - Engine::OnNodeAttach(*this, *trigger_); + this->RegisterMe(); + this->AttachToMe(target->GetNodeId()); + this->AttachToMe(trigger->GetNodeId()); } ~SnapshotNode() { - Engine::OnNodeDetach(*this, *target_); - Engine::OnNodeDetach(*this, *trigger_); - Engine::OnNodeDestroy(*this); + this->DetachFromMe(trigger_->GetNodeId()); + this->DetachFromMe(target_->GetNodeId()); + this->UnregisterMe(); } - virtual void Update(void* turnPtr) override + virtual UpdateResult Update(TurnId turnId) 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())); + trigger_->SetCurrentTurn(turnId); bool changed = false; if (! trigger_->Events().empty()) { - const S& newValue = target_->ValueRef(); + const S& newValue = target_->Value(); - if (! Equals(newValue, this->value_)) + if (! Equals(newValue, this->Value())) { changed = true; - this->value_ = newValue; + this->Value() = newValue; } } - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - if (changed) - Engine::OnNodePulse(*this, turn); + return UpdateResult::changed; else - Engine::OnNodeIdlePulse(*this, turn); + return UpdateResult::unchanged; } - virtual const char* GetNodeType() const override { return "SnapshotNode"; } - virtual int DependencyCount() const override { return 2; } + virtual const char* GetNodeType() const override + { return "Snapshot"; } + + virtual int GetDependencyCount() const override + { return 2; } private: - const std::shared_ptr> target_; - const std::shared_ptr> trigger_; + std::shared_ptr> target_; + std::shared_ptr> trigger_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// MonitorNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E -> -class MonitorNode : public EventStreamNode +template +class MonitorNode : public EventStreamNode { - using Engine = typename MonitorNode::Engine; - public: - MonitorNode(const std::shared_ptr>& target) : - MonitorNode::EventStreamNode( ), + MonitorNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& target) : + MonitorNode::EventStreamNode( graphPtr ), target_( target ) { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *target_); + this->RegisterMe(); + this->AttachToMe(target->GetNodeId()); } ~MonitorNode() { - Engine::OnNodeDetach(*this, *target_); - Engine::OnNodeDestroy(*this); + this->DetachFromMe(target_->GetNodeId()); + this->UnregisterMe(); } - virtual void Update(void* turnPtr) override + virtual UpdateResult Update(TurnId turnId) 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())); + this->Events().push_back(target_->Value()); - if (! this->events_.empty()) - Engine::OnNodePulse(*this, turn); - else - Engine::OnNodeIdlePulse(*this, turn); + return UpdateResult::changed; } - virtual const char* GetNodeType() const override { return "MonitorNode"; } - virtual int DependencyCount() const override { return 1; } + virtual const char* GetNodeType() const override + { return "Monitor"; } + + virtual int GetDependencyCount() const override + { return 1; } private: - const std::shared_ptr> target_; + std::shared_ptr> target_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// PulseNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename E -> -class PulseNode : public EventStreamNode +template +class PulseNode : public EventStreamNode { - using Engine = typename PulseNode::Engine; - public: - PulseNode(const std::shared_ptr>& target, - const std::shared_ptr>& trigger) : - PulseNode::EventStreamNode( ), + PulseNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& target, const std::shared_ptr>& trigger) : + PulseNode::EventStreamNode( graphPtr ), target_( target ), trigger_( trigger ) { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *target_); - Engine::OnNodeAttach(*this, *trigger_); + this->RegisterMe(); + this->AttachToMe(target->GetNodeId()); + this->AttachToMe(trigger->GetNodeId()); } ~PulseNode() { - Engine::OnNodeDetach(*this, *target_); - Engine::OnNodeDetach(*this, *trigger_); - Engine::OnNodeDestroy(*this); + this->DetachFromMe(trigger_->GetNodeId()); + this->DetachFromMe(target_->GetNodeId()); + this->UnregisterMe(); } - virtual void Update(void* turnPtr) override + virtual UpdateResult Update(TurnId turnId) 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()); + for (size_t i=0; iEvents().size(); i++) + this->Events().push_back(target_->Value()); - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - if (! this->events_.empty()) - Engine::OnNodePulse(*this, turn); + if (! this->Events().empty()) + return UpdateResult::changed; else - Engine::OnNodeIdlePulse(*this, turn); + return UpdateResult::unchanged; } - virtual const char* GetNodeType() const override { return "PulseNode"; } - virtual int DependencyCount() const override { return 2; } + virtual const char* GetNodeType() const override + { return "PulseNode"; } + + virtual int GetDependencyCount() const override + { return 2; } private: - const std::shared_ptr> target_; - const std::shared_ptr> trigger_; + const std::shared_ptr> target_; + const std::shared_ptr> trigger_; }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/graph/ContinuationNodes.h b/include/react/detail/graph/ContinuationNodes.h deleted file mode 100644 index 6d494453..00000000 --- a/include/react/detail/graph/ContinuationNodes.h +++ /dev/null @@ -1,351 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_CONTINUATIONNODES_H_INCLUDED -#define REACT_DETAIL_GRAPH_CONTINUATIONNODES_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include -#include - -#include "GraphBase.h" - -#include "react/detail/ReactiveInput.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class SignalNode; - -template -class EventStreamNode; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// AddContinuationRangeWrapper -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct AddContinuationRangeWrapper -{ - AddContinuationRangeWrapper(const AddContinuationRangeWrapper& other) = default; - - AddContinuationRangeWrapper(AddContinuationRangeWrapper&& other) : - MyFunc( std::move(other.MyFunc) ) - {} - - template - < - typename FIn, - class = typename DisableIfSame::type - > - explicit AddContinuationRangeWrapper(FIn&& func) : - MyFunc( std::forward(func) ) - {} - - void operator()(EventRange range, const TArgs& ... args) - { - for (const auto& e : range) - MyFunc(e, args ...); - } - - F MyFunc; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ContinuationNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ContinuationNode : public NodeBase -{ -public: - ContinuationNode(TransactionFlagsT turnFlags) : - turnFlags_( turnFlags ) - {} - - virtual bool IsOutputNode() const { return true; } - -protected: - TransactionFlagsT turnFlags_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalContinuationNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename DOut, - typename S, - typename TFunc -> -class SignalContinuationNode : public ContinuationNode -{ - using Engine = typename SignalContinuationNode::Engine; - -public: - template - SignalContinuationNode(TransactionFlagsT turnFlags, - const std::shared_ptr>& trigger, F&& func) : - SignalContinuationNode::ContinuationNode( turnFlags ), - trigger_( trigger ), - func_( std::forward(func) ) - { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *trigger); - } - - ~SignalContinuationNode() - { - Engine::OnNodeDestroy(*this); - } - - virtual const char* GetNodeType() const override { return "SignalContinuationNode"; } - virtual int DependencyCount() const override { return 1; } - - virtual void Update(void* turnPtr) override - { -#ifdef REACT_ENABLE_LOGGING - using TurnT = typename D::Engine::TurnT; - TurnT& turn = *reinterpret_cast(turnPtr); -#endif - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - auto& storedValue = trigger_->ValueRef(); - auto& storedFunc = func_; - - TransactionFuncT cont - ( - // Copy value and func - [storedFunc,storedValue] () mutable - { - storedFunc(storedValue); - } - ); - - DomainSpecificInputManager::Instance() - .StoreContinuation( - DomainSpecificInputManager::Instance(), - this->turnFlags_, - std::move(cont)); - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - } - -private: - std::shared_ptr> trigger_; - - TFunc func_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventContinuationNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename DOut, - typename E, - typename TFunc -> -class EventContinuationNode : public ContinuationNode -{ - using Engine = typename EventContinuationNode::Engine; - -public: - template - EventContinuationNode(TransactionFlagsT turnFlags, - const std::shared_ptr>& trigger, F&& func) : - EventContinuationNode::ContinuationNode( turnFlags ), - trigger_( trigger ), - func_( std::forward(func) ) - { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *trigger); - } - - ~EventContinuationNode() - { - Engine::OnNodeDestroy(*this); - } - - virtual const char* GetNodeType() const override { return "EventContinuationNode"; } - virtual int DependencyCount() const override { return 1; } - - virtual void Update(void* turnPtr) override - { -#ifdef REACT_ENABLE_LOGGING - using TurnT = typename D::Engine::TurnT; - TurnT& turn = *reinterpret_cast(turnPtr); -#endif - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - auto& storedEvents = trigger_->Events(); - auto& storedFunc = func_; - - TransactionFuncT cont - ( - // Copy events and func - [storedFunc,storedEvents] () mutable - { - storedFunc(EventRange( storedEvents )); - } - ); - - DomainSpecificInputManager::Instance() - .StoreContinuation( - DomainSpecificInputManager::Instance(), - this->turnFlags_, - std::move(cont)); - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - } - -private: - std::shared_ptr> trigger_; - - TFunc func_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SyncedContinuationNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename DOut, - typename E, - typename TFunc, - typename ... TDepValues -> -class SyncedContinuationNode : public ContinuationNode -{ - using Engine = typename SyncedContinuationNode::Engine; - - using ValueTupleT = std::tuple; - - struct TupleBuilder_ - { - ValueTupleT operator()(const std::shared_ptr>& ... deps) - { - return ValueTupleT(deps->ValueRef() ...); - } - }; - - struct EvalFunctor_ - { - EvalFunctor_(const E& e, TFunc& f) : - MyEvent( e ), - MyFunc( f ) - {} - - void operator()(const TDepValues& ... vals) - { - MyFunc(MyEvent, vals ...); - } - - const E& MyEvent; - TFunc& MyFunc; - }; - -public: - template - SyncedContinuationNode(TransactionFlagsT turnFlags, - const std::shared_ptr>& trigger, F&& func, - const std::shared_ptr>& ... deps) : - SyncedContinuationNode::ContinuationNode( turnFlags ), - trigger_( trigger ), - func_( std::forward(func) ), - deps_( deps ... ) - { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *trigger); - - REACT_EXPAND_PACK(Engine::OnNodeAttach(*this, *deps)); - } - - ~SyncedContinuationNode() - { - Engine::OnNodeDetach(*this, *trigger_); - - apply( - DetachFunctor>...>( *this ), - deps_); - - Engine::OnNodeDestroy(*this); - } - - virtual const char* GetNodeType() const override { return "SyncedContinuationNode"; } - virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } - - virtual void Update(void* turnPtr) override - { - using TurnT = typename D::Engine::TurnT; - TurnT& turn = *reinterpret_cast(turnPtr); - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - // Update of this node could be triggered from deps, - // so make sure source doesnt contain events from last turn - trigger_->SetCurrentTurn(turn); - - auto& storedEvents = trigger_->Events(); - auto& storedFunc = func_; - - // Copy values to tuple - ValueTupleT storedValues = apply(TupleBuilder_( ), deps_); - - // Note: MSVC error, if using () initialization. - // Probably a compiler bug. - TransactionFuncT cont - { - // Copy events, func, value tuple (note: 2x copy) - [storedFunc,storedEvents,storedValues] () mutable - { - apply( - [&storedFunc,&storedEvents] (const TDepValues& ... vals) - { - storedFunc(EventRange( storedEvents ), vals ...); - }, - storedValues); - } - }; - - DomainSpecificInputManager::Instance() - .StoreContinuation( - DomainSpecificInputManager::Instance(), - this->turnFlags_, - std::move(cont)); - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - } - -private: - using DepHolderT = std::tuple>...>; - - std::shared_ptr> trigger_; - - TFunc func_; - DepHolderT deps_; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_DETAIL_GRAPH_CONTINUATIONNODES_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 00e35cbf..a68df789 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -15,13 +15,55 @@ #include #include #include +#include -#include "tbb/spin_mutex.h" +//#include "tbb/spin_mutex.h" #include "GraphBase.h" -#include "react/common/Concurrency.h" #include "react/common/Types.h" +/*****************************************/ REACT_BEGIN /*****************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Iterators for event processing +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class EventRange +{ +public: + using const_iterator = typename std::vector::const_iterator; + using size_type = typename std::vector::size_type; + + EventRange() = delete; + + EventRange(const EventRange&) = default; + EventRange& operator=(const EventRange&) = default; + + explicit EventRange(const std::vector& data) : + data_( data ) + { } + + const_iterator begin() const + { return data_.begin(); } + + const_iterator end() const + { return data_.end(); } + + size_type GetSize() const + { return data_.size(); } + + bool IsEmpty() const + { return data_.empty(); } + +private: + const std::vector& data_; +}; + +template +using EventSink = std::back_insert_iterator>; + +/******************************************/ REACT_END /******************************************/ + /***************************************/ REACT_IMPL_BEGIN /**************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -39,27 +81,27 @@ class EventStreamNode : public NodeBase public: using StorageType = std::vector; - EventStreamNode(IReactiveGroup* group) : NodeBase( group ) - { } - - EventStreamNode(NodeBase&&) = default; - EventStreamNode& operator=(NodeBase&&) = default; + EventStreamNode(EventStreamNode&&) = default; + EventStreamNode& operator=(EventStreamNode&&) = default; - EventStreamNode(const NodeBase&) = delete; - EventStreamNode& operator=(const NodeBase&) = delete; + EventStreamNode(const EventStreamNode&) = delete; + EventStreamNode& operator=(const EventStreamNode&) = delete; - void SetCurrentTurn(const TurnT& turn, bool forceUpdate = false, bool noClear = false) + void SetCurrentTurn(TurnId turnId, bool forceUpdate = false, bool noClear = false) { - this->AccessBufferForClearing([&] { - if (curTurnId_ != turn.Id() || forceUpdate) - { - curTurnId_ = turn.Id(); - if (!noClear) - events_.clear(); - } - }); + //this->AccessBufferForClearing([&] { + // if (curTurnId_ != turn.Id() || forceUpdate) + // { + // curTurnId_ = turn.Id(); + // if (!noClear) + // events_.clear(); + // } + //}); } + explicit EventStreamNode(const std::shared_ptr& graphPtr) : NodeBase( graphPtr ) + { } + StorageType& Events() { return events_; } @@ -77,10 +119,10 @@ class EventStreamNode : public NodeBase /// EventSourceNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class EventSourceNode : public EventStreamNode, public IInputNode +class EventSourceNode : public EventStreamNode { public: - EventSourceNode(IReactiveGroup* group) : EventSourceNode::EventStreamNode( group ) + EventSourceNode(const std::shared_ptr& graphPtr) : EventSourceNode::EventStreamNode( graphPtr ) { this->RegisterMe(); } ~EventSourceNode() @@ -92,41 +134,35 @@ class EventSourceNode : public EventStreamNode, public IInputNode virtual bool IsInputNode() const override { return true; } - virtual int DependencyCount() const override + virtual int GetDependencyCount() const override { return 0; } - virtual void Update(void* turnPtr) override - { REACT_ASSERT(false, "Updated EventSourceNode\n"); } - - template - void AddInput(V&& v) + virtual UpdateResult Update(TurnId turnId) override { - // Clear input from previous turn - if (changedFlag_) + if (this->Events().size() > 0 && !changedFlag_) { - changedFlag_ = false; - this->events_.clear(); + this->SetCurrentTurn(turnId, true, true); + changedFlag_ = true; + + return UpdateResult::changed; + } + else + { + return UpdateResult::unchanged; } - - this->events_.push_back(std::forward(v)); } - virtual bool ApplyInput(void* turnPtr) override + template + void EmitValue(U&& value) { - if (this->events_.size() > 0 && !changedFlag_) - { - using TurnT = typename D::Engine::TurnT; - TurnT& turn = *reinterpret_cast(turnPtr); - - this->SetCurrentTurn(turn, true, true); - changedFlag_ = true; - Engine::OnInputChange(*this, turn); - return true; - } - else + // Clear input from previous turn + if (changedFlag_) { - return false; + changedFlag_ = false; + this->Events().clear(); } + + this->Events().push_back(std::forward(value)); } private: @@ -140,8 +176,8 @@ template class EventMergeNode : public EventStreamNode { public: - EventMergeNode(IReactiveGroup* group, const std::shared_ptr>& ... deps) : - EventMergeNode::EventStreamNode( group ), + EventMergeNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& ... deps) : + EventMergeNode::EventStreamNode( graphPtr ), depHolder_( deps ... ) { this->RegisterMe(); @@ -154,9 +190,9 @@ class EventMergeNode : public EventStreamNode this->UnregisterMe(); } - virtual UpdateResult Update() override + virtual UpdateResult Update(TurnId turnId) override { - // this->SetCurrentTurn(turn, true); + this->SetCurrentTurn(turnId, true); apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(MergeFromDep(deps)); }, depHolder_); @@ -169,115 +205,18 @@ class EventMergeNode : public EventStreamNode virtual const char* GetNodeType() const override { return "EventMerge"; } - virtual int DependencyCount() const override - { return sizeof...(Es); } + virtual int GetDependencyCount() const override + { return sizeof...(TIns); } private: template - void MergeFromDep(const std::shared_ptr& other) + void MergeFromDep(const std::shared_ptr>& other) { //arg->SetCurrentTurn(turn); this->Events().insert(this->Events().end(), other->Events().begin(), other->Events().end()); } - std::tuple ...> depHolder_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventTransformNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EventTransformNode : public EventStreamNode -{ -public: - template - EventTransformNode(IReactiveGroup* group, const std::shared_ptr>& dep, U&& func) : - EventTransformNode::EventStreamNode( group ), - dep_( dep ), - func_( std::forward(func) ) - { - this->RegisterMe(); - this->AttachToMe(dep->GetNodeId()); - } - - ~EventTransformNode() - { - this->DetachFromMe(dep_->GetNodeId()); - this->UnregisterMe(); - } - - virtual UpdateResult Update() override - { - // this->SetCurrentTurn(turn, true); - - for (const auto& v : dep_->Events()) - this->Events().push_back(func_(v)); - - if (! this->Events().empty()) - return UpdateResult::changed; - else - return UpdateResult::unchanged; - } - - virtual const char* GetNodeType() const override - { return "EventTransform"; } - - virtual int DependencyCount() const override - { return 1; } - -private: - std::shared_ptr dep_; - - F func_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventFilterNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EventFilterNode : public EventStreamNode -{ -public: - template - EventFilterNode(IReactiveGroup* group, const std::shared_ptr>& dep, U&& pred) : - EventFilterNode::EventStreamNode( group ), - dep_( dep ), - pred_( std::forward(pred) ) - { - this->RegisterMe(); - this->AttachToMe(dep->GetNodeId()); - } - - ~EventFilterNode() - { - this->DetachFromMe(dep_->GetNodeId()); - this->UnregisterMe(); - } - - virtual UpdateResult Update() override - { - // this->SetCurrentTurn(turn, true); - - for (const auto& v : dep_->Events()) - if (pred_(v)) - this->Events().push_back(v); - - if (! this->Events().empty()) - return UpdateResult::changed; - else - return UpdateResult::unchanged; - } - - virtual const char* GetNodeType() const override - { return "EventFilter"; } - - virtual int DependencyCount() const override - { return 1; } - -private: - std::shared_ptr dep_; - - F pred_; + std::tuple> ...> depHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -287,8 +226,8 @@ template class EventFlattenNode : public EventStreamNode { public: - EventFlattenNode(IReactiveGroup* group, const std::shared_ptr>& outer, const std::shared_ptr>& inner) : - EventFlattenNode::EventStreamNode( group ), + EventFlattenNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& outer, const std::shared_ptr>& inner) : + EventFlattenNode::EventStreamNode( graphPtr ), outer_( outer ), inner_( inner ) { @@ -310,19 +249,19 @@ class EventFlattenNode : public EventStreamNode virtual bool IsDynamicNode() const override { return true; } - virtual int DependencyCount() const override + virtual int GetDependencyCount() const override { return 2; } - virtual UpdateResult Update() override + virtual UpdateResult Update(TurnId turnId) override { - // this->SetCurrentTurn(turn, true); - // inner_->SetCurrentTurn(turn); + this->SetCurrentTurn(turnId, true); + inner_->SetCurrentTurn(turnId); - auto newInner = GetNodePtr(outer_->ValueRef()); + auto newInner = GetNodePtr(outer_->Value()); if (newInner != inner_) { - newInner->SetCurrentTurn(turn); + newInner->SetCurrentTurn(turnId); // Topology has been changed auto oldInner = inner_; @@ -347,121 +286,6 @@ class EventFlattenNode : public EventStreamNode std::shared_ptr> inner_; }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SyncedEventTransformNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class SyncedEventTransformNode : public EventStreamNode -{ -public: - template - SyncedEventTransformNode(IReactiveGroup* group, const std::shared_ptr>& dep, U&& func, const std::shared_ptr>& ... syncs) : - SyncedEventTransformNode::EventStreamNode( group ), - dep_( dep ), - func_( std::forward(func) ), - syncs_( syncs ... ) - { - this->RegisterMe(); - this->AttachToMe(dep->GetNodeId()); - REACT_EXPAND_PACK(this->AttachToMe(syncs->GetNodeId())); - } - - ~SyncedEventTransformNode() - { - apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(syncs->GetNodeId())); }, syncHolder_); - this->DetachFromMe(dep_->GetNodeId()); - this->UnregisterMe(); - } - - virtual UpdateResult Update() override - { - //this->SetCurrentTurn(turn, true); - - // Update of this node could be triggered from deps, - // so make sure source doesnt contain events from last turn - // source_->SetCurrentTurn(turn); - - for (const auto& e : dep_->Events()) - this->Events().push_back(apply([this, &e] (const auto& ... syncs) { return this->func_(e, syncs->ValueRef() ...); }, syncHolder_)); - - if (! this->Events().empty()) - return UpdateResult::changed; - else - return UpdateResult::unchanged; - } - - virtual const char* GetNodeType() const override - { return "SyncedEventTransform"; } - - virtual int DependencyCount() const override - { return 1 + sizeof...(TSyncs); } - -private: - std::shared_ptr> dep_; - - F func_; - - std::tuple>...> syncHolder_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SyncedEventFilterNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class SyncedEventFilterNode : public EventStreamNode -{ -public: - template - SyncedEventFilterNode(IReactiveGroup* group, const std::shared_ptr>& dep, U&& pred, const std::shared_ptr>& ... syncs) : - SyncedEventFilterNode::EventStreamNode( group ), - dep_( dep ), - pred_( std::forward(pred) ), - syncs_( syncs ... ) - { - this->RegisterMe(); - this->AttachToMe(dep->GetNodeId()); - REACT_EXPAND_PACK(this->AttachToMe(syncs->GetNodeId())); - } - - ~SyncedEventFilterNode() - { - apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(syncs->GetNodeId())); }, syncHolder_); - this->DetachFromMe(dep_->GetNodeId()); - this->UnregisterMe(); - } - - virtual UpdateResult Update() override - { - // this->SetCurrentTurn(turn, true); - - // Update of this node could be triggered from deps, - // so make sure source doesnt contain events from last turn - //source_->SetCurrentTurn(turn); - - for (const auto& e : dep_->Events()) - if (apply([this, &e] (const auto& ... syncs) { return this->func_(e, syncs->ValueRef() ...); }, syncHolder_)) - this->Events().push_back(e); - - if (! this->Events().empty()) - return UpdateResult::changed; - else - return UpdateResult::unchanged; - } - - virtual const char* GetNodeType() const override - { return "SyncedEventFilter"; } - - virtual int DependencyCount() const override - { return 1 + sizeof...(TSyncs); } - -private: - std::shared_ptr> dep_; - - F pred_; - - std::tuple>...> syncHolder_; -}; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventProcessingNode /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -470,8 +294,8 @@ class EventProcessingNode : public EventStreamNode { public: template - EventProcessingNode(IReactiveGroup* group, const std::shared_ptr>& dep, U&& func) : - EventProcessingNode::EventStreamNode( group ), + EventProcessingNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& dep, U&& func) : + EventProcessingNode::EventStreamNode( graphPtr ), dep_( dep ), func_( std::forward(func) ) { @@ -485,11 +309,11 @@ class EventProcessingNode : public EventStreamNode this->UnregisterMe(); } - virtual UpdateResult Update() override + virtual UpdateResult Update(TurnId turnId) override { - //this->SetCurrentTurn(turn, true); + this->SetCurrentTurn(turnId, true); - func_(EventRange( source_->Events() ), std::back_inserter(this->Events())); + func_(EventRange( dep_->Events() ), std::back_inserter(this->Events())); if (! this->Events().empty()) return UpdateResult::changed; @@ -500,13 +324,13 @@ class EventProcessingNode : public EventStreamNode virtual const char* GetNodeType() const override { return "EventProcessing"; } - virtual int DependencyCount() const override + virtual int GetDependencyCount() const override { return 1; } private: - std::shared_ptr> source_; + std::shared_ptr> dep_; - TFunc func_; + F func_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -517,11 +341,11 @@ class SyncedEventProcessingNode : public EventStreamNode { public: template - SyncedEventProcessingNode(IReactiveGroup* group, const std::shared_ptr>& dep, U&& func, const std::shared_ptr>& ... syncs) : - SyncedEventProcessingNode::EventStreamNode( group ), + SyncedEventProcessingNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& dep, U&& func, const std::shared_ptr>& ... syncs) : + SyncedEventProcessingNode::EventStreamNode( graphPtr ), dep_( dep ), func_( std::forward(func) ), - syncs_( syncs ... ) + syncHolder_( syncs ... ) { this->RegisterMe(); this->AttachToMe(dep->GetNodeId()); @@ -535,17 +359,17 @@ class SyncedEventProcessingNode : public EventStreamNode this->UnregisterMe(); } - virtual UpdateResult Update() override + virtual UpdateResult Update(TurnId turnId) override { - //this->SetCurrentTurn(turn, true); + this->SetCurrentTurn(turnId, true); // Update of this node could be triggered from deps, // so make sure source doesnt contain events from last turn - //source_->SetCurrentTurn(turn); + source_->SetCurrentTurn(turnId); apply( [this] (const auto& ... syncs) { - func_(EventRange( source_->Events() ), std::back_inserter(this->Events()), syncs->ValueRef() ...); + func_(EventRange( this->dep_->Events() ), std::back_inserter(this->Events()), syncs->Value() ...); }, syncHolder_); @@ -558,7 +382,7 @@ class SyncedEventProcessingNode : public EventStreamNode virtual const char* GetNodeType() const override { return "SycnedEventProcessing"; } - virtual int DependencyCount() const override + virtual int GetDependencyCount() const override { return 1 + sizeof...(TSyncs); } private: @@ -576,8 +400,8 @@ template class EventJoinNode : public EventStreamNode> { public: - EventJoinNode(IReactiveGroup* group, const std::shared_ptr>& ... deps) : - EventJoinNode::EventStreamNode( group ), + EventJoinNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& ... deps) : + EventJoinNode::EventStreamNode( graphPtr ), slots_( deps ... ) { this->RegisterMe(); @@ -590,12 +414,12 @@ class EventJoinNode : public EventStreamNode> this->UnregisterMe(); } - virtual UpdateResult Update() override + virtual UpdateResult Update(TurnId turnId) override { - //this->SetCurrentTurn(turn, true); + this->SetCurrentTurn(turnId, true); // Move events into buffers - apply([this, &turn] (Slot& ... slots) { REACT_EXPAND_PACK(FetchBuffer(turn, slots)); }, slots_); + apply([this, turnId] (Slot& ... slots) { REACT_EXPAND_PACK(FetchBuffer(turnId, slots)); }, slots_); while (true) { @@ -631,7 +455,7 @@ class EventJoinNode : public EventStreamNode> virtual const char* GetNodeType() const override { return "EventJoin"; } - virtual int DependencyCount() const override + virtual int GetDependencyCount() const override { return sizeof...(Ts); } private: @@ -647,9 +471,9 @@ class EventJoinNode : public EventStreamNode> }; template - static void FetchBuffer(TurnT& turn, Slot& slot) + static void FetchBuffer(TurnId turnId, Slot& slot) { - //slot.Source->SetCurrentTurn(turn); + slot.Source->SetCurrentTurn(turnId); slot.buffer.insert(slot.buffer.end(), slot.source->Events().begin(), slot.source->Events().end()); } diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index 404108d0..43d091da 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -11,76 +11,18 @@ #include "react/detail/Defs.h" +#include #include #include -#include "react/common/Util.h" -#include "react/common/Timing.h" #include "react/common/Types.h" -#include "react/detail/IReactiveGroup.h" -#include "react/detail/IReactiveNode.h" +#include "react/common/Util.h" +#include "react/detail/IReactiveGraph.h" #include "react/detail/ObserverBase.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// WeightHint -/////////////////////////////////////////////////////////////////////////////////////////////////// -enum class WeightHint -{ - automatic, - light, - heavy -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// UpdateTimingPolicy -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - long long threshold -> -class UpdateTimingPolicy : - private ConditionalTimer -{ - using ScopedTimer = typename UpdateTimingPolicy::ScopedTimer; - -public: - class ScopedUpdateTimer : private ScopedTimer - { - public: - ScopedUpdateTimer(UpdateTimingPolicy& parent, const size_t& count) : - ScopedTimer( parent, count ) - {} - }; - - void ResetUpdateThreshold() { this->Reset(); } - void ForceUpdateThresholdExceeded(bool v) { this->ForceThresholdExceeded(v); } - bool IsUpdateThresholdExceeded() const { return this->IsThresholdExceeded(); } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DepCounter -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct CountHelper { static const int value = T::dependency_count; }; - -template -struct CountHelper> { static const int value = 1; }; - -template -struct DepCounter; - -template <> -struct DepCounter<0> { static int const value = 0; }; - -template -struct DepCounter -{ - static int const value = - CountHelper::type>::value + DepCounter::value; -}; +struct IReactiveGraph; /////////////////////////////////////////////////////////////////////////////////////////////////// /// NodeBase @@ -88,13 +30,14 @@ struct DepCounter class NodeBase : public IReactiveNode { public: - NodeBase(IReactiveGroup* group) : group_( group ) + NodeBase(const std::shared_ptr& graphPtr) : graphPtr_( graphPtr ) { } + + NodeBase(const NodeBase&) = delete; + NodeBase& operator=(const NodeBase&) = delete; NodeBase(NodeBase&&) = delete; NodeBase& operator=(NodeBase&&) = delete; - NodeBase(const NodeBase&) = delete; - NodeBase& operator=(const NodeBase&) = delete; virtual bool IsInputNode() const override { return false; } @@ -104,11 +47,8 @@ class NodeBase : public IReactiveNode virtual bool IsDynamicNode() const override { return false; } - - virtual bool IsHeavyweight() const override - { return false; } - void SetWeightHint(WeightHint weight) + /*void SetWeightHint(WeightHint weight) { switch (weight) { @@ -122,174 +62,42 @@ class NodeBase : public IReactiveNode this->ResetUpdateThreshold(); break; } - } + }*/ NodeId GetNodeId() const { return nodeId_; } + auto GraphPtr() const -> const std::shared_ptr& + { return graphPtr_; } + + auto GraphPtr() -> std::shared_ptr& + { return graphPtr_; } + protected: void RegisterMe() - { nodeId_ = group_->RegisterNode(); } + { nodeId_ = graphPtr_->RegisterNode(this); } void UnregisterMe() - { group_->UnregisterNode(nodeId_); } + { graphPtr_->UnregisterNode(nodeId_); } void AttachToMe(NodeId otherNodeId) - { group_->OnNodeAttach(nodeId_, otherNodeId); } + { graphPtr_->OnNodeAttach(nodeId_, otherNodeId); } void DetachFromMe(NodeId otherNodeId) - { group_->OnNodeDetach(nodeId_, otherNodeId); } + { graphPtr_->OnNodeDetach(nodeId_, otherNodeId); } void DynamicAttachToMe(NodeId otherNodeId, TurnId turnId) - { group_->OnDynamicNodeAttach(nodeId_, otherNodeId, turnId); } + { graphPtr_->OnDynamicNodeAttach(nodeId_, otherNodeId, turnId); } void DynamicDetachFromMe(NodeId otherNodeId, TurnId turnId) - { group_->OnDynamicNodeDetach(nodeId_, otherNodeId, turnId); } + { graphPtr_->OnDynamicNodeDetach(nodeId_, otherNodeId, turnId); } private: NodeId nodeId_; - IReactiveGroup* group_; -}; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Attach/detach helper functors -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct AttachFunctor -{ - AttachFunctor(TNode& node) : MyNode( node ) {} - - void operator()(const TDeps& ...deps) const - { - REACT_EXPAND_PACK(attach(deps)); - } - - template - void attach(const T& op) const - { - op.template AttachRec(*this); - } - - template - void attach(const std::shared_ptr& depPtr) const - { - D::Engine::OnNodeAttach(MyNode, *depPtr); - } - - TNode& MyNode; + std::shared_ptr graphPtr_; }; -template -struct DetachFunctor -{ - DetachFunctor(TNode& node) : MyNode( node ) {} - - void operator()(const TDeps& ... deps) const - { - REACT_EXPAND_PACK(detach(deps)); - } - - template - void detach(const T& op) const - { - op.template DetachRec(*this); - } - - template - void detach(const std::shared_ptr& depPtr) const - { - D::Engine::OnNodeDetach(MyNode, *depPtr); - } - - TNode& MyNode; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ReactiveOpBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ReactiveOpBase -{ -public: - using DepHolderT = std::tuple; - - template - ReactiveOpBase(DontMove, TDepsIn&& ... deps) : - deps_( std::forward(deps) ... ) - {} - - ReactiveOpBase(ReactiveOpBase&& other) : - deps_( std::move(other.deps_) ) - {} - - // Can't be copied, only moved - ReactiveOpBase(const ReactiveOpBase& other) = delete; - - template - void Attach(TNode& node) const - { - apply(AttachFunctor{ node }, deps_); - } - - template - void Detach(TNode& node) const - { - apply(DetachFunctor{ node }, deps_); - } - - template - void AttachRec(const TFunctor& functor) const - { - // Same memory layout, different func - apply(reinterpret_cast&>(functor), deps_); - } - - template - void DetachRec(const TFunctor& functor) const - { - apply(reinterpret_cast&>(functor), deps_); - } - -public: - static const int dependency_count = DepCounter::value; - -protected: - DepHolderT deps_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Iterators for event processing -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EventRange -{ -public: - using const_iterator = typename std::vector::const_iterator; - using size_type = typename std::vector::size_type; - - // Copy ctor - EventRange(const EventRange&) = default; - - // Copy assignment - EventRange& operator=(const EventRange&) = default; - - const_iterator begin() const { return data_.begin(); } - const_iterator end() const { return data_.end(); } - - size_type Size() const { return data_.size(); } - bool IsEmpty() const { return data_.empty(); } - - explicit EventRange(const std::vector& data) : - data_( data ) - {} - -private: - const std::vector& data_; -}; - -template -using EventEmitter = std::back_insert_iterator>; - /****************************************/ REACT_IMPL_END /***************************************/ #endif // REACT_DETAIL_GRAPH_GRAPHBASE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index b597e828..b90e118e 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -23,337 +23,167 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class SignalNode; -template +template class EventStreamNode; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ObserverAction -/////////////////////////////////////////////////////////////////////////////////////////////////// -enum class ObserverAction -{ - next, - stop_and_detach -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// AddObserverRangeWrapper -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct AddObserverRangeWrapper -{ - AddObserverRangeWrapper(const AddObserverRangeWrapper& other) = default; - - AddObserverRangeWrapper(AddObserverRangeWrapper&& other) : - MyFunc( std::move(other.MyFunc) ) - {} - - template - < - typename FIn, - class = typename DisableIfSame::type - > - explicit AddObserverRangeWrapper(FIn&& func) : - MyFunc( std::forward(func) ) - {} - - ObserverAction operator()(EventRange range, const TArgs& ... args) - { - for (const auto& e : range) - if (MyFunc(e, args ...) == ObserverAction::stop_and_detach) - return ObserverAction::stop_and_detach; - - return ObserverAction::next; - } - - F MyFunc; -}; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// ObserverNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ObserverNode : - public NodeBase, - public IObserver +class ObserverNode : public NodeBase, public IObserver { public: - ObserverNode() = default; + ObserverNode(IReactiveGraph* group) : NodeBase( group ) + { } + + ObserverNode(ObserverNode&&) = default; + ObserverNode& operator=(ObserverNode&&) = default; - virtual bool IsOutputNode() const { return true; } + ObserverNode(const ObserverNode&) = delete; + ObserverNode& operator=(const ObserverNode&) = delete; + + virtual bool IsOutputNode() const + { return true; } }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// SignalObserverNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename S, - typename TFunc -> -class SignalObserverNode : - public ObserverNode +template +class SignalObserverNode : public ObserverNode { - using Engine = typename SignalObserverNode::Engine; - public: - template - SignalObserverNode(const std::shared_ptr>& subject, F&& func) : - SignalObserverNode::ObserverNode( ), + template + SignalObserverNode(IReactiveGraph* group, const std::shared_ptr>& subject, FIn&& func) : + SignalObserverNode::ObserverNode( group ), subject_( subject ), - func_( std::forward(func) ) + func_( std::forward(func) ) { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *subject); + this->RegisterMe(); + this->AttachToMe(subject->GetNodeId()); } ~SignalObserverNode() { - Engine::OnNodeDestroy(*this); + this->DetachFromMe(subject->GetNodeId()); + this->UnregisterMe(); } - virtual const char* GetNodeType() const override { return "SignalObserverNode"; } - virtual int DependencyCount() const override { return 1; } - - virtual void Update(void* turnPtr) override - { -#ifdef REACT_ENABLE_LOGGING - using TurnT = typename D::Engine::TurnT; - TurnT& turn = *reinterpret_cast(turnPtr); -#endif - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); + virtual const char* GetNodeType() const override + { return "SignalObserver"; } - bool shouldDetach = false; + virtual int DependencyCount() const override + { return 1; } - if (auto p = subject_.lock()) - {// timer - using TimerT = typename SignalObserverNode::ScopedUpdateTimer; - TimerT scopedTimer( *this, 1 ); - - if (func_(p->ValueRef()) == ObserverAction::stop_and_detach) - shouldDetach = true; - }// ~timer - - if (shouldDetach) - DomainSpecificInputManager::Instance() - .QueueObserverForDetach(*this); - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - } - - virtual void UnregisterSelf() override + virtual UpdateResult Update(TurnId turnId) override { - if (auto p = subject_.lock()) - p->UnregisterObserver(this); + func_(subject_->Value()); + return UpdateResult::unchanged; } private: - virtual void detachObserver() override - { - if (auto p = subject_.lock()) - { - Engine::OnNodeDetach(*this, *p); - subject_.reset(); - } - } + std::shared_ptr> subject_; - std::weak_ptr> subject_; - TFunc func_; + F func_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventObserverNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E, - typename TFunc -> -class EventObserverNode : - public ObserverNode +template +class EventObserverNode : public ObserverNode { - using Engine = typename EventObserverNode::Engine; - public: - template - EventObserverNode(const std::shared_ptr>& subject, F&& func) : - EventObserverNode::ObserverNode( ), + template + EventObserverNode(IReactiveGraph* group, const std::shared_ptr>& subject, FIn&& func) : + EventObserverNode::ObserverNode( group ), subject_( subject ), - func_( std::forward(func) ) + func_( std::forward(func) ) { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *subject); + this->RegisterMe(); + this->AttachToMe(subject->GetNodeId()); } ~EventObserverNode() { - Engine::OnNodeDestroy(*this); + this->DetachFromMe(subject->GetNodeId()); + this->UnregisterMe(); } - virtual const char* GetNodeType() const override { return "EventObserverNode"; } - virtual int DependencyCount() const override { return 1; } - - virtual void Update(void* turnPtr) override - { -#ifdef REACT_ENABLE_LOGGING - using TurnT = typename D::Engine::TurnT; - TurnT& turn = *reinterpret_cast(turnPtr); -#endif - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - bool shouldDetach = false; - - if (auto p = subject_.lock()) - {// timer - using TimerT = typename EventObserverNode::ScopedUpdateTimer; - TimerT scopedTimer( *this, p->Events().size() ); + virtual const char* GetNodeType() const override + { return "EventObserverNode"; } - shouldDetach = func_(EventRange( p->Events() )) == ObserverAction::stop_and_detach; + virtual int DependencyCount() const override + { return 1; } - }// ~timer - - if (shouldDetach) - DomainSpecificInputManager::Instance() - .QueueObserverForDetach(*this); - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - } - - virtual void UnregisterSelf() override + virtual UpdateResult Update(TurnId turnId) override { - if (auto p = subject_.lock()) - p->UnregisterObserver(this); + func_(EventRange( subject_->Events() )); + return UpdateResult::unchanged; } private: - std::weak_ptr> subject_; - - TFunc func_; + std::shared_ptr> subject_; - virtual void detachObserver() - { - if (auto p = subject_.lock()) - { - Engine::OnNodeDetach(*this, *p); - subject_.reset(); - } - } + F func_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// SyncedObserverNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename E, - typename TFunc, - typename ... TDepValues -> -class SyncedObserverNode : - public ObserverNode +template +class SyncedObserverNode : public ObserverNode { - using Engine = typename SyncedObserverNode::Engine; - public: - template - SyncedObserverNode(const std::shared_ptr>& subject, F&& func, - const std::shared_ptr>& ... deps) : - SyncedObserverNode::ObserverNode( ), + template + SyncedObserverNode(IReactiveGraph* group, const std::shared_ptr>& subject, FIn&& func, const std::shared_ptr>& ... syncs) : + SyncedObserverNode::ObserverNode( group ), subject_( subject ), - func_( std::forward(func) ), - deps_( deps ... ) + func_( std::forward(func) ), + syncHolder_( syncs ... ) { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *subject); - - REACT_EXPAND_PACK(Engine::OnNodeAttach(*this, *deps)); + this->RegisterMe(); + this->AttachToMe(subject->GetNodeId()); + REACT_EXPAND_PACK(this->AttachToMe(syncs->GetNodeId())); } ~SyncedObserverNode() { - Engine::OnNodeDestroy(*this); + apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(syncs->GetNodeId())); }, syncHolder_); + this->DetachFromMe(subject_->GetNodeId()); + this->UnregisterMe(); } - virtual const char* GetNodeType() const override { return "SyncedObserverNode"; } - virtual int DependencyCount() const override { return 1 + sizeof...(TDepValues); } + virtual const char* GetNodeType() const override + { return "SyncedObserverNode"; } - virtual void Update(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 shouldDetach = false; - - if (auto p = subject_.lock()) - { - // Update of this node could be triggered from deps, - // so make sure source doesnt contain events from last turn - p->SetCurrentTurn(turn); - - {// timer - using TimerT = typename SyncedObserverNode::ScopedUpdateTimer; - TimerT scopedTimer( *this, p->Events().size() ); + virtual int DependencyCount() const override + { return 1 + sizeof...(TSyncs); } + + virtual UpdateResult Update(TurnId turnId) override + { + // Update of this node could be triggered from deps, + // so make sure source doesnt contain events from last turn + p->SetCurrentTurn(turnId); - shouldDetach = apply( - [this, &p] (const std::shared_ptr>& ... args) - { - return func_(EventRange( p->Events() ), args->ValueRef() ...); - }, - deps_) == ObserverAction::stop_and_detach; - - }// ~timer - } - - if (shouldDetach) - DomainSpecificInputManager::Instance() - .QueueObserverForDetach(*this); - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - } + shouldDetach = apply( + [this] (const auto& ... syncs) + { + func_(EventRange( this->subject_->Events() ), syncs->Value() ...); + }, + syncHolder_); - virtual void UnregisterSelf() override - { - if (auto p = subject_.lock()) - p->UnregisterObserver(this); + return UpdateResult::unchanged; } private: - using DepHolderT = std::tuple>...>; - - std::weak_ptr> subject_; + std::shared_ptr> subject_; - TFunc func_; - DepHolderT deps_; - - virtual void detachObserver() - { - if (auto p = subject_.lock()) - { - Engine::OnNodeDetach(*this, *p); - - apply( - DetachFunctor>...>( *this ), - deps_); + F func_; - subject_.reset(); - } - } + std::tuple>...> syncHolder_; }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/graph/PropagationMT.h b/include/react/detail/graph/PropagationMT.h new file mode 100644 index 00000000..e69de29b diff --git a/include/react/detail/graph/PropagationST.h b/include/react/detail/graph/PropagationST.h new file mode 100644 index 00000000..bfbc0828 --- /dev/null +++ b/include/react/detail/graph/PropagationST.h @@ -0,0 +1,284 @@ + +// Copyright Sebastian Jeckel 2016. +// 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_PROPAGATIONST_H_INCLUDED +#define REACT_DETAIL_GRAPH_PROPAGATIONST_H_INCLUDED + +#pragma once + +#include "react/detail/Defs.h" + +#include +#include +#include +#include + +#include "react/common/Containers.h" +#include "react/common/Types.h" +#include "react/detail/IReactiveGraph.h" + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +class SingleThreadedGraph : public IReactiveGraph +{ +public: + // IReactiveGraph + virtual NodeId RegisterNode(IReactiveNode* nodePtr) override; + virtual void UnregisterNode(NodeId node) override; + + virtual void OnNodeAttach(NodeId node, NodeId parentId) override; + virtual void OnNodeDetach(NodeId node, NodeId parentId) override; + + virtual void OnDynamicNodeAttach(NodeId node, NodeId parentId, TurnId turnId) override; + virtual void OnDynamicNodeDetach(NodeId node, NodeId parentId, TurnId turnId) override; + + virtual void AddInput(NodeId nodeId, std::function inputCallback) override; + // ~IReactiveGraph + + template + void DoTransaction(TransactionFlags flags, F&& transactionCallback); + +private: + struct NodeData + { + NodeData() = default; + + NodeData(const NodeData&) = default; + NodeData& operator=(const NodeData&) = default; + + NodeData(IReactiveNode* nodePtrIn) : + nodePtr( nodePtrIn ) + { } + + int level = 0; + int newLevel = 0 ; + bool queued = false; + + IReactiveNode* nodePtr = nullptr; + + std::vector successors; + }; + + class TopoQueue + { + public: + void Push(NodeId nodeId, int level) + { queueData_.emplace_back(nodeId, level); } + + bool FetchNext(); + + const std::vector& Next() const + { return nextData_; } + + bool IsEmpty() const + { return queueData_.empty(); } + + private: + using Entry = std::pair; + + std::vector queueData_; + std::vector nextData_; + + int minLevel_ = (std::numeric_limits::max)(); + }; + + void Propagate(); + + void ScheduleSuccessors(NodeData & node); + void InvalidateSuccessors(NodeData & node); + +private: + int refCount_ = 1; + + TopoQueue scheduledNodes_; + IndexMap nodeData_; + std::vector changedInputs_; + + bool isTransactionActive_ = false; +}; + +NodeId SingleThreadedGraph::RegisterNode(IReactiveNode* nodePtr) +{ + return nodeData_.Insert(NodeData{ nodePtr }); +} + +void SingleThreadedGraph::UnregisterNode(NodeId nodeId) +{ + nodeData_.Remove(nodeId); + +} + +void SingleThreadedGraph::OnNodeAttach(NodeId nodeId, NodeId parentId) +{ + auto& node = nodeData_[nodeId]; + auto& parent = nodeData_[parentId]; + + parent.successors.push_back(nodeId); + + if (node.level <= parent.level) + node.level = parent.level + 1; +} + +void SingleThreadedGraph::OnNodeDetach(NodeId nodeId, NodeId parentId) +{ + auto& parent = nodeData_[parentId]; + auto& successors = parent.successors; + + successors.erase(std::find(successors.begin(), successors.end(), nodeId)); +} + +void SingleThreadedGraph::OnDynamicNodeAttach(NodeId nodeId, NodeId parentId, TurnId turnId) +{ + OnNodeAttach(nodeId, parentId); +} + +void SingleThreadedGraph::OnDynamicNodeDetach(NodeId nodeId, NodeId parentId, TurnId turnId) +{ + OnNodeDetach(nodeId, parentId); +} + +void SingleThreadedGraph::AddInput(NodeId nodeId, std::function inputCallback) +{ + auto& node = nodeData_[nodeId]; + auto* nodePtr = node.nodePtr; + + // This write to the input buffer of the respective node. + inputCallback(); + + if (isTransactionActive_) + { + // If a transaction is active, don't propagate immediately. + // Instead, record the node and wait for more inputs. + changedInputs_.push_back(nodeId); + } + else + { + // Update the node. This applies the input buffer to the node value and checks if it changed. + if (nodePtr->Update(0) == UpdateResult::changed) + { + // Propagate changes through the graph + ScheduleSuccessors(node); + + if (! scheduledNodes_.IsEmpty()) + Propagate(); + } + } +} + +template +void SingleThreadedGraph::DoTransaction(TransactionFlags flags, F&& transactionCallback) +{ + // Transaction callback may add multiple inputs. + isTransactionActive_ = true; + std::forward(transactionCallback)(); + isTransactionActive_ = false; + + // Apply all buffered inputs. + for (NodeId nodeId : changedInputs_) + { + auto& node = nodeData_[nodeId]; + + if (node.nodePtr->Update(0) == UpdateResult::changed) + ScheduleSuccessors(node); + } + + changedInputs_.clear(); + + // Propagate changes through the graph. + if (! scheduledNodes_.IsEmpty()) + Propagate(); +} + +void SingleThreadedGraph::Propagate() +{ + while (scheduledNodes_.FetchNext()) + { + for (NodeId nodeId : scheduledNodes_.Next()) + { + auto& node = nodeData_[nodeId]; + + if (node.level < node.newLevel) + { + // Re-schedule this node + node.level = node.newLevel; + InvalidateSuccessors(node); + scheduledNodes_.Push(nodeId, node.level); + continue; + } + + auto result = node.nodePtr->Update(0); + + if (result == UpdateResult::changed) + { + ScheduleSuccessors(node); + } + else if (result == UpdateResult::shifted) + { + // Re-schedule this node + InvalidateSuccessors(node); + scheduledNodes_.Push(nodeId, node.level); + continue; + } + + node.queued = false; + } + } +} + +void SingleThreadedGraph::ScheduleSuccessors(NodeData& node) +{ + for (NodeId succId : node.successors) + { + auto& succ = nodeData_[succId]; + + if (!succ.queued) + { + succ.queued = true; + scheduledNodes_.Push(succId, succ.level); + } + } +} + +void SingleThreadedGraph::InvalidateSuccessors(NodeData& node) +{ + for (NodeId succId : node.successors) + { + auto& succ = nodeData_[succId]; + + if (succ.newLevel <= node.level) + succ.newLevel = node.level + 1; + } +} + +bool SingleThreadedGraph::TopoQueue::FetchNext() +{ + // Throw away previous values + nextData_.clear(); + + // Find min level of nodes in queue data + minLevel_ = (std::numeric_limits::max)(); + for (const auto& e : queueData_) + if (minLevel_ > e.second) + minLevel_ = e.second; + + // Swap entries with min level to the end + auto p = std::partition(queueData_.begin(), queueData_.end(), [t = minLevel_] (const Entry& e) { return t != e.second; }); + + // Move min level values to next data + nextData_.reserve(std::distance(p, queueData_.end())); + + for (auto it = p; it != queueData_.end(); ++it) + nextData_.push_back(it->first); + + // Truncate moved entries + queueData_.resize(std::distance(queueData_.begin(), p)); + + return !nextData_.empty(); +} + +/****************************************/ REACT_IMPL_END /***************************************/ + +#endif // REACT_DETAIL_GRAPH_PROPAGATIONST_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/ReactorNodes.h b/include/react/detail/graph/ReactorNodes.h deleted file mode 100644 index c85cdcc0..00000000 --- a/include/react/detail/graph/ReactorNodes.h +++ /dev/null @@ -1,259 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_REACTORNODES_H_INCLUDED -#define REACT_DETAIL_GRAPH_REACTORNODES_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include -#include -#include -#include - -#include - -#include "GraphBase.h" -#include "EventNodes.h" -#include "SignalNodes.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ReactorNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TContext -> -class ReactorNode : - public NodeBase -{ - using Engine = typename ReactorNode::Engine; - -public: - using CoroutineT = boost::coroutines::coroutine*>; - using PullT = typename CoroutineT::pull_type; - using PushT = typename CoroutineT::push_type; - using TurnT = typename D::Engine::TurnT; - - template - ReactorNode(F&& func) : - ReactorNode::NodeBase( ), - func_( std::forward(func) ) - { - Engine::OnNodeCreate(*this); - } - - ~ReactorNode() - { - Engine::OnNodeDestroy(*this); - } - - void StartLoop() - { - mainLoop_ = PullT - ( - [&] (PushT& out) - { - curOutPtr_ = &out; - - TContext ctx( *this ); - - // Start the loop function. - // It will reach it its first Await at some point. - while (true) - { - func_(ctx); - } - } - ); - - // First blocking event is not initiated by Tick() but after loop creation. - NodeBase* p = mainLoop_.get(); - doAttach(p); - - ++depCount_; - } - - virtual const char* GetNodeType() const override { return "ReactorNode"; } - - virtual bool IsDynamicNode() const override { return true; } - virtual bool IsOutputNode() const override { return true; } - - virtual void Update(void* turnPtr) override - { - turnPtr_ = reinterpret_cast(turnPtr); - - mainLoop_(); - - NodeBase* p = mainLoop_.get(); - - if (p != nullptr) - { - doDynAttach(p); - } - else - { - offsets_.clear(); - } - - turnPtr_ = nullptr; - } - - virtual int DependencyCount() const override - { - return depCount_; - } - - template - E& Await(const std::shared_ptr>& events) - { - // First attach to target event node - (*curOutPtr_)(events.get()); - - while (! checkEvent(events)) - (*curOutPtr_)(nullptr); - - doDynDetach(events.get()); - - auto index = offsets_[reinterpret_cast(events.get())]++; - return events->Events()[index]; - } - - template - void RepeatUntil(const std::shared_ptr>& eventsPtr, const F& func) - { - // First attach to target event node - if (turnPtr_ != nullptr) - { - (*curOutPtr_)(eventsPtr.get()); - } - else - { - // Non-dynamic attach in case first loop until is encountered before the loop was - // suspended for the first time. - doAttach(eventsPtr.get()); - } - - // Don't enter loop if event already present - if (! checkEvent(eventsPtr)) - { - auto* parentOutPtr = curOutPtr_; - - // Create and start loop - PullT nestedLoop_ - { - [&] (PushT& out) - { - curOutPtr_ = &out; - while (true) - func(); - } - }; - - // First suspend from initial loop run - (*parentOutPtr)(nestedLoop_.get()); - - // Further iterations - while (! checkEvent(eventsPtr)) - { - // Advance loop, forward blocking event to parent, and suspend - nestedLoop_(); - (*parentOutPtr)(nestedLoop_.get()); - } - - curOutPtr_ = parentOutPtr; - } - - // Detach when this function is exited - if (turnPtr_ != nullptr) - Engine::OnDynamicNodeDetach(*this, *eventsPtr, *turnPtr_); - else - Engine::OnNodeDetach(*this, *eventsPtr); - - --depCount_; - } - - template - const S& Get(const std::shared_ptr>& sigPtr) - { - // Do dynamic attach followed by attach if Get() happens during a turn. - if (turnPtr_ != nullptr) - { - // Request attach - (*curOutPtr_)(sigPtr.get()); - - doDynDetach(sigPtr.get()); - } - - return sigPtr->ValueRef(); - } - -private: - template - bool checkEvent(const std::shared_ptr>& events) - { - if (turnPtr_ == nullptr) - return false; - - events->SetCurrentTurn(*turnPtr_); - - auto index = reinterpret_cast(events.get()); - return offsets_[index] < events->Events().size(); - } - - void doAttach(NodeBase* nodePtr) - { - assert(nodePtr != nullptr); - assert(turnPtr_ == nullptr); - Engine::OnNodeAttach(*this, *nodePtr); - ++depCount_; - } - - void doDetach(NodeBase* nodePtr) - { - assert(nodePtr != nullptr); - assert(turnPtr_ == nullptr); - Engine::OnNodeDetach(*this, *nodePtr); - --depCount_; - } - - void doDynAttach(NodeBase* nodePtr) - { - assert(nodePtr != nullptr); - assert(turnPtr_ != nullptr); - Engine::OnDynamicNodeAttach(*this, *nodePtr, *turnPtr_); - ++depCount_; - } - - void doDynDetach(NodeBase* nodePtr) - { - assert(nodePtr != nullptr); - assert(turnPtr_ != nullptr); - Engine::OnDynamicNodeDetach(*this, *nodePtr, *turnPtr_); - --depCount_; - } - - std::function func_; - - PullT mainLoop_; - TurnT* turnPtr_; - - PushT* curOutPtr_ = nullptr; - - uint depCount_ = 0; - - std::unordered_map offsets_; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_DETAIL_GRAPH_REACTORNODES_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index 8f9424dc..b926ffa1 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -28,307 +28,202 @@ bool Equals(const L& lhs, const R& rhs); /// SignalNode /////////////////////////////////////////////////////////////////////////////////////////////////// template -class SignalNode : public ObservableNode +class SignalNode : public NodeBase { public: - SignalNode() = default; + SignalNode(SignalNode&&) = default; + SignalNode& operator=(SignalNode&&) = default; + + SignalNode(const SignalNode&) = delete; + SignalNode& operator=(const SignalNode&) = delete; template - explicit SignalNode(U&& value) : - SignalNode::ObservableNode( ), + SignalNode(const std::shared_ptr& graphPtr, U&& value) : + SignalNode::NodeBase( graphPtr ), value_( std::forward(value) ) - {} + { } - const S& ValueRef() const - { - return value_; - } + T& Value() + { return value_; } -protected: - S value_; -}; + const T& Value() const + { return value_; } -template -using SignalNodePtrT = std::shared_ptr>; +private: + T value_; +}; /////////////////////////////////////////////////////////////////////////////////////////////////// /// VarNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class VarNode : - public SignalNode, - public IInputNode +template +class VarSignalNode : public SignalNode { - using Engine = typename VarNode::Engine; - public: template - VarNode(T&& value) : - VarNode::SignalNode( std::forward(value) ), + VarSignalNode(const std::shared_ptr& graphPtr, T&& value) : + VarSignalNode::SignalNode( graphPtr, std::forward(value) ), newValue_( value ) - { - Engine::OnNodeCreate(*this); - } + { this->RegisterMe(); } - ~VarNode() - { - Engine::OnNodeDestroy(*this); - } + ~VarSignalNode() + { this->UnregisterMe(); } - virtual const char* GetNodeType() const override { return "VarNode"; } - virtual bool IsInputNode() const override { return true; } - virtual int DependencyCount() const override { return 0; } + virtual const char* GetNodeType() const override + { return "VarSignal"; } - virtual void Update(void* turnPtr) override - { - REACT_ASSERT(false, "Ticked VarNode\n"); - } - - template - void AddInput(V&& newValue) - { - newValue_ = std::forward(newValue); + virtual bool IsInputNode() const override + { return true; } - isInputAdded_ = true; - - // isInputAdded_ takes precedences over isInputModified_ - // the only difference between the two is that isInputModified_ doesn't/can't compare - isInputModified_ = false; - } + virtual int GetDependencyCount() const override + { return 0; } - // This is signal-specific - template - void ModifyInput(F& func) + virtual UpdateResult Update(TurnId turnId) override { - // There hasn't been any Set(...) input yet, modify. - if (! isInputAdded_) - { - func(this->value_); - - isInputModified_ = true; - } - // There's a newValue, modify newValue instead. - // The modified newValue will handled like before, i.e. it'll be compared to value_ - // in ApplyInput - else - { - func(newValue_); - } - } - - virtual bool ApplyInput(void* turnPtr) override - { - using TurnT = typename D::Engine::TurnT; - TurnT& turn = *reinterpret_cast(turnPtr); - if (isInputAdded_) { isInputAdded_ = false; - if (! Equals(this->value_, newValue_)) + if (! Equals(this->Value(), newValue_)) { - this->value_ = std::move(newValue_); - Engine::OnInputChange(*this, turn); - return true; + this->Value() = std::move(newValue_); + return UpdateResult::changed; } else { - return false; + return UpdateResult::unchanged; } } else if (isInputModified_) { isInputModified_ = false; - - Engine::OnInputChange(*this, turn); - return true; + return UpdateResult::changed; } else { - return false; + return UpdateResult::unchanged; } } -private: - S newValue_; - bool isInputAdded_ = false; - bool isInputModified_ = false; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// FunctionOp -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename S, - typename F, - typename ... TDeps -> -class FunctionOp : public ReactiveOpBase -{ -public: - template - FunctionOp(FIn&& func, TDepsIn&& ... deps) : - FunctionOp::ReactiveOpBase( DontMove(), std::forward(deps) ... ), - func_( std::forward(func) ) - {} - - FunctionOp(FunctionOp&& other) : - FunctionOp::ReactiveOpBase( std::move(other) ), - func_( std::move(other.func_) ) - {} - - S Evaluate() + template + void SetValue(U&& newValue) { - return apply(EvalFunctor( func_ ), this->deps_); + newValue_ = std::forward(newValue); + + isInputAdded_ = true; + + // isInputAdded_ takes precedences over isInputModified_ + // the only difference between the two is that isInputModified_ doesn't/can't compare + isInputModified_ = false; } -private: - // Eval - struct EvalFunctor + // This is signal-specific + template + void ModifyValue(F&& func) { - EvalFunctor(F& f) : MyFunc( f ) {} - - template - S operator()(T&& ... args) + // There hasn't been any Set(...) input yet, modify. + if (! isInputAdded_) { - return MyFunc(eval(args) ...); - } + func(this->Value()); - template - static auto eval(T& op) -> decltype(op.Evaluate()) - { - return op.Evaluate(); + isInputModified_ = true; } - - template - static auto eval(const std::shared_ptr& depPtr) -> decltype(depPtr->ValueRef()) + // There's a newValue, modify newValue instead. + // The modified newValue will handled like before, i.e. it'll be compared to value_ + // in ApplyInput + else { - return depPtr->ValueRef(); + func(newValue_); } - - F& MyFunc; - }; + } private: - F func_; + T newValue_; + bool isInputAdded_ = false; + bool isInputModified_ = false; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// SignalOpNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename S, - typename TOp -> -class SignalOpNode : public SignalNode +template +class SignalFuncNode : public SignalNode { - using Engine = typename SignalOpNode::Engine; - public: - template - SignalOpNode(TArgs&& ... args) : - SignalOpNode::SignalNode( ), - op_( std::forward(args) ... ) + template + SignalFuncNode(const std::shared_ptr& graphPtr, U&& func, const std::shared_ptr>& ... deps) : + SignalFuncNode::SignalNode( graphPtr, func(deps->Value() ...) ), + func_( std::forward(func) ), + depHolder_( deps ... ) { - this->value_ = op_.Evaluate(); - - Engine::OnNodeCreate(*this); - op_.template Attach(*this); + this->RegisterMe(); + REACT_EXPAND_PACK(this->AttachToMe(deps->GetNodeId())); } - ~SignalOpNode() + ~SignalFuncNode() { - if (!wasOpStolen_) - op_.template Detach(*this); - Engine::OnNodeDestroy(*this); + apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(this->DetachFromMe(deps->GetNodeId())); }, depHolder_); + this->UnregisterMe(); } - virtual void Update(void* turnPtr) override - { - using TurnT = typename D::Engine::TurnT; - TurnT& turn = *reinterpret_cast(turnPtr); - - REACT_LOG(D::Log().template Append(GetObjectId(*this), turn.Id())); - + virtual UpdateResult Update(TurnId turnId) override + { bool changed = false; - {// timer - using TimerT = typename SignalOpNode::ScopedUpdateTimer; - TimerT scopedTimer( *this, 1 ); - - S newValue = op_.Evaluate(); - - if (! Equals(this->value_, newValue)) - { - this->value_ = std::move(newValue); - changed = true; - } - }// ~timer + T newValue = apply([this] (const auto& ... deps) { return this->func_(deps->Value() ...); }, depHolder_); - REACT_LOG(D::Log().template Append(GetObjectId(*this), turn.Id())); + if (! Equals(this->Value(), newValue)) + { + this->Value() = std::move(newValue); + changed = true; + } if (changed) - Engine::OnNodePulse(*this, turn); + return UpdateResult::changed; else - Engine::OnNodeIdlePulse(*this, turn); + return UpdateResult::unchanged; } - virtual const char* GetNodeType() const override { return "SignalOpNode"; } - virtual int DependencyCount() const override { return TOp::dependency_count; } + virtual const char* GetNodeType() const override + { return "SignalFunc"; } - TOp StealOp() - { - REACT_ASSERT(wasOpStolen_ == false, "Op was already stolen."); - wasOpStolen_ = true; - op_.template Detach(*this); - return std::move(op_); - } + virtual int GetDependencyCount() const override + { return sizeof...(TDeps); } private: - TOp op_; - bool wasOpStolen_ = false; + std::tuple> ...> depHolder_; + + F func_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// FlattenNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename TOuter, - typename TInner -> -class FlattenNode : public SignalNode +template +class SignalFlattenNode : public SignalNode { - using Engine = typename FlattenNode::Engine; - public: - FlattenNode(const std::shared_ptr>& outer, - const std::shared_ptr>& inner) : - FlattenNode::SignalNode( inner->ValueRef() ), + SignalFlattenNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& outer, const std::shared_ptr>& inner) : + SignalFlattenNode::SignalNode( graphPtr, inner->Value() ), outer_( outer ), inner_( inner ) { - Engine::OnNodeCreate(*this); - Engine::OnNodeAttach(*this, *outer_); - Engine::OnNodeAttach(*this, *inner_); + this->RegisterMe(); + this->AttachToMe(outer->GetNodeId()); + this->AttachToMe(inner->GetNodeId()); } - ~FlattenNode() + ~SignalFlattenNode() { - Engine::OnNodeDetach(*this, *inner_); - Engine::OnNodeDetach(*this, *outer_); - Engine::OnNodeDestroy(*this); + this->DetachFromMe(inner->GetNodeId()); + this->DetachFromMe(outer->GetNodeId()); + this->UnregisterMe(); } - virtual void Update(void* turnPtr) override + virtual UpdateResult Update(TurnId turnId) override { - using TurnT = typename D::Engine::TurnT; - TurnT& turn = *reinterpret_cast(turnPtr); - - auto newInner = GetNodePtr(outer_->ValueRef()); + auto newInner = GetNodePtr(outer_->Value()); if (newInner != inner_) { @@ -336,36 +231,35 @@ class FlattenNode : public SignalNode auto oldInner = inner_; inner_ = newInner; - Engine::OnDynamicNodeDetach(*this, *oldInner, turn); - Engine::OnDynamicNodeAttach(*this, *newInner, turn); + this->DynamicDetachFromMe(oldInner->GetNodeId(), 0); + this->DynamicAttachToMe(newInner->GetNodeId(), 0); - return; + return UpdateResult::shifted; } - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - REACT_LOG(D::Log().template Append( - GetObjectId(*this), turn.Id())); - - if (! Equals(this->value_, inner_->ValueRef())) + if (! Equals(this->Value(), inner_->Value())) { - this->value_ = inner_->ValueRef(); - Engine::OnNodePulse(*this, turn); + this->Value() = inner_->Value(); + return UpdateResult::changed; } else { - Engine::OnNodeIdlePulse(*this, turn); + return UpdateResult::unchanged; } } - virtual const char* GetNodeType() const override { return "FlattenNode"; } - virtual bool IsDynamicNode() const override { return true; } - virtual int DependencyCount() const override { return 2; } + virtual const char* GetNodeType() const override + { return "SignalFlatten"; } + + virtual bool IsDynamicNode() const override + { return true; } + + virtual int GetDependencyCount() const override + { return 2; } private: - std::shared_ptr> outer_; - std::shared_ptr> inner_; + std::shared_ptr> outer_; + std::shared_ptr> inner_; }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/engine/PulsecountEngine.h b/include/react/engine/PulsecountEngine.h index 95308f3c..78999c6f 100644 --- a/include/react/engine/PulsecountEngine.h +++ b/include/react/engine/PulsecountEngine.h @@ -9,6 +9,8 @@ #pragma once +#if 0 + #include "react/detail/Defs.h" #include @@ -144,4 +146,6 @@ struct NodeUpdateTimerEnabled> : std::tru /****************************************/ REACT_IMPL_END /***************************************/ -#endif // REACT_DETAIL_ENGINE_PULSECOUNTENGINE_H_INCLUDED \ No newline at end of file +#endif // REACT_DETAIL_ENGINE_PULSECOUNTENGINE_H_INCLUDED + +#endif \ No newline at end of file diff --git a/include/react/engine/SubtreeEngine.h b/include/react/engine/SubtreeEngine.h index cfda49d1..447c1184 100644 --- a/include/react/engine/SubtreeEngine.h +++ b/include/react/engine/SubtreeEngine.h @@ -4,6 +4,8 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#if 0 + #ifndef REACT_DETAIL_ENGINE_SUBTREEENGINE_H_INCLUDED #define REACT_DETAIL_ENGINE_SUBTREEENGINE_H_INCLUDED @@ -191,4 +193,6 @@ struct NodeUpdateTimerEnabled> : std::true_t /****************************************/ REACT_IMPL_END /***************************************/ -#endif // REACT_DETAIL_ENGINE_SUBTREEENGINE_H_INCLUDED \ No newline at end of file +#endif // REACT_DETAIL_ENGINE_SUBTREEENGINE_H_INCLUDED + +#endif \ No newline at end of file diff --git a/include/react/engine/ToposortEngine.h b/include/react/engine/ToposortEngine.h index 331ecd11..2575bbd9 100644 --- a/include/react/engine/ToposortEngine.h +++ b/include/react/engine/ToposortEngine.h @@ -4,6 +4,8 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#if 0 + #ifndef REACT_DETAIL_ENGINE_TOPOSORTENGINE_H_INCLUDED #define REACT_DETAIL_ENGINE_TOPOSORTENGINE_H_INCLUDED @@ -207,4 +209,6 @@ struct NodeUpdateTimerEnabled> : std::true_ /****************************************/ REACT_IMPL_END /***************************************/ -#endif // REACT_DETAIL_ENGINE_TOPOSORTENGINE_H_INCLUDED \ No newline at end of file +#endif // REACT_DETAIL_ENGINE_TOPOSORTENGINE_H_INCLUDED + +#endif \ No newline at end of file diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index f2e9c00c..0d380c1c 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -154,11 +154,12 @@ + - - + + @@ -167,11 +168,8 @@ - - - - - + + @@ -181,21 +179,16 @@ - - - - + - - diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index 2b7a6008..1174c9b1 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -25,15 +25,9 @@ {11a75126-8bfd-4693-be4b-4e06ab73450c} - - {f58215db-3a12-432a-a4c1-e24a90df70a9} - {3f444875-ab1a-4bbd-99eb-7018c930098a} - - {22da08e3-fb85-4fed-9fbc-8944ef866ccc} - @@ -51,9 +45,6 @@ Header Files\common - - Header Files - Header Files\common @@ -66,30 +57,12 @@ Header Files\detail - - Header Files\detail - - - Header Files\detail - Header Files\detail\graph Header Files\detail\graph - - Header Files\logging - - - Header Files\logging - - - Header Files\logging - - - Header Files - Header Files\detail @@ -102,30 +75,12 @@ Header Files\detail\graph - - Header Files\detail - - - Header Files - - - Header Files\detail - - - Header Files - Header Files\detail\graph Header Files\engine - - Header Files - - - Header Files - Header Files\common @@ -135,20 +90,35 @@ Header Files\common - + Header Files\detail - - Header Files\detail + + Header Files\detail\graph + + + Header Files\detail\graph + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files - - Source Files\logging - - - Source Files\logging - Source Files\engine @@ -158,5 +128,8 @@ Source Files\engine + + Source Files + \ No newline at end of file diff --git a/src/engine/PulsecountEngine.cpp b/src/engine/PulsecountEngine.cpp index 5fa3a844..0be62ba6 100644 --- a/src/engine/PulsecountEngine.cpp +++ b/src/engine/PulsecountEngine.cpp @@ -4,6 +4,8 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#if 0 + #include "react/engine/PulsecountEngine.h" #include @@ -281,4 +283,6 @@ void EngineBase::OnDynamicNodeDetach(Node& node, Node& parent, Turn& turn) }// ~parent.ShiftMutex (write) } // ~namespace pulsecount -/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file +/****************************************/ REACT_IMPL_END /***************************************/ + +#endif \ No newline at end of file diff --git a/src/engine/SubtreeEngine.cpp b/src/engine/SubtreeEngine.cpp index c71d6e0f..8584fcc2 100644 --- a/src/engine/SubtreeEngine.cpp +++ b/src/engine/SubtreeEngine.cpp @@ -4,6 +4,8 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#if 0 + #include "react/engine/SubtreeEngine.h" #include @@ -341,4 +343,6 @@ void EngineBase::invalidateSuccessors(Node& node) } } // ~namespace subtree -/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file +/****************************************/ REACT_IMPL_END /***************************************/ + +#endif \ No newline at end of file diff --git a/src/engine/ToposortEngine.cpp b/src/engine/ToposortEngine.cpp index 54612191..b8db5f67 100644 --- a/src/engine/ToposortEngine.cpp +++ b/src/engine/ToposortEngine.cpp @@ -4,6 +4,8 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) +#if 0 + #include "react/engine/ToposortEngine.h" #include "tbb/parallel_for.h" @@ -216,4 +218,6 @@ void ParEngineBase::invalidateSuccessors(ParNode& node) } } // ~namespace toposort -/****************************************/ REACT_IMPL_END /***************************************/ \ No newline at end of file +/****************************************/ REACT_IMPL_END /***************************************/ + +#endif \ No newline at end of file From 54d1f02576b09229cf9f2cb7e66f912e197cdf27 Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 5 Aug 2016 03:18:28 +0200 Subject: [PATCH 222/266] Redesign progress. --- include/react/Algorithm.h | 51 ++-- include/react/Event.h | 270 +++++++------------- include/react/Signal.h | 105 ++++---- include/react/detail/ObserverBase.h | 2 - include/react/detail/graph/AlgorithmNodes.h | 4 +- include/react/detail/graph/EventNodes.h | 26 +- 6 files changed, 189 insertions(+), 269 deletions(-) diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index e2a1f3a9..d41d0fef 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -23,41 +23,34 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Hold - Hold the most recent event in a signal /////////////////////////////////////////////////////////////////////////////////////////////////// -template -auto Hold(const Events& events, U&& init) -> Signal +template +auto Hold(T&& initialValue, const EventBase& events) -> Signal { using REACT_IMPL::HoldNode; + using REACT_IMPL::PrivateNodeInterface; - return Signal( - std::make_shared>( - std::forward(init), GetNodePtr(events))); + return Signal( std::make_shared>( + PrivateNodeInterface::GraphPtr(events), std::forward(initialValue), PrivateNodeInterface::NodePtr(events)) ); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Monitor - Emits value changes of target signal /////////////////////////////////////////////////////////////////////////////////////////////////// -template -auto Monitor(const Signal& target) -> Events +template +auto Monitor(const SignalBase& signal) -> Event { using REACT_IMPL::MonitorNode; + using REACT_IMPL::PrivateNodeInterface; - return Events( - std::make_shared>( - GetNodePtr(target))); + return Event( std::make_shared>( + PrivateNodeInterface::GraphPtr(signal), PrivateNodeInterface::NodePtr(signal)) ); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Iterate - Iteratively combines signal value with values from event stream (aka Fold) /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename S, - typename E, - typename V, - typename F, - -> -auto Iterate(const Events& events, V&& init, F&& func) -> Signal +template +auto Iterate(T&& init, F&& func, const Events& events) -> Signal { using REACT_IMPL::IterateNode; using REACT_IMPL::IterateByRefNode; @@ -178,28 +171,28 @@ auto Iterate(const Events& events, V&& init, const SignalPack& /// Snapshot - Sets signal value to value of other signal when event is received /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Snapshot(const Events& trigger, const Signal& target) -> Signal +auto Snapshot(const Signal& signal, const Events& trigger) -> Signal { using REACT_IMPL::SnapshotNode; + using REACT_IMPL::GetCheckedGraphPtr; + using REACT_IMPL::PrivateNodeInterface; - return Signal( - std::make_shared>( - GetNodePtr(target), GetNodePtr(trigger))); + return Events( std::make_shared>( + GetCheckedGraphPtr(signal, trigger), PrivateNodeInterface::NodePtr(signal), PrivateNodeInterface::NodePtr(trigger)) ); } - - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Pulse - Emits value of target signal when event is received /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Pulse(const Events& trigger, const Signal& target) -> Events +auto Pulse(const Signal& signal, const Events& trigger) -> Events { using REACT_IMPL::PulseNode; + using REACT_IMPL::GetCheckedGraphPtr; + using REACT_IMPL::PrivateNodeInterface; - return Events( - std::make_shared>( - GetNodePtr(target), GetNodePtr(trigger))); + return Events( std::make_shared>( + GetCheckedGraphPtr(signal, trigger), PrivateNodeInterface::NodePtr(signal), PrivateNodeInterface::NodePtr(trigger)) ); } /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/Event.h b/include/react/Event.h index 5af37093..e244ec46 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -25,11 +25,11 @@ /// Events /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class EventBase { private: - using NodeType = REACT_IMPL::EventStreamNode; + using NodeType = REACT_IMPL::EventStreamNode; public: EventBase() = default; @@ -69,16 +69,26 @@ class EventBase auto NodePtr() const -> const std::shared_ptr& { return nodePtr_; } - template - auto CreateProcessingNode(FIn&& func, const EventBase& dep) -> decltype(auto) + template + auto CreateProcessingNode(F&& func, const EventBase& dep) -> decltype(auto) { - using F = typename std::decay::type; - using ProcessingNodeType = REACT_IMPL::EventProcessingNode; + using ProcessingNodeType = REACT_IMPL::EventProcessingNode::type>; return std::make_shared( REACT_IMPL::PrivateNodeInterface::GraphPtr(dep), - REACT_IMPL::PrivateNodeInterface::NodePtr(dep), - std::forward(func)); + std::forward(func), + REACT_IMPL::PrivateNodeInterface::NodePtr(dep)); + } + + template + auto CreateSyncedProcessingNode(F&& func, const EventBase& dep, const SignalBase ... syncs) -> decltype(auto) + { + using SyncedProcessingNodeType = REACT_IMPL::SyncedEventProcessingNode::type, Us ...>; + + return std::make_shared( + REACT_IMPL::GetCheckedGraphPtr(dep, syncs ...), + std::forward(func), + REACT_IMPL::PrivateNodeInterface::NodePtr(dep), REACT_IMPL::PrivateNodeInterface::NodePtr(syncs) ...); } private: @@ -90,11 +100,11 @@ class EventBase /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventSource /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EventSourceBase : public EventBase +template +class EventSourceBase : public EventBase { private: - using NodeType = REACT_IMPL::EventSourceNode; + using NodeType = REACT_IMPL::EventSourceNode; public: using EventBase::EventBase; @@ -112,25 +122,25 @@ class EventSourceBase : public EventBase EventSourceBase::EventBase( std::make_shared(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) { } - void Emit(const T& value) + void Emit(const E& value) { EmitValue(value); } - void Emit(T&& value) + void Emit(E&& value) { EmitValue(std::move(value)); } - template ::value>::type> + template ::value>::type> void Emit() { EmitValue(Token::value); } - EventSourceBase& operator<<(const T& value) + EventSourceBase& operator<<(const E& value) { EmitValue(e); return *this; } - EventSourceBase& operator<<(T&& value) + EventSourceBase& operator<<(E&& value) { EmitValue(std::move(value)); return *this; } private: - template - void EmitValue(U&& value) + template + void EmitValue(T&& value) { using REACT_IMPL::NodeId; using REACT_IMPL::IReactiveGraph; @@ -139,20 +149,20 @@ class EventSourceBase : public EventBase NodeId nodeId = castedPtr->GetNodeId(); auto& graphPtr = NodePtr()->GraphPtr(); - graphPtr->AddInput(nodeId, [castedPtr, &value] { castedPtr->EmitValue(std::forward(value)); }); + graphPtr->AddInput(nodeId, [castedPtr, &value] { castedPtr->EmitValue(std::forward(value)); }); } }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Event /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Event : public EventBase +template +class Event : public EventBase { public: using EventBase::EventBase; - using ValueType = T; + using ValueType = E; Event() = delete; @@ -162,19 +172,24 @@ class Event : public EventBase Event(Event&&) = default; Event& operator=(Event&&) = default; - template - Event(F&& func, const EventBase& dep) : + template + Event(F&& func, const EventBase& dep) : Event::EventBase( CreateProcessingNode(std::forward(func), dep) ) { } + + template + Event(F&& func, const EventBase& dep, const SignalBase ... signals) : + Event::EventBase( CreateSyncedProcessingNode(std::forward(func), dep, signals ...) ) + { } }; -template -class Event : public EventBase +template +class Event : public EventBase { public: using EventBase::EventBase; - using ValueType = T; + using ValueType = E; Event() = delete; @@ -184,29 +199,34 @@ class Event : public EventBase Event(Event&&) = default; Event& operator=(Event&&) = default; - Event(Event&& other) : + Event(Event&& other) : Event::EventBase( std::move(other) ) { } - Event& operator=(Event&& other) + Event& operator=(Event&& other) { Event::EventBase::operator=(std::move(other)); return *this; } - template - Event(F&& func, const EventBase& dep) : + template + Event(F&& func, const EventBase& dep) : Event::EventBase( CreateProcessingNode(std::forward(func), dep) ) { } + + template + Event(F&& func, const EventBase& dep, const SignalBase ... signals) : + Event::EventBase( SyncedCreateProcessingNode(std::forward(func), dep, signals ...) ) + { } }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventSource /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EventSource : public EventSourceBase +template +class EventSource : public EventSourceBase { public: using EventSourceBase::EventSourceBase; - using ValueType = T; + using ValueType = E; EventSource() = delete; @@ -217,13 +237,13 @@ class EventSource : public EventSourceBase EventSource& operator=(EventSource&&) = default; }; -template -class EventSource : public EventSourceBase +template +class EventSource : public EventSourceBase { public: using EventSourceBase::EventSourceBase; - using ValueType = T; + using ValueType = E; EventSource() = delete; @@ -233,11 +253,11 @@ class EventSource : public EventSourceBase EventSource(EventSource&&) = default; EventSource& operator=(EventSource&&) = default; - EventSource(EventSource&& other) : + EventSource(EventSource&& other) : EventSource::EventSourceBase( std::move(other) ) { } - EventSource& operator=(EventSource&& other) + EventSource& operator=(EventSource&& other) { EventSource::EventSourceBase::operator=(std::move(other)); return *this; } }; @@ -251,7 +271,7 @@ auto Merge(const EventBase& dep1, const EventBase& ... deps) -> decltype using REACT_IMPL::GetCheckedGraphPtr; using REACT_IMPL::PrivateNodeInterface; - static_assert(sizeof...(Us) > 0, "Merge: 2+ arguments are required."); + static_assert(sizeof...(Us) > 0, "Merge requires at least 2 inputs."); // If supplied, use merge type, otherwise default to common type. using E = typename std::conditional< @@ -268,174 +288,80 @@ auto Merge(const EventBase& dep1, const EventBase& ... deps) -> decltype /////////////////////////////////////////////////////////////////////////////////////////////////// /// Filter /////////////////////////////////////////////////////////////////////////////////////////////////// -template -auto Filter(F&& pred, const EventBase& dep) -> Event +template +auto Filter(F&& pred, const EventBase& dep) -> Event { - auto filterFunc = [capturedPred = std::forward(pred)] (EventRange inRange, EventSink out) + auto filterFunc = [capturedPred = std::forward(pred)] (EventRange inRange, EventSink out) { std::copy_if(inRange.begin(), inRange.end(), out, capturedPred); }; - return Event(std::move(filterFunc), dep); + return Event(std::move(filterFunc), dep); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Transform -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -auto Transform(F&& op, const EventBase& dep) -> Event +template +auto Filter(F&& pred, const EventBase& dep, const SignalBase& ... signals) -> Event { - auto transformFunc = [capturedPred = std::forward(op)] (EventRange inRange, EventSink out) - { std::transform(inRange.begin(), inRange.end(), out, pred); }; - - return Event(std::move(transformFunc), dep); -} - -#if 0 - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Filter - Synced -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename E, - typename FIn, - typename ... TDepValues -> -auto Filter(const Events& source, const SignalPack& depPack, FIn&& func) -> Events -{ - using REACT_IMPL::SyncedEventFilterNode; - - using F = typename std::decay::type; - - struct NodeBuilder_ + auto filterFunc = [capturedPred = std::forward(pred)] (EventRange inRange, EventSink out, const Us& ... values) { - NodeBuilder_(const Events& source, FIn&& func) : - MySource( source ), - MyFunc( std::forward(func) ) - {} - - auto operator()(const Signal& ... deps) - -> Events - { - return Events( - std::make_shared>( - GetNodePtr(MySource), std::forward(MyFunc), GetNodePtr(deps) ...)); - } - - const Events& MySource; - FIn MyFunc; + for (const auto& v : inRange) + if (capturedPred(v, values ...)) + *out++ = v; }; - return REACT_IMPL::apply( - NodeBuilder_( source, std::forward(func) ), - depPack.Data); + return Event(std::move(filterFunc), dep, signals ...); } - - /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Transform - Synced +/// Transform /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename TIn, - typename FIn, - typename ... TDepValues, - typename TOut = typename std::result_of::type -> -auto Transform(const Events& source, FIn&& func, const Signal& ... deps) -> Events +template +auto Transform(F&& op, const EventBase& dep) -> Event { - using REACT_IMPL::SyncedEventTransformNode; + auto transformFunc = [capturedOp = std::forward(op)] (EventRange inRange, EventSink out) + { std::transform(inRange.begin(), inRange.end(), out, capturedOp); }; - using F = typename std::decay::type; - - struct NodeBuilder_ - { - NodeBuilder_(const Events& source, FIn&& func) : - MySource( source ), - MyFunc( std::forward(func) ) - {} - - auto operator()(const Signal& ... deps) - -> Events - { - return Events( - std::make_shared>( - GetNodePtr(MySource), std::forward(MyFunc), GetNodePtr(deps) ...)); - } - - const Events& MySource; - FIn MyFunc; - }; - - return REACT_IMPL::apply( - NodeBuilder_( source, std::forward(func) ), - depPack.Data); + return Event(std::move(transformFunc), dep); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Process - Synced -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename TOut, - typename TIn, - typename FIn, - typename ... TDepValues -> -auto Process(const Events& source, const SignalPack& depPack, FIn&& func) -> Events +template +auto Transform(F&& op, const EventBase& dep, const SignalBase& ... signals) -> Event { - using REACT_IMPL::SyncedEventProcessingNode; - - using F = typename std::decay::type; - - struct NodeBuilder_ + auto transformFunc = [capturedOp = std::forward(pred)] (EventRange inRange, EventSink out, const Vs& ... values) { - NodeBuilder_(const Events& source, FIn&& func) : - MySource( source ), - MyFunc( std::forward(func) ) - {} - - auto operator()(const Signal& ... deps) - -> Events - { - return Events( - std::make_shared>( - GetNodePtr(MySource), std::forward(MyFunc), GetNodePtr(deps) ...)); - } - - const Events& MySource; - FIn MyFunc; + for (const auto& v : inRange) + *out++ = capturedPred(v, values ...); }; - return REACT_IMPL::apply( - NodeBuilder_( source, std::forward(func) ), - depPack.Data); + return Event(std::move(transformFunc), dep, signals ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Flatten /////////////////////////////////////////////////////////////////////////////////////////////////// -template +/*template auto Flatten(const Signal>& outer) -> Events { return Events( std::make_shared, TInnerValue>>( GetNodePtr(outer), GetNodePtr(outer.Value()))); -} +}*/ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Join /////////////////////////////////////////////////////////////////////////////////////////////////// -template -auto Join(const Events& ... args) -> Events> +template +auto Join(const EventBase& ... deps) -> Event, unique> { using REACT_IMPL::EventJoinNode; + using REACT_IMPL::GetCheckedGraphPtr; + using REACT_IMPL::PrivateNodeInterface; + + static_assert(sizeof...(Ts) > 1, "Join requires at least 2 inputs."); - static_assert(sizeof...(TArgs) > 1, "Join: 2+ arguments are required."); + // If supplied, use merge type, otherwise default to common type. + const auto& graphPtr = GetCheckedGraphPtr(deps ...); - return Events< std::tuple>( - std::make_shared>( - GetNodePtr(args) ...)); + return Event, unique>( + std::make_shared>(graphPtr, PrivateNodeInterface::NodePtr(deps) ...)); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -443,7 +369,7 @@ auto Join(const Events& ... args) -> Events> /////////////////////////////////////////////////////////////////////////////////////////////////// enum class Token { value }; -struct Tokenizer +/*struct Tokenizer { template Token operator()(const T&) const { return Token::value; } @@ -453,9 +379,7 @@ template auto Tokenize(T&& source) -> decltype(auto) { return Transform(source, Tokenizer{ }); -} - -#endif +}*/ /******************************************/ REACT_END /******************************************/ diff --git a/include/react/Signal.h b/include/react/Signal.h index ea60ce3a..e8222bf3 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -29,11 +29,11 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// SignalBase /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class SignalBase { private: - using NodeType = REACT_IMPL::SignalNode; + using NodeType = REACT_IMPL::SignalNode; public: SignalBase() = default; @@ -46,12 +46,12 @@ class SignalBase ~SignalBase() = default; - // Internal node ctor + // Private node ctor explicit SignalBase(std::shared_ptr&& nodePtr) : nodePtr_( std::move(nodePtr) ) { } - const T& Value() const + const S& Value() const { return nodePtr_->Value(); } protected: @@ -61,16 +61,18 @@ class SignalBase auto NodePtr() const -> const std::shared_ptr& { return nodePtr_; } - template - auto CreateFuncNode(FIn&& func, const SignalBase& dep1, const SignalBase& ... deps) -> decltype(auto) + template + auto CreateFuncNode(F&& func, const SignalBase& dep1, const SignalBase& ... deps) -> decltype(auto) { - using F = typename std::decay::type; - using FuncNodeType = REACT_IMPL::SignalFuncNode; + using REACT_IMPL::GetCheckedGraphPtr; + using REACT_IMPL::PrivateNodeInterface; + + using FuncNodeType = REACT_IMPL::SignalFuncNode::type, T1, Ts ...>; return std::make_shared( - REACT_IMPL::GetCheckedGraphPtr(dep1, deps ...), - std::forward(func), - REACT_IMPL::PrivateNodeInterface::NodePtr(dep1), REACT_IMPL::PrivateNodeInterface::NodePtr(deps) ...); + GetCheckedGraphPtr(dep1, deps ...), + std::forward(func), + PrivateNodeInterface::NodePtr(dep1), PrivateNodeInterface::NodePtr(deps) ...); } private: @@ -82,8 +84,8 @@ class SignalBase /////////////////////////////////////////////////////////////////////////////////////////////////// /// VarSignalBase /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class VarSignalBase : public SignalBase +template +class VarSignalBase : public SignalBase { public: using SignalBase::SignalBase; @@ -96,16 +98,16 @@ class VarSignalBase : public SignalBase VarSignalBase(VarSignalBase&&) = default; VarSignalBase& operator=(VarSignalBase&&) = default; - void Set(const T& newValue) + void Set(const S& newValue) { SetValue(newValue); } - void Set(T&& newValue) + void Set(S&& newValue) { SetValue(std::move(newValue)); } - void operator<<=(const T& newValue) + void operator<<=(const S& newValue) { SetValue(newValue); } - void operator<<=(T&& newValue) + void operator<<=(S&& newValue) { SetValue(std::move(newValue)); } template @@ -113,26 +115,29 @@ class VarSignalBase : public SignalBase { ModifyValue(func); } protected: - template - auto CreateVarNode(U&& value, const TGroup& group) -> decltype(auto) + template + auto CreateVarNode(T&& value, const TGroup& group) -> decltype(auto) { - using VarNodeType = REACT_IMPL::VarSignalNode; - return std::make_shared(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(value)); + using REACT_IMPL::PrivateReactiveGroupInterface; + + using VarNodeType = REACT_IMPL::VarSignalNode; + + return std::make_shared(PrivateReactiveGroupInterface::GraphPtr(group), std::forward(value)); } private: - template - void SetValue(U&& newValue) + template + void SetValue(T&& newValue) { using REACT_IMPL::NodeId; using REACT_IMPL::IReactiveGraph; - using VarNodeType = REACT_IMPL::VarSignalNode; + using VarNodeType = REACT_IMPL::VarSignalNode; VarNodeType* castedPtr = static_cast(this->NodePtr().get()); NodeId nodeId = castedPtr->GetNodeId(); auto& graphPtr = NodePtr()->GraphPtr(); - graphPtr->AddInput(nodeId, [castedPtr, &newValue] { castedPtr->SetValue(std::forward(newValue)); }); + graphPtr->AddInput(nodeId, [castedPtr, &newValue] { castedPtr->SetValue(std::forward(newValue)); }); } template @@ -140,7 +145,7 @@ class VarSignalBase : public SignalBase { using REACT_IMPL::NodeId; using REACT_IMPL::IReactiveGraph; - using VarNodeType = REACT_IMPL::VarSignalNode; + using VarNodeType = REACT_IMPL::VarSignalNode; VarNodeType* castedPtr = static_cast(this->NodePtr().get()); @@ -153,13 +158,13 @@ class VarSignalBase : public SignalBase /////////////////////////////////////////////////////////////////////////////////////////////////// /// Signal /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Signal : public SignalBase +template +class Signal : public SignalBase { public: using SignalBase::SignalBase; - using ValueType = T; + using ValueType = S; Signal() = delete; @@ -175,13 +180,13 @@ class Signal : public SignalBase { } }; -template -class Signal : public SignalBase +template +class Signal : public SignalBase { public: using SignalBase::SignalBase; - using ValueType = T; + using ValueType = S; Signal() = delete; @@ -191,15 +196,15 @@ class Signal : public SignalBase Signal(Signal&&) = default; Signal& operator=(Signal&&) = default; - Signal(Signal&& other) : + Signal(Signal&& other) : Signal::SignalBase( std::move(other) ) { } - Signal& operator=(Signal&& other) + Signal& operator=(Signal&& other) { Signal::SignalBase::operator=(std::move(other)); return *this; } - template - Signal(F&& func, const SignalBase& ... deps) : + template + Signal(F&& func, const SignalBase& ... deps) : Signal::SignalBase( CreateFuncNode(std::forward(func), deps ...) ) { } }; @@ -207,13 +212,13 @@ class Signal : public SignalBase /////////////////////////////////////////////////////////////////////////////////////////////////// /// Signal /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class VarSignal : public VarSignalBase +template +class VarSignal : public VarSignalBase { public: using VarSignalBase::VarSignalBase; - using ValueType = T; + using ValueType = S; VarSignal() = delete; @@ -223,19 +228,19 @@ class VarSignal : public VarSignalBase VarSignal(VarSignal&&) = default; VarSignal& operator=(VarSignal&&) = default; - template - VarSignal(U&& value, const TGroup& group) : - VarSignal::VarSignalBase( CreateVarNode(std::forward(value), group) ) + template + VarSignal(T&& value, const TGroup& group) : + VarSignal::VarSignalBase( CreateVarNode(std::forward(value), group) ) { } }; -template -class VarSignal : public VarSignalBase +template +class VarSignal : public VarSignalBase { public: using VarSignalBase::VarSignalBase; - using ValueType = T; + using ValueType = S; VarSignal() = delete; @@ -245,16 +250,16 @@ class VarSignal : public VarSignalBase VarSignal(VarSignal&&) = default; VarSignal& operator=(VarSignal&&) = default; - VarSignal(VarSignal&& other) : + VarSignal(VarSignal&& other) : VarSignal::VarSignalBase( std::move(other) ) { } - VarSignal& operator=(VarSignal&& other) + VarSignal& operator=(VarSignal&& other) { VarSignal::SignalBase::operator=(std::move(other)); return *this; } - template - VarSignal(U&& value, const TGroup& group) : - VarSignal::VarSignalBase( CreateVarNode(std::forward(value), group) ) + template + VarSignal(T&& value, const TGroup& group) : + VarSignal::VarSignalBase( CreateVarNode(std::forward(value), group) ) { } }; diff --git a/include/react/detail/ObserverBase.h b/include/react/detail/ObserverBase.h index 73f637fd..6653ff04 100644 --- a/include/react/detail/ObserverBase.h +++ b/include/react/detail/ObserverBase.h @@ -15,8 +15,6 @@ #include #include -#include "IReactiveNode.h" - /***************************************/ REACT_IMPL_BEGIN /**************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index 4c4fdc35..e3b8d584 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -86,10 +86,10 @@ class IterateNode : public SignalNode { public: template - IterateNode(const std::shared_ptr& graphPtr, U&& init, const std::shared_ptr>& events, V&& func) : + IterateNode(const std::shared_ptr& graphPtr, U&& init, const std::shared_ptr>& events, FIn&& func) : IterateNode::SignalNode( graphPtr, std::forward(init) ), events_( events ), - func_( std::forward(func) ) + func_( std::forward(func) ) { this->RegisterMe(); this->AttachToMe(events->GetNodeId()); diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index a68df789..124636fc 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -293,11 +293,11 @@ template class EventProcessingNode : public EventStreamNode { public: - template - EventProcessingNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& dep, U&& func) : + template + EventProcessingNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& dep) : EventProcessingNode::EventStreamNode( graphPtr ), - dep_( dep ), - func_( std::forward(func) ) + func_( std::forward(func) ), + dep_( dep ) { this->RegisterMe(); this->AttachToMe(dep->GetNodeId()); @@ -328,9 +328,9 @@ class EventProcessingNode : public EventStreamNode { return 1; } private: - std::shared_ptr> dep_; - F func_; + + std::shared_ptr> dep_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -340,11 +340,11 @@ template class SyncedEventProcessingNode : public EventStreamNode { public: - template - SyncedEventProcessingNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& dep, U&& func, const std::shared_ptr>& ... syncs) : + template + SyncedEventProcessingNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& dep, const std::shared_ptr>& ... syncs) : SyncedEventProcessingNode::EventStreamNode( graphPtr ), + func_( std::forward(func) ), dep_( dep ), - func_( std::forward(func) ), syncHolder_( syncs ... ) { this->RegisterMe(); @@ -364,7 +364,7 @@ class SyncedEventProcessingNode : public EventStreamNode this->SetCurrentTurn(turnId, true); // Update of this node could be triggered from deps, // so make sure source doesnt contain events from last turn - source_->SetCurrentTurn(turnId); + dep_->SetCurrentTurn(turnId); apply( [this] (const auto& ... syncs) @@ -386,10 +386,10 @@ class SyncedEventProcessingNode : public EventStreamNode { return 1 + sizeof...(TSyncs); } private: - std::shared_ptr> dep_; - F func_; + std::shared_ptr> dep_; + std::tuple>...> syncHolder_; }; @@ -473,7 +473,7 @@ class EventJoinNode : public EventStreamNode> template static void FetchBuffer(TurnId turnId, Slot& slot) { - slot.Source->SetCurrentTurn(turnId); + slot.source->SetCurrentTurn(turnId); slot.buffer.insert(slot.buffer.end(), slot.source->Events().begin(), slot.source->Events().end()); } From bd4c67ad0f6e5c511b7910b043c72458c6897e71 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sat, 6 Aug 2016 17:45:04 +0200 Subject: [PATCH 223/266] Redesign progress. --- examples/src/BasicSignals.cpp | 337 ++++------------- include/react/API.h | 7 + include/react/Algorithm.h | 30 +- include/react/Event.h | 88 +++-- include/react/Group.h | 28 +- include/react/Observer.h | 403 +++++++-------------- include/react/Reactor.h | 76 ---- include/react/Signal.h | 81 +++-- include/react/detail/EngineBase.h | 49 --- include/react/detail/ObserverBase.h | 77 ---- include/react/detail/graph/EventNodes.h | 6 +- include/react/detail/graph/GraphBase.h | 4 +- include/react/detail/graph/ObserverNodes.h | 95 +++-- include/react/detail/graph/SignalNodes.h | 84 +++-- project/msvc/CppReact.vcxproj | 2 - project/msvc/CppReact.vcxproj.filters | 6 - 16 files changed, 415 insertions(+), 958 deletions(-) delete mode 100644 include/react/Reactor.h delete mode 100644 include/react/detail/EngineBase.h delete mode 100644 include/react/detail/ObserverBase.h diff --git a/examples/src/BasicSignals.cpp b/examples/src/BasicSignals.cpp index 7f6943f2..bebdf7bf 100644 --- a/examples/src/BasicSignals.cpp +++ b/examples/src/BasicSignals.cpp @@ -12,7 +12,8 @@ #include #include "react/Signal.h" -/* +#include "react/Observer.h" + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Example 1 - Hello world /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -21,74 +22,37 @@ namespace example1 using namespace std; using namespace react; - // Defines a domain. + // The concat function + string ConcatFunc(string first, string second) + { return first + string(" ") + second; } + + // Defines a group. // Each domain represents a separate dependency graph, managed by a dedicated propagation engine. // Reactives of different domains can not be combined. - ReactiveGroup - - // Define type aliases for the given domain in this namespace. - // Now we can use VarSignalT instead of D::VarSignalT. - USING_REACTIVE_DOMAIN(D) - - // The concat function - string concatFunc(string first, string second) { - return first + string(" ") + second; - } + ReactiveGroup<> group; + + // The two words + VarSignal firstWord( string("Change"), group ); + VarSignal secondWord( string("me!"), group ); // A signal that concatenates both words - namespace v1 - { - // The two words - VarSignalT firstWord( string("Change") ); - VarSignalT secondWord( string("me!") ); - - SignalT bothWords = MakeSignal(With(firstWord,secondWord), concatFunc); - - void Run() - { - cout << "Example 1 - Hello world (MakeSignal)" << endl; - - // Imperative imperative value access - cout << bothWords.Value() << endl; - - // Imperative imperative change - firstWord <<= string("Hello"); - - cout << bothWords.Value() << endl; - - secondWord <<= string("World"); - - cout << bothWords.Value() << endl; - - cout << endl; - } - } + Signal bothWords(ConcatFunc, firstWord, secondWord); - // Using overloaded operator + instead of explicit MakeSignal - namespace v2 + void Run() { - // The two words - VarSignalT firstWord = MakeVar(string("Change")); - VarSignalT secondWord = MakeVar(string("me!")); - - SignalT bothWords = firstWord + string(" ") + secondWord; + cout << "Example 1 - Hello world" << endl; - void Run() - { - cout << "Example 1 - Hello world (operators)" << endl; + cout << bothWords.Value() << endl; - cout << bothWords.Value() << endl; + firstWord <<= string("Hello"); - firstWord <<= string("Hello"); + cout << bothWords.Value() << endl; - cout << bothWords.Value() << endl; + secondWord <<= string("World"); - secondWord <<= string("World"); + cout << bothWords.Value() << endl; - cout << bothWords.Value() << endl; - - cout << endl; - } + cout << endl; } } @@ -100,19 +64,19 @@ namespace example2 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) + ReactiveGroup<> group; - VarSignalT x = MakeVar(1); - SignalT xAbs = MakeSignal(x, [] (int x) { return abs(x); }); + VarSignal x( 1, group ); + + Signal xAbs( [] (int v) { return abs(v); }, x); void Run() { cout << "Example 2 - Reacting to value changes" << endl; - Observe(xAbs, [] (int newValue) { - cout << "xAbs changed to " << newValue << endl; - }); + Observer<> obs( + [] (int newValue) { cout << "xAbs changed to " << newValue << endl; }, + xAbs ); // initially x is 1 x <<= 2; // output: xAbs changed to 2 @@ -131,28 +95,31 @@ namespace example3 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) + int sumFunc(int a, int b) + { return a + b; } + + ReactiveGroup<> group; - VarSignalT a = MakeVar(1); - VarSignalT b = MakeVar(1); + VarSignal a( 1, group ); + VarSignal b( 1, group ); - SignalT x = a + b; - SignalT y = a + b; - SignalT z = x + y; + Signal x( sumFunc, a, b ); + Signal y( sumFunc, a, b ); + Signal z( sumFunc, x, y ); void Run() { cout << "Example 3 - Changing multiple inputs" << endl; - Observe(z, [] (int newValue) { - std::cout << "z changed to " << newValue << std::endl; - }); + Observer<> obs( + [] (int newValue) { cout << "z changed to " << newValue << endl; }, + z ); a <<= 2; // output: z changed to 6 b <<= 2; // output: z changed to 8 - DoTransaction([] { + group.DoTransaction([&] + { a <<= 4; b <<= 4; }); // output: z changed to 16 @@ -169,27 +136,24 @@ namespace example4 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) + ReactiveGroup<> group; - VarSignalT> data = MakeVar(vector{ }); + VarSignal> data( group ); void Run() { cout << "Example 4 - Modifying signal values in place" << endl; - data.Modify([] (vector& data) { - data.push_back("Hello"); - }); + data.Modify([] (vector& data) + { data.push_back("Hello"); }); - data.Modify([] (vector& data) { - data.push_back("World"); - }); + data.Modify([] (vector& data) + { data.push_back("World"); }); for (const auto& s : data.Value()) cout << s << " "; cout << endl; - // output: Hell World + // output: Hello World cout << endl; } @@ -203,168 +167,49 @@ namespace example5 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) + ReactiveGroup<> group; // Helpers - using ExprPairT = pair; - using ExprVectT = vector; + using ExprPairType = pair; + using ExprVectType = vector; - string makeExprStr(int a, int b, const char* op) + string MakeExprStr(int a, int b, const char* op) { return to_string(a) + string(op) + to_string(b); } - ExprPairT makeExprPair(const string& s, int v) - { - return make_pair(s, v); - } - - void printExpressions(const ExprVectT& expressions) + void PrintExpressions(const ExprVectType& expressions) { cout << "Expressions: " << endl; for (const auto& p : expressions) cout << "\t" << p.first << " is " << p.second << endl; } - // Version 1 - Intermediate signals - namespace v1 - { - // Input operands - VarSignalT a = MakeVar(1); - VarSignalT b = MakeVar(2); - - // Calculations - SignalT sum = a + b; - SignalT diff = a - b; - SignalT prod = a * b; - - using std::placeholders::_1; - using std::placeholders::_2; - - // Stringified expressions - SignalT sumExpr = - MakeSignal(With(a,b), bind(makeExprStr, _1, _2, "+")); - - SignalT diffExpr = - MakeSignal(With(a,b), bind(makeExprStr, _1, _2, "-")); - - SignalT prodExpr = - MakeSignal(With(a,b), bind(makeExprStr, _1, _2, "*")); - - // The expression vector - SignalT expressions = MakeSignal( - With( - MakeSignal(With(sumExpr, sum), &makeExprPair), - MakeSignal(With(diffExpr, diff), &makeExprPair), - MakeSignal(With(prodExpr, prod), &makeExprPair) - ), - [] (const ExprPairT& sumP, const ExprPairT& diffP, const ExprPairT& prodP) { - return ExprVectT{ sumP, diffP, prodP}; - }); - - void Run() - { - cout << "Example 5 - Complex signals (v1)" << endl; - - Observe(expressions, printExpressions); - - a <<= 10; - b <<= 20; - - cout << endl; - } - } + // Input operands + VarSignal a( 1, group ); + VarSignal b( 2, group ); - // Version 2 - Intermediate signals in a function - namespace v2 - { - SignalT createExpressionSignal(const SignalT& a, const SignalT& b) - { - using std::placeholders::_1; - using std::placeholders::_2; - - // Inside a function, we can use auto - auto sumExpr = - MakeSignal(With(a,b), bind(makeExprStr, _1, _2, "+")); - - auto diffExpr = - MakeSignal(With(a,b), bind(makeExprStr, _1, _2, "-")); - - auto prodExpr = - MakeSignal(With(a,b), bind(makeExprStr, _1, _2, "*")); - - return MakeSignal( - With( - MakeSignal(With(sumExpr, a + b), &makeExprPair), - MakeSignal(With(diffExpr, a - b), &makeExprPair), - MakeSignal(With(prodExpr, a * b), &makeExprPair) - ), - [] (const ExprPairT& sumP, const ExprPairT& diffP, const ExprPairT& prodP) { - return ExprVectT{ sumP, diffP, prodP }; - }); - } - - // Input operands - VarSignalT a = MakeVar(1); - VarSignalT b = MakeVar(2); - - // The expression vector - SignalT expressions = createExpressionSignal(a, b); - - void Run() + // The expression vector + Signal expressions( + [] (int a, int b) { - cout << "Example 5 - Complex signals (v2)" << endl; - - Observe(expressions, printExpressions); - - a <<= 30; - b <<= 40; - - cout << endl; - } - } - - // Version 3 - Imperative function - namespace v3 - { - // Input operands - VarSignalT a = MakeVar(1); - VarSignalT b = MakeVar(2); - - // The expression vector - SignalT expressions = MakeSignal(With(a,b), [] (int a, int b) { - ExprVectT result; - - result.push_back( - make_pair( - makeExprStr(a, b, "+"), - a + b)); - - result.push_back( - make_pair( - makeExprStr(a, b, "-"), - a - b)); - - result.push_back( - make_pair( - makeExprStr(a, b, "*"), - a * b)); - + ExprVectType result; + result.push_back(make_pair(MakeExprStr(a, b, "+"), a + b)); + result.push_back(make_pair(MakeExprStr(a, b, "-"), a - b)); + result.push_back(make_pair(MakeExprStr(a, b, "*"), a * b)); return result; - }); + }, a, b ); - void Run() - { - cout << "Example 5 - Complex signals (v3)" << endl; + void Run() + { + cout << "Example 5 - Complex signals (v3)" << endl; - Observe(expressions, printExpressions); + Observer<> obs(PrintExpressions, expressions); - a <<= 50; - b <<= 60; + a <<= 50; + b <<= 60; - cout << endl; - } + cout << endl; } } @@ -373,45 +218,11 @@ namespace example5 /////////////////////////////////////////////////////////////////////////////////////////////////// int main() { - example1::v1::Run(); - example1::v2::Run(); - + example1::Run(); example2::Run(); - example3::Run(); - example4::Run(); - - example5::v1::Run(); - example5::v2::Run(); - example5::v3::Run(); - - return 0; -} - -*/ - -using namespace react; - -int main() -{ - auto group = ReactiveGroup( ); - - auto sig1 = VarSignal( 1, group ); - auto sig2 = VarSignal( 2, group ); - - sig1.Set(1); - sig1 <<= 1; - - sig2.Modify([] (int& value) { value = 3; }); - - group.DoTransaction( - [&] - { - sig1 <<= 2; - }); - - auto sig3 = Signal( [] (auto a, auto b) { return a + b; }, sig1, sig2 ); + example5::Run(); return 0; } \ No newline at end of file diff --git a/include/react/API.h b/include/react/API.h index b445b74c..68b6f531 100644 --- a/include/react/API.h +++ b/include/react/API.h @@ -23,6 +23,12 @@ enum OwnershipPolicy shared }; +enum ReferencePolicy +{ + strong, + weak +}; + enum ThreadingPolicy { sequential, @@ -81,6 +87,7 @@ class EventSource; enum class Token; // Observers +template class Observer; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index d41d0fef..e6624d74 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -171,7 +171,7 @@ auto Iterate(const Events& events, V&& init, const SignalPack& /// Snapshot - Sets signal value to value of other signal when event is received /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Snapshot(const Signal& signal, const Events& trigger) -> Signal +auto Snapshot(const SignalBase& signal, const EventBase& trigger) -> Signal { using REACT_IMPL::SnapshotNode; using REACT_IMPL::GetCheckedGraphPtr; @@ -185,40 +185,16 @@ auto Snapshot(const Signal& signal, const Events& trigger) -> Signal /// Pulse - Emits value of target signal when event is received /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Pulse(const Signal& signal, const Events& trigger) -> Events +auto Pulse(const SignalBase& signal, const EventBase& trigger) -> Event { using REACT_IMPL::PulseNode; using REACT_IMPL::GetCheckedGraphPtr; using REACT_IMPL::PrivateNodeInterface; - return Events( std::make_shared>( + return Event( std::make_shared>( GetCheckedGraphPtr(signal, trigger), PrivateNodeInterface::NodePtr(signal), PrivateNodeInterface::NodePtr(trigger)) ); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Changed - Emits token when target signal was changed -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -auto Changed(const Signal& target) -> Events -{ - return Monitor(target).Tokenize(); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ChangedTo - Emits token when target signal was changed to value -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename T, - typename V, -> -auto ChangedTo(const Signal& target, V&& value) -> Events -{ - return Monitor(target) - .Filter([=] (const S& v) { return v == value; }) - .Tokenize(); -} - /******************************************/ REACT_END /******************************************/ #endif // REACT_ALGORITHM_H_INCLUDED \ No newline at end of file diff --git a/include/react/Event.h b/include/react/Event.h index e244ec46..637268ff 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -24,7 +24,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Events /////////////////////////////////////////////////////////////////////////////////////////////////// - template class EventBase { @@ -32,16 +31,6 @@ class EventBase using NodeType = REACT_IMPL::EventStreamNode; public: - EventBase() = default; - - EventBase(const EventBase&) = default; - EventBase& operator=(const EventBase&) = default; - - EventBase(EventBase&&) = default; - EventBase& operator=(EventBase&&) = default; - - ~EventBase() = default; - // Node ctor explicit EventBase(std::shared_ptr&& nodePtr) : nodePtr_( std::move(nodePtr) ) @@ -63,6 +52,14 @@ class EventBase { return REACT::Transform(*this, std::forward(f)); }*/ protected: + EventBase() = default; + + EventBase(const EventBase&) = default; + EventBase& operator=(const EventBase&) = default; + + EventBase(EventBase&&) = default; + EventBase& operator=(EventBase&&) = default; + auto NodePtr() -> std::shared_ptr& { return nodePtr_; } @@ -72,23 +69,23 @@ class EventBase template auto CreateProcessingNode(F&& func, const EventBase& dep) -> decltype(auto) { - using ProcessingNodeType = REACT_IMPL::EventProcessingNode::type>; + using REACT_IMPL::PrivateNodeInterface; + using EventNodeType = REACT_IMPL::EventProcessingNode::type>; - return std::make_shared( - REACT_IMPL::PrivateNodeInterface::GraphPtr(dep), - std::forward(func), - REACT_IMPL::PrivateNodeInterface::NodePtr(dep)); + return std::make_shared(PrivateNodeInterface::GraphPtr(dep), std::forward(func), PrivateNodeInterface::NodePtr(dep)); } template - auto CreateSyncedProcessingNode(F&& func, const EventBase& dep, const SignalBase ... syncs) -> decltype(auto) + auto CreateSyncedProcessingNode(F&& func, const EventBase& dep, const SignalBase& ... syncs) -> decltype(auto) { - using SyncedProcessingNodeType = REACT_IMPL::SyncedEventProcessingNode::type, Us ...>; + using REACT_IMPL::GetCheckedGraphPtr; + using REACT_IMPL::PrivateNodeInterface; + using EventNodeType = REACT_IMPL::SyncedEventProcessingNode::type, Us ...>; - return std::make_shared( - REACT_IMPL::GetCheckedGraphPtr(dep, syncs ...), + return std::make_shared( + GetCheckedGraphPtr(dep, syncs ...), std::forward(func), - REACT_IMPL::PrivateNodeInterface::NodePtr(dep), REACT_IMPL::PrivateNodeInterface::NodePtr(syncs) ...); + PrivateNodeInterface::NodePtr(dep), PrivateNodeInterface::NodePtr(syncs) ...); } private: @@ -108,19 +105,6 @@ class EventSourceBase : public EventBase public: using EventBase::EventBase; - - EventSourceBase() = default; - - EventSourceBase(const EventSourceBase&) = default; - EventSourceBase& operator=(const EventSourceBase&) = default; - - EventSourceBase(EventSourceBase&& other) = default; - EventSourceBase& operator=(EventSourceBase&& other) = default; - - template - explicit EventSourceBase(const TGroup& group) : - EventSourceBase::EventBase( std::make_shared(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) - { } void Emit(const E& value) { EmitValue(value); } @@ -138,6 +122,24 @@ class EventSourceBase : public EventBase EventSourceBase& operator<<(E&& value) { EmitValue(std::move(value)); return *this; } +protected: + EventSourceBase() = default; + + EventSourceBase(const EventSourceBase&) = default; + EventSourceBase& operator=(const EventSourceBase&) = default; + + EventSourceBase(EventSourceBase&& other) = default; + EventSourceBase& operator=(EventSourceBase&& other) = default; + + template + auto CreateSourceNode(const TGroup& group) -> decltype(auto) + { + using REACT_IMPL::PrivateReactiveGroupInterface; + using SrcNodeType = REACT_IMPL::EventSourceNode; + + return std::make_shared(PrivateReactiveGroupInterface::GraphPtr(group)); + } + private: template void EmitValue(T&& value) @@ -178,7 +180,7 @@ class Event : public EventBase { } template - Event(F&& func, const EventBase& dep, const SignalBase ... signals) : + Event(F&& func, const EventBase& dep, const SignalBase& ... signals) : Event::EventBase( CreateSyncedProcessingNode(std::forward(func), dep, signals ...) ) { } }; @@ -212,7 +214,7 @@ class Event : public EventBase { } template - Event(F&& func, const EventBase& dep, const SignalBase ... signals) : + Event(F&& func, const EventBase& dep, const SignalBase& ... signals) : Event::EventBase( SyncedCreateProcessingNode(std::forward(func), dep, signals ...) ) { } }; @@ -235,6 +237,12 @@ class EventSource : public EventSourceBase EventSource(EventSource&&) = default; EventSource& operator=(EventSource&&) = default; + + // Construct event source + template + explicit EventSource(const TGroup& group) : + EventSource::EventSourceBase( CreateSourceNode(group) ) + { } }; template @@ -253,10 +261,18 @@ class EventSource : public EventSourceBase EventSource(EventSource&&) = default; EventSource& operator=(EventSource&&) = default; + // Construct event source + template + explicit EventSource(const TGroup& group) : + EventSource::EventSourceBase( CreateSourceNode(group) ) + { } + + // Construct from unique EventSource(EventSource&& other) : EventSource::EventSourceBase( std::move(other) ) { } + // Assign from unique EventSource& operator=(EventSource&& other) { EventSource::EventSourceBase::operator=(std::move(other)); return *this; } }; diff --git a/include/react/Group.h b/include/react/Group.h index cf58e72a..71434d1e 100644 --- a/include/react/Group.h +++ b/include/react/Group.h @@ -86,23 +86,6 @@ class TransactionStatus friend void AsyncTransaction(TransactionFlagsT flags, TransactionStatus& status, F&& func); }; -/////////////////////////////////////////////////////////////////////////////////////////////// -/// DoTransaction -/////////////////////////////////////////////////////////////////////////////////////////////// -template -void DoTransaction(F&& func) -{ - using REACT_IMPL::DomainSpecificInputManager; - DomainSpecificInputManager::Instance().DoTransaction(0, std::forward(func)); -} - -template -void DoTransaction(TransactionFlags flags, F&& func) -{ - using REACT_IMPL::DomainSpecificInputManager; - DomainSpecificInputManager::Instance().DoTransaction(flags, std::forward(func)); -} - /////////////////////////////////////////////////////////////////////////////////////////////// /// AsyncTransaction /////////////////////////////////////////////////////////////////////////////////////////////// @@ -221,6 +204,15 @@ class ReactiveGroup : public ReactiveGroupBase ReactiveGroup(ReactiveGroup&& other) = default; ReactiveGroup& operator=(ReactiveGroup&& other) = default; + + // Construct from unique + ReactiveGroup(ReactiveGroup&& other) : + ReactiveGroup::ReactiveGroupBase( std::move(other) ) + { } + + // Assign from unique + ReactiveGroup& operator=(ReactiveGroup&& other) + { ReactiveGroup::ReactiveGroupBase::operator=(std::move(other)); return *this; } }; /******************************************/ REACT_END /******************************************/ @@ -260,7 +252,7 @@ static auto GetCheckedGraphPtr(const TBase1& dep1, const TBases& ... deps) -> co { const std::shared_ptr& graphPtr1 = PrivateNodeInterface::GraphPtr(dep1); - auto rawGraphPtrs = { PrivateNodeInterface::GraphPtr(deps).get() ... }; + std::initializer_list rawGraphPtrs = { PrivateNodeInterface::GraphPtr(deps).get() ... }; bool isSameGraphForAllDeps = std::all_of(rawGraphPtrs.begin(), rawGraphPtrs.end(), [&] (IReactiveGraph* p) { return p == graphPtr1.get(); }); diff --git a/include/react/Observer.h b/include/react/Observer.h index 705d4ee9..9d5c6409 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -4,337 +4,176 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#if 0 - #ifndef REACT_OBSERVER_H_INCLUDED #define REACT_OBSERVER_H_INCLUDED #pragma once #include "react/detail/Defs.h" +#include "react/API.h" +#include "react/Group.h" #include #include -#include "react/common/Util.h" -#include "react/detail/IReactiveNode.h" -#include "react/detail/ObserverBase.h" #include "react/detail/graph/ObserverNodes.h" /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Signal; - -template -class Events; - -using REACT_IMPL::ObserverAction; -using REACT_IMPL::WeightHint; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Observer +/// ObserverBase /////////////////////////////////////////////////////////////////////////////////////////////////// -class Observer +class ObserverBase { private: - using SubjectPtrT = std::shared_ptr>; - using NodeT = REACT_IMPL::ObserverNode; + using NodeType = REACT_IMPL::ObserverNode; public: - // Default ctor - Observer() : - nodePtr_( nullptr ), - subjectPtr_( nullptr ) - {} - - // Move ctor - Observer(Observer&& other) : - nodePtr_( other.nodePtr_ ), - subjectPtr_( std::move(other.subjectPtr_) ) - { - other.nodePtr_ = nullptr; - other.subjectPtr_.reset(); - } + // Private node ctor + explicit ObserverBase(std::shared_ptr&& nodePtr) : + nodePtr_( std::move(nodePtr) ) + { } - // Node ctor - Observer(NodeT* nodePtr, const SubjectPtrT& subjectPtr) : - nodePtr_( nodePtr ), - subjectPtr_( subjectPtr ) - {} + void Cancel() + { nodePtr_.reset(); } - // Move assignment - Observer& operator=(Observer&& other) - { - nodePtr_ = other.nodePtr_; - subjectPtr_ = std::move(other.subjectPtr_); + bool IsCancelled() const + { return nodePtr_ != nullptr; } - other.nodePtr_ = nullptr; - other.subjectPtr_.reset(); +protected: + ObserverBase() = default; - return *this; - } + ObserverBase(const ObserverBase&) = default; + ObserverBase& operator=(const ObserverBase&) = default; - // Deleted copy ctor and assignment - Observer(const Observer&) = delete; - Observer& operator=(const Observer&) = delete; + ObserverBase(ObserverBase&&) = default; + ObserverBase& operator=(ObserverBase&&) = default; - void Detach() - { - assert(IsValid()); - subjectPtr_->UnregisterObserver(nodePtr_); - } + auto NodePtr() -> std::shared_ptr& + { return nodePtr_; } - bool IsValid() const - { - return nodePtr_ != nullptr; - } + auto NodePtr() const -> const std::shared_ptr& + { return nodePtr_; } - void SetWeightHint(WeightHint weight) + template + auto CreateSignalObserverNode(F&& func, const SignalBase& dep1, const SignalBase& ... deps) -> decltype(auto) { - assert(IsValid()); - nodePtr_->SetWeightHint(weight); + using REACT_IMPL::GetCheckedGraphPtr; + using REACT_IMPL::PrivateNodeInterface; + using ObsNodeType = REACT_IMPL::SignalObserverNode::type, T1, Ts ...>; + + return std::make_shared( + GetCheckedGraphPtr(dep1, deps ...), + std::forward(func), + PrivateNodeInterface::NodePtr(dep1), PrivateNodeInterface::NodePtr(deps) ...); } -private: - // Owned by subject - NodeT* nodePtr_; - - // While the observer handle exists, the subject is not destroyed - SubjectPtrT subjectPtr_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ScopedObserver -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ScopedObserver -{ -public: - // Move ctor - ScopedObserver(ScopedObserver&& other) : - obs_( std::move(other.obs_) ) - {} - - // Construct from observer - ScopedObserver(Observer&& obs) : - obs_( std::move(obs) ) - {} - - // Move assignment - ScopedObserver& operator=(ScopedObserver&& other) + template + auto CreateEventObserverNode(F&& func, const EventBase& dep) -> decltype(auto) { - obs_ = std::move(other.obs_); - } + using REACT_IMPL::PrivateNodeInterface; + using ObsNodeType = REACT_IMPL::EventObserverNode::type, T>; - // Deleted default ctor, copy ctor and assignment - ScopedObserver() = delete; - ScopedObserver(const ScopedObserver&) = delete; - ScopedObserver& operator=(const ScopedObserver&) = delete; - - ~ScopedObserver() - { - obs_.Detach(); + return std::make_shared(PrivateNodeInterface::GraphPtr(dep), std::forward(func), PrivateNodeInterface::NodePtr(dep)); } - bool IsValid() const + template + auto CreateSyncedEventObserverNode(F&& func, const EventBase& dep, const SignalBase& ... syncs) -> decltype(auto) { - return obs_.IsValid(); - } - - void SetWeightHint(WeightHint weight) - { - obs_.SetWeightHint(weight); + using REACT_IMPL::GetCheckedGraphPtr; + using REACT_IMPL::PrivateNodeInterface; + using ObsNodeType = REACT_IMPL::SyncedEventObserverNode::type, T, Us ...>; + + return std::make_shared( + GetCheckedGraphPtr(dep, syncs ...), + std::forward(func), + PrivateNodeInterface::NodePtr(dep), PrivateNodeInterface::NodePtr(syncs) ...); } private: - Observer obs_; + std::shared_ptr nodePtr_; + + friend struct REACT_IMPL::PrivateNodeInterface; }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Observe - Signals +/// Observer /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename FIn, - typename S -> -auto Observe(const Signal& subject, FIn&& func) - -> Observer +template <> +class Observer : public ObserverBase { - using REACT_IMPL::IObserver; - using REACT_IMPL::ObserverNode; - using REACT_IMPL::SignalObserverNode; - using REACT_IMPL::AddDefaultReturnValueWrapper; - - using F = typename std::decay::type; - using R = typename std::result_of::type; - using WrapperT = AddDefaultReturnValueWrapper; - - // If return value of passed function is void, add ObserverAction::next as - // default return value. - using NodeT = typename std::conditional< - std::is_same::value, - SignalObserverNode, - SignalObserverNode - >::type; - - const auto& subjectPtr = GetNodePtr(subject); - - std::unique_ptr> nodePtr( new NodeT(subjectPtr, std::forward(func)) ); - ObserverNode* rawNodePtr = nodePtr.get(); +public: + using ObserverBase::ObserverBase; - subjectPtr->RegisterObserver(std::move(nodePtr)); + Observer() = delete; - return Observer( rawNodePtr, subjectPtr ); -} + Observer(const Observer&) = delete; + Observer& operator=(const Observer&) = delete; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Observe - Events -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename FIn, - typename E -> -auto Observe(const Events& subject, FIn&& func) - -> Observer -{ - using REACT_IMPL::IObserver; - using REACT_IMPL::ObserverNode; - using REACT_IMPL::EventObserverNode; - using REACT_IMPL::AddDefaultReturnValueWrapper; - using REACT_IMPL::AddObserverRangeWrapper; - using REACT_IMPL::IsCallableWith; - using REACT_IMPL::EventRange; - - using F = typename std::decay::type; - - using WrapperT = - typename std::conditional< - IsCallableWith>::value, - F, - typename std::conditional< - IsCallableWith::value, - AddObserverRangeWrapper, - typename std::conditional< - IsCallableWith>::value, - AddDefaultReturnValueWrapper, - typename std::conditional< - IsCallableWith::value, - AddObserverRangeWrapper>, - void - >::type - >::type - >::type - >::type; - - static_assert( - ! std::is_same::value, - "Observe: Passed function does not match any of the supported signatures."); - - using NodeT = EventObserverNode; - - const auto& subjectPtr = GetNodePtr(subject); - - std::unique_ptr> nodePtr( new NodeT(subjectPtr, std::forward(func)) ); - ObserverNode* rawNodePtr = nodePtr.get(); - - subjectPtr->RegisterObserver(std::move(nodePtr)); - - return Observer( rawNodePtr, subjectPtr ); -} + Observer(Observer&&) = default; + Observer& operator=(Observer&&) = default; + + // Construct signal observer + template + Observer(F&& func, const SignalBase& ... subjects) : + Observer::ObserverBase( CreateSignalObserverNode(std::forward(func), subjects ...) ) + { } + + // Construct event observer + template + Observer(F&& func, const EventBase& subject) : + Observer::ObserverBase( CreateEventObserverNode(std::forward(func), subject ) ) + { } + + // Constructed synced event observer + template + Observer(F&& func, const EventBase& subject, const SignalBase& ... signals) : + Observer::ObserverBase( CreateSyncedEventObserverNode(std::forward(func), subject, signals ...) ) + { } +}; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Observe - Synced -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename FIn, - typename E, - typename ... TDepValues -> -auto Observe(const Events& subject, - const SignalPack& depPack, FIn&& func) - -> Observer +template <> +class Observer : public ObserverBase { - using REACT_IMPL::IObserver; - using REACT_IMPL::ObserverNode; - using REACT_IMPL::SyncedObserverNode; - using REACT_IMPL::AddDefaultReturnValueWrapper; - using REACT_IMPL::AddObserverRangeWrapper; - using REACT_IMPL::IsCallableWith; - using REACT_IMPL::EventRange; - - using F = typename std::decay::type; - - using WrapperT = - typename std::conditional< - IsCallableWith, TDepValues ...>::value, - F, - typename std::conditional< - IsCallableWith::value, - AddObserverRangeWrapper, - typename std::conditional< - IsCallableWith, TDepValues ...>::value, - AddDefaultReturnValueWrapper, - typename std::conditional< - IsCallableWith::value, - AddObserverRangeWrapper, - TDepValues...>, - void - >::type - >::type - >::type - >::type; - - static_assert( - ! std::is_same::value, - "Observe: Passed function does not match any of the supported signatures."); - - using NodeT = SyncedObserverNode; - - struct NodeBuilder_ - { - NodeBuilder_(const Events& subject, FIn&& func) : - MySubject( subject ), - MyFunc( std::forward(func) ) - {} - - auto operator()(const Signal& ... deps) - -> ObserverNode* - { - return new NodeT( - GetNodePtr(MySubject), std::forward(MyFunc), GetNodePtr(deps) ... ); - } - - const Events& MySubject; - FIn MyFunc; - }; - - const auto& subjectPtr = GetNodePtr(subject); - - std::unique_ptr> nodePtr( REACT_IMPL::apply( - NodeBuilder_( subject, std::forward(func) ), - depPack.Data) ); - - ObserverNode* rawNodePtr = nodePtr.get(); - - subjectPtr->RegisterObserver(std::move(nodePtr)); - - return Observer( rawNodePtr, subjectPtr ); -} +public: + using ObserverBase::ObserverBase; + + Observer() = delete; + + Observer(const Observer&) = default; + Observer& operator=(const Observer&) = default; + + Observer(Observer&&) = default; + Observer& operator=(Observer&&) = default; + + // Construct from unique + Observer(Observer&& other) : + Observer::ObserverBase( std::move(other) ) + { } + + // Assign from unique + Observer& operator=(Observer&& other) + { Observer::ObserverBase::operator=(std::move(other)); return *this; } + + // Construct signal observer + template + Observer(F&& func, const SignalBase& ... subjects) : + Observer::ObserverBase( CreateSignalObserverNode(std::forward(func), subjects ...) ) + { } + + // Construct event observer + template + Observer(F&& func, const EventBase& subject) : + Observer::ObserverBase( CreateEventObserverNode(std::forward(func), subject ) ) + { } + + // Constructed synced event observer + template + Observer(F&& func, const EventBase& subject, const SignalBase& ... signals) : + Observer::ObserverBase( CreateSyncedEventObserverNode(std::forward(func), subject, signals ...) ) + { } +}; /******************************************/ REACT_END /******************************************/ -#endif // REACT_OBSERVER_H_INCLUDED - -#endif \ No newline at end of file +#endif // REACT_OBSERVER_H_INCLUDED \ No newline at end of file diff --git a/include/react/Reactor.h b/include/react/Reactor.h deleted file mode 100644 index 92d3b006..00000000 --- a/include/react/Reactor.h +++ /dev/null @@ -1,76 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_REACTOR_H_INCLUDED -#define REACT_REACTOR_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include -#include - -#include "react/common/Util.h" - -#include "react/Event.h" -#include "react/detail/ReactiveBase.h" -#include "react/detail/graph/ReactorNodes.h" - -/*****************************************/ REACT_BEGIN /*****************************************/ - -template -class Reactor -{ -public: - class Context; - - using NodeT = REACT_IMPL::ReactorNode; - - class Context - { - public: - Context(NodeT& node) : - node_( node ) - {} - - template - E& Await(const Events& evn) - { - return node_.Await(GetNodePtr(evn)); - } - - template - void RepeatUntil(const Events& evn, F&& func) - { - node_.RepeatUntil(GetNodePtr(evn), std::forward(func)); - } - - template - const S& Get(const Signal& sig) - { - return node_.Get(GetNodePtr(sig)); - } - - private: - NodeT& node_; - }; - - template - explicit Reactor(F&& func) : - nodePtr_( new REACT_IMPL::ReactorNode(std::forward(func)) ) - { - nodePtr_->StartLoop(); - } - -private: - std::unique_ptr nodePtr_; -}; - -/******************************************/ REACT_END /******************************************/ - -#endif // REACT_REACTOR_H_INCLUDED \ No newline at end of file diff --git a/include/react/Signal.h b/include/react/Signal.h index e8222bf3..c46a0b94 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -36,16 +36,6 @@ class SignalBase using NodeType = REACT_IMPL::SignalNode; public: - SignalBase() = default; - - SignalBase(const SignalBase&) = default; - SignalBase& operator=(const SignalBase&) = default; - - SignalBase(SignalBase&&) = default; - SignalBase& operator=(SignalBase&&) = default; - - ~SignalBase() = default; - // Private node ctor explicit SignalBase(std::shared_ptr&& nodePtr) : nodePtr_( std::move(nodePtr) ) @@ -55,6 +45,14 @@ class SignalBase { return nodePtr_->Value(); } protected: + SignalBase() = default; + + SignalBase(const SignalBase&) = default; + SignalBase& operator=(const SignalBase&) = default; + + SignalBase(SignalBase&&) = default; + SignalBase& operator=(SignalBase&&) = default; + auto NodePtr() -> std::shared_ptr& { return nodePtr_; } @@ -66,7 +64,6 @@ class SignalBase { using REACT_IMPL::GetCheckedGraphPtr; using REACT_IMPL::PrivateNodeInterface; - using FuncNodeType = REACT_IMPL::SignalFuncNode::type, T1, Ts ...>; return std::make_shared( @@ -90,14 +87,6 @@ class VarSignalBase : public SignalBase public: using SignalBase::SignalBase; - VarSignalBase() = default; - - VarSignalBase(const VarSignalBase&) = default; - VarSignalBase& operator=(const VarSignalBase&) = default; - - VarSignalBase(VarSignalBase&&) = default; - VarSignalBase& operator=(VarSignalBase&&) = default; - void Set(const S& newValue) { SetValue(newValue); } @@ -115,11 +104,27 @@ class VarSignalBase : public SignalBase { ModifyValue(func); } protected: + VarSignalBase() = default; + + VarSignalBase(const VarSignalBase&) = default; + VarSignalBase& operator=(const VarSignalBase&) = default; + + VarSignalBase(VarSignalBase&&) = default; + VarSignalBase& operator=(VarSignalBase&&) = default; + + template + auto CreateVarNode(const TGroup& group) -> decltype(auto) + { + using REACT_IMPL::PrivateReactiveGroupInterface; + using VarNodeType = REACT_IMPL::VarSignalNode; + + return std::make_shared(PrivateReactiveGroupInterface::GraphPtr(group)); + } + template auto CreateVarNode(T&& value, const TGroup& group) -> decltype(auto) { using REACT_IMPL::PrivateReactiveGroupInterface; - using VarNodeType = REACT_IMPL::VarSignalNode; return std::make_shared(PrivateReactiveGroupInterface::GraphPtr(group), std::forward(value)); @@ -174,9 +179,10 @@ class Signal : public SignalBase Signal(Signal&&) = default; Signal& operator=(Signal&&) = default; - template - Signal(F&& func, const SignalBase& ... deps) : - SignalBase( CreateFuncNode(std::forward(func), deps ...) ) + // Construct func signal + template + Signal(F&& func, const SignalBase& ... deps) : + Signal::SignalBase( CreateFuncNode(std::forward(func), deps ...) ) { } }; @@ -196,17 +202,20 @@ class Signal : public SignalBase Signal(Signal&&) = default; Signal& operator=(Signal&&) = default; + // Construct func signal + template + Signal(F&& func, const SignalBase& ... deps) : + Signal::SignalBase( CreateFuncNode(std::forward(func), deps ...) ) + { } + + // Construct from unique Signal(Signal&& other) : Signal::SignalBase( std::move(other) ) { } + // Assign from unique Signal& operator=(Signal&& other) { Signal::SignalBase::operator=(std::move(other)); return *this; } - - template - Signal(F&& func, const SignalBase& ... deps) : - Signal::SignalBase( CreateFuncNode(std::forward(func), deps ...) ) - { } }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -228,6 +237,13 @@ class VarSignal : public VarSignalBase VarSignal(VarSignal&&) = default; VarSignal& operator=(VarSignal&&) = default; + // Construct with default + template + explicit VarSignal(const TGroup& group) : + VarSignal::VarSignalBase( CreateVarNode( group) ) + { } + + // Construct with value template VarSignal(T&& value, const TGroup& group) : VarSignal::VarSignalBase( CreateVarNode(std::forward(value), group) ) @@ -250,13 +266,22 @@ class VarSignal : public VarSignalBase VarSignal(VarSignal&&) = default; VarSignal& operator=(VarSignal&&) = default; + // Construct from unique VarSignal(VarSignal&& other) : VarSignal::VarSignalBase( std::move(other) ) { } + // Assign from unique VarSignal& operator=(VarSignal&& other) { VarSignal::SignalBase::operator=(std::move(other)); return *this; } + // Construct with default + template + explicit VarSignal(const TGroup& group) : + VarSignal::VarSignalBase( CreateVarNode( group) ) + { } + + // Construct with value template VarSignal(T&& value, const TGroup& group) : VarSignal::VarSignalBase( CreateVarNode(std::forward(value), group) ) diff --git a/include/react/detail/EngineBase.h b/include/react/detail/EngineBase.h deleted file mode 100644 index 501c9c26..00000000 --- a/include/react/detail/EngineBase.h +++ /dev/null @@ -1,49 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_ENGINEBASE_H_INCLUDED -#define REACT_DETAIL_ENGINEBASE_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include -#include -#include -#include - -#include "tbb/queuing_mutex.h" - -#include "react/common/Concurrency.h" -#include "react/common/Types.h" -#include "react/detail/ReactiveInput.h" -#include "react/detail/IReactiveNode.h" -#include "react/detail/IReactiveEngine.h" -#include "react/detail/ObserverBase.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// TurnBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -class TurnBase -{ -public: - inline TurnBase(TurnIdT id, TransactionFlagsT flags) : - id_( id ) - {} - - inline TurnIdT Id() const { return id_; } - -private: - TurnIdT id_; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_DETAIL_ENGINEBASE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/ObserverBase.h b/include/react/detail/ObserverBase.h deleted file mode 100644 index 6653ff04..00000000 --- a/include/react/detail/ObserverBase.h +++ /dev/null @@ -1,77 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_OBSERVERBASE_H_INCLUDED -#define REACT_DETAIL_OBSERVERBASE_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include -#include - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IObserver -/////////////////////////////////////////////////////////////////////////////////////////////////// -class IObserver -{ -public: - virtual ~IObserver() {} - - virtual void UnregisterSelf() = 0; - -private: - virtual void detachObserver() = 0; - - template - friend class Observable; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Observable -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Observable -{ -public: - Observable() = default; - - ~Observable() - { - for (const auto& p : observers_) - if (p != nullptr) - p->detachObserver(); - } - - void RegisterObserver(std::unique_ptr&& obsPtr) - { - observers_.push_back(std::move(obsPtr)); - } - - void UnregisterObserver(IObserver* rawObsPtr) - { - for (auto it = observers_.begin(); it != observers_.end(); ++it) - { - if (it->get() == rawObsPtr) - { - it->get()->detachObserver(); - observers_.erase(it); - break; - } - } - } - -private: - std::vector> observers_; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_DETAIL_OBSERVERBASE_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 124636fc..bd966944 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -99,7 +99,8 @@ class EventStreamNode : public NodeBase //}); } - explicit EventStreamNode(const std::shared_ptr& graphPtr) : NodeBase( graphPtr ) + explicit EventStreamNode(const std::shared_ptr& graphPtr) : + NodeBase( graphPtr ) { } StorageType& Events() @@ -122,7 +123,8 @@ template class EventSourceNode : public EventStreamNode { public: - EventSourceNode(const std::shared_ptr& graphPtr) : EventSourceNode::EventStreamNode( graphPtr ) + EventSourceNode(const std::shared_ptr& graphPtr) : + EventSourceNode::EventStreamNode( graphPtr ) { this->RegisterMe(); } ~EventSourceNode() diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index 43d091da..b694e34e 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -18,7 +18,6 @@ #include "react/common/Types.h" #include "react/common/Util.h" #include "react/detail/IReactiveGraph.h" -#include "react/detail/ObserverBase.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -30,7 +29,8 @@ struct IReactiveGraph; class NodeBase : public IReactiveNode { public: - NodeBase(const std::shared_ptr& graphPtr) : graphPtr_( graphPtr ) + NodeBase(const std::shared_ptr& graphPtr) : + graphPtr_( graphPtr ) { } NodeBase(const NodeBase&) = delete; diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index b90e118e..0713c157 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -23,27 +23,22 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class SignalNode; -template +template class EventStreamNode; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// ObserverNode +/// SignalObserverNode /////////////////////////////////////////////////////////////////////////////////////////////////// -class ObserverNode : public NodeBase, public IObserver +class ObserverNode : public NodeBase { public: - ObserverNode(IReactiveGraph* group) : NodeBase( group ) + ObserverNode(const std::shared_ptr& graphPtr) : + ObserverNode::NodeBase( graphPtr ) { } - ObserverNode(ObserverNode&&) = default; - ObserverNode& operator=(ObserverNode&&) = default; - - ObserverNode(const ObserverNode&) = delete; - ObserverNode& operator=(const ObserverNode&) = delete; - virtual bool IsOutputNode() const { return true; } }; @@ -51,56 +46,56 @@ class ObserverNode : public NodeBase, public IObserver /////////////////////////////////////////////////////////////////////////////////////////////////// /// SignalObserverNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class SignalObserverNode : public ObserverNode { public: template - SignalObserverNode(IReactiveGraph* group, const std::shared_ptr>& subject, FIn&& func) : - SignalObserverNode::ObserverNode( group ), - subject_( subject ), - func_( std::forward(func) ) + SignalObserverNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& ... deps) : + SignalObserverNode::ObserverNode( graphPtr ), + func_( std::forward(func) ), + depHolder_( deps ... ) { this->RegisterMe(); - this->AttachToMe(subject->GetNodeId()); + REACT_EXPAND_PACK(this->AttachToMe(deps->GetNodeId())); } ~SignalObserverNode() { - this->DetachFromMe(subject->GetNodeId()); + apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(this->DetachFromMe(deps->GetNodeId())); }, depHolder_); this->UnregisterMe(); } virtual const char* GetNodeType() const override { return "SignalObserver"; } - virtual int DependencyCount() const override - { return 1; } + virtual int GetDependencyCount() const override + { return sizeof...(TDeps); } virtual UpdateResult Update(TurnId turnId) override { - func_(subject_->Value()); + apply([this] (const auto& ... deps) { this->func_(deps->Value() ...); }, depHolder_); return UpdateResult::unchanged; } private: - std::shared_ptr> subject_; - F func_; + + std::tuple> ...> depHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventObserverNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class EventObserverNode : public ObserverNode { public: template - EventObserverNode(IReactiveGraph* group, const std::shared_ptr>& subject, FIn&& func) : - EventObserverNode::ObserverNode( group ), - subject_( subject ), - func_( std::forward(func) ) + EventObserverNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& subject) : + EventObserverNode::ObserverNode( graphPtr ), + func_( std::forward(func) ), + subject_( subject ) { this->RegisterMe(); this->AttachToMe(subject->GetNodeId()); @@ -108,40 +103,40 @@ class EventObserverNode : public ObserverNode ~EventObserverNode() { - this->DetachFromMe(subject->GetNodeId()); + this->DetachFromMe(subject_->GetNodeId()); this->UnregisterMe(); } virtual const char* GetNodeType() const override - { return "EventObserverNode"; } + { return "EventObserver"; } - virtual int DependencyCount() const override + virtual int GetDependencyCount() const override { return 1; } virtual UpdateResult Update(TurnId turnId) override { - func_(EventRange( subject_->Events() )); + func_(EventRange( subject_->Events() )); return UpdateResult::unchanged; } private: - std::shared_ptr> subject_; - F func_; + + std::shared_ptr> subject_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// SyncedObserverNode +/// SyncedEventObserverNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class SyncedObserverNode : public ObserverNode +template +class SyncedEventObserverNode : public ObserverNode { public: template - SyncedObserverNode(IReactiveGraph* group, const std::shared_ptr>& subject, FIn&& func, const std::shared_ptr>& ... syncs) : - SyncedObserverNode::ObserverNode( group ), - subject_( subject ), + SyncedEventObserverNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& subject, const std::shared_ptr>& ... syncs) : + SyncedEventObserverNode::ObserverNode( graphPtr ), func_( std::forward(func) ), + subject_( subject ), syncHolder_( syncs ... ) { this->RegisterMe(); @@ -149,7 +144,7 @@ class SyncedObserverNode : public ObserverNode REACT_EXPAND_PACK(this->AttachToMe(syncs->GetNodeId())); } - ~SyncedObserverNode() + ~SyncedEventObserverNode() { apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(syncs->GetNodeId())); }, syncHolder_); this->DetachFromMe(subject_->GetNodeId()); @@ -157,32 +152,26 @@ class SyncedObserverNode : public ObserverNode } virtual const char* GetNodeType() const override - { return "SyncedObserverNode"; } + { return "SyncedEventObserver"; } - virtual int DependencyCount() const override + virtual int GetDependencyCount() const override { return 1 + sizeof...(TSyncs); } virtual UpdateResult Update(TurnId turnId) override { // Update of this node could be triggered from deps, // so make sure source doesnt contain events from last turn - p->SetCurrentTurn(turnId); - - shouldDetach = apply( - [this] (const auto& ... syncs) - { - func_(EventRange( this->subject_->Events() ), syncs->Value() ...); - }, - syncHolder_); + subject_->SetCurrentTurn(turnId); + apply([this] (const auto& ... syncs) { func_(EventRange( this->subject_->Events() ), syncs->Value() ...); }, syncHolder_); return UpdateResult::unchanged; } private: - std::shared_ptr> subject_; - F func_; + std::shared_ptr> subject_; + std::tuple>...> syncHolder_; }; diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index b926ffa1..d45366e1 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -27,7 +27,7 @@ bool Equals(const L& lhs, const R& rhs); /////////////////////////////////////////////////////////////////////////////////////////////////// /// SignalNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class SignalNode : public NodeBase { public: @@ -37,29 +37,39 @@ class SignalNode : public NodeBase SignalNode(const SignalNode&) = delete; SignalNode& operator=(const SignalNode&) = delete; - template - SignalNode(const std::shared_ptr& graphPtr, U&& value) : + explicit SignalNode(const std::shared_ptr& graphPtr) : + SignalNode::NodeBase( graphPtr ), + value_( ) + { } + + template + SignalNode(const std::shared_ptr& graphPtr, T&& value) : SignalNode::NodeBase( graphPtr ), value_( std::forward(value) ) { } - T& Value() + S& Value() { return value_; } - const T& Value() const + const S& Value() const { return value_; } private: - T value_; + S value_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// VarNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class VarSignalNode : public SignalNode +template +class VarSignalNode : public SignalNode { public: + explicit VarSignalNode(const std::shared_ptr& graphPtr) : + VarSignalNode::SignalNode( graphPtr ), + newValue_( ) + { this->RegisterMe(); } + template VarSignalNode(const std::shared_ptr& graphPtr, T&& value) : VarSignalNode::SignalNode( graphPtr, std::forward(value) ), @@ -84,7 +94,7 @@ class VarSignalNode : public SignalNode { isInputAdded_ = false; - if (! Equals(this->Value(), newValue_)) + if (! (this->Value() == newValue_)) { this->Value() = std::move(newValue_); return UpdateResult::changed; @@ -106,10 +116,10 @@ class VarSignalNode : public SignalNode } } - template - void SetValue(U&& newValue) + template + void SetValue(T&& newValue) { - newValue_ = std::forward(newValue); + newValue_ = std::forward(newValue); isInputAdded_ = true; @@ -139,7 +149,7 @@ class VarSignalNode : public SignalNode } private: - T newValue_; + S newValue_; bool isInputAdded_ = false; bool isInputModified_ = false; }; @@ -147,14 +157,14 @@ class VarSignalNode : public SignalNode /////////////////////////////////////////////////////////////////////////////////////////////////// /// SignalOpNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class SignalFuncNode : public SignalNode +template +class SignalFuncNode : public SignalNode { public: - template - SignalFuncNode(const std::shared_ptr& graphPtr, U&& func, const std::shared_ptr>& ... deps) : + template + SignalFuncNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& ... deps) : SignalFuncNode::SignalNode( graphPtr, func(deps->Value() ...) ), - func_( std::forward(func) ), + func_( std::forward(func) ), depHolder_( deps ... ) { this->RegisterMe(); @@ -167,13 +177,19 @@ class SignalFuncNode : public SignalNode this->UnregisterMe(); } + virtual const char* GetNodeType() const override + { return "SignalFunc"; } + + virtual int GetDependencyCount() const override + { return sizeof...(TDeps); } + virtual UpdateResult Update(TurnId turnId) override { bool changed = false; - T newValue = apply([this] (const auto& ... deps) { return this->func_(deps->Value() ...); }, depHolder_); + S newValue = apply([this] (const auto& ... deps) { return this->func_(deps->Value() ...); }, depHolder_); - if (! Equals(this->Value(), newValue)) + if (! (this->Value() == newValue)) { this->Value() = std::move(newValue); changed = true; @@ -185,16 +201,10 @@ class SignalFuncNode : public SignalNode return UpdateResult::unchanged; } - virtual const char* GetNodeType() const override - { return "SignalFunc"; } - - virtual int GetDependencyCount() const override - { return sizeof...(TDeps); } - private: - std::tuple> ...> depHolder_; - F func_; + + std::tuple> ...> depHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -221,6 +231,15 @@ class SignalFlattenNode : public SignalNode this->UnregisterMe(); } + virtual const char* GetNodeType() const override + { return "SignalFlatten"; } + + virtual bool IsDynamicNode() const override + { return true; } + + virtual int GetDependencyCount() const override + { return 2; } + virtual UpdateResult Update(TurnId turnId) override { auto newInner = GetNodePtr(outer_->Value()); @@ -248,15 +267,6 @@ class SignalFlattenNode : public SignalNode } } - virtual const char* GetNodeType() const override - { return "SignalFlatten"; } - - virtual bool IsDynamicNode() const override - { return true; } - - virtual int GetDependencyCount() const override - { return 2; } - private: std::shared_ptr> outer_; std::shared_ptr> inner_; diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index 0d380c1c..7ef4b0dc 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -160,10 +160,8 @@ - - diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index 1174c9b1..c5384429 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -54,9 +54,6 @@ Header Files\detail - - Header Files\detail - Header Files\detail\graph @@ -66,9 +63,6 @@ Header Files\detail - - Header Files\detail - Header Files\detail\graph From 5114927925c60319f3830ef8d9c8540fc4a162a9 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 7 Aug 2016 23:26:12 +0200 Subject: [PATCH 224/266] Redesign WIP. Removed reactor stuff. Fixed basic examples. Observers, algorithms. Event buffer clearing. --- examples/CMakeLists.txt | 12 - examples/src/BasicAlgorithms.cpp | 238 +++++++++--------- examples/src/BasicEvents.cpp | 188 +++++++------- examples/src/BasicObservers.cpp | 142 ++--------- examples/src/BasicReactors.cpp | 161 ------------ examples/src/BasicSignals.cpp | 4 +- include/react/API.h | 12 +- include/react/Algorithm.h | 142 +++-------- include/react/Event.h | 26 +- include/react/Group.h | 3 +- include/react/Signal.h | 18 +- include/react/common/Util.h | 6 + include/react/detail/IReactiveGraph.h | 34 +-- include/react/detail/graph/AlgorithmNodes.h | 88 ++++--- include/react/detail/graph/EventNodes.h | 79 ++---- include/react/detail/graph/GraphBase.h | 13 +- include/react/detail/graph/ObserverNodes.h | 10 +- include/react/detail/graph/PropagationST.h | 45 +++- include/react/detail/graph/SignalNodes.h | 13 +- project/msvc/CppReact.sln | 15 +- project/msvc/Example_BasicReactors.vcxproj | 3 - .../Example_BasicReactors.vcxproj.filters | 5 - 22 files changed, 421 insertions(+), 836 deletions(-) delete mode 100644 examples/src/BasicReactors.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 940479ae..417819a0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -14,18 +14,6 @@ target_link_libraries(Example_BasicEvents CppReact) add_executable(Example_BasicObservers src/BasicObservers.cpp) target_link_libraries(Example_BasicObservers CppReact) -### Example_BasicReactors -find_package(Boost 1.55 COMPONENTS coroutine context system) -if(Boost_FOUND) - include_directories(${Boost_INCLUDE_DIRS}) - add_executable(Example_BasicReactors src/BasicReactors.cpp) - target_link_libraries(Example_BasicReactors CppReact ${Boost_LIBRARIES}) -else() - message("boost::coroutine not found. Skipping build of Example_BasicReactors.") -endif() -#add_exyecutable(Example_BasicReactors src/BasicReactors.cpp) -#target_link_libraries(Example_BasicReactors CppReact) - ### Example_BasicSignals add_executable(Example_BasicSignals src/BasicSignals.cpp) target_link_libraries(Example_BasicSignals CppReact) diff --git a/examples/src/BasicAlgorithms.cpp b/examples/src/BasicAlgorithms.cpp index dd20b7bc..1e84305d 100644 --- a/examples/src/BasicAlgorithms.cpp +++ b/examples/src/BasicAlgorithms.cpp @@ -9,7 +9,6 @@ #include #include -#include "react/Domain.h" #include "react/Signal.h" #include "react/Event.h" #include "react/Observer.h" @@ -23,15 +22,12 @@ namespace example1 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) + ReactiveGroup<> group; - class Sensor + struct Sensor { - public: - USING_REACTIVE_DOMAIN(D) - - EventSourceT Samples = MakeEventSource(); - SignalT LastSample = Hold(Samples, 0); + EventSource samples { group }; + Signal lastSample = Hold(0, samples); }; void Run() @@ -40,14 +36,18 @@ namespace example1 Sensor mySensor; - Observe(mySensor.LastSample, [] (int v) { - std::cout << v << std::endl; - }); + Observer<> obs( + [] (int v) + { + cout << v << endl; + }, + mySensor.lastSample); - mySensor.Samples << 20 << 21 << 21 << 22; // output: 20, 21, 22 + mySensor.samples << 20 << 21 << 21 << 22; // output: 20, 21, 22 - DoTransaction([&] { - mySensor.Samples << 30 << 31 << 31 << 32; + group.DoTransaction([&] + { + mySensor.samples << 30 << 31 << 31 << 32; }); // output: 32 cout << endl; @@ -62,17 +62,12 @@ namespace example2 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) + ReactiveGroup<> group; - class Employee + struct Employee { - public: - USING_REACTIVE_DOMAIN(D) - - VarSignalT Name = MakeVar(string( "Bob" )); - VarSignalT Salary = MakeVar(3000); - - EventsT SalaryChanged = Monitor(Salary); + VarSignal name { string( "Bob" ), group }; + VarSignal salary { 3000, group }; }; void Run() @@ -81,12 +76,13 @@ namespace example2 Employee bob; - Observe( - bob.SalaryChanged, - With(bob.Name), - [] (int newSalary, const string& name) { - cout << name << " now earns " << newSalary << endl; - }); + Observer<> obs( + [] (EventRange in, const string& name) + { + for (int newSalary : in) + cout << name << " now earns " << newSalary << endl; + }, + Monitor(bob.salary), bob.name); cout << endl; } @@ -100,21 +96,21 @@ namespace example3 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) + ReactiveGroup<> group; - class Counter + struct Counter { - public: - USING_REACTIVE_DOMAIN(D) + EventSource<> increment { group }; - EventSourceT<> Increment = MakeEventSource(); - - SignalT Count = Iterate( - Increment, + Signal count = Iterate( 0, - [] (Token, int oldCount) { - return oldCount + 1; - }); + [] (EventRange<> in, int count) + { + for (auto _ : in) + ++count; + return count; + }, + increment); }; void Run() @@ -123,12 +119,11 @@ namespace example3 Counter myCounter; - // Note: Using function-style operator() instead of .Emit() and .Value() - myCounter.Increment(); - myCounter.Increment(); - myCounter.Increment(); + myCounter.increment.Emit(); + myCounter.increment.Emit(); + myCounter.increment.Emit(); - cout << myCounter.Count() << endl; // output: 3 + cout << myCounter.count.Value() << endl; // output: 3 cout << endl; } @@ -142,34 +137,33 @@ namespace example4 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) + ReactiveGroup<> group; - class Sensor + struct Sensor { - public: - USING_REACTIVE_DOMAIN(D) - - EventSourceT Input = MakeEventSource(); + EventSource input{ group }; - SignalT Count = Iterate( - Tokenize(Input), + Signal count = Iterate( 0, - [] (Token, int oldCount) { - return oldCount + 1; - }); - - SignalT Sum = Iterate( - Input, + [] (EventRange in, int count) + { + for (auto _ : in) + count++; + return count; + }, + input); + + Signal sum = Iterate( 0.0f, - [] (float v, float sum) { - return v + sum; - }); - - SignalT Average = MakeSignal( - With(Sum,Count), - [] (float sum, int count) { - return count != 0 ? sum / count : 0.0f; - }); + [] (EventRange in, float sum) + { + for (auto v : in) + sum += v; + return sum; + }, + input); + + VarSignal average{ group }; }; void Run() @@ -178,9 +172,9 @@ namespace example4 Sensor mySensor; - mySensor.Input << 10.0f << 5.0f << 10.0f << 8.0f; + mySensor.input << 10.0f << 5.0f << 10.0f << 8.0f; - cout << "Average: " << mySensor.Average() << endl; // output: 8.25 + cout << "Average: " << mySensor.average.Value() << endl; // output: 8.25 cout << endl; } @@ -194,31 +188,35 @@ namespace example5 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) + ReactiveGroup<> group; enum ECmd { increment, decrement, reset }; class Counter { - public: - USING_REACTIVE_DOMAIN(D) - - EventSourceT Update = MakeEventSource(); - VarSignalT Delta = MakeVar(1); - VarSignalT Start = MakeVar(0); - - SignalT Count = Iterate( - Update, - Start.Value(), - With(Delta, Start), - [] (int cmd, int oldCount, int delta, int start) { + private: + static int DoCounterLoop(EventRange in, int count, int delta, int start) + { + for (int cmd : in) + { if (cmd == increment) - return oldCount + delta; + count += delta; else if (cmd == decrement) - return oldCount - delta; + count -= delta; else - return start; - }); + count = start; + } + + return count; + } + + public: + EventSource update{ group }; + + VarSignal delta{ 1, group }; + VarSignal start{ 0, group }; + + Signal count{ Iterate(start.Value(), DoCounterLoop, update, delta, start) }; }; void Run() @@ -227,23 +225,23 @@ namespace example5 Counter myCounter; - cout << "Start: " << myCounter.Count() << endl; // output: 0 + cout << "Start: " << myCounter.count.Value() << endl; // output: 0 - myCounter.Update(increment); - myCounter.Update(increment); - myCounter.Update(increment); + myCounter.update.Emit(increment); + myCounter.update.Emit(increment); + myCounter.update.Emit(increment); - cout << "3x increment by 1: " << myCounter.Count() << endl; // output: 3 + cout << "3x increment by 1: " << myCounter.count.Value() << endl; // output: 3 - myCounter.Delta <<= 5; - myCounter.Update(decrement); + myCounter.delta <<= 5; + myCounter.update.Emit(decrement); - cout << "1x decrement by 5: " << myCounter.Count() << endl; // output: -2 + cout << "1x decrement by 5: " << myCounter.count.Value() << endl; // output: -2 - myCounter.Start <<= 100; - myCounter.Update(reset); + myCounter.start <<= 100; + myCounter.update.Emit(reset); - cout << "reset to 100: " << myCounter.Count() << endl; // output: 100 + cout << "reset to 100: " << myCounter.count.Value() << endl; // output: 100 cout << endl; } @@ -257,32 +255,32 @@ namespace example6 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) + ReactiveGroup<> group; class Sensor { - public: - USING_REACTIVE_DOMAIN(D) - - EventSourceT Input = MakeEventSource(); - - VarSignalT Threshold = MakeVar(42); - - SignalT> AllSamples = Iterate( - Input, - vector{}, - [] (int input, vector& all) { + private: + static void DoIterateAllSamples(EventRange in, vector& all) + { + for (int input : in) all.push_back(input); - }); + } - SignalT> CriticalSamples = Iterate( - Input, - vector{}, - With(Threshold), - [] (int input, vector& critical, int threshold) { + static void DoIterateCritSamples(EventRange in, vector& critical, int threshold) + { + for (int input : in) if (input > threshold) critical.push_back(input); - }); + } + + public: + EventSource input{ group }; + + VarSignal threshold{ 42, group }; + + Signal> allSamples{ Iterate>(vector{ }, DoIterateAllSamples, input) }; + + Signal> criticalSamples{ Iterate>(vector{ }, DoIterateCritSamples, input, threshold) }; }; void Run() @@ -291,15 +289,15 @@ namespace example6 Sensor mySensor; - mySensor.Input << 40 << 29 << 43 << 50; + mySensor.input << 40 << 29 << 43 << 50; cout << "All samples: "; - for (auto const& v : mySensor.AllSamples()) + for (auto const& v : mySensor.allSamples.Value()) cout << v << " "; cout << endl; cout << "Critical samples: "; - for (auto const& v : mySensor.CriticalSamples()) + for (auto const& v : mySensor.criticalSamples.Value()) cout << v << " "; cout << endl; diff --git a/examples/src/BasicEvents.cpp b/examples/src/BasicEvents.cpp index 0db396c3..b9a7cfbd 100644 --- a/examples/src/BasicEvents.cpp +++ b/examples/src/BasicEvents.cpp @@ -8,7 +8,6 @@ #include #include -#include "react/Domain.h" #include "react/Event.h" #include "react/Observer.h" @@ -20,36 +19,33 @@ namespace example1 using namespace std; using namespace react; - // Defines a domain. - // Each domain represents a separate dependency graph, managed by a dedicated propagation engine. - // Reactives of different domains can not be combined. - REACTIVE_DOMAIN(D, sequential) - - // Define type aliases for the given domain in this namespace. - // Now we can use EventSourceT instead of D::EventSourceT. - USING_REACTIVE_DOMAIN(D) + // Defines a group. + // Each group represents a separate dependency graph. + // Reactives from different groups can not be mixed. + ReactiveGroup<> group; // An event source that emits values of type string namespace v1 { - EventSourceT mySource = MakeEventSource(); + EventSource mySource( group ); void Run() { cout << "Example 1 - Hello world (string source)" << endl; - Observe(mySource, [] (const string& s) { - std::cout << s << std::endl; - }); + Observer<> obs( + [] (EventRange in) + { + for (const auto& s : in) + cout << s << std::endl; + }, + mySource); mySource << string("Hello world #1"); // Or without the operator: mySource.Emit(string("Hello world #2")); - // Or as a function call: - mySource(string("Hello world #3")); - cout << endl; } } @@ -57,7 +53,7 @@ namespace example1 // An event source without an explicit value type namespace v2 { - EventSourceT<> helloWorldTrigger = MakeEventSource(); + EventSource<> helloWorldTrigger( group ); void Run() { @@ -65,18 +61,19 @@ namespace example1 int count = 0; - Observe(helloWorldTrigger, [&] (Token) { - cout << "Hello world #" << ++count << endl; - }); + Observer<> obs( + [&] (EventRange<> in) + { + for (auto t : in) + cout << "Hello world #" << ++count << endl; + }, + helloWorldTrigger); helloWorldTrigger.Emit(); // Or without the stream operator: helloWorldTrigger << Token::value; - // Or as a function call: - helloWorldTrigger(); - cout << endl; } } @@ -90,57 +87,32 @@ namespace example2 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) + ReactiveGroup<> group; // An event stream that merges both sources - namespace v1 - { - EventSourceT<> leftClick = MakeEventSource(); - EventSourceT<> rightClick = MakeEventSource(); - - EventsT<> anyClick = Merge(leftClick, rightClick); - - void Run() - { - cout << "Example 2 - Merging event streams (Merge)" << endl; + EventSource<> leftClick( group ); + EventSource<> rightClick( group ); - int count = 0; - - Observe(anyClick, [&] (Token) { - cout << "clicked #" << ++count << endl; - }); - - leftClick.Emit(); // output: clicked #1 - rightClick.Emit(); // output: clicked #2 + Event<> anyClick = Merge(leftClick, rightClick); - cout << endl; - } - } - - // Using overloaded operator | instead of explicit Merge - namespace v2 + void Run() { - EventSourceT<> leftClick = MakeEventSource(); - EventSourceT<> rightClick = MakeEventSource(); + cout << "Example 2 - Merging event streams (Merge)" << endl; - EventsT<> anyClick = leftClick | rightClick; + int count = 0; - void Run() - { - cout << "Example 2 - Merging event streams (operator)" << endl; - - int count = 0; - - Observe(anyClick, [&] (Token) { - cout << "clicked #" << ++count << endl; - }); + Observer<> obs( + [&] (EventRange<> in) + { + for (auto t : in) + cout << "clicked #" << ++count << endl; + }, + anyClick); - leftClick.Emit(); // output: clicked #1 - rightClick.Emit(); // output: clicked #2 + leftClick.Emit(); // output: clicked #1 + rightClick.Emit(); // output: clicked #2 - cout << endl; - } + cout << endl; } } @@ -152,22 +124,23 @@ namespace example3 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) + ReactiveGroup<> group; - EventSourceT numbers = MakeEventSource(); + EventSource numbers( group ); - EventsT greater10 = Filter(numbers, [] (int n) { - return n > 10; - }); + Event greater10 = Filter([] (int n) { return n > 10; }, numbers); void Run() { cout << "Example 3 - Filtering events" << endl; - Observe(greater10, [] (int n) { - cout << n << endl; - }); + Observer<> obs( + [&] (EventRange in) + { + for (auto n : in) + cout << n << endl; + }, + greater10); numbers << 5 << 11 << 7 << 100; // output: 11, 100 @@ -183,32 +156,40 @@ namespace example4 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) + ReactiveGroup<> group; // Data types - enum class ETag { normal, critical }; - using TaggedNum = pair; + enum class Tag { normal, critical }; + using TaggedNum = pair; - EventSourceT numbers = MakeEventSource(); + EventSource numbers( group ); - EventsT tagged = Transform(numbers, [] (int n) { - if (n > 10) - return TaggedNum( ETag::critical, n ); - else - return TaggedNum( ETag::normal, n ); - }); + Event tagged = Transform( + [] (int n) + { + if (n > 10) + return TaggedNum( Tag::critical, n ); + else + return TaggedNum( Tag::normal, n ); + }, + numbers); void Run() { cout << "Example 4 - Transforming events" << endl; - Observe(tagged, [] (const TaggedNum& t) { - if (t.first == ETag::critical) - cout << "(critical) " << t.second << endl; - else - cout << "(normal) " << t.second << endl; - }); + Observer<> obs( + [] (EventRange in) + { + for (TaggedNum e : in) + { + if (e.first == Tag::critical) + cout << "(critical) " << e.second << endl; + else + cout << "(normal) " << e.second << endl; + } + }, + tagged); numbers << 5; // output: (normal) 5 numbers << 20; // output: (critical) 20 @@ -225,20 +206,24 @@ namespace example5 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) + ReactiveGroup<> group; - EventSourceT src = MakeEventSource(); + EventSource src( group ); void Run() { cout << "Example 5 - Queuing multiple inputs" << endl; - Observe(src, [] (int v) { - cout << v << endl; - }); // output: 1, 2, 3, 4 + Observer<> obs( + [] (EventRange in) + { + for (int e : in) + cout << e << endl; + }, src); + // output: 1, 2, 3, 4 - DoTransaction([] { + group.DoTransaction([] + { src << 1 << 2 << 3; src << 4; }); @@ -254,14 +239,9 @@ int main() { example1::v1::Run(); example1::v2::Run(); - - example2::v1::Run(); - example2::v2::Run(); - + example2::Run(); example3::Run(); - example4::Run(); - example5::Run(); return 0; diff --git a/examples/src/BasicObservers.cpp b/examples/src/BasicObservers.cpp index 2c1b911e..0a282528 100644 --- a/examples/src/BasicObservers.cpp +++ b/examples/src/BasicObservers.cpp @@ -8,7 +8,6 @@ #include #include -#include "react/Domain.h" #include "react/Signal.h" #include "react/Event.h" #include "react/Observer.h" @@ -21,77 +20,25 @@ namespace example1 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) + ReactiveGroup<> group; - auto x = MakeVar(1); + VarSignal x( 1, group ); - namespace v1 + void Run() { - void Run() - { - cout << "Example 1 - Creating subject-bound observers (v1)" << endl; - - { - // Create a signal in the function scope - auto mySignal = MakeSignal(x, [] (int x) { return x; } ); - - // The lifetime of the observer is bound to mySignal. - // After Run() returns, mySignal is destroyed, and so is the observer - Observe(mySignal, [] (int mySignal) { - cout << mySignal << endl; - }); - - x <<= 2; // output: 2 - } + cout << "Example 1 - Creating subject-bound observers (v1)" << endl; - x <<= 3; // no ouput - - cout << endl; - } - } - - namespace v2 - { - void Run() { - cout << "Example 1 - Creating subject-bound observers (v2)" << endl; - - // Outer scope - { - // Unbound observer - ObserverT obs; - - // Inner scope - { - auto mySignal = MakeSignal(x, [] (int x) { return x; } ); - - // Move-assign to obs - obs = Observe(mySignal, [] (int mySignal) { - cout << mySignal << endl; - }); - - // The node linked to mySignal is now also owned by obs - - x <<= 2; // output: 2 - } - // ~Inner scope - - // mySignal was destroyed, but as long as obs exists and is still - // attached to the signal node, this signal node won't be destroyed + Signal mySignal( [] (int x) { return x; }, x ); - x <<= 3; // output: 3 - } - // ~Outer scope + Observer<> obs( [] (int mySignal) { cout << mySignal << endl; }, mySignal ); - // obs was destroyed - // -> the signal node is no longer owned by anything and is destroyed - // -> the observer node is destroyed as it was bound to the subject + x <<= 2; // output: 2 + } - x <<= 4; // no ouput + x <<= 3; // no ouput - cout << endl; - } + cout << endl; } } @@ -103,71 +50,28 @@ namespace example2 using namespace std; using namespace react; - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) + ReactiveGroup<> group; - EventSourceT<> trigger = MakeEventSource(); + EventSource<> trigger( group ); void Run() { cout << "Example 2 - Detaching observers manually" << endl; - ObserverT obs = Observe(trigger, [] (Token) { - cout << "Triggered!" << endl; - }); + Observer<> obs( + [] (EventRange<> in) + { + for (auto _ : in) + cout << "Triggered!" << endl; + }, + trigger); trigger.Emit(); // output: Triggered! - obs.Detach(); // Remove the observer - - trigger.Emit(); // no output - - cout << endl; - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 3 - Using scoped observers -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace example3 -{ - using namespace std; - using namespace react; - - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) - - EventSourceT<> trigger = MakeEventSource(); - - void Run() - { - cout << "Example 3 - Using scoped observers" << endl; - - // Inner scope - { - ScopedObserverT scopedObs - ( - Observe(trigger, [] (Token) { - cout << "Triggered!" << endl; - }) - ); - - trigger.Emit(); // output: Triggered! - } - // ~Inner scope + obs.Cancel(); // Remove the observer trigger.Emit(); // no output - // Note the semantic difference between ScopedObserverT and ObserverT. - // - // During its lifetime, the ObserverT handle of an observer guarantees that the - // observed subject will not be destroyed and allows explicit detach. - // But even after the ObserverT handle is destroyed, the subject may continue to exist - // and so will the observer. - // - // ScopedObserverT has similar semantics to a scoped lock. - // When it's destroyed, it detaches and destroys the observer. - cout << endl; } } @@ -177,12 +81,8 @@ namespace example3 /////////////////////////////////////////////////////////////////////////////////////////////////// int main() { - example1::v1::Run(); - example1::v2::Run(); - + example1::Run(); example2::Run(); - example3::Run(); - return 0; } \ No newline at end of file diff --git a/examples/src/BasicReactors.cpp b/examples/src/BasicReactors.cpp deleted file mode 100644 index 534c1346..00000000 --- a/examples/src/BasicReactors.cpp +++ /dev/null @@ -1,161 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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) - -#include -#include -#include - -#include "react/Domain.h" -#include "react/Signal.h" -#include "react/Event.h" -#include "react/Reactor.h" - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 1 - Creating reactive loops -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace example1 -{ - using namespace std; - using namespace react; - - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) - - using PointT = pair; - using PathT = vector; - - vector paths; - - EventSourceT mouseDown = MakeEventSource(); - EventSourceT mouseUp = MakeEventSource(); - EventSourceT mouseMove = MakeEventSource(); - - ReactorT loop - { - [&] (ReactorT::Context ctx) - { - PathT points; - - points.emplace_back(ctx.Await(mouseDown)); - - ctx.RepeatUntil(mouseUp, [&] { - points.emplace_back(ctx.Await(mouseMove)); - }); - - points.emplace_back(ctx.Await(mouseUp)); - - paths.push_back(points); - } - }; - - void Run() - { - cout << "Example 1 - Creating reactive loops" << endl; - - mouseDown << PointT( 1,1 ); - mouseMove << PointT( 2,2 ) << PointT( 3,3 ) << PointT( 4,4 ); - mouseUp << PointT( 5,5 ); - - mouseMove << PointT( 999,999 ); - - mouseDown << PointT( 10,10 ); - mouseMove << PointT( 20,20 ); - mouseUp << PointT( 30,30 ); - - for (const auto& path : paths) - { - cout << "Path: "; - for (const auto& point : path) - cout << "(" << point.first << "," << point.second << ") "; - cout << endl; - } - - cout << endl; - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Example 2 - Creating reactive loops -/////////////////////////////////////////////////////////////////////////////////////////////////// -namespace example2 -{ - using namespace std; - using namespace react; - - REACTIVE_DOMAIN(D, sequential) - USING_REACTIVE_DOMAIN(D) - - using PointT = pair; - using PathT = vector; - - vector paths; - - EventSourceT mouseDown = MakeEventSource(); - EventSourceT mouseUp = MakeEventSource(); - EventSourceT mouseMove = MakeEventSource(); - - VarSignalT counter = MakeVar(103); - - ReactorT loop - { - [&] (ReactorT::Context ctx) - { - PathT points; - - points.emplace_back(ctx.Await(mouseDown)); - - auto count = ctx.Get(counter); - - ctx.RepeatUntil(mouseUp, [&] { - points.emplace_back(ctx.Await(mouseMove)); - }); - - points.emplace_back(ctx.Await(mouseUp)); - - paths.push_back(points); - } - }; - - void Run() - { - cout << "Example 2 - Creating reactive loops" << endl; - - mouseDown << PointT( 1,1 ); - mouseMove << PointT( 2,2 ) << PointT( 3,3 ) << PointT( 4,4 ); - mouseUp << PointT( 5,5 ); - - counter <<= 42; - - mouseMove << PointT( 999,999 ); - - counter <<= 80; - - mouseDown << PointT( 10,10 ); - mouseMove << PointT( 20,20 ); - mouseUp << PointT( 30,30 ); - - for (const auto& path : paths) - { - cout << "Path: "; - for (const auto& point : path) - cout << "(" << point.first << "," << point.second << ") "; - cout << endl; - } - - cout << endl; - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Run examples -/////////////////////////////////////////////////////////////////////////////////////////////////// -int main() -{ - example1::Run(); - example2::Run(); - - return 0; -} \ No newline at end of file diff --git a/examples/src/BasicSignals.cpp b/examples/src/BasicSignals.cpp index bebdf7bf..3007d199 100644 --- a/examples/src/BasicSignals.cpp +++ b/examples/src/BasicSignals.cpp @@ -27,8 +27,8 @@ namespace example1 { return first + string(" ") + second; } // Defines a group. - // Each domain represents a separate dependency graph, managed by a dedicated propagation engine. - // Reactives of different domains can not be combined. + // Each group represents a separate dependency graph. + // Reactives from different groups can not be mixed. ReactiveGroup<> group; // The two words diff --git a/include/react/API.h b/include/react/API.h index 68b6f531..435e6562 100644 --- a/include/react/API.h +++ b/include/react/API.h @@ -72,19 +72,21 @@ template class VarSignal; // Events -template +enum class Token; + +template class EventBase; -template +template class EventSourceBase; -template +template class Event; -template +template class EventSource; -enum class Token; + // Observers template diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index e6624d74..af6bce46 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -28,9 +28,10 @@ auto Hold(T&& initialValue, const EventBase& events) -> Signal { using REACT_IMPL::HoldNode; using REACT_IMPL::PrivateNodeInterface; + using REACT_IMPL::NodeCtorTag; - return Signal( std::make_shared>( - PrivateNodeInterface::GraphPtr(events), std::forward(initialValue), PrivateNodeInterface::NodePtr(events)) ); + return Signal( NodeCtorTag{ }, std::make_shared>( + PrivateNodeInterface::GraphPtr(events), std::forward(initialValue), PrivateNodeInterface::NodePtr(events)) ); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -41,8 +42,9 @@ auto Monitor(const SignalBase& signal) -> Event { using REACT_IMPL::MonitorNode; using REACT_IMPL::PrivateNodeInterface; + using REACT_IMPL::NodeCtorTag; - return Event( std::make_shared>( + return Event( NodeCtorTag{ }, std::make_shared>( PrivateNodeInterface::GraphPtr(signal), PrivateNodeInterface::NodePtr(signal)) ); } @@ -50,121 +52,46 @@ auto Monitor(const SignalBase& signal) -> Event /// Iterate - Iteratively combines signal value with values from event stream (aka Fold) /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Iterate(T&& init, F&& func, const Events& events) -> Signal +auto Iterate(T&& init, F&& func, const EventBase& events) -> Signal { using REACT_IMPL::IterateNode; using REACT_IMPL::IterateByRefNode; - using REACT_IMPL::AddIterateRangeWrapper; - using REACT_IMPL::AddIterateByRefRangeWrapper; using REACT_IMPL::IsCallableWith; - using REACT_IMPL::EventRange; - - using TFunc = typename std::decay::type; - - using NodeT = - typename std::conditional< - IsCallableWith,S>::value, - IterateNode, - typename std::conditional< - IsCallableWith::value, - IterateNode>, - typename std::conditional< - IsCallableWith, S&>::value, - IterateByRefNode, - typename std::conditional< - IsCallableWith::value, - IterateByRefNode>, - void - >::type - >::type - >::type - >::type; - - static_assert( - ! std::is_same::value, - "Iterate: Passed function does not match any of the supported signatures."); - - return Signal( - std::make_shared( - std::forward(init), GetNodePtr(events), std::forward(func))); + using REACT_IMPL::PrivateNodeInterface; + using REACT_IMPL::NodeCtorTag; + + using FuncType = typename std::decay::type; + using IterNodeType = typename std::conditional< + IsCallableWith,S>::value, + IterateNode, + IterateByRefNode>::type; + + return Signal( NodeCtorTag{ }, std::make_shared( + PrivateNodeInterface::GraphPtr(events), std::forward(init), std::forward(func), PrivateNodeInterface::NodePtr(events) )); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Iterate - Synced /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename S, - typename E, - typename V, - typename FIn, - typename ... TDepValues -> -auto Iterate(const Events& events, V&& init, const SignalPack& depPack, FIn&& func) -> Signal +template +auto Iterate(T&& init, F&& func, const EventBase& events, const SignalBase& ... signals) -> Signal { using REACT_IMPL::SyncedIterateNode; using REACT_IMPL::SyncedIterateByRefNode; - using REACT_IMPL::AddIterateRangeWrapper; - using REACT_IMPL::AddIterateByRefRangeWrapper; using REACT_IMPL::IsCallableWith; - using REACT_IMPL::EventRange; - - using F = typename std::decay::type; - - using NodeT = - typename std::conditional< - IsCallableWith,S,TDepValues ...>::value, - SyncedIterateNode, - typename std::conditional< - IsCallableWith::value, - SyncedIterateNode, - TDepValues ...>, - typename std::conditional< - IsCallableWith,S&,TDepValues ...>::value, - SyncedIterateByRefNode, - typename std::conditional< - IsCallableWith::value, - SyncedIterateByRefNode, - TDepValues ...>, - void - >::type - >::type - >::type - >::type; - - static_assert( - ! std::is_same::value, - "Iterate: Passed function does not match any of the supported signatures."); - - //static_assert(NodeT::dummy_error, "DUMP MY TYPE" ); - - struct NodeBuilder_ - { - NodeBuilder_(const Events& source, V&& init, FIn&& func) : - MySource( source ), - MyInit( std::forward(init) ), - MyFunc( std::forward(func) ) - {} - - auto operator()(const Signal& ... deps) - -> Signal - { - return Signal( - std::make_shared( - std::forward(MyInit), GetNodePtr(MySource), - std::forward(MyFunc), GetNodePtr(deps) ...)); - } - - const Events& MySource; - V MyInit; - FIn MyFunc; - }; - - return REACT_IMPL::apply( - NodeBuilder_( events, std::forward(init), std::forward(func) ), - depPack.Data); + using REACT_IMPL::GetCheckedGraphPtr; + using REACT_IMPL::PrivateNodeInterface; + using REACT_IMPL::NodeCtorTag; + + using FuncType = typename std::decay::type; + using IterNodeType = typename std::conditional< + IsCallableWith, S, Us ...>::value, + SyncedIterateNode, + SyncedIterateByRefNode>::type; + + return Signal( NodeCtorTag{ }, std::make_shared( + GetCheckedGraphPtr(events, signals ...), + std::forward(init), std::forward(func), PrivateNodeInterface::NodePtr(events), PrivateNodeInterface::NodePtr(signals) ...)); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -176,8 +103,9 @@ auto Snapshot(const SignalBase& signal, const EventBase& trigger) -> Signa using REACT_IMPL::SnapshotNode; using REACT_IMPL::GetCheckedGraphPtr; using REACT_IMPL::PrivateNodeInterface; + using REACT_IMPL::NodeCtorTag; - return Events( std::make_shared>( + return Events( NodeCtorTag{ }, std::make_shared>( GetCheckedGraphPtr(signal, trigger), PrivateNodeInterface::NodePtr(signal), PrivateNodeInterface::NodePtr(trigger)) ); } @@ -191,7 +119,7 @@ auto Pulse(const SignalBase& signal, const EventBase& trigger) -> Event( std::make_shared>( + return Event( NodeCtorTag{ }, std::make_shared>( GetCheckedGraphPtr(signal, trigger), PrivateNodeInterface::NodePtr(signal), PrivateNodeInterface::NodePtr(trigger)) ); } diff --git a/include/react/Event.h b/include/react/Event.h index 637268ff..13fdcb5c 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -31,8 +31,8 @@ class EventBase using NodeType = REACT_IMPL::EventStreamNode; public: - // Node ctor - explicit EventBase(std::shared_ptr&& nodePtr) : + // Private node ctor + EventBase(REACT_IMPL::NodeCtorTag, std::shared_ptr&& nodePtr) : nodePtr_( std::move(nodePtr) ) { } @@ -176,12 +176,12 @@ class Event : public EventBase template Event(F&& func, const EventBase& dep) : - Event::EventBase( CreateProcessingNode(std::forward(func), dep) ) + Event::EventBase( REACT_IMPL::NodeCtorTag{ }, CreateProcessingNode(std::forward(func), dep) ) { } template Event(F&& func, const EventBase& dep, const SignalBase& ... signals) : - Event::EventBase( CreateSyncedProcessingNode(std::forward(func), dep, signals ...) ) + Event::EventBase( REACT_IMPL::NodeCtorTag{ }, CreateSyncedProcessingNode(std::forward(func), dep, signals ...) ) { } }; @@ -210,12 +210,12 @@ class Event : public EventBase template Event(F&& func, const EventBase& dep) : - Event::EventBase( CreateProcessingNode(std::forward(func), dep) ) + Event::EventBase( REACT_IMPL::NodeCtorTag{ }, CreateProcessingNode(std::forward(func), dep) ) { } template Event(F&& func, const EventBase& dep, const SignalBase& ... signals) : - Event::EventBase( SyncedCreateProcessingNode(std::forward(func), dep, signals ...) ) + Event::EventBase( REACT_IMPL::NodeCtorTag{ }, CreateSyncedProcessingNode(std::forward(func), dep, signals ...) ) { } }; @@ -241,7 +241,7 @@ class EventSource : public EventSourceBase // Construct event source template explicit EventSource(const TGroup& group) : - EventSource::EventSourceBase( CreateSourceNode(group) ) + EventSource::EventSourceBase( REACT_IMPL::NodeCtorTag{ }, CreateSourceNode(group) ) { } }; @@ -264,7 +264,7 @@ class EventSource : public EventSourceBase // Construct event source template explicit EventSource(const TGroup& group) : - EventSource::EventSourceBase( CreateSourceNode(group) ) + EventSource::EventSourceBase( REACT_IMPL::NodeCtorTag{ }, CreateSourceNode(group) ) { } // Construct from unique @@ -286,6 +286,7 @@ auto Merge(const EventBase& dep1, const EventBase& ... deps) -> decltype using REACT_IMPL::EventMergeNode; using REACT_IMPL::GetCheckedGraphPtr; using REACT_IMPL::PrivateNodeInterface; + using REACT_IMPL::NodeCtorTag; static_assert(sizeof...(Us) > 0, "Merge requires at least 2 inputs."); @@ -297,8 +298,8 @@ auto Merge(const EventBase& dep1, const EventBase& ... deps) -> decltype const auto& graphPtr = GetCheckedGraphPtr(dep1, deps ...); - return Event( - std::make_shared>(graphPtr, PrivateNodeInterface::NodePtr(dep1), PrivateNodeInterface::NodePtr(deps) ...)); + return Event( NodeCtorTag{ }, std::make_shared>( + graphPtr, PrivateNodeInterface::NodePtr(dep1), PrivateNodeInterface::NodePtr(deps) ...)); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -370,14 +371,15 @@ auto Join(const EventBase& ... deps) -> Event, unique> using REACT_IMPL::EventJoinNode; using REACT_IMPL::GetCheckedGraphPtr; using REACT_IMPL::PrivateNodeInterface; + using REACT_IMPL::NodeCtorTag; static_assert(sizeof...(Ts) > 1, "Join requires at least 2 inputs."); // If supplied, use merge type, otherwise default to common type. const auto& graphPtr = GetCheckedGraphPtr(deps ...); - return Event, unique>( - std::make_shared>(graphPtr, PrivateNodeInterface::NodePtr(deps) ...)); + return Event, unique>( NodeCtorTag{ }, std::make_shared>( + graphPtr, PrivateNodeInterface::NodePtr(deps) ...)); } /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/Group.h b/include/react/Group.h index 71434d1e..c8324d1c 100644 --- a/include/react/Group.h +++ b/include/react/Group.h @@ -15,8 +15,6 @@ #include #include "react/API.h" -#include "react/Signal.h" -//#include "react/Event.h" #include "react/detail/IReactiveGraph.h" #include "react/detail/graph/PropagationST.h" @@ -25,6 +23,7 @@ struct PrivateReactiveGroupInterface; struct PrivateConcurrentReactiveGroupInterface; +struct NodeCtorTag { }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/Signal.h b/include/react/Signal.h index c46a0b94..22e4e548 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -37,7 +37,7 @@ class SignalBase public: // Private node ctor - explicit SignalBase(std::shared_ptr&& nodePtr) : + SignalBase(REACT_IMPL::NodeCtorTag, std::shared_ptr&& nodePtr) : nodePtr_( std::move(nodePtr) ) { } @@ -181,8 +181,8 @@ class Signal : public SignalBase // Construct func signal template - Signal(F&& func, const SignalBase& ... deps) : - Signal::SignalBase( CreateFuncNode(std::forward(func), deps ...) ) + explicit Signal(F&& func, const SignalBase& ... deps) : + Signal::SignalBase( REACT_IMPL::NodeCtorTag{ }, CreateFuncNode(std::forward(func), deps ...) ) { } }; @@ -204,8 +204,8 @@ class Signal : public SignalBase // Construct func signal template - Signal(F&& func, const SignalBase& ... deps) : - Signal::SignalBase( CreateFuncNode(std::forward(func), deps ...) ) + explicit Signal(F&& func, const SignalBase& ... deps) : + Signal::SignalBase( REACT_IMPL::NodeCtorTag{ }, CreateFuncNode(std::forward(func), deps ...) ) { } // Construct from unique @@ -240,13 +240,13 @@ class VarSignal : public VarSignalBase // Construct with default template explicit VarSignal(const TGroup& group) : - VarSignal::VarSignalBase( CreateVarNode( group) ) + VarSignal::VarSignalBase( REACT_IMPL::NodeCtorTag{ }, CreateVarNode( group) ) { } // Construct with value template VarSignal(T&& value, const TGroup& group) : - VarSignal::VarSignalBase( CreateVarNode(std::forward(value), group) ) + VarSignal::VarSignalBase( REACT_IMPL::NodeCtorTag{ }, CreateVarNode(std::forward(value), group) ) { } }; @@ -278,13 +278,13 @@ class VarSignal : public VarSignalBase // Construct with default template explicit VarSignal(const TGroup& group) : - VarSignal::VarSignalBase( CreateVarNode( group) ) + VarSignal::VarSignalBase( REACT_IMPL::NodeCtorTag{ }, CreateVarNode( group) ) { } // Construct with value template VarSignal(T&& value, const TGroup& group) : - VarSignal::VarSignalBase( CreateVarNode(std::forward(value), group) ) + VarSignal::VarSignalBase( REACT_IMPL::NodeCtorTag{ }, CreateVarNode(std::forward(value), group) ) { } }; diff --git a/include/react/common/Util.h b/include/react/common/Util.h index 741cd61d..4721723d 100644 --- a/include/react/common/Util.h +++ b/include/react/common/Util.h @@ -216,6 +216,12 @@ class IsCallableWith static const bool value = sizeof(check(nullptr)) == sizeof(YesT); }; +template +bool IsBitmaskSet(T flags, T mask) +{ + return (flags & mask) != (T)0; +} + /****************************************/ REACT_IMPL_END /***************************************/ // Expand args by wrapping them in a dummy function diff --git a/include/react/detail/IReactiveGraph.h b/include/react/detail/IReactiveGraph.h index ddb29689..4cf87bf0 100644 --- a/include/react/detail/IReactiveGraph.h +++ b/include/react/detail/IReactiveGraph.h @@ -37,6 +37,16 @@ enum class UpdateResult shifted }; +enum class NodeFlags +{ + none, + input, + output, + dynamic, + buffered +}; +REACT_DEFINE_BITMASK_OPERATORS(NodeFlags) + struct IReactiveGraph; struct IReactiveNode; @@ -47,7 +57,7 @@ struct IReactiveGraph { virtual ~IReactiveGraph() = default; - virtual NodeId RegisterNode(IReactiveNode* nodePtr) = 0; + virtual NodeId RegisterNode(IReactiveNode* nodePtr, NodeFlags flags) = 0; virtual void UnregisterNode(NodeId node) = 0; virtual void OnNodeAttach(NodeId nodeId, NodeId parentId) = 0; @@ -66,33 +76,13 @@ struct IReactiveNode { virtual ~IReactiveNode() = default; - /// Returns unique type identifier virtual const char* GetNodeType() const = 0; - // Note: Could get rid of this ugly ptr by adding a template parameter to the interface - // But that would mean all engine nodes need that template parameter too - so rather cast virtual UpdateResult Update(TurnId turnId) = 0; - /// Input nodes can be manipulated externally. - virtual bool IsInputNode() const = 0; - - /// Output nodes can't have any successors. - virtual bool IsOutputNode() const = 0; - - /// Dynamic nodes may change in topology as a result of tick. - virtual bool IsDynamicNode() const = 0; - - // Number of predecessors. virtual int GetDependencyCount() const = 0; -}; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EPropagationMode -/////////////////////////////////////////////////////////////////////////////////////////////////// -enum EPropagationMode -{ - sequential_propagation, - parallel_propagation + virtual void ClearBuffer() = 0; }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index e3b8d584..6d696532 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -81,15 +81,15 @@ struct AddIterateByRefRangeWrapper /////////////////////////////////////////////////////////////////////////////////////////////////// /// IterateNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class IterateNode : public SignalNode { public: - template - IterateNode(const std::shared_ptr& graphPtr, U&& init, const std::shared_ptr>& events, FIn&& func) : - IterateNode::SignalNode( graphPtr, std::forward(init) ), - events_( events ), - func_( std::forward(func) ) + template + IterateNode(const std::shared_ptr& graphPtr, T&& init, FIn&& func, const std::shared_ptr>& events) : + IterateNode::SignalNode( graphPtr, std::forward(init) ), + func_( std::forward(func) ), + events_( events ) { this->RegisterMe(); this->AttachToMe(events->GetNodeId()); @@ -105,7 +105,7 @@ class IterateNode : public SignalNode { S newValue = func_(EventRange( events_->Events() ), this->Value()); - if (! Equals(newValue, this->Value())) + if (! (newValue == this->Value())) { this->Value() = std::move(newValue); return UpdateResult::changed; @@ -125,19 +125,19 @@ class IterateNode : public SignalNode private: std::shared_ptr> events_; - F func_; + F func_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// IterateByRefNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class IterateByRefNode : public SignalNode { public: - template - IterateByRefNode(const std::shared_ptr& graphPtr, SIn&& init, const std::shared_ptr>& events, FIn&& func) : - IterateByRefNode::SignalNode( graphPtr, std::forward(init) ), + template + IterateByRefNode(const std::shared_ptr& graphPtr, T&& init, FIn&& func, const std::shared_ptr>& events) : + IterateByRefNode::SignalNode( graphPtr, std::forward(init) ), func_( std::forward(func) ), events_( events ) { @@ -174,33 +174,31 @@ class IterateByRefNode : public SignalNode /////////////////////////////////////////////////////////////////////////////////////////////////// /// SyncedIterateNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class SyncedIterateNode : public SignalNode { public: - template - SyncedIterateNode(const std::shared_ptr& graphPtr, SIn&& init, const std::shared_ptr>& events, FIn&& func, const std::shared_ptr>& ... syncs) : - SyncedIterateNode::SignalNode( graphPtr, std::forward(init) ), - events_( events ), + template + SyncedIterateNode(const std::shared_ptr& graphPtr, T&& init, FIn&& func, const std::shared_ptr>& events, const std::shared_ptr>& ... syncs) : + SyncedIterateNode::SignalNode( graphPtr, std::forward(init) ), func_( std::forward(func) ), + events_( events ), syncHolder_( syncs ... ) { this->RegisterMe(); - this->AttachToMe(dep->GetNodeId()); + this->AttachToMe(events->GetNodeId()); REACT_EXPAND_PACK(this->AttachToMe(syncs->GetNodeId())); } ~SyncedIterateNode() { apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(syncs->GetNodeId())); }, syncHolder_); - this->DetachFromMe(dep_->GetNodeId()); + this->DetachFromMe(events_->GetNodeId()); this->UnregisterMe(); } virtual UpdateResult Update(TurnId turnId) override { - events_->SetCurrentTurn(turn); - S newValue = apply( [this] (const auto& ... syncs) { @@ -208,7 +206,7 @@ class SyncedIterateNode : public SignalNode }, syncHolder_); - if (! Equals(newValue, this->Value())) + if (! (newValue == this->Value())) { this->Value() = std::move(newValue); return UpdateResult::changed; @@ -226,42 +224,42 @@ class SyncedIterateNode : public SignalNode { return 1 + sizeof...(TSyncs); } private: - std::shared_ptr> events_; - F func_; + std::shared_ptr> events_; + std::tuple>...> syncHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// SyncedIterateByRefNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class SyncedIterateByRefNode : public SignalNode { public: - template - SyncedIterateByRefNode(const std::shared_ptr& graphPtr, SIn&& init, const std::shared_ptr>& events, FIn&& func, const std::shared_ptr>& ... syncs) : + template + SyncedIterateByRefNode(const std::shared_ptr& graphPtr, T&& init, FIn&& func, const std::shared_ptr>& events, const std::shared_ptr>& ... syncs) : SyncedIterateByRefNode::SignalNode( graphPtr, std::forward(init) ), - events_( events ), func_( std::forward(func) ), + events_( events ), syncHolder_( syncs ... ) { this->RegisterMe(); - this->AttachToMe(dep->GetNodeId()); + this->AttachToMe(events->GetNodeId()); REACT_EXPAND_PACK(this->AttachToMe(syncs->GetNodeId())); } ~SyncedIterateByRefNode() { apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(syncs->GetNodeId())); }, syncHolder_); - this->DetachFromMe(dep_->GetNodeId()); + this->DetachFromMe(events_->GetNodeId()); this->UnregisterMe(); } virtual UpdateResult Update(TurnId turnId) override { - events_->SetCurrentTurn(turn); + events_->SetCurrentTurn(turnId); bool changed = false; @@ -272,7 +270,7 @@ class SyncedIterateByRefNode : public SignalNode { func_(EventRange( events_->Events() ), this->Value(), args->Value() ...); }, - deps_); + syncHolder_); return UpdateResult::changed; } @@ -289,23 +287,23 @@ class SyncedIterateByRefNode : public SignalNode { return 1 + sizeof...(TSyncs); } private: - std::shared_ptr> events_; - F func_; + std::shared_ptr> events_; + std::tuple>...> syncHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// HoldNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class HoldNode : public SignalNode +template +class HoldNode : public SignalNode { public: - template - HoldNode(const std::shared_ptr& graphPtr, TIn&& init, const std::shared_ptr>& events) : - HoldNode::SignalNode( graphPtr, std::forward(init) ), + template + HoldNode(const std::shared_ptr& graphPtr, T&& init, const std::shared_ptr>& events) : + HoldNode::SignalNode( graphPtr, std::forward(init) ), events_( events ) { this->RegisterMe(); @@ -329,7 +327,7 @@ class HoldNode : public SignalNode { const S& newValue = events_->Events().back(); - if (! Equals(newValue, this->Value())) + if (! (newValue == this->Value())) { changed = true; this->Value() = newValue; @@ -410,11 +408,11 @@ class SnapshotNode : public SignalNode /////////////////////////////////////////////////////////////////////////////////////////////////// /// MonitorNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class MonitorNode : public EventStreamNode +template +class MonitorNode : public EventStreamNode { public: - MonitorNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& target) : + MonitorNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& target) : MonitorNode::EventStreamNode( graphPtr ), target_( target ) { @@ -430,7 +428,7 @@ class MonitorNode : public EventStreamNode virtual UpdateResult Update(TurnId turnId) override { - this->SetCurrentTurn(turn, true); + this->SetCurrentTurn(turnId, true); this->Events().push_back(target_->Value()); @@ -444,7 +442,7 @@ class MonitorNode : public EventStreamNode { return 1; } private: - std::shared_ptr> target_; + std::shared_ptr> target_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index bd966944..caf2e049 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -27,7 +27,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Iterators for event processing /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class EventRange { public: @@ -87,33 +87,21 @@ class EventStreamNode : public NodeBase EventStreamNode(const EventStreamNode&) = delete; EventStreamNode& operator=(const EventStreamNode&) = delete; - void SetCurrentTurn(TurnId turnId, bool forceUpdate = false, bool noClear = false) - { - //this->AccessBufferForClearing([&] { - // if (curTurnId_ != turn.Id() || forceUpdate) - // { - // curTurnId_ = turn.Id(); - // if (!noClear) - // events_.clear(); - // } - //}); - } - explicit EventStreamNode(const std::shared_ptr& graphPtr) : NodeBase( graphPtr ) { } - StorageType& Events() + StorageType& Events() { return events_; } - const StorageType& Events() const + const StorageType& Events() const { return events_; } -protected: - StorageType events_; + virtual void ClearBuffer() override + { events_.clear(); }; private: - uint curTurnId_ { (std::numeric_limits::max)() }; + StorageType events_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -125,7 +113,7 @@ class EventSourceNode : public EventStreamNode public: EventSourceNode(const std::shared_ptr& graphPtr) : EventSourceNode::EventStreamNode( graphPtr ) - { this->RegisterMe(); } + { this->RegisterMe(NodeFlags::buffered | NodeFlags::input); } ~EventSourceNode() { this->UnregisterMe(); } @@ -133,19 +121,13 @@ class EventSourceNode : public EventStreamNode virtual const char* GetNodeType() const override { return "EventSource"; } - virtual bool IsInputNode() const override - { return true; } - virtual int GetDependencyCount() const override { return 0; } virtual UpdateResult Update(TurnId turnId) override { - if (this->Events().size() > 0 && !changedFlag_) - { - this->SetCurrentTurn(turnId, true, true); - changedFlag_ = true; - + if (this->Events().size() > 0) + { return UpdateResult::changed; } else @@ -157,18 +139,8 @@ class EventSourceNode : public EventStreamNode template void EmitValue(U&& value) { - // Clear input from previous turn - if (changedFlag_) - { - changedFlag_ = false; - this->Events().clear(); - } - this->Events().push_back(std::forward(value)); } - -private: - bool changedFlag_ = false; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -182,7 +154,7 @@ class EventMergeNode : public EventStreamNode EventMergeNode::EventStreamNode( graphPtr ), depHolder_( deps ... ) { - this->RegisterMe(); + this->RegisterMe(NodeFlags::buffered); REACT_EXPAND_PACK(this->AttachToMe(deps->GetNodeId())); } @@ -194,8 +166,6 @@ class EventMergeNode : public EventStreamNode virtual UpdateResult Update(TurnId turnId) override { - this->SetCurrentTurn(turnId, true); - apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(MergeFromDep(deps)); }, depHolder_); if (! this->Events().empty()) @@ -214,7 +184,6 @@ class EventMergeNode : public EventStreamNode template void MergeFromDep(const std::shared_ptr>& other) { - //arg->SetCurrentTurn(turn); this->Events().insert(this->Events().end(), other->Events().begin(), other->Events().end()); } @@ -233,7 +202,7 @@ class EventFlattenNode : public EventStreamNode outer_( outer ), inner_( inner ) { - this->RegisterMe(); + this->RegisterMe(NodeFlags::buffered | NodeFlags::dynamic); this->AttachToMe(outer->GetNodeId()); this->AttachToMe(inner->GetNodeId()); } @@ -248,17 +217,11 @@ class EventFlattenNode : public EventStreamNode virtual const char* GetNodeType() const override { return "EventFlatten"; } - virtual bool IsDynamicNode() const override - { return true; } - virtual int GetDependencyCount() const override { return 2; } virtual UpdateResult Update(TurnId turnId) override { - this->SetCurrentTurn(turnId, true); - inner_->SetCurrentTurn(turnId); - auto newInner = GetNodePtr(outer_->Value()); if (newInner != inner_) @@ -301,7 +264,7 @@ class EventProcessingNode : public EventStreamNode func_( std::forward(func) ), dep_( dep ) { - this->RegisterMe(); + this->RegisterMe(NodeFlags::buffered); this->AttachToMe(dep->GetNodeId()); } @@ -313,8 +276,6 @@ class EventProcessingNode : public EventStreamNode virtual UpdateResult Update(TurnId turnId) override { - this->SetCurrentTurn(turnId, true); - func_(EventRange( dep_->Events() ), std::back_inserter(this->Events())); if (! this->Events().empty()) @@ -349,7 +310,7 @@ class SyncedEventProcessingNode : public EventStreamNode dep_( dep ), syncHolder_( syncs ... ) { - this->RegisterMe(); + this->RegisterMe(NodeFlags::buffered); this->AttachToMe(dep->GetNodeId()); REACT_EXPAND_PACK(this->AttachToMe(syncs->GetNodeId())); } @@ -363,10 +324,9 @@ class SyncedEventProcessingNode : public EventStreamNode virtual UpdateResult Update(TurnId turnId) override { - this->SetCurrentTurn(turnId, true); - // Update of this node could be triggered from deps, - // so make sure source doesnt contain events from last turn - dep_->SetCurrentTurn(turnId); + // Updates might be triggered even if only sync nodes changed. Ignore those. + if (dep_->Events().empty()) + return UpdateResult::unchanged; apply( [this] (const auto& ... syncs) @@ -406,7 +366,7 @@ class EventJoinNode : public EventStreamNode> EventJoinNode::EventStreamNode( graphPtr ), slots_( deps ... ) { - this->RegisterMe(); + this->RegisterMe(NodeFlags::buffered); REACT_EXPAND_PACK(this->AttachToMe(deps->GetNodeId())); } @@ -418,8 +378,6 @@ class EventJoinNode : public EventStreamNode> virtual UpdateResult Update(TurnId turnId) override { - this->SetCurrentTurn(turnId, true); - // Move events into buffers apply([this, turnId] (Slot& ... slots) { REACT_EXPAND_PACK(FetchBuffer(turnId, slots)); }, slots_); @@ -429,7 +387,7 @@ class EventJoinNode : public EventStreamNode> // All slots ready? apply( - [this,&isReady] (Slot& ... slots) { + [this, &isReady] (Slot& ... slots) { // Todo: combine return values instead REACT_EXPAND_PACK(CheckSlot(slots, isReady)); }, @@ -475,7 +433,6 @@ class EventJoinNode : public EventStreamNode> template static void FetchBuffer(TurnId turnId, Slot& slot) { - slot.source->SetCurrentTurn(turnId); slot.buffer.insert(slot.buffer.end(), slot.source->Events().begin(), slot.source->Events().end()); } diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index b694e34e..8466d77b 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -38,15 +38,6 @@ class NodeBase : public IReactiveNode NodeBase(NodeBase&&) = delete; NodeBase& operator=(NodeBase&&) = delete; - - virtual bool IsInputNode() const override - { return false; } - - virtual bool IsOutputNode() const override - { return false; } - - virtual bool IsDynamicNode() const override - { return false; } /*void SetWeightHint(WeightHint weight) { @@ -74,8 +65,8 @@ class NodeBase : public IReactiveNode { return graphPtr_; } protected: - void RegisterMe() - { nodeId_ = graphPtr_->RegisterNode(this); } + void RegisterMe(NodeFlags flags = NodeFlags::none) + { nodeId_ = graphPtr_->RegisterNode(this, flags); } void UnregisterMe() { graphPtr_->UnregisterNode(nodeId_); } diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index 0713c157..abdf1c6e 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -39,8 +39,8 @@ class ObserverNode : public NodeBase ObserverNode::NodeBase( graphPtr ) { } - virtual bool IsOutputNode() const - { return true; } + virtual void ClearBuffer() override + { }; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -56,7 +56,7 @@ class SignalObserverNode : public ObserverNode func_( std::forward(func) ), depHolder_( deps ... ) { - this->RegisterMe(); + this->RegisterMe(NodeFlags::output); REACT_EXPAND_PACK(this->AttachToMe(deps->GetNodeId())); } @@ -97,7 +97,7 @@ class EventObserverNode : public ObserverNode func_( std::forward(func) ), subject_( subject ) { - this->RegisterMe(); + this->RegisterMe(NodeFlags::output); this->AttachToMe(subject->GetNodeId()); } @@ -139,7 +139,7 @@ class SyncedEventObserverNode : public ObserverNode subject_( subject ), syncHolder_( syncs ... ) { - this->RegisterMe(); + this->RegisterMe(NodeFlags::output); this->AttachToMe(subject->GetNodeId()); REACT_EXPAND_PACK(this->AttachToMe(syncs->GetNodeId())); } diff --git a/include/react/detail/graph/PropagationST.h b/include/react/detail/graph/PropagationST.h index bfbc0828..863df8e5 100644 --- a/include/react/detail/graph/PropagationST.h +++ b/include/react/detail/graph/PropagationST.h @@ -26,7 +26,7 @@ class SingleThreadedGraph : public IReactiveGraph { public: // IReactiveGraph - virtual NodeId RegisterNode(IReactiveNode* nodePtr) override; + virtual NodeId RegisterNode(IReactiveNode* nodePtr, NodeFlags flags) override; virtual void UnregisterNode(NodeId node) override; virtual void OnNodeAttach(NodeId node, NodeId parentId) override; @@ -49,15 +49,19 @@ class SingleThreadedGraph : public IReactiveGraph NodeData(const NodeData&) = default; NodeData& operator=(const NodeData&) = default; - NodeData(IReactiveNode* nodePtrIn) : - nodePtr( nodePtrIn ) + NodeData(IReactiveNode* nodePtrIn, NodeFlags flagsIn) : + nodePtr( nodePtrIn ), + flags( flagsIn ) { } + NodeFlags flags = NodeFlags::none; + int level = 0; int newLevel = 0 ; bool queued = false; IReactiveNode* nodePtr = nullptr; + std::vector successors; }; @@ -89,6 +93,7 @@ class SingleThreadedGraph : public IReactiveGraph void ScheduleSuccessors(NodeData & node); void InvalidateSuccessors(NodeData & node); + void ClearBufferedNodes(); private: int refCount_ = 1; @@ -97,12 +102,14 @@ class SingleThreadedGraph : public IReactiveGraph IndexMap nodeData_; std::vector changedInputs_; + std::vector pendingBufferedNodes_; + bool isTransactionActive_ = false; }; -NodeId SingleThreadedGraph::RegisterNode(IReactiveNode* nodePtr) +NodeId SingleThreadedGraph::RegisterNode(IReactiveNode* nodePtr, NodeFlags flags) { - return nodeData_.Insert(NodeData{ nodePtr }); + return nodeData_.Insert(NodeData{ nodePtr, flags }); } void SingleThreadedGraph::UnregisterNode(NodeId nodeId) @@ -159,12 +166,17 @@ void SingleThreadedGraph::AddInput(NodeId nodeId, std::function inputCal // Update the node. This applies the input buffer to the node value and checks if it changed. if (nodePtr->Update(0) == UpdateResult::changed) { + if (IsBitmaskSet(node.flags, NodeFlags::buffered)) + pendingBufferedNodes_.push_back(nodePtr); + // Propagate changes through the graph ScheduleSuccessors(node); if (! scheduledNodes_.IsEmpty()) Propagate(); - } + } + + ClearBufferedNodes(); } } @@ -180,9 +192,15 @@ void SingleThreadedGraph::DoTransaction(TransactionFlags flags, F&& transactionC for (NodeId nodeId : changedInputs_) { auto& node = nodeData_[nodeId]; + auto* nodePtr = node.nodePtr; + + if (nodePtr->Update(0) == UpdateResult::changed) + { + if (IsBitmaskSet(node.flags, NodeFlags::buffered)) + pendingBufferedNodes_.push_back(nodePtr); - if (node.nodePtr->Update(0) == UpdateResult::changed) ScheduleSuccessors(node); + } } changedInputs_.clear(); @@ -199,6 +217,7 @@ void SingleThreadedGraph::Propagate() for (NodeId nodeId : scheduledNodes_.Next()) { auto& node = nodeData_[nodeId]; + auto* nodePtr = node.nodePtr; if (node.level < node.newLevel) { @@ -209,10 +228,13 @@ void SingleThreadedGraph::Propagate() continue; } - auto result = node.nodePtr->Update(0); + auto result = nodePtr->Update(0); if (result == UpdateResult::changed) { + if (IsBitmaskSet(node.flags, NodeFlags::buffered)) + pendingBufferedNodes_.push_back(nodePtr); + ScheduleSuccessors(node); } else if (result == UpdateResult::shifted) @@ -253,6 +275,13 @@ void SingleThreadedGraph::InvalidateSuccessors(NodeData& node) } } +void SingleThreadedGraph::ClearBufferedNodes() +{ + for (IReactiveNode* nodePtr : pendingBufferedNodes_) + nodePtr->ClearBuffer(); + pendingBufferedNodes_.clear(); +} + bool SingleThreadedGraph::TopoQueue::FetchNext() { // Throw away previous values diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index d45366e1..30128a3e 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -54,6 +54,9 @@ class SignalNode : public NodeBase const S& Value() const { return value_; } + virtual void ClearBuffer() override + { }; + private: S value_; }; @@ -68,7 +71,7 @@ class VarSignalNode : public SignalNode explicit VarSignalNode(const std::shared_ptr& graphPtr) : VarSignalNode::SignalNode( graphPtr ), newValue_( ) - { this->RegisterMe(); } + { this->RegisterMe(NodeFlags::input); } template VarSignalNode(const std::shared_ptr& graphPtr, T&& value) : @@ -82,9 +85,6 @@ class VarSignalNode : public SignalNode virtual const char* GetNodeType() const override { return "VarSignal"; } - virtual bool IsInputNode() const override - { return true; } - virtual int GetDependencyCount() const override { return 0; } @@ -219,7 +219,7 @@ class SignalFlattenNode : public SignalNode outer_( outer ), inner_( inner ) { - this->RegisterMe(); + this->RegisterMe(NodeFlags::dynamic); this->AttachToMe(outer->GetNodeId()); this->AttachToMe(inner->GetNodeId()); } @@ -234,9 +234,6 @@ class SignalFlattenNode : public SignalNode virtual const char* GetNodeType() const override { return "SignalFlatten"; } - virtual bool IsDynamicNode() const override - { return true; } - virtual int GetDependencyCount() const override { return 2; } diff --git a/project/msvc/CppReact.sln b/project/msvc/CppReact.sln index b414852d..bd00b26a 100644 --- a/project/msvc/CppReact.sln +++ b/project/msvc/CppReact.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.30501.0 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CppReact", "CppReact.vcxproj", "{5E56AAB9-4E33-4B9E-A315-E85CEDB75CF1}" EndProject @@ -27,8 +27,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example_BasicEvents", "Exam EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example_BasicObservers", "Example_BasicObservers.vcxproj", "{CC66BFA0-D609-46E0-9FD1-F9CC902410B1}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example_BasicReactors", "Example_BasicReactors.vcxproj", "{D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example_BasicSignals", "Example_BasicSignals.vcxproj", "{230C9137-CCD0-47E2-8F1F-2E1DD19984A1}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example_BasicSynchronization", "Example_BasicSynchronization.vcxproj", "{269329F8-A9E1-41AC-9C37-3A82A082A62C}" @@ -105,14 +103,6 @@ Global {CC66BFA0-D609-46E0-9FD1-F9CC902410B1}.Release|Win32.Build.0 = Release|Win32 {CC66BFA0-D609-46E0-9FD1-F9CC902410B1}.Release|x64.ActiveCfg = Release|x64 {CC66BFA0-D609-46E0-9FD1-F9CC902410B1}.Release|x64.Build.0 = Release|x64 - {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Debug|Win32.ActiveCfg = Debug|Win32 - {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Debug|Win32.Build.0 = Debug|Win32 - {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Debug|x64.ActiveCfg = Debug|x64 - {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Debug|x64.Build.0 = Debug|x64 - {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Release|Win32.ActiveCfg = Release|Win32 - {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Release|Win32.Build.0 = Release|Win32 - {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Release|x64.ActiveCfg = Release|x64 - {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96}.Release|x64.Build.0 = Release|x64 {230C9137-CCD0-47E2-8F1F-2E1DD19984A1}.Debug|Win32.ActiveCfg = Debug|Win32 {230C9137-CCD0-47E2-8F1F-2E1DD19984A1}.Debug|Win32.Build.0 = Debug|Win32 {230C9137-CCD0-47E2-8F1F-2E1DD19984A1}.Debug|x64.ActiveCfg = Debug|x64 @@ -142,7 +132,6 @@ Global {D7B70D3B-F14D-4A85-B164-EAB88C358E85} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} {BD777649-97F1-4810-BF21-CB27F7672BF4} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} {CC66BFA0-D609-46E0-9FD1-F9CC902410B1} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} - {D976F4D4-B472-4709-BFB5-B1BEEA1F7E96} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} {230C9137-CCD0-47E2-8F1F-2E1DD19984A1} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} {269329F8-A9E1-41AC-9C37-3A82A082A62C} = {518ACABC-E4A7-4E2D-9A04-FFA669A30DBF} EndGlobalSection diff --git a/project/msvc/Example_BasicReactors.vcxproj b/project/msvc/Example_BasicReactors.vcxproj index ba7b1dad..247db825 100644 --- a/project/msvc/Example_BasicReactors.vcxproj +++ b/project/msvc/Example_BasicReactors.vcxproj @@ -136,9 +136,6 @@ true - - - {5e56aab9-4e33-4b9e-a315-e85cedb75cf1} diff --git a/project/msvc/Example_BasicReactors.vcxproj.filters b/project/msvc/Example_BasicReactors.vcxproj.filters index 7398067f..6a1782f7 100644 --- a/project/msvc/Example_BasicReactors.vcxproj.filters +++ b/project/msvc/Example_BasicReactors.vcxproj.filters @@ -14,9 +14,4 @@ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - Source Files - - \ No newline at end of file From 1fc56425a7ea44b4cbbb618974ff2b1e76fe7d12 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 8 Aug 2016 23:25:30 +0200 Subject: [PATCH 225/266] Fixes. --- include/react/API.h | 2 +- include/react/detail/IReactiveGraph.h | 10 +++++----- include/react/detail/graph/AlgorithmNodes.h | 10 ++-------- include/react/detail/graph/PropagationST.h | 2 ++ 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/include/react/API.h b/include/react/API.h index 435e6562..dbbf88f9 100644 --- a/include/react/API.h +++ b/include/react/API.h @@ -44,7 +44,7 @@ enum class WeightHint enum class TransactionFlags { - none = 1 << 0, + none = 0, allow_merging = 1 << 1 }; diff --git a/include/react/detail/IReactiveGraph.h b/include/react/detail/IReactiveGraph.h index 4cf87bf0..e081844f 100644 --- a/include/react/detail/IReactiveGraph.h +++ b/include/react/detail/IReactiveGraph.h @@ -39,11 +39,11 @@ enum class UpdateResult enum class NodeFlags { - none, - input, - output, - dynamic, - buffered + none = 0, + input = 1 << 0, + output = 1 << 1, + dynamic = 1 << 2, + buffered = 1 << 3 }; REACT_DEFINE_BITMASK_OPERATORS(NodeFlags) diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index 6d696532..b5bb7aa6 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -416,7 +416,7 @@ class MonitorNode : public EventStreamNode MonitorNode::EventStreamNode( graphPtr ), target_( target ) { - this->RegisterMe(); + this->RegisterMe(NodeFlags::buffered); this->AttachToMe(target->GetNodeId()); } @@ -428,10 +428,7 @@ class MonitorNode : public EventStreamNode virtual UpdateResult Update(TurnId turnId) override { - this->SetCurrentTurn(turnId, true); - this->Events().push_back(target_->Value()); - return UpdateResult::changed; } @@ -457,7 +454,7 @@ class PulseNode : public EventStreamNode target_( target ), trigger_( trigger ) { - this->RegisterMe(); + this->RegisterMe(NodeFlags::buffered); this->AttachToMe(target->GetNodeId()); this->AttachToMe(trigger->GetNodeId()); } @@ -471,9 +468,6 @@ class PulseNode : public EventStreamNode virtual UpdateResult Update(TurnId turnId) override { - this->SetCurrentTurn(turn, true); - trigger_->SetCurrentTurn(turn); - for (size_t i=0; iEvents().size(); i++) this->Events().push_back(target_->Value()); diff --git a/include/react/detail/graph/PropagationST.h b/include/react/detail/graph/PropagationST.h index 863df8e5..96d4fee4 100644 --- a/include/react/detail/graph/PropagationST.h +++ b/include/react/detail/graph/PropagationST.h @@ -208,6 +208,8 @@ void SingleThreadedGraph::DoTransaction(TransactionFlags flags, F&& transactionC // Propagate changes through the graph. if (! scheduledNodes_.IsEmpty()) Propagate(); + + ClearBufferedNodes(); } void SingleThreadedGraph::Propagate() From 5a988398d629623ae6ace185c27974c001fd87a6 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 5 Sep 2016 23:02:30 +0200 Subject: [PATCH 226/266] Progress. SlotNodes. --- examples/src/BasicEvents.cpp | 38 ++- examples/src/BasicSignals.cpp | 36 ++- include/react/API.h | 20 +- include/react/Event.h | 115 ++++++++- include/react/Group.h | 28 ++- include/react/Signal.h | 133 +++++++++-- include/react/detail/IReactiveGraph.h | 50 ++-- include/react/detail/graph/AlgorithmNodes.h | 94 +++++--- include/react/detail/graph/EventNodes.h | 228 ++++++++++++------ include/react/detail/graph/GraphBase.h | 16 +- include/react/detail/graph/ObserverNodes.h | 35 +-- include/react/detail/graph/PropagationST.h | 251 +++++++++++++++----- include/react/detail/graph/SignalNodes.h | 209 ++++++++++++---- 13 files changed, 941 insertions(+), 312 deletions(-) diff --git a/examples/src/BasicEvents.cpp b/examples/src/BasicEvents.cpp index b9a7cfbd..0f4f3f06 100644 --- a/examples/src/BasicEvents.cpp +++ b/examples/src/BasicEvents.cpp @@ -237,12 +237,46 @@ namespace example5 /////////////////////////////////////////////////////////////////////////////////////////////////// int main() { - example1::v1::Run(); + using namespace std; + using namespace react; + + ReactiveGroup<> group; + + auto ev1 = EventSource( group ); + auto ev2 = EventSource( group ); + + auto slot1 = EventSlot( ev1, group ); + auto slot2 = EventSlot( ev1, group ); + + Observer<> obs1( + [] (EventRange in) + { + for (int e : in) + cout << e << endl; + }, slot1); + + Observer<> obs2( + [] (EventRange in) + { + for (int e : in) + cout << e << endl; + }, slot2); + + ev1 << 10 << 20 << 30; + ev2 << 11 << 22 << 33; + + slot1.Set(ev2); + slot2.Set(ev2); + + ev1 << 10 << 20 << 30; + ev2 << 11 << 22 << 33; + + /*example1::v1::Run(); example1::v2::Run(); example2::Run(); example3::Run(); example4::Run(); - example5::Run(); + example5::Run();*/ return 0; } \ No newline at end of file diff --git a/examples/src/BasicSignals.cpp b/examples/src/BasicSignals.cpp index 3007d199..44369987 100644 --- a/examples/src/BasicSignals.cpp +++ b/examples/src/BasicSignals.cpp @@ -218,11 +218,43 @@ namespace example5 /////////////////////////////////////////////////////////////////////////////////////////////////// int main() { - example1::Run(); + using namespace std; + using namespace react; + + ReactiveGroup<> group; + + auto sig1 = VarSignal( 10, group ); + auto sig2 = VarSignal( 22, group ); + + auto slot1 = SignalSlot( sig1, group ); + auto slot2 = SignalSlot( sig1, group ); + + printf("%d\n", slot1.Value()); + printf("%d\n", slot2.Value()); + + slot1.Set(sig2); + slot2.Set(sig2); + + printf("%d\n", slot1.Value()); + printf("%d\n", slot2.Value()); + + group.DoTransaction([&] + { + slot1.Set(sig2); + slot2.Set(sig2); + }); + + group.EnqueueTransaction([&] + { + slot1.Set(sig2); + slot2.Set(sig2); + }); + + /*example1::Run(); example2::Run(); example3::Run(); example4::Run(); - example5::Run(); + example5::Run();*/ return 0; } \ No newline at end of file diff --git a/include/react/API.h b/include/react/API.h index dbbf88f9..928d1007 100644 --- a/include/react/API.h +++ b/include/react/API.h @@ -59,18 +59,24 @@ template class ReactiveGroup; // Signals -template +template class SignalBase; -template +template class VarSignalBase; -template +template +class SignalSlotBase; + +template class Signal; -template +template class VarSignal; +template +class SignalSlot; + // Events enum class Token; @@ -80,13 +86,17 @@ class EventBase; template class EventSourceBase; +template +class EventSlotBase; + template class Event; template class EventSource; - +template +class EventSlot; // Observers template diff --git a/include/react/Event.h b/include/react/Event.h index 13fdcb5c..2c9222fd 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -100,9 +100,6 @@ class EventBase template class EventSourceBase : public EventBase { -private: - using NodeType = REACT_IMPL::EventSourceNode; - public: using EventBase::EventBase; @@ -137,7 +134,7 @@ class EventSourceBase : public EventBase using REACT_IMPL::PrivateReactiveGroupInterface; using SrcNodeType = REACT_IMPL::EventSourceNode; - return std::make_shared(PrivateReactiveGroupInterface::GraphPtr(group)); + return std::make_shared(PrivateReactiveGroupInterface::GraphPtr(group)); } private: @@ -145,9 +142,10 @@ class EventSourceBase : public EventBase void EmitValue(T&& value) { using REACT_IMPL::NodeId; - using REACT_IMPL::IReactiveGraph; + using REACT_IMPL::ReactiveGraph; + using SrcNodeType = REACT_IMPL::EventSourceNode; - NodeType* castedPtr = static_cast(this->NodePtr().get()); + SrcNodeType* castedPtr = static_cast(this->NodePtr().get()); NodeId nodeId = castedPtr->GetNodeId(); auto& graphPtr = NodePtr()->GraphPtr(); @@ -155,6 +153,55 @@ class EventSourceBase : public EventBase } }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventSlotBase +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class EventSlotBase : public EventBase +{ +public: + using EventBase::EventBase; + + void Set(const EventBase& newInput) + { SetInput(newInput); } + + void operator<<=(const EventBase& newInput) + { SetInput(newInput); } + +protected: + EventSlotBase() = default; + + EventSlotBase(const EventSlotBase&) = default; + EventSlotBase& operator=(const EventSlotBase&) = default; + + EventSlotBase(EventSlotBase&&) = default; + EventSlotBase& operator=(EventSlotBase&&) = default; + + auto CreateSlotNode(const EventBase& input, const ReactiveGroupBase& group) -> decltype(auto) + { + using REACT_IMPL::PrivateNodeInterface; + using REACT_IMPL::PrivateReactiveGroupInterface; + using SlotNodeType = REACT_IMPL::EventSlotNode; + + return std::make_shared(PrivateReactiveGroupInterface::GraphPtr(group), PrivateNodeInterface::NodePtr(input)); + } + +private: + void SetInput(const EventBase& newInput) + { + using REACT_IMPL::PrivateNodeInterface; + using REACT_IMPL::NodeId; + using REACT_IMPL::ReactiveGraph; + using SlotNodeType = REACT_IMPL::EventSlotNode; + + SlotNodeType* castedPtr = static_cast(this->NodePtr().get()); + + NodeId nodeId = castedPtr->GetInputNodeId(); + auto& graphPtr = NodePtr()->GraphPtr(); + graphPtr->AddInput(nodeId, [castedPtr, &newInput] { castedPtr->SetInput(PrivateNodeInterface::NodePtr(newInput)); }); + } +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Event /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -277,6 +324,62 @@ class EventSource : public EventSourceBase { EventSource::EventSourceBase::operator=(std::move(other)); return *this; } }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventSlot +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class EventSlot : public EventSlotBase +{ +public: + using EventSlotBase::EventSlotBase; + + using ValueType = E; + + EventSlot() = delete; + + EventSlot(const EventSlot&) = delete; + EventSlot& operator=(const EventSlot&) = delete; + + EventSlot(EventSlot&&) = default; + EventSlot& operator=(EventSlot&&) = default; + + // Construct with value + EventSlot(const EventBase& input, const ReactiveGroupBase& group) : + EventSlot::EventSlotBase( REACT_IMPL::NodeCtorTag{ }, CreateSlotNode(input, group) ) + { } +}; + +template +class EventSlot : public EventSlotBase +{ +public: + using EventSlotBase::EventSlotBase; + + using ValueType = E; + + EventSlot() = delete; + + EventSlot(const EventSlot&) = default; + EventSlot& operator=(const EventSlot&) = default; + + EventSlot(EventSlot&&) = default; + EventSlot& operator=(EventSlot&&) = default; + + // Construct from unique + EventSlot(EventSlot&& other) : + EventSlot::EventSlotBase( std::move(other) ) + { } + + // Assign from unique + EventSlot& operator=(EventSlot&& other) + { EventSlot::EventSlotBase::operator=(std::move(other)); return *this; } + + // Construct with value + EventSlot(const SignalBase& input, const ReactiveGroupBase& group) : + EventSlot::EventSlotBase( REACT_IMPL::NodeCtorTag{ }, CreateSlotNode(input, group) ) + { } +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Merge /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/Group.h b/include/react/Group.h index c8324d1c..4ab5d2af 100644 --- a/include/react/Group.h +++ b/include/react/Group.h @@ -140,7 +140,7 @@ void AsyncTransaction(TransactionFlagsT flags, TransactionStatus& status, F&& fu /////////////////////////////////////////////////////////////////////////////////////////////////// class ReactiveGroupBase { - using GraphType = REACT_IMPL::SingleThreadedGraph; + using GraphType = REACT_IMPL::ReactiveGraph; public: ReactiveGroupBase() : @@ -157,11 +157,15 @@ class ReactiveGroupBase template void DoTransaction(F&& func) - { DoTransaction(TransactionFlags::none, std::forward(func)); } + { graphPtr_->DoTransaction(std::forward(func)); } template - void DoTransaction(TransactionFlags flags, F&& func) - { graphPtr_->DoTransaction(flags, std::forward(func)); } + void EnqueueTransaction(F&& func) + { EnqueueTransaction(TransactionFlags::none, std::forward(func)); } + + template + void EnqueueTransaction(TransactionFlags flags, F&& func) + { graphPtr_->EnqueueTransaction(flags, std::forward(func)); } protected: auto GraphPtr() -> std::shared_ptr& @@ -229,31 +233,31 @@ struct PrivateNodeInterface { return base.NodePtr(); } template - static auto GraphPtr(const TBase& base) -> const std::shared_ptr& + static auto GraphPtr(const TBase& base) -> const std::shared_ptr& { return base.NodePtr()->GraphPtr(); } template - static auto GraphPtr(TBase& base) -> std::shared_ptr& + static auto GraphPtr(TBase& base) -> std::shared_ptr& { return base.NodePtr()->GraphPtr(); } }; struct PrivateReactiveGroupInterface { - static auto GraphPtr(const ReactiveGroupBase& group) -> const std::shared_ptr& + static auto GraphPtr(const ReactiveGroupBase& group) -> const std::shared_ptr& { return group.GraphPtr(); } - static auto GraphPtr(ReactiveGroupBase& group) -> std::shared_ptr& + static auto GraphPtr(ReactiveGroupBase& group) -> std::shared_ptr& { return group.GraphPtr(); } }; template -static auto GetCheckedGraphPtr(const TBase1& dep1, const TBases& ... deps) -> const std::shared_ptr& +static auto GetCheckedGraphPtr(const TBase1& dep1, const TBases& ... deps) -> const std::shared_ptr& { - const std::shared_ptr& graphPtr1 = PrivateNodeInterface::GraphPtr(dep1); + const std::shared_ptr& graphPtr1 = PrivateNodeInterface::GraphPtr(dep1); - std::initializer_list rawGraphPtrs = { PrivateNodeInterface::GraphPtr(deps).get() ... }; + std::initializer_list rawGraphPtrs = { PrivateNodeInterface::GraphPtr(deps).get() ... }; - bool isSameGraphForAllDeps = std::all_of(rawGraphPtrs.begin(), rawGraphPtrs.end(), [&] (IReactiveGraph* p) { return p == graphPtr1.get(); }); + bool isSameGraphForAllDeps = std::all_of(rawGraphPtrs.begin(), rawGraphPtrs.end(), [&] (ReactiveGraph* p) { return p == graphPtr1.get(); }); REACT_ASSERT(isSameGraphForAllDeps, "All dependencies must belong to the same group."); diff --git a/include/react/Signal.h b/include/react/Signal.h index 22e4e548..dab00344 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -112,8 +112,7 @@ class VarSignalBase : public SignalBase VarSignalBase(VarSignalBase&&) = default; VarSignalBase& operator=(VarSignalBase&&) = default; - template - auto CreateVarNode(const TGroup& group) -> decltype(auto) + auto CreateVarNode(const ReactiveGroupBase& group) -> decltype(auto) { using REACT_IMPL::PrivateReactiveGroupInterface; using VarNodeType = REACT_IMPL::VarSignalNode; @@ -121,8 +120,8 @@ class VarSignalBase : public SignalBase return std::make_shared(PrivateReactiveGroupInterface::GraphPtr(group)); } - template - auto CreateVarNode(T&& value, const TGroup& group) -> decltype(auto) + template + auto CreateVarNode(T&& value, const ReactiveGroupBase& group) -> decltype(auto) { using REACT_IMPL::PrivateReactiveGroupInterface; using VarNodeType = REACT_IMPL::VarSignalNode; @@ -135,7 +134,7 @@ class VarSignalBase : public SignalBase void SetValue(T&& newValue) { using REACT_IMPL::NodeId; - using REACT_IMPL::IReactiveGraph; + using REACT_IMPL::ReactiveGraph; using VarNodeType = REACT_IMPL::VarSignalNode; VarNodeType* castedPtr = static_cast(this->NodePtr().get()); @@ -149,7 +148,7 @@ class VarSignalBase : public SignalBase void ModifyValue(const F& func) { using REACT_IMPL::NodeId; - using REACT_IMPL::IReactiveGraph; + using REACT_IMPL::ReactiveGraph; using VarNodeType = REACT_IMPL::VarSignalNode; VarNodeType* castedPtr = static_cast(this->NodePtr().get()); @@ -160,6 +159,55 @@ class VarSignalBase : public SignalBase } }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SignalSlotBase +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class SignalSlotBase : public SignalBase +{ +public: + using SignalBase::SignalBase; + + void Set(const SignalBase& newInput) + { SetInput(newInput); } + + void operator<<=(const SignalBase& newInput) + { SetInput(newInput); } + +protected: + SignalSlotBase() = default; + + SignalSlotBase(const SignalSlotBase&) = default; + SignalSlotBase& operator=(const SignalSlotBase&) = default; + + SignalSlotBase(SignalSlotBase&&) = default; + SignalSlotBase& operator=(SignalSlotBase&&) = default; + + auto CreateSlotNode(const SignalBase& input, const ReactiveGroupBase& group) -> decltype(auto) + { + using REACT_IMPL::PrivateNodeInterface; + using REACT_IMPL::PrivateReactiveGroupInterface; + using SlotNodeType = REACT_IMPL::SignalSlotNode; + + return std::make_shared(PrivateReactiveGroupInterface::GraphPtr(group), PrivateNodeInterface::NodePtr(input)); + } + +private: + void SetInput(const SignalBase& newInput) + { + using REACT_IMPL::PrivateNodeInterface; + using REACT_IMPL::NodeId; + using REACT_IMPL::ReactiveGraph; + using SlotNodeType = REACT_IMPL::SignalSlotNode; + + SlotNodeType* castedPtr = static_cast(this->NodePtr().get()); + + NodeId nodeId = castedPtr->GetInputNodeId(); + auto& graphPtr = NodePtr()->GraphPtr(); + graphPtr->AddInput(nodeId, [castedPtr, &newInput] { castedPtr->SetInput(PrivateNodeInterface::NodePtr(newInput)); }); + } +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Signal /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -219,7 +267,7 @@ class Signal : public SignalBase }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Signal +/// VarSignal /////////////////////////////////////////////////////////////////////////////////////////////////// template class VarSignal : public VarSignalBase @@ -273,7 +321,7 @@ class VarSignal : public VarSignalBase // Assign from unique VarSignal& operator=(VarSignal&& other) - { VarSignal::SignalBase::operator=(std::move(other)); return *this; } + { VarSignal::VarSignalBase::operator=(std::move(other)); return *this; } // Construct with default template @@ -289,15 +337,70 @@ class VarSignal : public VarSignalBase }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Flatten +/// SignalSlot /////////////////////////////////////////////////////////////////////////////////////////////////// -/*template -auto Flatten(const SignalBase>& outer) -> Signal +template +class SignalSlot : public SignalSlotBase { - return Signal( - std::make_shared, TInner>>( - GetNodePtr(outer), GetNodePtr(outer.Value()))); -}*/ +public: + using SignalSlotBase::SignalSlotBase; + + using ValueType = S; + + SignalSlot() = delete; + + SignalSlot(const SignalSlot&) = delete; + SignalSlot& operator=(const SignalSlot&) = delete; + + SignalSlot(SignalSlot&&) = default; + SignalSlot& operator=(SignalSlot&&) = default; + + // Construct with default + explicit SignalSlot(const ReactiveGroupBase& group) : + SignalSlot::SignalSlotBase( REACT_IMPL::NodeCtorTag{ }, CreateSlotNode( group) ) + { } + + // Construct with value + SignalSlot(const SignalBase& input, const ReactiveGroupBase& group) : + SignalSlot::SignalSlotBase( REACT_IMPL::NodeCtorTag{ }, CreateSlotNode(input, group) ) + { } +}; + +template +class SignalSlot : public SignalSlotBase +{ +public: + using SignalSlotBase::SignalSlotBase; + + using ValueType = S; + + SignalSlot() = delete; + + SignalSlot(const SignalSlot&) = default; + SignalSlot& operator=(const SignalSlot&) = default; + + SignalSlot(SignalSlot&&) = default; + SignalSlot& operator=(SignalSlot&&) = default; + + // Construct from unique + SignalSlot(SignalSlot&& other) : + SignalSlot::SignalSlotBase( std::move(other) ) + { } + + // Assign from unique + SignalSlot& operator=(SignalSlot&& other) + { SignalSlot::SignalSlotBase::operator=(std::move(other)); return *this; } + + // Construct with default + explicit SignalSlot(const ReactiveGroupBase& group) : + SignalSlot::SignalSlotBase( REACT_IMPL::NodeCtorTag{ }, CreateSlotNode( group) ) + { } + + // Construct with value + SignalSlot(const SignalBase& input, const ReactiveGroupBase& group) : + SignalSlot::SignalSlotBase( REACT_IMPL::NodeCtorTag{ }, CreateSlotNode(input, group) ) + { } +}; /******************************************/ REACT_END /******************************************/ diff --git a/include/react/detail/IReactiveGraph.h b/include/react/detail/IReactiveGraph.h index e081844f..594f3801 100644 --- a/include/react/detail/IReactiveGraph.h +++ b/include/react/detail/IReactiveGraph.h @@ -13,6 +13,7 @@ #include #include +#include #include #include "react/API.h" @@ -26,48 +27,28 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// using NodeId = size_t; using TurnId = size_t; +using LinkId = size_t; static NodeId invalid_node_id = (std::numeric_limits::max)(); static TurnId invalid_turn_id = (std::numeric_limits::max)(); +static LinkId invalid_link_id = (std::numeric_limits::max)(); enum class UpdateResult { unchanged, - changed, - shifted + changed }; -enum class NodeFlags +enum class NodeCategory { - none = 0, - input = 1 << 0, - output = 1 << 1, - dynamic = 1 << 2, - buffered = 1 << 3 + normal, + input, + dyninput, + output, + link }; -REACT_DEFINE_BITMASK_OPERATORS(NodeFlags) -struct IReactiveGraph; -struct IReactiveNode; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IReactiveGraph -/////////////////////////////////////////////////////////////////////////////////////////////////// -struct IReactiveGraph -{ - virtual ~IReactiveGraph() = default; - - virtual NodeId RegisterNode(IReactiveNode* nodePtr, NodeFlags flags) = 0; - virtual void UnregisterNode(NodeId node) = 0; - - virtual void OnNodeAttach(NodeId nodeId, NodeId parentId) = 0; - virtual void OnNodeDetach(NodeId nodeId, NodeId parentId) = 0; - - virtual void OnDynamicNodeAttach(NodeId nodeId, NodeId parentId, TurnId turn) = 0; - virtual void OnDynamicNodeDetach(NodeId nodeId, NodeId parentId, TurnId turn) = 0; - - virtual void AddInput(NodeId nodeId, std::function inputCallback) = 0; -}; +class ReactiveGraph; /////////////////////////////////////////////////////////////////////////////////////////////////// /// IReactiveNode @@ -78,11 +59,16 @@ struct IReactiveNode virtual const char* GetNodeType() const = 0; - virtual UpdateResult Update(TurnId turnId) = 0; + virtual UpdateResult Update(TurnId turnId, int successorCount) = 0; virtual int GetDependencyCount() const = 0; +}; + +using LinkOutputList = std::vector>; - virtual void ClearBuffer() = 0; +struct ILinkOutputNode : public IReactiveNode +{ + virtual void CollectOutput(LinkOutputList& output) = 0; }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index b5bb7aa6..62ff5c9a 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -86,7 +86,7 @@ class IterateNode : public SignalNode { public: template - IterateNode(const std::shared_ptr& graphPtr, T&& init, FIn&& func, const std::shared_ptr>& events) : + IterateNode(const std::shared_ptr& graphPtr, T&& init, FIn&& func, const std::shared_ptr>& events) : IterateNode::SignalNode( graphPtr, std::forward(init) ), func_( std::forward(func) ), events_( events ) @@ -101,10 +101,12 @@ class IterateNode : public SignalNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId) override + virtual UpdateResult Update(TurnId turnId, int successorCount) override { S newValue = func_(EventRange( events_->Events() ), this->Value()); + events_->DecrementPendingSuccessorCount(); + if (! (newValue == this->Value())) { this->Value() = std::move(newValue); @@ -136,7 +138,7 @@ class IterateByRefNode : public SignalNode { public: template - IterateByRefNode(const std::shared_ptr& graphPtr, T&& init, FIn&& func, const std::shared_ptr>& events) : + IterateByRefNode(const std::shared_ptr& graphPtr, T&& init, FIn&& func, const std::shared_ptr>& events) : IterateByRefNode::SignalNode( graphPtr, std::forward(init) ), func_( std::forward(func) ), events_( events ) @@ -151,10 +153,12 @@ class IterateByRefNode : public SignalNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId) override + virtual UpdateResult Update(TurnId turnId, int successorCount) override { func_(EventRange( events_->Events() ), this->Value()); + events_->DecrementPendingSuccessorCount(); + // Always assume change return UpdateResult::changed; } @@ -179,7 +183,7 @@ class SyncedIterateNode : public SignalNode { public: template - SyncedIterateNode(const std::shared_ptr& graphPtr, T&& init, FIn&& func, const std::shared_ptr>& events, const std::shared_ptr>& ... syncs) : + SyncedIterateNode(const std::shared_ptr& graphPtr, T&& init, FIn&& func, const std::shared_ptr>& events, const std::shared_ptr>& ... syncs) : SyncedIterateNode::SignalNode( graphPtr, std::forward(init) ), func_( std::forward(func) ), events_( events ), @@ -197,8 +201,12 @@ class SyncedIterateNode : public SignalNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId) override + virtual UpdateResult Update(TurnId turnId, int successorCount) override { + // Updates might be triggered even if only sync nodes changed. Ignore those. + if (events_->Events().empty()) + return UpdateResult::unchanged; + S newValue = apply( [this] (const auto& ... syncs) { @@ -206,6 +214,8 @@ class SyncedIterateNode : public SignalNode }, syncHolder_); + events_->DecrementPendingSuccessorCount(); + if (! (newValue == this->Value())) { this->Value() = std::move(newValue); @@ -239,7 +249,7 @@ class SyncedIterateByRefNode : public SignalNode { public: template - SyncedIterateByRefNode(const std::shared_ptr& graphPtr, T&& init, FIn&& func, const std::shared_ptr>& events, const std::shared_ptr>& ... syncs) : + SyncedIterateByRefNode(const std::shared_ptr& graphPtr, T&& init, FIn&& func, const std::shared_ptr>& events, const std::shared_ptr>& ... syncs) : SyncedIterateByRefNode::SignalNode( graphPtr, std::forward(init) ), func_( std::forward(func) ), events_( events ), @@ -257,27 +267,23 @@ class SyncedIterateByRefNode : public SignalNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId) override + virtual UpdateResult Update(TurnId turnId, int successorCount) override { - events_->SetCurrentTurn(turnId); + // Updates might be triggered even if only sync nodes changed. Ignore those. + if (events_->Events().empty()) + return UpdateResult::unchanged; - bool changed = false; + apply( + [this] (const auto& ... args) + { + func_(EventRange( events_->Events() ), this->Value(), args->Value() ...); + }, + syncHolder_); - if (! events_->Events().empty()) - { - apply( - [this] (const auto& ... args) - { - func_(EventRange( events_->Events() ), this->Value(), args->Value() ...); - }, - syncHolder_); + events_->DecrementPendingSuccessorCount(); + this->SetPendingSuccessorCount(successorCount); - return UpdateResult::changed; - } - else - { - return UpdateResult::unchanged; - } + return UpdateResult::changed; } virtual const char* GetNodeType() const override @@ -302,7 +308,7 @@ class HoldNode : public SignalNode { public: template - HoldNode(const std::shared_ptr& graphPtr, T&& init, const std::shared_ptr>& events) : + HoldNode(const std::shared_ptr& graphPtr, T&& init, const std::shared_ptr>& events) : HoldNode::SignalNode( graphPtr, std::forward(init) ), events_( events ) { @@ -319,7 +325,7 @@ class HoldNode : public SignalNode virtual const char* GetNodeType() const override { return "HoldNode"; } - virtual UpdateResult Update(TurnId turnId) override + virtual UpdateResult Update(TurnId turnId, int successorCount) override { bool changed = false; @@ -332,6 +338,8 @@ class HoldNode : public SignalNode changed = true; this->Value() = newValue; } + + events_->DecrementPendingSuccessorCount(); } if (changed) @@ -354,7 +362,7 @@ template class SnapshotNode : public SignalNode { public: - SnapshotNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& target, const std::shared_ptr>& trigger) : + SnapshotNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& target, const std::shared_ptr>& trigger) : SnapshotNode::SignalNode( graphPtr, target->Value() ), target_( target ), trigger_( trigger ) @@ -371,21 +379,21 @@ class SnapshotNode : public SignalNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId) override + virtual UpdateResult Update(TurnId turnId, int successorCount) override { - trigger_->SetCurrentTurn(turnId); - bool changed = false; if (! trigger_->Events().empty()) { const S& newValue = target_->Value(); - if (! Equals(newValue, this->Value())) + if (! (newValue == this->Value())) { changed = true; this->Value() = newValue; } + + trigger_->DecrementPendingSuccessorCount(); } if (changed) @@ -412,11 +420,11 @@ template class MonitorNode : public EventStreamNode { public: - MonitorNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& target) : + MonitorNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& target) : MonitorNode::EventStreamNode( graphPtr ), target_( target ) { - this->RegisterMe(NodeFlags::buffered); + this->RegisterMe(); this->AttachToMe(target->GetNodeId()); } @@ -426,9 +434,12 @@ class MonitorNode : public EventStreamNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId) override + virtual UpdateResult Update(TurnId turnId, int successorCount) override { this->Events().push_back(target_->Value()); + + this->SetPendingSuccessorCount(successorCount); + return UpdateResult::changed; } @@ -449,12 +460,12 @@ template class PulseNode : public EventStreamNode { public: - PulseNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& target, const std::shared_ptr>& trigger) : + PulseNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& target, const std::shared_ptr>& trigger) : PulseNode::EventStreamNode( graphPtr ), target_( target ), trigger_( trigger ) { - this->RegisterMe(NodeFlags::buffered); + this->RegisterMe(); this->AttachToMe(target->GetNodeId()); this->AttachToMe(trigger->GetNodeId()); } @@ -466,19 +477,26 @@ class PulseNode : public EventStreamNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId) override + virtual UpdateResult Update(TurnId turnId, int successorCount) override { for (size_t i=0; iEvents().size(); i++) this->Events().push_back(target_->Value()); + trigger_->DecrementPendingSuccessorCount(); + if (! this->Events().empty()) + { + this->SetPendingSuccessorCount(successorCount); return UpdateResult::changed; + } else + { return UpdateResult::unchanged; + } } virtual const char* GetNodeType() const override - { return "PulseNode"; } + { return "Pulse"; } virtual int GetDependencyCount() const override { return 2; } diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index caf2e049..6a457a98 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -27,19 +27,19 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Iterators for event processing /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class EventRange { public: - using const_iterator = typename std::vector::const_iterator; - using size_type = typename std::vector::size_type; + using const_iterator = typename std::vector::const_iterator; + using size_type = typename std::vector::size_type; EventRange() = delete; EventRange(const EventRange&) = default; EventRange& operator=(const EventRange&) = default; - explicit EventRange(const std::vector& data) : + explicit EventRange(const std::vector& data) : data_( data ) { } @@ -56,11 +56,11 @@ class EventRange { return data_.empty(); } private: - const std::vector& data_; + const std::vector& data_; }; -template -using EventSink = std::back_insert_iterator>; +template +using EventSink = std::back_insert_iterator>; /******************************************/ REACT_END /******************************************/ @@ -69,17 +69,17 @@ using EventSink = std::back_insert_iterator>; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class SignalNode; /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventStreamNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class EventStreamNode : public NodeBase { public: - using StorageType = std::vector; + using StorageType = std::vector; EventStreamNode(EventStreamNode&&) = default; EventStreamNode& operator=(EventStreamNode&&) = default; @@ -87,7 +87,7 @@ class EventStreamNode : public NodeBase EventStreamNode(const EventStreamNode&) = delete; EventStreamNode& operator=(const EventStreamNode&) = delete; - explicit EventStreamNode(const std::shared_ptr& graphPtr) : + explicit EventStreamNode(const std::shared_ptr& graphPtr) : NodeBase( graphPtr ) { } @@ -97,11 +97,37 @@ class EventStreamNode : public NodeBase const StorageType& Events() const { return events_; } - virtual void ClearBuffer() override - { events_.clear(); }; + + void SetPendingSuccessorCount(int count) + { + if (count == 0) + { + // If there are no successors, buffer is cleared immediately. + events_.clear(); + } + else + { + // Otherwise, the last finished successor clears it. + pendingSuccessorCount_ = count; + } + } + + void DecrementPendingSuccessorCount() + { + // Not all predecessors of a node might be visited during a turn. + // In this case, the count is zero and the call to this function should be ignored. + if (pendingSuccessorCount_ == 0) + return; + + // Last successor to arrive clears the buffer. + if (pendingSuccessorCount_-- == 1) + events_.clear(); + }; private: StorageType events_; + + std::atomic pendingSuccessorCount_ = 0; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -111,9 +137,9 @@ template class EventSourceNode : public EventStreamNode { public: - EventSourceNode(const std::shared_ptr& graphPtr) : + EventSourceNode(const std::shared_ptr& graphPtr) : EventSourceNode::EventStreamNode( graphPtr ) - { this->RegisterMe(NodeFlags::buffered | NodeFlags::input); } + { this->RegisterMe(NodeCategory::input); } ~EventSourceNode() { this->UnregisterMe(); } @@ -124,10 +150,11 @@ class EventSourceNode : public EventStreamNode virtual int GetDependencyCount() const override { return 0; } - virtual UpdateResult Update(TurnId turnId) override + virtual UpdateResult Update(TurnId turnId, int successorCount) override { - if (this->Events().size() > 0) - { + if (! this->Events().empty()) + { + this->SetPendingSuccessorCount(successorCount); return UpdateResult::changed; } else @@ -138,9 +165,7 @@ class EventSourceNode : public EventStreamNode template void EmitValue(U&& value) - { - this->Events().push_back(std::forward(value)); - } + { this->Events().push_back(std::forward(value)); } }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -150,11 +175,11 @@ template class EventMergeNode : public EventStreamNode { public: - EventMergeNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& ... deps) : + EventMergeNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& ... deps) : EventMergeNode::EventStreamNode( graphPtr ), depHolder_( deps ... ) { - this->RegisterMe(NodeFlags::buffered); + this->RegisterMe(); REACT_EXPAND_PACK(this->AttachToMe(deps->GetNodeId())); } @@ -164,14 +189,19 @@ class EventMergeNode : public EventStreamNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId) override + virtual UpdateResult Update(TurnId turnId, int successorCount) override { apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(MergeFromDep(deps)); }, depHolder_); if (! this->Events().empty()) + { + this->SetPendingSuccessorCount(successorCount); return UpdateResult::changed; + } else + { return UpdateResult::unchanged; + } } virtual const char* GetNodeType() const override @@ -185,70 +215,108 @@ class EventMergeNode : public EventStreamNode void MergeFromDep(const std::shared_ptr>& other) { this->Events().insert(this->Events().end(), other->Events().begin(), other->Events().end()); + other->DecrementPendingSuccessorCount(); } std::tuple> ...> depHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventFlattenNode +/// EventSlotNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EventFlattenNode : public EventStreamNode +template +class EventSlotNode : public EventStreamNode { public: - EventFlattenNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& outer, const std::shared_ptr>& inner) : - EventFlattenNode::EventStreamNode( graphPtr ), - outer_( outer ), - inner_( inner ) + EventSlotNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& dep) : + EventSlotNode::EventStreamNode( graphPtr ), + slotInput_( *this, dep ) { - this->RegisterMe(NodeFlags::buffered | NodeFlags::dynamic); - this->AttachToMe(outer->GetNodeId()); - this->AttachToMe(inner->GetNodeId()); + slotInput_.nodeId = GraphPtr()->RegisterNode(&slotInput_, NodeCategory::dyninput); + this->RegisterMe(); + + this->AttachToMe(slotInput_.nodeId); + this->AttachToMe(dep->GetNodeId()); } - ~EventFlattenNode() + ~EventSlotNode() { - this->DetachFromMe(inner->GetNodeId()); - this->DetachFromMe(outer->GetNodeId()); + this->DetachFromMe(slotInput_.dep->GetNodeId()); + this->DetachFromMe(slotInput_.nodeId); + this->UnregisterMe(); + GraphPtr()->UnregisterNode(slotInput_.nodeId); } virtual const char* GetNodeType() const override - { return "EventFlatten"; } + { return "EventSlot"; } virtual int GetDependencyCount() const override { return 2; } - virtual UpdateResult Update(TurnId turnId) override + virtual UpdateResult Update(TurnId turnId, int successorCount) override { - auto newInner = GetNodePtr(outer_->Value()); + this->Events().insert(this->Events().end(), slotInput_.dep->Events().begin(), slotInput_.dep->Events().end()); - if (newInner != inner_) + slotInput_.dep->DecrementPendingSuccessorCount(); + + if (! this->Events().empty()) + { + this->SetPendingSuccessorCount(successorCount); + return UpdateResult::changed; + } + else { - newInner->SetCurrentTurn(turnId); + return UpdateResult::unchanged; + } + } + + void SetInput(const std::shared_ptr>& newInput) + { slotInput_.newDep = newInput; } + + NodeId GetInputNodeId() const + { return slotInput_.nodeId; } - // Topology has been changed - auto oldInner = inner_; - inner_ = newInner; +private: + struct VirtualInputNode : public IReactiveNode + { + VirtualInputNode(EventSlotNode& parentIn, const std::shared_ptr>& depIn) : + parent( parentIn ), + dep( depIn ) + { } - this->DynamicDetachFromMe(oldInner->GetNodeId(), 0); - this->DynamicAttachToMe(newInner->GetNodeId(), 0); + virtual const char* GetNodeType() const override + { return "EventSlotVirtualInput"; } + + virtual int GetDependencyCount() const override + { return 0; } + + virtual UpdateResult Update(TurnId turnId, int successorCount) override + { + if (dep != newDep) + { + parent.DynamicDetachFromMe(dep->GetNodeId(), 0); + parent.DynamicAttachToMe(newDep->GetNodeId(), 0); - return UpdateResult::shifted; + dep = std::move(newDep); + return UpdateResult::changed; + } + else + { + newDep.reset(); + return UpdateResult::unchanged; + } } - this->Events().insert(this->Events().end(), inner_->Events().begin(), inner_->Events().end()); + EventSlotNode& parent; - if (! this->Events().empty()) - return UpdateResult::changed; - else - return UpdateResult::unchanged; - } + NodeId nodeId; -private: - std::shared_ptr> outer_; - std::shared_ptr> inner_; + std::shared_ptr> dep; + std::shared_ptr> newDep; + }; + + VirtualInputNode slotInput_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -259,12 +327,12 @@ class EventProcessingNode : public EventStreamNode { public: template - EventProcessingNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& dep) : + EventProcessingNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& dep) : EventProcessingNode::EventStreamNode( graphPtr ), func_( std::forward(func) ), dep_( dep ) { - this->RegisterMe(NodeFlags::buffered); + this->RegisterMe(); this->AttachToMe(dep->GetNodeId()); } @@ -274,14 +342,21 @@ class EventProcessingNode : public EventStreamNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId) override + virtual UpdateResult Update(TurnId turnId, int successorCount) override { func_(EventRange( dep_->Events() ), std::back_inserter(this->Events())); + dep_->DecrementPendingSuccessorCount(); + if (! this->Events().empty()) + { + this->SetPendingSuccessorCount(successorCount); return UpdateResult::changed; + } else + { return UpdateResult::unchanged; + } } virtual const char* GetNodeType() const override @@ -304,13 +379,13 @@ class SyncedEventProcessingNode : public EventStreamNode { public: template - SyncedEventProcessingNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& dep, const std::shared_ptr>& ... syncs) : + SyncedEventProcessingNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& dep, const std::shared_ptr>& ... syncs) : SyncedEventProcessingNode::EventStreamNode( graphPtr ), func_( std::forward(func) ), dep_( dep ), syncHolder_( syncs ... ) { - this->RegisterMe(NodeFlags::buffered); + this->RegisterMe(); this->AttachToMe(dep->GetNodeId()); REACT_EXPAND_PACK(this->AttachToMe(syncs->GetNodeId())); } @@ -322,7 +397,7 @@ class SyncedEventProcessingNode : public EventStreamNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId) override + virtual UpdateResult Update(TurnId turnId, int successorCount) override { // Updates might be triggered even if only sync nodes changed. Ignore those. if (dep_->Events().empty()) @@ -335,14 +410,21 @@ class SyncedEventProcessingNode : public EventStreamNode }, syncHolder_); + dep_->DecrementPendingSuccessorCount(); + if (! this->Events().empty()) + { + this->SetPendingSuccessorCount(successorCount); return UpdateResult::changed; + } else + { return UpdateResult::unchanged; + } } virtual const char* GetNodeType() const override - { return "SycnedEventProcessing"; } + { return "SyncedEventProcessing"; } virtual int GetDependencyCount() const override { return 1 + sizeof...(TSyncs); } @@ -350,7 +432,7 @@ class SyncedEventProcessingNode : public EventStreamNode private: F func_; - std::shared_ptr> dep_; + std::shared_ptr> dep_; std::tuple>...> syncHolder_; }; @@ -362,11 +444,11 @@ template class EventJoinNode : public EventStreamNode> { public: - EventJoinNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& ... deps) : + EventJoinNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& ... deps) : EventJoinNode::EventStreamNode( graphPtr ), slots_( deps ... ) { - this->RegisterMe(NodeFlags::buffered); + this->RegisterMe(); REACT_EXPAND_PACK(this->AttachToMe(deps->GetNodeId())); } @@ -376,9 +458,9 @@ class EventJoinNode : public EventStreamNode> this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId) override + virtual UpdateResult Update(TurnId turnId, int successorCount) override { - // Move events into buffers + // Move events into buffers. apply([this, turnId] (Slot& ... slots) { REACT_EXPAND_PACK(FetchBuffer(turnId, slots)); }, slots_); while (true) @@ -396,7 +478,7 @@ class EventJoinNode : public EventStreamNode> if (!isReady) break; - // Pop values from buffers and emit tuple + // Pop values from buffers and emit tuple. apply( [this] (Slot& ... slots) { @@ -407,9 +489,14 @@ class EventJoinNode : public EventStreamNode> } if (! this->Events().empty()) + { + this->SetPendingSuccessorCount(successorCount); return UpdateResult::changed; + } else + { return UpdateResult::unchanged; + } } virtual const char* GetNodeType() const override @@ -434,6 +521,7 @@ class EventJoinNode : public EventStreamNode> static void FetchBuffer(TurnId turnId, Slot& slot) { slot.buffer.insert(slot.buffer.end(), slot.source->Events().begin(), slot.source->Events().end()); + slot.source->DecrementPendingSuccessorCount(); } template diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index 8466d77b..d39809bb 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -21,7 +21,7 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -struct IReactiveGraph; +class ReactiveGraph; /////////////////////////////////////////////////////////////////////////////////////////////////// /// NodeBase @@ -29,7 +29,7 @@ struct IReactiveGraph; class NodeBase : public IReactiveNode { public: - NodeBase(const std::shared_ptr& graphPtr) : + NodeBase(const std::shared_ptr& graphPtr) : graphPtr_( graphPtr ) { } @@ -58,15 +58,15 @@ class NodeBase : public IReactiveNode NodeId GetNodeId() const { return nodeId_; } - auto GraphPtr() const -> const std::shared_ptr& + auto GraphPtr() const -> const std::shared_ptr& { return graphPtr_; } - auto GraphPtr() -> std::shared_ptr& + auto GraphPtr() -> std::shared_ptr& { return graphPtr_; } protected: - void RegisterMe(NodeFlags flags = NodeFlags::none) - { nodeId_ = graphPtr_->RegisterNode(this, flags); } + void RegisterMe(NodeCategory category = NodeCategory::normal) + { nodeId_ = graphPtr_->RegisterNode(this, category); } void UnregisterMe() { graphPtr_->UnregisterNode(nodeId_); } @@ -84,9 +84,9 @@ class NodeBase : public IReactiveNode { graphPtr_->OnDynamicNodeDetach(nodeId_, otherNodeId, turnId); } private: - NodeId nodeId_; + NodeId nodeId_; - std::shared_ptr graphPtr_; + std::shared_ptr graphPtr_; }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index abdf1c6e..f8887d8b 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -35,12 +35,9 @@ class EventStreamNode; class ObserverNode : public NodeBase { public: - ObserverNode(const std::shared_ptr& graphPtr) : + ObserverNode(const std::shared_ptr& graphPtr) : ObserverNode::NodeBase( graphPtr ) { } - - virtual void ClearBuffer() override - { }; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -51,12 +48,12 @@ class SignalObserverNode : public ObserverNode { public: template - SignalObserverNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& ... deps) : + SignalObserverNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& ... deps) : SignalObserverNode::ObserverNode( graphPtr ), func_( std::forward(func) ), depHolder_( deps ... ) { - this->RegisterMe(NodeFlags::output); + this->RegisterMe(NodeCategory::output); REACT_EXPAND_PACK(this->AttachToMe(deps->GetNodeId())); } @@ -72,7 +69,7 @@ class SignalObserverNode : public ObserverNode virtual int GetDependencyCount() const override { return sizeof...(TDeps); } - virtual UpdateResult Update(TurnId turnId) override + virtual UpdateResult Update(TurnId turnId, int successorCount) override { apply([this] (const auto& ... deps) { this->func_(deps->Value() ...); }, depHolder_); return UpdateResult::unchanged; @@ -92,12 +89,12 @@ class EventObserverNode : public ObserverNode { public: template - EventObserverNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& subject) : + EventObserverNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& subject) : EventObserverNode::ObserverNode( graphPtr ), func_( std::forward(func) ), subject_( subject ) { - this->RegisterMe(NodeFlags::output); + this->RegisterMe(NodeCategory::output); this->AttachToMe(subject->GetNodeId()); } @@ -113,9 +110,10 @@ class EventObserverNode : public ObserverNode virtual int GetDependencyCount() const override { return 1; } - virtual UpdateResult Update(TurnId turnId) override + virtual UpdateResult Update(TurnId turnId, int successorCount) override { func_(EventRange( subject_->Events() )); + subject_->DecrementPendingSuccessorCount(); return UpdateResult::unchanged; } @@ -133,13 +131,13 @@ class SyncedEventObserverNode : public ObserverNode { public: template - SyncedEventObserverNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& subject, const std::shared_ptr>& ... syncs) : + SyncedEventObserverNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& subject, const std::shared_ptr>& ... syncs) : SyncedEventObserverNode::ObserverNode( graphPtr ), func_( std::forward(func) ), subject_( subject ), syncHolder_( syncs ... ) { - this->RegisterMe(NodeFlags::output); + this->RegisterMe(NodeCategory::output); this->AttachToMe(subject->GetNodeId()); REACT_EXPAND_PACK(this->AttachToMe(syncs->GetNodeId())); } @@ -157,13 +155,16 @@ class SyncedEventObserverNode : public ObserverNode virtual int GetDependencyCount() const override { return 1 + sizeof...(TSyncs); } - virtual UpdateResult Update(TurnId turnId) override - { - // Update of this node could be triggered from deps, - // so make sure source doesnt contain events from last turn - subject_->SetCurrentTurn(turnId); + virtual UpdateResult Update(TurnId turnId, int successorCount) override + { + // Updates might be triggered even if only sync nodes changed. Ignore those. + if (events_->Events().empty()) + return UpdateResult::unchanged; apply([this] (const auto& ... syncs) { func_(EventRange( this->subject_->Events() ), syncs->Value() ...); }, syncHolder_); + + subject_->DecrementPendingSuccessorCount(); + return UpdateResult::unchanged; } diff --git a/include/react/detail/graph/PropagationST.h b/include/react/detail/graph/PropagationST.h index 96d4fee4..e0eb2f59 100644 --- a/include/react/detail/graph/PropagationST.h +++ b/include/react/detail/graph/PropagationST.h @@ -12,34 +12,100 @@ #include "react/detail/Defs.h" #include +#include #include #include #include +#include +#include + #include "react/common/Containers.h" #include "react/common/Types.h" #include "react/detail/IReactiveGraph.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ -class SingleThreadedGraph : public IReactiveGraph +class ReactiveGraph; + +class TransactionQueue +{ +public: + TransactionQueue(ReactiveGraph& graph) : + graph_( graph ) + { } + + TransactionQueue(const TransactionQueue&) = delete; + TransactionQueue& operator=(const TransactionQueue&) = delete; + + TransactionQueue(TransactionQueue&&) = default; + TransactionQueue& operator =(TransactionQueue&&) = default; + + template + void Push(TransactionFlags flags, F&& transaction) + { + if (count_.fetch_add(1, std::memory_order_relaxed) == 0) + tbb::task::enqueue(*new(tbb::task::allocate_root()) WorkerTask(*this)); + } + +private: + struct StoredTransaction + { + TransactionFlags flags; + std::function callback; + }; + + class WorkerTask : public tbb::task + { + public: + WorkerTask(TransactionQueue& parent) : + parent_( parent ) + { } + + tbb::task* execute() + { + parent_.ProcessQueue(); + return nullptr; + } + + private: + TransactionQueue& parent_; + }; + + void ProcessQueue(); + + size_t ProcessNextBatch(); + + tbb::concurrent_queue transactions_; + + std::atomic count_{ 0 }; + + ReactiveGraph& graph_; +}; + +class ReactiveGraph { public: - // IReactiveGraph - virtual NodeId RegisterNode(IReactiveNode* nodePtr, NodeFlags flags) override; - virtual void UnregisterNode(NodeId node) override; + NodeId RegisterNode(IReactiveNode* nodePtr, NodeCategory category); + void UnregisterNode(NodeId nodeId); - virtual void OnNodeAttach(NodeId node, NodeId parentId) override; - virtual void OnNodeDetach(NodeId node, NodeId parentId) override; + void OnNodeAttach(NodeId node, NodeId parentId); + void OnNodeDetach(NodeId node, NodeId parentId); - virtual void OnDynamicNodeAttach(NodeId node, NodeId parentId, TurnId turnId) override; - virtual void OnDynamicNodeDetach(NodeId node, NodeId parentId, TurnId turnId) override; + void OnDynamicNodeAttach(NodeId node, NodeId parentId, TurnId turnId); + void OnDynamicNodeDetach(NodeId node, NodeId parentId, TurnId turnId); - virtual void AddInput(NodeId nodeId, std::function inputCallback) override; - // ~IReactiveGraph + LinkId AddGroupLink(ILinkNodeOutput* nodePtr, NodeCategory category); + void RemoveGroupLink(LinkId nodeId); template - void DoTransaction(TransactionFlags flags, F&& transactionCallback); + void AddInput(NodeId nodeId, F&& inputCallback); + + template + void DoTransaction(F&& transactionCallback); + + template + void EnqueueTransaction(TransactionFlags flags, F&& transactionCallback); private: struct NodeData @@ -49,12 +115,12 @@ class SingleThreadedGraph : public IReactiveGraph NodeData(const NodeData&) = default; NodeData& operator=(const NodeData&) = default; - NodeData(IReactiveNode* nodePtrIn, NodeFlags flagsIn) : + NodeData(IReactiveNode* nodePtrIn, NodeCategory categoryIn) : nodePtr( nodePtrIn ), - flags( flagsIn ) + category(categoryIn) { } - NodeFlags flags = NodeFlags::none; + NodeCategory category = NodeCategory::normal; int level = 0; int newLevel = 0 ; @@ -90,35 +156,34 @@ class SingleThreadedGraph : public IReactiveGraph }; void Propagate(); + void UpdateLinkNodes(); void ScheduleSuccessors(NodeData & node); void InvalidateSuccessors(NodeData & node); - void ClearBufferedNodes(); private: - int refCount_ = 1; + TransactionQueue transactionQueue_{ *this }; TopoQueue scheduledNodes_; IndexMap nodeData_; std::vector changedInputs_; - - std::vector pendingBufferedNodes_; + std::vector scheduledLinkNodes_; bool isTransactionActive_ = false; }; -NodeId SingleThreadedGraph::RegisterNode(IReactiveNode* nodePtr, NodeFlags flags) +NodeId ReactiveGraph::RegisterNode(IReactiveNode* nodePtr, NodeCategory category) { - return nodeData_.Insert(NodeData{ nodePtr, flags }); + return nodeData_.Insert(NodeData{ nodePtr, category }); } -void SingleThreadedGraph::UnregisterNode(NodeId nodeId) +void ReactiveGraph::UnregisterNode(NodeId nodeId) { nodeData_.Remove(nodeId); } -void SingleThreadedGraph::OnNodeAttach(NodeId nodeId, NodeId parentId) +void ReactiveGraph::OnNodeAttach(NodeId nodeId, NodeId parentId) { auto& node = nodeData_[nodeId]; auto& parent = nodeData_[parentId]; @@ -129,7 +194,7 @@ void SingleThreadedGraph::OnNodeAttach(NodeId nodeId, NodeId parentId) node.level = parent.level + 1; } -void SingleThreadedGraph::OnNodeDetach(NodeId nodeId, NodeId parentId) +void ReactiveGraph::OnNodeDetach(NodeId nodeId, NodeId parentId) { auto& parent = nodeData_[parentId]; auto& successors = parent.successors; @@ -137,17 +202,18 @@ void SingleThreadedGraph::OnNodeDetach(NodeId nodeId, NodeId parentId) successors.erase(std::find(successors.begin(), successors.end(), nodeId)); } -void SingleThreadedGraph::OnDynamicNodeAttach(NodeId nodeId, NodeId parentId, TurnId turnId) +void ReactiveGraph::OnDynamicNodeAttach(NodeId nodeId, NodeId parentId, TurnId turnId) { OnNodeAttach(nodeId, parentId); } -void SingleThreadedGraph::OnDynamicNodeDetach(NodeId nodeId, NodeId parentId, TurnId turnId) +void ReactiveGraph::OnDynamicNodeDetach(NodeId nodeId, NodeId parentId, TurnId turnId) { OnNodeDetach(nodeId, parentId); } -void SingleThreadedGraph::AddInput(NodeId nodeId, std::function inputCallback) +template +void ReactiveGraph::AddInput(NodeId nodeId, F&& inputCallback) { auto& node = nodeData_[nodeId]; auto* nodePtr = node.nodePtr; @@ -163,25 +229,22 @@ void SingleThreadedGraph::AddInput(NodeId nodeId, std::function inputCal } else { + int successorCount = node.successors.size(); + // Update the node. This applies the input buffer to the node value and checks if it changed. - if (nodePtr->Update(0) == UpdateResult::changed) + if (nodePtr->Update(0, successorCount) == UpdateResult::changed) { - if (IsBitmaskSet(node.flags, NodeFlags::buffered)) - pendingBufferedNodes_.push_back(nodePtr); - // Propagate changes through the graph ScheduleSuccessors(node); if (! scheduledNodes_.IsEmpty()) Propagate(); } - - ClearBufferedNodes(); } } template -void SingleThreadedGraph::DoTransaction(TransactionFlags flags, F&& transactionCallback) +void ReactiveGraph::DoTransaction(F&& transactionCallback) { // Transaction callback may add multiple inputs. isTransactionActive_ = true; @@ -194,10 +257,12 @@ void SingleThreadedGraph::DoTransaction(TransactionFlags flags, F&& transactionC auto& node = nodeData_[nodeId]; auto* nodePtr = node.nodePtr; - if (nodePtr->Update(0) == UpdateResult::changed) + int successorCount = node.successors.size(); + + if (nodePtr->Update(0, successorCount) == UpdateResult::changed) { - if (IsBitmaskSet(node.flags, NodeFlags::buffered)) - pendingBufferedNodes_.push_back(nodePtr); + if (node.category == NodeCategory::dyninput) + InvalidateSuccessors(node); ScheduleSuccessors(node); } @@ -209,10 +274,17 @@ void SingleThreadedGraph::DoTransaction(TransactionFlags flags, F&& transactionC if (! scheduledNodes_.IsEmpty()) Propagate(); - ClearBufferedNodes(); + if (!scheduledLinkNodes_.empty()) + UpdateLinkNodes(); } -void SingleThreadedGraph::Propagate() +template +void ReactiveGraph::EnqueueTransaction(TransactionFlags flags, F&& transactionCallback) +{ + transactionQueue_.Push(flags, std::forward(transactionCallback)); +} + +void ReactiveGraph::Propagate() { while (scheduledNodes_.FetchNext()) { @@ -230,29 +302,27 @@ void SingleThreadedGraph::Propagate() continue; } - auto result = nodePtr->Update(0); + int successorCount = node.successors.size(); - if (result == UpdateResult::changed) + if (nodePtr->Update(0, successorCount) == UpdateResult::changed) { - if (IsBitmaskSet(node.flags, NodeFlags::buffered)) - pendingBufferedNodes_.push_back(nodePtr); - ScheduleSuccessors(node); } - else if (result == UpdateResult::shifted) - { - // Re-schedule this node - InvalidateSuccessors(node); - scheduledNodes_.Push(nodeId, node.level); - continue; - } node.queued = false; } } } -void SingleThreadedGraph::ScheduleSuccessors(NodeData& node) +void ReactiveGraph::UpdateLinkNodes() +{ + for (const auto& x : scheduledLinkNodes_) + { + + } +} + +void ReactiveGraph::ScheduleSuccessors(NodeData& node) { for (NodeId succId : node.successors) { @@ -261,12 +331,16 @@ void SingleThreadedGraph::ScheduleSuccessors(NodeData& node) if (!succ.queued) { succ.queued = true; - scheduledNodes_.Push(succId, succ.level); + + if (node.category != NodeCategory::link) + scheduledNodes_.Push(succId, succ.level); + else + scheduledLinkNodes_.push_back(succId); } } } -void SingleThreadedGraph::InvalidateSuccessors(NodeData& node) +void ReactiveGraph::InvalidateSuccessors(NodeData& node) { for (NodeId succId : node.successors) { @@ -277,14 +351,7 @@ void SingleThreadedGraph::InvalidateSuccessors(NodeData& node) } } -void SingleThreadedGraph::ClearBufferedNodes() -{ - for (IReactiveNode* nodePtr : pendingBufferedNodes_) - nodePtr->ClearBuffer(); - pendingBufferedNodes_.clear(); -} - -bool SingleThreadedGraph::TopoQueue::FetchNext() +bool ReactiveGraph::TopoQueue::FetchNext() { // Throw away previous values nextData_.clear(); @@ -310,6 +377,68 @@ bool SingleThreadedGraph::TopoQueue::FetchNext() return !nextData_.empty(); } +void TransactionQueue::ProcessQueue() +{ + for (;;) + { + size_t popCount = ProcessNextBatch(); + if (count_.fetch_sub(popCount) == popCount) + return; + } +} + +size_t TransactionQueue::ProcessNextBatch() +{ + StoredTransaction curTransaction; + size_t popCount = 0; + bool canMerge = false; + bool skipPop = false; + bool isDone = false; + + // Outer loop. One transaction per iteration. + for (;;) + { + if (!skipPop) + { + if (transactions_.try_pop(curTransaction)) + return popCount; + + canMerge = IsBitmaskSet(curTransaction.flags, TransactionFlags::allow_merging); + ++popCount; + } + else + { + skipPop = false; + } + + graph_.DoTransaction([&] + { + curTransaction.callback(); + + if (canMerge) + { + // Inner loop. Mergeable transactions are merged + for (;;) + { + if (transactions_.try_pop(curTransaction)) + return; + + canMerge = IsBitmaskSet(curTransaction.flags, TransactionFlags::allow_merging); + ++popCount; + + if (!canMerge) + { + skipPop = true; + return; + } + + curTransaction.callback(); + } + } + }); + } +} + /****************************************/ REACT_IMPL_END /***************************************/ #endif // REACT_DETAIL_GRAPH_PROPAGATIONST_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index 30128a3e..2bdd3eac 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -12,7 +12,9 @@ #include "react/detail/Defs.h" #include +#include #include +#include #include "GraphBase.h" @@ -37,13 +39,13 @@ class SignalNode : public NodeBase SignalNode(const SignalNode&) = delete; SignalNode& operator=(const SignalNode&) = delete; - explicit SignalNode(const std::shared_ptr& graphPtr) : + explicit SignalNode(const std::shared_ptr& graphPtr) : SignalNode::NodeBase( graphPtr ), value_( ) { } template - SignalNode(const std::shared_ptr& graphPtr, T&& value) : + SignalNode(const std::shared_ptr& graphPtr, T&& value) : SignalNode::NodeBase( graphPtr ), value_( std::forward(value) ) { } @@ -54,9 +56,6 @@ class SignalNode : public NodeBase const S& Value() const { return value_; } - virtual void ClearBuffer() override - { }; - private: S value_; }; @@ -68,13 +67,13 @@ template class VarSignalNode : public SignalNode { public: - explicit VarSignalNode(const std::shared_ptr& graphPtr) : + explicit VarSignalNode(const std::shared_ptr& graphPtr) : VarSignalNode::SignalNode( graphPtr ), newValue_( ) - { this->RegisterMe(NodeFlags::input); } + { this->RegisterMe(NodeCategory::input); } template - VarSignalNode(const std::shared_ptr& graphPtr, T&& value) : + VarSignalNode(const std::shared_ptr& graphPtr, T&& value) : VarSignalNode::SignalNode( graphPtr, std::forward(value) ), newValue_( value ) { this->RegisterMe(); } @@ -88,7 +87,7 @@ class VarSignalNode : public SignalNode virtual int GetDependencyCount() const override { return 0; } - virtual UpdateResult Update(TurnId turnId) override + virtual UpdateResult Update(TurnId turnId, int successorCount) override { if (isInputAdded_) { @@ -162,7 +161,7 @@ class SignalFuncNode : public SignalNode { public: template - SignalFuncNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& ... deps) : + SignalFuncNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& ... deps) : SignalFuncNode::SignalNode( graphPtr, func(deps->Value() ...) ), func_( std::forward(func) ), depHolder_( deps ... ) @@ -183,7 +182,7 @@ class SignalFuncNode : public SignalNode virtual int GetDependencyCount() const override { return sizeof...(TDeps); } - virtual UpdateResult Update(TurnId turnId) override + virtual UpdateResult Update(TurnId turnId, int successorCount) override { bool changed = false; @@ -208,54 +207,43 @@ class SignalFuncNode : public SignalNode }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// FlattenNode +/// SignalSlotNode /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class SignalFlattenNode : public SignalNode +template +class SignalSlotNode : public SignalNode { public: - SignalFlattenNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& outer, const std::shared_ptr>& inner) : - SignalFlattenNode::SignalNode( graphPtr, inner->Value() ), - outer_( outer ), - inner_( inner ) + SignalSlotNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& dep) : + SignalSlotNode::SignalNode( graphPtr, dep->Value() ), + slotInput_( *this, dep ) { - this->RegisterMe(NodeFlags::dynamic); - this->AttachToMe(outer->GetNodeId()); - this->AttachToMe(inner->GetNodeId()); + slotInput_.nodeId = GraphPtr()->RegisterNode(&slotInput_, NodeCategory::dyninput); + this->RegisterMe(); + + this->AttachToMe(slotInput_.nodeId); + this->AttachToMe(dep->GetNodeId()); } - ~SignalFlattenNode() + ~SignalSlotNode() { - this->DetachFromMe(inner->GetNodeId()); - this->DetachFromMe(outer->GetNodeId()); + this->DetachFromMe(slotInput_.dep->GetNodeId()); + this->DetachFromMe(slotInput_.nodeId); + this->UnregisterMe(); + GraphPtr()->UnregisterNode(slotInput_.nodeId); } virtual const char* GetNodeType() const override - { return "SignalFlatten"; } + { return "SignalSlot"; } virtual int GetDependencyCount() const override { return 2; } - virtual UpdateResult Update(TurnId turnId) override + virtual UpdateResult Update(TurnId turnId, int successorCount) override { - auto newInner = GetNodePtr(outer_->Value()); - - if (newInner != inner_) + if (! (this->Value() == slotInput_.dep->Value())) { - // Topology has been changed - auto oldInner = inner_; - inner_ = newInner; - - this->DynamicDetachFromMe(oldInner->GetNodeId(), 0); - this->DynamicAttachToMe(newInner->GetNodeId(), 0); - - return UpdateResult::shifted; - } - - if (! Equals(this->Value(), inner_->Value())) - { - this->Value() = inner_->Value(); + this->Value() = slotInput_.dep->Value(); return UpdateResult::changed; } else @@ -264,9 +252,142 @@ class SignalFlattenNode : public SignalNode } } + void SetInput(const std::shared_ptr>& newInput) + { slotInput_.newDep = newInput; } + + NodeId GetInputNodeId() const + { return slotInput_.nodeId; } + +private: + struct VirtualInputNode : public IReactiveNode + { + VirtualInputNode(SignalSlotNode& parentIn, const std::shared_ptr>& depIn) : + parent( parentIn ), + dep( depIn ) + { } + + virtual const char* GetNodeType() const override + { return "SignalSlotVirtualInput"; } + + virtual int GetDependencyCount() const override + { return 0; } + + virtual UpdateResult Update(TurnId turnId, int successorCount) override + { + if (dep != newDep) + { + parent.DynamicDetachFromMe(dep->GetNodeId(), 0); + parent.DynamicAttachToMe(newDep->GetNodeId(), 0); + + dep = std::move(newDep); + return UpdateResult::changed; + } + else + { + newDep.reset(); + return UpdateResult::unchanged; + } + } + + SignalSlotNode& parent; + + NodeId nodeId; + + std::shared_ptr> dep; + std::shared_ptr> newDep; + }; + + VirtualInputNode slotInput_; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SignalBridgeNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class SignalLinkNode : public SignalNode +{ +public: + SignalLinkNode(const std::shared_ptr& graphPtr, const std::shared_ptr& srcGraphPtr, const std::shared_ptr>& dep) : + SignalLinkNode::SignalNode( graphPtr, dep->Value() ), + linkOutput_( *this, srcGraphPtr, dep ) + { + slotInput_.nodeId = GraphPtr()->RegisterNode(&linkOutput_, NodeCategory::dyninput); + this->RegisterMe(); + + this->AttachToMe(slotInput_.nodeId); + this->AttachToMe(dep->GetNodeId()); + } + + ~SignalLinkNode() + { + this->DetachFromMe(slotInput_.dep->GetNodeId()); + this->DetachFromMe(slotInput_.nodeId); + + this->UnregisterMe(); + GraphPtr()->UnregisterNode(slotInput_.nodeId); + } + + virtual const char* GetNodeType() const override + { return "SignalLink"; } + + virtual int GetDependencyCount() const override + { return 2; } + + virtual UpdateResult Update(TurnId turnId, int successorCount) override + { + {// outputDataMutex + std::lock_guard lock(linkOutput_.outputDataMutex); + this->Value() = std::move(linkOutput_.outputData.front()); + linkOutput_.outputData.pop(); + }// ~outputDataMutex + + return UpdateResult::changed; + } + private: - std::shared_ptr> outer_; - std::shared_ptr> inner_; + struct VirtualOutputNode : public ILinkOutputNode + { + VirtualOutputNode(SignalSlotNode& parentIn, const std::shared_ptr& srcGraphPtrIn, const std::shared_ptr>& depIn) : + parent( parentIn ), + srcGraphPtr(srcGraphPtrIn), + dep( depIn ) + { } + + virtual const char* GetNodeType() const override + { return "SignalLinkVirtualOutput"; } + + virtual int GetDependencyCount() const override + { return 1; } + + virtual UpdateResult Update(TurnId turnId, int successorCount) override + { + {// outputDataMutex + std::lock_guard lock(outputDataMutex); + outputData.push(dep->Value()); + }// ~outputDataMutex + + return UpdateResult::changed; + } + + virtual void CollectOutput(LinkOutputList& output) override + { output.emplace_back(srcGraphPtr.get(), &parent); } + + SignalSlotNode& parent; + + NodeId nodeId; + + std::shared_ptr> dep; + + std::mutex outputDataMutex; + + std::queue> outputData; + + std::shared_ptr srcGraphPtr; + }; + + VirtualOutputNode linkOutput_; + + }; /****************************************/ REACT_IMPL_END /***************************************/ From a7ae030ef76f7c2de5bba1ec5d1014671c69500c Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 9 Dec 2016 18:13:31 +0100 Subject: [PATCH 227/266] Progress... --- examples/src/Main.cpp | 245 ++++++++++++++++++-- include/react/API.h | 12 +- include/react/Event.h | 221 +++++++++++++++--- include/react/Group.h | 2 +- include/react/Observer.h | 89 ++++++-- include/react/Signal.h | 246 +++++++++++++++++---- include/react/detail/IReactiveGraph.h | 8 +- include/react/detail/graph/EventNodes.h | 101 ++++++++- include/react/detail/graph/PropagationST.h | 63 ++++-- include/react/detail/graph/SignalNodes.h | 90 ++++---- 10 files changed, 888 insertions(+), 189 deletions(-) diff --git a/examples/src/Main.cpp b/examples/src/Main.cpp index 89f61f07..62d5c079 100644 --- a/examples/src/Main.cpp +++ b/examples/src/Main.cpp @@ -4,37 +4,248 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -//#define REACT_ENABLE_LOGGING +#include +#include +#include +#include -#include "react/Domain.h" #include "react/Signal.h" #include "react/Event.h" #include "react/Algorithm.h" +#include "react/Observer.h" -#include "tbb/tick_count.h" +using namespace react; -#include +template +T Multiply(T a, T b) +{ + return a * b; +} -using namespace std; -using namespace react; +template void PrintValue(T v) +{ + printf("Value: %d\n", v); +} + +template void PrintArea(T v) +{ + printf("Area: %d\n", v); +} + +template void PrintVolume(T v) +{ + printf("Volume: %d\n", v); +} -void testme() +template void PrintEvents(EventRange evts) { - // Note: This project exists as a sandbox where I occasionally stage new examples. - // Currently it's empty. + printf("Processing events...\n"); + + for (const auto& e : evts) + printf(" Event: %d\n", e); +} + +template bool FilterFunc(T v) +{ + return v > 10; +} + +int main2() +{ + ReactiveGroup<> group; + + { + // Signals + VarSignal x{ group, 0 }; + VarSignal y{ group, 0 }; + VarSignal z{ group, 0 }; + + Signal area{ Multiply, x, y }; + Signal volume{ Multiply, area, z }; + + Observer<> areaObs{ PrintArea, area }; + Observer<> volumeObs{ PrintVolume, volume }; + + x.Set(2); // a: 0, v: 0 + y.Set(2); // a: 4, v: 0 + z.Set(2); // a: 4, v: 8 + + group.DoTransaction([&] + { + x <<= 100; + y <<= 3; + y <<= 4; + }); + + // a: 400, v: 800 + } + + { + // Events + EventSource button1{ group }; + EventSource button2{ group }; + + Event anyButton = Merge(button1, button2); + Event filtered = Filter(FilterFunc, anyButton); + + Observer<> eventObs{ PrintEvents, anyButton }; + + button1.Emit(1); + button2.Emit(2); + + group.DoTransaction([&] + { + for (int i=0; i<10; ++i) + button1.Emit(42); + }); + } + + { + // Dynamic signals + VarSignal s1{ group, 10 }; + VarSignal s2{ group, 22 }; + + SignalSlot slot{ s1 }; + + Observer<> areaObs{ PrintValue, slot }; + + s1.Set(42); + + slot.Set(s2); + + s2.Set(667); + } + + { + ReactiveGroup<> group1; + ReactiveGroup<> group2; + + VarSignal s1{ group1, 10 }; + VarSignal s2{ group2, 11 }; + + Signal v{ Multiply, s1, s2 }; + + Observer<> obs{ PrintValue, v }; + + s1.Set(555); + + std::this_thread::sleep_for(std::chrono::seconds(5)); + } + + { + ReactiveGroup<> group1; + ReactiveGroup<> group2; + + EventSource e1{ group1 }; + EventSource e2{ group2 }; + + auto merged = Merge(group2, e1, e2); + + auto joined = Join(e1, e2); + auto joined2 = Join(group1, e1, e2); + + Observer<> eventObs{ PrintEvents, merged }; + + e1.Emit(222); + + std::this_thread::sleep_for(std::chrono::seconds(5)); + } + + return 0; } int main() { - testme(); + ReactiveGroup<> group; + + VarSignal a{ }; + VarSignal b{ }; + +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +/*int main2() +{ + ReactiveGroup<> group1; + ReactiveGroup<> group2; + ReactiveGroup<> group3; + + VarSignal x{ 0, group1 }; + VarSignal y{ 0, group2 }; + VarSignal z{ 0, group3 }; + + Signal area{ Multiply, x, y }; + Signal volume{ Multiply, area, z }; + + Observer<> obs{ PrintAreaAndVolume, area, volume }; + + Signal> volumeHistory = Iterate>( vector{ }, PushToVector, Monitor(volume)); + + x <<= 2; + y <<= 2; + z <<= 2; + + group.DoTransaction([&] + { + x <<= 100; + y <<= 200; + z <<= 300; + }); + + obs.Cancel(); + + x <<= 42; + + printf("History:\n"); + for (auto t : volumeHistory.Value()) + printf("%d ", t); + printf("\n"); + + return 0; +} + + +int main3() +{ + using namespace std; + using namespace react; + + ReactiveGroup<> group1; + ReactiveGroup<> group2; + + auto sig1 = VarSignal( 10, group1 ); -#ifdef REACT_ENABLE_LOGGING - std::ofstream logfile; - logfile.open("log.txt"); + auto link1 = SignalLink( sig1, group2 ); + auto link2 = SignalLink( sig1, group2 ); - D::Log().Write(logfile); - logfile.close(); -#endif + sig1.Set(10); return 0; -} \ No newline at end of file +}*/ \ No newline at end of file diff --git a/include/react/API.h b/include/react/API.h index 928d1007..79a44241 100644 --- a/include/react/API.h +++ b/include/react/API.h @@ -29,12 +29,6 @@ enum ReferencePolicy weak }; -enum ThreadingPolicy -{ - sequential, - concurrent -}; - enum class WeightHint { automatic, @@ -68,6 +62,9 @@ class VarSignalBase; template class SignalSlotBase; +template +class SignalLinkBase; + template class Signal; @@ -77,6 +74,9 @@ class VarSignal; template class SignalSlot; +template +class SignalLink; + // Events enum class Token; diff --git a/include/react/Event.h b/include/react/Event.h index 2c9222fd..89bcc0c8 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -19,6 +19,12 @@ #include "react/detail/graph/EventNodes.h" +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +struct PrivateEventLinkNodeInterface; + +/****************************************/ REACT_IMPL_END /***************************************/ + /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -32,7 +38,7 @@ class EventBase public: // Private node ctor - EventBase(REACT_IMPL::NodeCtorTag, std::shared_ptr&& nodePtr) : + EventBase(REACT_IMPL::CtorTag, std::shared_ptr&& nodePtr) : nodePtr_( std::move(nodePtr) ) { } @@ -128,13 +134,10 @@ class EventSourceBase : public EventBase EventSourceBase(EventSourceBase&& other) = default; EventSourceBase& operator=(EventSourceBase&& other) = default; - template - auto CreateSourceNode(const TGroup& group) -> decltype(auto) + auto CreateSourceNode(const std::shared_ptr& graphPtr) -> decltype(auto) { - using REACT_IMPL::PrivateReactiveGroupInterface; using SrcNodeType = REACT_IMPL::EventSourceNode; - - return std::make_shared(PrivateReactiveGroupInterface::GraphPtr(group)); + return std::make_shared(graphPtr); } private: @@ -177,7 +180,7 @@ class EventSlotBase : public EventBase EventSlotBase(EventSlotBase&&) = default; EventSlotBase& operator=(EventSlotBase&&) = default; - auto CreateSlotNode(const EventBase& input, const ReactiveGroupBase& group) -> decltype(auto) + auto CreateSlotNode(const std::shared_ptr& graphPtr, const EventBase& input) -> decltype(auto) { using REACT_IMPL::PrivateNodeInterface; using REACT_IMPL::PrivateReactiveGroupInterface; @@ -202,6 +205,38 @@ class EventSlotBase : public EventBase } }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventLinkBase +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class EventLinkBase : public EventBase +{ +public: + using EventBase::EventBase; + +protected: + EventLinkBase() = default; + + EventLinkBase(const EventLinkBase&) = default; + EventLinkBase& operator=(const EventLinkBase&) = default; + + EventLinkBase(EventLinkBase&&) = default; + EventLinkBase& operator=(EventLinkBase&&) = default; + + static auto CreateLinkNode(const std::shared_ptr& graphPtr, const EventBase& input) -> decltype(auto) + { + using REACT_IMPL::PrivateNodeInterface; + using REACT_IMPL::PrivateReactiveGroupInterface; + using EventNodeType = REACT_IMPL::EventLinkNode; + + auto node = std::make_shared(graphPtr, PrivateNodeInterface::GraphPtr(input), PrivateNodeInterface::NodePtr(input)); + node->SetWeakSelfPtr(std::weak_ptr{ node }); + return node; + } + + friend struct REACT_IMPL::PrivateEventLinkNodeInterface; +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Event /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -223,12 +258,22 @@ class Event : public EventBase template Event(F&& func, const EventBase& dep) : - Event::EventBase( REACT_IMPL::NodeCtorTag{ }, CreateProcessingNode(std::forward(func), dep) ) + Event::EventBase( REACT_IMPL::CtorTag{ }, CreateProcessingNode(std::forward(func), dep) ) + { } + + template + Event(const ReactiveGroupBase& group, F&& func, const EventBase& dep) : + Event::EventBase( REACT_IMPL::CtorTag{ }, CreateProcessingNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), dep) ) { } template Event(F&& func, const EventBase& dep, const SignalBase& ... signals) : - Event::EventBase( REACT_IMPL::NodeCtorTag{ }, CreateSyncedProcessingNode(std::forward(func), dep, signals ...) ) + Event::EventBase( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(std::forward(func), dep, signals ...) ) + { } + + template + Event(const ReactiveGroupBase& group, F&& func, const EventBase& dep, const SignalBase& ... signals) : + Event::EventBase( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), dep, signals ...) ) { } }; @@ -257,12 +302,22 @@ class Event : public EventBase template Event(F&& func, const EventBase& dep) : - Event::EventBase( REACT_IMPL::NodeCtorTag{ }, CreateProcessingNode(std::forward(func), dep) ) + Event::EventBase( REACT_IMPL::CtorTag{ }, CreateProcessingNode(std::forward(func), dep) ) + { } + + template + Event(const ReactiveGroupBase& group, F&& func, const EventBase& dep) : + Event::EventBase( REACT_IMPL::CtorTag{ }, CreateProcessingNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), dep) ) { } template Event(F&& func, const EventBase& dep, const SignalBase& ... signals) : - Event::EventBase( REACT_IMPL::NodeCtorTag{ }, CreateSyncedProcessingNode(std::forward(func), dep, signals ...) ) + Event::EventBase( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(std::forward(func), dep, signals ...) ) + { } + + template + Event(const ReactiveGroupBase& group, F&& func, const EventBase& dep, const SignalBase& ... signals) : + Event::EventBase( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), dep, signals ...) ) { } }; @@ -286,9 +341,8 @@ class EventSource : public EventSourceBase EventSource& operator=(EventSource&&) = default; // Construct event source - template - explicit EventSource(const TGroup& group) : - EventSource::EventSourceBase( REACT_IMPL::NodeCtorTag{ }, CreateSourceNode(group) ) + explicit EventSource(const ReactiveGroupBase& group) : + EventSource::EventSourceBase( REACT_IMPL::CtorTag{ }, CreateSourceNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) { } }; @@ -309,9 +363,8 @@ class EventSource : public EventSourceBase EventSource& operator=(EventSource&&) = default; // Construct event source - template - explicit EventSource(const TGroup& group) : - EventSource::EventSourceBase( REACT_IMPL::NodeCtorTag{ }, CreateSourceNode(group) ) + explicit EventSource(const ReactiveGroupBase& group) : + EventSource::EventSourceBase( REACT_IMPL::CtorTag{ }, CreateSourceNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) { } // Construct from unique @@ -344,8 +397,8 @@ class EventSlot : public EventSlotBase EventSlot& operator=(EventSlot&&) = default; // Construct with value - EventSlot(const EventBase& input, const ReactiveGroupBase& group) : - EventSlot::EventSlotBase( REACT_IMPL::NodeCtorTag{ }, CreateSlotNode(input, group) ) + EventSlot(const ReactiveGroupBase& group, const EventBase& input) : + EventSlot::EventSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) { } }; @@ -375,21 +428,43 @@ class EventSlot : public EventSlotBase { EventSlot::EventSlotBase::operator=(std::move(other)); return *this; } // Construct with value - EventSlot(const SignalBase& input, const ReactiveGroupBase& group) : - EventSlot::EventSlotBase( REACT_IMPL::NodeCtorTag{ }, CreateSlotNode(input, group) ) + EventSlot(const ReactiveGroupBase& group, const SignalBase& input) : + EventSlot::EventSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) { } }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Merge /////////////////////////////////////////////////////////////////////////////////////////////////// +template +auto Merge(const ReactiveGroupBase& group, const EventBase& dep1, const EventBase& ... deps) -> decltype(auto) +{ + using REACT_IMPL::EventMergeNode; + using REACT_IMPL::PrivateEventLinkNodeInterface; + using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::CtorTag; + + static_assert(sizeof...(Us) > 0, "Merge requires at least 2 inputs."); + + // If supplied, use merge type, otherwise default to common type. + using E = typename std::conditional< + std::is_same::value, + typename std::common_type::type, + T>::type; + + const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); + + return Event( CtorTag{ }, std::make_shared>( + graphPtr, PrivateEventLinkNodeInterface::GetLocalEventNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalEventNodePtr(graphPtr, deps) ...)); +} + template auto Merge(const EventBase& dep1, const EventBase& ... deps) -> decltype(auto) { using REACT_IMPL::EventMergeNode; - using REACT_IMPL::GetCheckedGraphPtr; + using REACT_IMPL::PrivateEventLinkNodeInterface; using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::NodeCtorTag; + using REACT_IMPL::CtorTag; static_assert(sizeof...(Us) > 0, "Merge requires at least 2 inputs."); @@ -399,15 +474,24 @@ auto Merge(const EventBase& dep1, const EventBase& ... deps) -> decltype typename std::common_type::type, T>::type; - const auto& graphPtr = GetCheckedGraphPtr(dep1, deps ...); + const auto& graphPtr = PrivateNodeInterface::GraphPtr(dep1); - return Event( NodeCtorTag{ }, std::make_shared>( - graphPtr, PrivateNodeInterface::NodePtr(dep1), PrivateNodeInterface::NodePtr(deps) ...)); + return Event( CtorTag{ }, std::make_shared>( + graphPtr, PrivateEventLinkNodeInterface::GetLocalEventNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalEventNodePtr(graphPtr, deps) ...)); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Filter /////////////////////////////////////////////////////////////////////////////////////////////////// +template +auto Filter(const ReactiveGroupBase& group, F&& pred, const EventBase& dep) -> Event +{ + auto filterFunc = [capturedPred = std::forward(pred)] (EventRange inRange, EventSink out) + { std::copy_if(inRange.begin(), inRange.end(), out, capturedPred); }; + + return Event(group, std::move(filterFunc), dep); +} + template auto Filter(F&& pred, const EventBase& dep) -> Event { @@ -417,6 +501,19 @@ auto Filter(F&& pred, const EventBase& dep) -> Event return Event(std::move(filterFunc), dep); } +template +auto Filter(const ReactiveGroupBase& group, F&& pred, const EventBase& dep, const SignalBase& ... signals) -> Event +{ + auto filterFunc = [capturedPred = std::forward(pred)] (EventRange inRange, EventSink out, const Us& ... values) + { + for (const auto& v : inRange) + if (capturedPred(v, values ...)) + *out++ = v; + }; + + return Event(group, std::move(filterFunc), dep, signals ...); +} + template auto Filter(F&& pred, const EventBase& dep, const SignalBase& ... signals) -> Event { @@ -433,6 +530,15 @@ auto Filter(F&& pred, const EventBase& dep, const SignalBase& ... signals /////////////////////////////////////////////////////////////////////////////////////////////////// /// Transform /////////////////////////////////////////////////////////////////////////////////////////////////// +template +auto Transform(const ReactiveGroupBase& group, F&& op, const EventBase& dep) -> Event +{ + auto transformFunc = [capturedOp = std::forward(op)] (EventRange inRange, EventSink out) + { std::transform(inRange.begin(), inRange.end(), out, capturedOp); }; + + return Event(group, std::move(transformFunc), dep); +} + template auto Transform(F&& op, const EventBase& dep) -> Event { @@ -442,6 +548,18 @@ auto Transform(F&& op, const EventBase& dep) -> Event return Event(std::move(transformFunc), dep); } +template +auto Transform(const ReactiveGroupBase& group, F&& op, const EventBase& dep, const SignalBase& ... signals) -> Event +{ + auto transformFunc = [capturedOp = std::forward(pred)] (EventRange inRange, EventSink out, const Vs& ... values) + { + for (const auto& v : inRange) + *out++ = capturedPred(v, values ...); + }; + + return Event(group, std::move(transformFunc), dep, signals ...); +} + template auto Transform(F&& op, const EventBase& dep, const SignalBase& ... signals) -> Event { @@ -468,21 +586,38 @@ auto Flatten(const Signal>& outer) -> Events /////////////////////////////////////////////////////////////////////////////////////////////////// /// Join /////////////////////////////////////////////////////////////////////////////////////////////////// -template -auto Join(const EventBase& ... deps) -> Event, unique> +template +auto Join(const ReactiveGroupBase& group, const EventBase& dep1, const EventBase& ... deps) -> Event, unique> +{ + using REACT_IMPL::EventJoinNode; + using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::PrivateEventLinkNodeInterface; + using REACT_IMPL::CtorTag; + + static_assert(sizeof...(Us) > 0, "Join requires at least 2 inputs."); + + // If supplied, use merge type, otherwise default to common type. + const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); + + return Event, unique>( CtorTag{ }, std::make_shared>( + graphPtr, PrivateEventLinkNodeInterface::GetLocalEventNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalEventNodePtr(graphPtr, deps) ...)); +} + +template +auto Join(const EventBase& dep1, const EventBase& ... deps) -> Event, unique> { using REACT_IMPL::EventJoinNode; - using REACT_IMPL::GetCheckedGraphPtr; using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::NodeCtorTag; + using REACT_IMPL::PrivateEventLinkNodeInterface; + using REACT_IMPL::CtorTag; - static_assert(sizeof...(Ts) > 1, "Join requires at least 2 inputs."); + static_assert(sizeof...(Us) > 0, "Join requires at least 2 inputs."); // If supplied, use merge type, otherwise default to common type. - const auto& graphPtr = GetCheckedGraphPtr(deps ...); + const auto& graphPtr = PrivateNodeInterface::GraphPtr(dep1); - return Event, unique>( NodeCtorTag{ }, std::make_shared>( - graphPtr, PrivateNodeInterface::NodePtr(deps) ...)); + return Event, unique>( CtorTag{ }, std::make_shared>( + graphPtr, PrivateEventLinkNodeInterface::GetLocalEventNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalEventNodePtr(graphPtr, deps) ...)); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -512,6 +647,24 @@ bool Equals(const EventBase& lhs, const EventBase& rhs) return lhs.Equals(rhs); } +struct PrivateEventLinkNodeInterface +{ + template + static auto GetLocalEventNodePtr(const std::shared_ptr& targetGraph, const EventBase& dep) -> std::shared_ptr> + { + const std::shared_ptr& sourceGraph = PrivateNodeInterface::GraphPtr(dep); + + if (sourceGraph == targetGraph) + { + return PrivateNodeInterface::NodePtr(dep); + } + else + { + return EventLinkBase::CreateLinkNode(targetGraph, dep); + } + } +}; + /****************************************/ REACT_IMPL_END /***************************************/ #endif // REACT_EVENT_H_INCLUDED \ No newline at end of file diff --git a/include/react/Group.h b/include/react/Group.h index 4ab5d2af..ac5c1b91 100644 --- a/include/react/Group.h +++ b/include/react/Group.h @@ -23,7 +23,7 @@ struct PrivateReactiveGroupInterface; struct PrivateConcurrentReactiveGroupInterface; -struct NodeCtorTag { }; +struct CtorTag { }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/Observer.h b/include/react/Observer.h index 9d5c6409..590c5baa 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -56,38 +56,51 @@ class ObserverBase { return nodePtr_; } template - auto CreateSignalObserverNode(F&& func, const SignalBase& dep1, const SignalBase& ... deps) -> decltype(auto) + auto CreateSignalObserverNode(const std::shared_ptr& graphPtr, F&& func, const SignalBase& dep1, const SignalBase& ... deps) -> decltype(auto) { - using REACT_IMPL::GetCheckedGraphPtr; - using REACT_IMPL::PrivateNodeInterface; + using REACT_IMPL::PrivateSignalLinkNodeInterface; using ObsNodeType = REACT_IMPL::SignalObserverNode::type, T1, Ts ...>; return std::make_shared( - GetCheckedGraphPtr(dep1, deps ...), + graphPtr, std::forward(func), - PrivateNodeInterface::NodePtr(dep1), PrivateNodeInterface::NodePtr(deps) ...); + PrivateSignalLinkNodeInterface::GetLocalSignalNodePtr(graphPtr, dep1), PrivateSignalLinkNodeInterface::GetLocalSignalNodePtr(graphPtr, deps) ...); } - template - auto CreateEventObserverNode(F&& func, const EventBase& dep) -> decltype(auto) + template + auto CreateSignalObserverNode(F&& func, const SignalBase& dep1, const SignalBase& ... deps) -> decltype(auto) { using REACT_IMPL::PrivateNodeInterface; + return CreateSignalObserverNode(PrivateNodeInterface::GraphPtr(dep1), std::forward(func), dep1, deps ...); + } + + template + auto CreateEventObserverNode(const std::shared_ptr& graphPtr, F&& func, const EventBase& dep) -> decltype(auto) + { + using REACT_IMPL::PrivateEventLinkNodeInterface; using ObsNodeType = REACT_IMPL::EventObserverNode::type, T>; - return std::make_shared(PrivateNodeInterface::GraphPtr(dep), std::forward(func), PrivateNodeInterface::NodePtr(dep)); + return std::make_shared(graphPtr, std::forward(func), PrivateEventLinkNodeInterface::GetLocalEventNodePtr(graphPtr, dep)); + } + + template + auto CreateEventObserverNode(F&& func, const EventBase& dep) -> decltype(auto) + { + using REACT_IMPL::PrivateNodeInterface; + return CreateSignalObserverNode(PrivateNodeInterface::GraphPtr(dep1), std::forward(func), dep1, deps ...); } template auto CreateSyncedEventObserverNode(F&& func, const EventBase& dep, const SignalBase& ... syncs) -> decltype(auto) { - using REACT_IMPL::GetCheckedGraphPtr; using REACT_IMPL::PrivateNodeInterface; + using REACT_IMPL::PrivateSignalLinkNodeInterface; using ObsNodeType = REACT_IMPL::SyncedEventObserverNode::type, T, Us ...>; + const auto& graphPtr = PrivateNodeInterface::GraphPtr(dep); + return std::make_shared( - GetCheckedGraphPtr(dep, syncs ...), - std::forward(func), - PrivateNodeInterface::NodePtr(dep), PrivateNodeInterface::NodePtr(syncs) ...); + graphPtr, std::forward(func), PrivateSignalLinkNodeInterface::GetLocalSignalNodePtr(graphPtr, dep), PrivateSignalLinkNodeInterface::GetLocalSignalNodePtr(graphPtr, syncs) ...); } private: @@ -113,23 +126,41 @@ class Observer : public ObserverBase Observer(Observer&&) = default; Observer& operator=(Observer&&) = default; - // Construct signal observer + // Construct signal observer with implicit group template Observer(F&& func, const SignalBase& ... subjects) : Observer::ObserverBase( CreateSignalObserverNode(std::forward(func), subjects ...) ) { } - // Construct event observer + // Construct signal observer with explicit group + template + Observer(const ReactiveGroupBase& group, F&& func, const SignalBase& ... subjects) : + Observer::ObserverBase( CreateSignalObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subjects ...) ) + { } + + // Construct event observer with implicit group template Observer(F&& func, const EventBase& subject) : - Observer::ObserverBase( CreateEventObserverNode(std::forward(func), subject ) ) + Observer::ObserverBase( CreateEventObserverNode(std::forward(func), subject) ) { } - // Constructed synced event observer + // Construct event observer with explicit group + template + Observer(const ReactiveGroupBase& group, F&& func, const EventBase& subject) : + Observer::ObserverBase( CreateEventObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subject) ) + { } + + // Constructed synced event observer with implicit group template Observer(F&& func, const EventBase& subject, const SignalBase& ... signals) : Observer::ObserverBase( CreateSyncedEventObserverNode(std::forward(func), subject, signals ...) ) { } + + // Constructed synced event observer with explicit group + template + Observer(const ReactiveGroupBase& group, F&& func, const EventBase& subject, const SignalBase& ... signals) : + Observer::ObserverBase( CreateSyncedEventObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subject, signals ...) ) + { } }; template <> @@ -155,23 +186,41 @@ class Observer : public ObserverBase Observer& operator=(Observer&& other) { Observer::ObserverBase::operator=(std::move(other)); return *this; } - // Construct signal observer + // Construct signal observer with implicit group template Observer(F&& func, const SignalBase& ... subjects) : Observer::ObserverBase( CreateSignalObserverNode(std::forward(func), subjects ...) ) { } - // Construct event observer + // Construct signal observer with explicit group + template + Observer(const ReactiveGroupBase& group, F&& func, const SignalBase& ... subjects) : + Observer::ObserverBase( CreateSignalObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subjects ...) ) + { } + + // Construct event observer with implicit group template Observer(F&& func, const EventBase& subject) : - Observer::ObserverBase( CreateEventObserverNode(std::forward(func), subject ) ) + Observer::ObserverBase( CreateEventObserverNode(std::forward(func), subject) ) { } - // Constructed synced event observer + // Construct event observer with explicit group + template + Observer(const ReactiveGroupBase& group, F&& func, const EventBase& subject) : + Observer::ObserverBase( CreateEventObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subject) ) + { } + + // Constructed synced event observer with implicit group template Observer(F&& func, const EventBase& subject, const SignalBase& ... signals) : Observer::ObserverBase( CreateSyncedEventObserverNode(std::forward(func), subject, signals ...) ) { } + + // Constructed synced event observer with explicit group + template + Observer(const ReactiveGroupBase& group, F&& func, const EventBase& subject, const SignalBase& ... signals) : + Observer::ObserverBase( CreateSyncedEventObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subject, signals ...) ) + { } }; /******************************************/ REACT_END /******************************************/ diff --git a/include/react/Signal.h b/include/react/Signal.h index dab00344..4f733f47 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -24,6 +24,12 @@ #include "react/detail/graph/SignalNodes.h" +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +struct PrivateSignalLinkNodeInterface; + +/****************************************/ REACT_IMPL_END /***************************************/ + /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -37,7 +43,7 @@ class SignalBase public: // Private node ctor - SignalBase(REACT_IMPL::NodeCtorTag, std::shared_ptr&& nodePtr) : + SignalBase(REACT_IMPL::CtorTag, std::shared_ptr&& nodePtr) : nodePtr_( std::move(nodePtr) ) { } @@ -60,16 +66,22 @@ class SignalBase { return nodePtr_; } template - auto CreateFuncNode(F&& func, const SignalBase& dep1, const SignalBase& ... deps) -> decltype(auto) + auto CreateFuncNode(const std::shared_ptr& graphPtr, F&& func, const SignalBase& dep1, const SignalBase& ... deps) -> decltype(auto) { - using REACT_IMPL::GetCheckedGraphPtr; - using REACT_IMPL::PrivateNodeInterface; + using REACT_IMPL::PrivateSignalLinkNodeInterface; using FuncNodeType = REACT_IMPL::SignalFuncNode::type, T1, Ts ...>; return std::make_shared( - GetCheckedGraphPtr(dep1, deps ...), + graphPtr, std::forward(func), - PrivateNodeInterface::NodePtr(dep1), PrivateNodeInterface::NodePtr(deps) ...); + PrivateSignalLinkNodeInterface::GetLocalSignalNodePtr(graphPtr, dep1), PrivateSignalLinkNodeInterface::GetLocalSignalNodePtr(graphPtr, deps) ...); + } + + template + auto CreateFuncNode(F&& func, const SignalBase& dep1, const SignalBase& ... deps) -> decltype(auto) + { + using REACT_IMPL::PrivateNodeInterface; + return CreateFuncNode(PrivateNodeInterface::GraphPtr(dep1), std::forward(func), dep1, deps ...); } private: @@ -78,6 +90,7 @@ class SignalBase friend struct REACT_IMPL::PrivateNodeInterface; }; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// VarSignalBase /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -112,21 +125,17 @@ class VarSignalBase : public SignalBase VarSignalBase(VarSignalBase&&) = default; VarSignalBase& operator=(VarSignalBase&&) = default; - auto CreateVarNode(const ReactiveGroupBase& group) -> decltype(auto) + static auto CreateVarNode(const std::shared_ptr& graphPtr) -> decltype(auto) { - using REACT_IMPL::PrivateReactiveGroupInterface; using VarNodeType = REACT_IMPL::VarSignalNode; - - return std::make_shared(PrivateReactiveGroupInterface::GraphPtr(group)); + return std::make_shared(graphPtr); } template - auto CreateVarNode(T&& value, const ReactiveGroupBase& group) -> decltype(auto) + static auto CreateVarNode(const std::shared_ptr& graphPtr, T&& value) -> decltype(auto) { - using REACT_IMPL::PrivateReactiveGroupInterface; using VarNodeType = REACT_IMPL::VarSignalNode; - - return std::make_shared(PrivateReactiveGroupInterface::GraphPtr(group), std::forward(value)); + return std::make_shared(graphPtr, std::forward(value)); } private: @@ -183,13 +192,12 @@ class SignalSlotBase : public SignalBase SignalSlotBase(SignalSlotBase&&) = default; SignalSlotBase& operator=(SignalSlotBase&&) = default; - auto CreateSlotNode(const SignalBase& input, const ReactiveGroupBase& group) -> decltype(auto) + static auto CreateSlotNode(const std::shared_ptr& graphPtr, const SignalBase& input) -> decltype(auto) { using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::PrivateReactiveGroupInterface; using SlotNodeType = REACT_IMPL::SignalSlotNode; - return std::make_shared(PrivateReactiveGroupInterface::GraphPtr(group), PrivateNodeInterface::NodePtr(input)); + return std::make_shared(graphPtr, PrivateNodeInterface::NodePtr(input)); } private: @@ -197,7 +205,6 @@ class SignalSlotBase : public SignalBase { using REACT_IMPL::PrivateNodeInterface; using REACT_IMPL::NodeId; - using REACT_IMPL::ReactiveGraph; using SlotNodeType = REACT_IMPL::SignalSlotNode; SlotNodeType* castedPtr = static_cast(this->NodePtr().get()); @@ -208,6 +215,38 @@ class SignalSlotBase : public SignalBase } }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SignalLinkBase +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class SignalLinkBase : public SignalBase +{ +public: + using SignalBase::SignalBase; + +protected: + SignalLinkBase() = default; + + SignalLinkBase(const SignalLinkBase&) = default; + SignalLinkBase& operator=(const SignalLinkBase&) = default; + + SignalLinkBase(SignalLinkBase&&) = default; + SignalLinkBase& operator=(SignalLinkBase&&) = default; + + static auto CreateLinkNode(const std::shared_ptr& graphPtr, const SignalBase& input) -> decltype(auto) + { + using REACT_IMPL::PrivateNodeInterface; + using REACT_IMPL::PrivateReactiveGroupInterface; + using LinkNodeType = REACT_IMPL::SignalLinkNode; + + auto node = std::make_shared(graphPtr, PrivateNodeInterface::GraphPtr(input), PrivateNodeInterface::NodePtr(input)); + node->SetWeakSelfPtr(std::weak_ptr{ node }); + return node; + } + + friend struct REACT_IMPL::PrivateSignalLinkNodeInterface; +}; + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Signal /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -227,10 +266,16 @@ class Signal : public SignalBase Signal(Signal&&) = default; Signal& operator=(Signal&&) = default; - // Construct func signal + // Construct func signal with explicit group + template + explicit Signal(const ReactiveGroupBase& group, F&& func, const SignalBase& ... deps) : + Signal::SignalBase( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), deps ...) ) + { } + + // Construct func signal with implicit group template explicit Signal(F&& func, const SignalBase& ... deps) : - Signal::SignalBase( REACT_IMPL::NodeCtorTag{ }, CreateFuncNode(std::forward(func), deps ...) ) + Signal::SignalBase( REACT_IMPL::CtorTag{ }, CreateFuncNode(std::forward(func), deps ...) ) { } }; @@ -250,12 +295,6 @@ class Signal : public SignalBase Signal(Signal&&) = default; Signal& operator=(Signal&&) = default; - // Construct func signal - template - explicit Signal(F&& func, const SignalBase& ... deps) : - Signal::SignalBase( REACT_IMPL::NodeCtorTag{ }, CreateFuncNode(std::forward(func), deps ...) ) - { } - // Construct from unique Signal(Signal&& other) : Signal::SignalBase( std::move(other) ) @@ -264,6 +303,18 @@ class Signal : public SignalBase // Assign from unique Signal& operator=(Signal&& other) { Signal::SignalBase::operator=(std::move(other)); return *this; } + + // Construct func signal with explicit group + template + explicit Signal(const ReactiveGroupBase& group, F&& func, const SignalBase& ... deps) : + Signal::SignalBase( REACT_IMPL::CtorTag{ }, CreateFuncNode(std::forward(func), deps ...) ) + { } + + // Construct func signal with implicit group + template + explicit Signal(F&& func, const SignalBase& ... deps) : + Signal::SignalBase( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), deps ...) ) + { } }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -285,16 +336,15 @@ class VarSignal : public VarSignalBase VarSignal(VarSignal&&) = default; VarSignal& operator=(VarSignal&&) = default; - // Construct with default - template - explicit VarSignal(const TGroup& group) : - VarSignal::VarSignalBase( REACT_IMPL::NodeCtorTag{ }, CreateVarNode( group) ) + // Construct with group + default + explicit VarSignal(const ReactiveGroupBase& group) : + VarSignal::VarSignalBase( REACT_IMPL::CtorTag{ }, CreateVarNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) { } - // Construct with value - template - VarSignal(T&& value, const TGroup& group) : - VarSignal::VarSignalBase( REACT_IMPL::NodeCtorTag{ }, CreateVarNode(std::forward(value), group) ) + // Construct with group + value + template + VarSignal(const ReactiveGroupBase& group, T&& value) : + VarSignal::VarSignalBase( REACT_IMPL::CtorTag{ }, CreateVarNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(value)) ) { } }; @@ -324,15 +374,14 @@ class VarSignal : public VarSignalBase { VarSignal::VarSignalBase::operator=(std::move(other)); return *this; } // Construct with default - template - explicit VarSignal(const TGroup& group) : - VarSignal::VarSignalBase( REACT_IMPL::NodeCtorTag{ }, CreateVarNode( group) ) + explicit VarSignal(const ReactiveGroupBase& group) : + VarSignal::VarSignalBase( REACT_IMPL::CtorTag{ }, CreateVarNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) { } // Construct with value - template - VarSignal(T&& value, const TGroup& group) : - VarSignal::VarSignalBase( REACT_IMPL::NodeCtorTag{ }, CreateVarNode(std::forward(value), group) ) + template + VarSignal(const ReactiveGroupBase& group, T&& value) : + VarSignal::VarSignalBase( REACT_IMPL::CtorTag{ }, CreateVarNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(value)) ) { } }; @@ -355,14 +404,19 @@ class SignalSlot : public SignalSlotBase SignalSlot(SignalSlot&&) = default; SignalSlot& operator=(SignalSlot&&) = default; - // Construct with default + // Construct with group + default explicit SignalSlot(const ReactiveGroupBase& group) : - SignalSlot::SignalSlotBase( REACT_IMPL::NodeCtorTag{ }, CreateSlotNode( group) ) + SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) { } - // Construct with value - SignalSlot(const SignalBase& input, const ReactiveGroupBase& group) : - SignalSlot::SignalSlotBase( REACT_IMPL::NodeCtorTag{ }, CreateSlotNode(input, group) ) + // Construct with group + value + SignalSlot(const ReactiveGroupBase& group, const SignalBase& input) : + SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) + { } + + // Construct with value + explicit SignalSlot(const SignalBase& input) : + SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(input), input) ) { } }; @@ -391,14 +445,90 @@ class SignalSlot : public SignalSlotBase SignalSlot& operator=(SignalSlot&& other) { SignalSlot::SignalSlotBase::operator=(std::move(other)); return *this; } - // Construct with default + // Construct with group + default explicit SignalSlot(const ReactiveGroupBase& group) : - SignalSlot::SignalSlotBase( REACT_IMPL::NodeCtorTag{ }, CreateSlotNode( group) ) + SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) + { } + + // Construct with group + value + SignalSlot(const ReactiveGroupBase& group, const SignalBase& input) : + SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) + { } + + // Construct with value + explicit SignalSlot(const SignalBase& input) : + SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(input), input) ) + { } +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SignalLink +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class SignalLink : public SignalLinkBase +{ +public: + using SignalLinkBase::SignalLinkBase; + + using ValueType = S; + + SignalLink() = delete; + + SignalLink(const SignalLink&) = delete; + SignalLink& operator=(const SignalLink&) = delete; + + SignalLink(SignalLink&&) = default; + SignalLink& operator=(SignalLink&&) = default; + + // Construct with default + explicit SignalLink(const ReactiveGroupBase& group) : + SignalLink::SignalLinkBase( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) + { } + + // Construct with group + value + SignalLink(const ReactiveGroupBase& group, const SignalBase& input) : + SignalLink::SignalLinkBase( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) + { } + + // Construct with value + explicit SignalLink(const SignalBase& input) : + SignalLink::SignalLinkBase( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(input), input) ) + { } +}; + +template +class SignalLink : public SignalLinkBase +{ +public: + using SignalLinkBase::SignalLinkBase; + + using ValueType = S; + + SignalLink() = delete; + + SignalLink(const SignalLink&) = default; + SignalLink& operator=(const SignalLink&) = default; + + SignalLink(SignalLink&&) = default; + SignalLink& operator=(SignalLink&&) = default; + + // Construct from unique + SignalLink(SignalLink&& other) : + SignalLink::SignalLinkBase( std::move(other) ) + { } + + // Assign from unique + SignalLink& operator=(SignalSlot&& other) + { SignalLink::SignalLinkBase::operator=(std::move(other)); return *this; } + + // Construct with default + explicit SignalLink(const ReactiveGroupBase& group) : + SignalLink::SignalLinkBase( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) { } // Construct with value - SignalSlot(const SignalBase& input, const ReactiveGroupBase& group) : - SignalSlot::SignalSlotBase( REACT_IMPL::NodeCtorTag{ }, CreateSlotNode(input, group) ) + SignalLink(const ReactiveGroupBase& group, const SignalBase& input) : + SignalLink::SignalLinkBase( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) { } }; @@ -412,6 +542,24 @@ bool Equals(const SignalBase& lhs, const SignalBase& rhs) return lhs.Equals(rhs); } +struct PrivateSignalLinkNodeInterface +{ + template + static auto GetLocalSignalNodePtr(const std::shared_ptr& targetGraph, const SignalBase& sig) -> std::shared_ptr> + { + const std::shared_ptr& sourceGraph = PrivateNodeInterface::GraphPtr(sig); + + if (sourceGraph == targetGraph) + { + return PrivateNodeInterface::NodePtr(sig); + } + else + { + return SignalLinkBase::CreateLinkNode(targetGraph, sig); + } + } +}; + /****************************************/ REACT_IMPL_END /***************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/IReactiveGraph.h b/include/react/detail/IReactiveGraph.h index 594f3801..2ac8f291 100644 --- a/include/react/detail/IReactiveGraph.h +++ b/include/react/detail/IReactiveGraph.h @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include "react/API.h" @@ -45,7 +45,7 @@ enum class NodeCategory input, dyninput, output, - link + linkoutput }; class ReactiveGraph; @@ -64,11 +64,11 @@ struct IReactiveNode virtual int GetDependencyCount() const = 0; }; -using LinkOutputList = std::vector>; +using LinkOutputMap = std::unordered_map>>; struct ILinkOutputNode : public IReactiveNode { - virtual void CollectOutput(LinkOutputList& output) = 0; + virtual void CollectOutput(LinkOutputMap& output) = 0; }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 6a457a98..f188e55c 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -469,7 +469,8 @@ class EventJoinNode : public EventStreamNode> // All slots ready? apply( - [this, &isReady] (Slot& ... slots) { + [this, &isReady] (Slot& ... slots) + { // Todo: combine return values instead REACT_EXPAND_PACK(CheckSlot(slots, isReady)); }, @@ -534,6 +535,104 @@ class EventJoinNode : public EventStreamNode> std::tuple...> slots_; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// EventLinkNode +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class EventLinkNode : public EventStreamNode +{ +public: + EventLinkNode(const std::shared_ptr& graphPtr, const std::shared_ptr& srcGraphPtr, const std::shared_ptr>& dep) : + EventLinkNode::EventStreamNode( graphPtr ), + linkOutput_( srcGraphPtr, dep ) + { + this->RegisterMe(NodeCategory::input); + } + + ~EventLinkNode() + { + this->UnregisterMe(); + } + + void SetWeakSelfPtr(const std::weak_ptr& self) + { linkOutput_.parent = self; } + + virtual const char* GetNodeType() const override + { return "EventLink"; } + + virtual int GetDependencyCount() const override + { return 1; } + + virtual UpdateResult Update(TurnId turnId, int successorCount) override + { + this->SetPendingSuccessorCount(successorCount); + return UpdateResult::changed; + } + + void SetEvents(EventLinkNode::EventStreamNode::StorageType&& events) + { this->Events() = std::move(events); } + +private: + struct VirtualOutputNode : public ILinkOutputNode + { + VirtualOutputNode(const std::shared_ptr& srcGraphPtrIn, const std::shared_ptr>& depIn) : + parent( ), + srcGraphPtr( srcGraphPtrIn ), + dep( depIn ) + { + nodeId = srcGraphPtr->RegisterNode(this, NodeCategory::linkoutput); + srcGraphPtr->OnNodeAttach(nodeId, dep->GetNodeId()); + } + + ~VirtualOutputNode() + { + srcGraphPtr->OnNodeDetach(nodeId, dep->GetNodeId()); + srcGraphPtr->UnregisterNode(nodeId); + } + + virtual const char* GetNodeType() const override + { return "EventLinkOutput"; } + + virtual int GetDependencyCount() const override + { return 1; } + + virtual UpdateResult Update(TurnId turnId, int successorCount) override + { + return UpdateResult::changed; + } + + virtual void CollectOutput(LinkOutputMap& output) override + { + if (auto p = parent.lock()) + { + auto* rawPtr = p->GraphPtr().get(); + output[rawPtr].push_back( + [storedParent = std::move(p), storedEvents = dep->Events()] () mutable + { + NodeId nodeId = storedParent->GetNodeId(); + auto& graphPtr = storedParent->GraphPtr(); + + graphPtr->AddInput(nodeId, + [&storedParent, &storedEvents] + { + storedParent->SetEvents(std::move(storedEvents)); + }); + }); + } + } + + std::weak_ptr parent; + + NodeId nodeId; + + std::shared_ptr> dep; + + std::shared_ptr srcGraphPtr; + }; + + VirtualOutputNode linkOutput_; +}; + /****************************************/ REACT_IMPL_END /***************************************/ #endif // REACT_DETAIL_GRAPH_EVENTNODES_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/PropagationST.h b/include/react/detail/graph/PropagationST.h index e0eb2f59..182cfcd0 100644 --- a/include/react/detail/graph/PropagationST.h +++ b/include/react/detail/graph/PropagationST.h @@ -44,6 +44,8 @@ class TransactionQueue template void Push(TransactionFlags flags, F&& transaction) { + transactions_.push(StoredTransaction{ flags, std::forward(transaction) }); + if (count_.fetch_add(1, std::memory_order_relaxed) == 0) tbb::task::enqueue(*new(tbb::task::allocate_root()) WorkerTask(*this)); } @@ -95,9 +97,6 @@ class ReactiveGraph void OnDynamicNodeAttach(NodeId node, NodeId parentId, TurnId turnId); void OnDynamicNodeDetach(NodeId node, NodeId parentId, TurnId turnId); - LinkId AddGroupLink(ILinkNodeOutput* nodePtr, NodeCategory category); - void RemoveGroupLink(LinkId nodeId); - template void AddInput(NodeId nodeId, F&& inputCallback); @@ -118,7 +117,7 @@ class ReactiveGraph NodeData(IReactiveNode* nodePtrIn, NodeCategory categoryIn) : nodePtr( nodePtrIn ), category(categoryIn) - { } + { } NodeCategory category = NodeCategory::normal; @@ -127,7 +126,6 @@ class ReactiveGraph bool queued = false; IReactiveNode* nodePtr = nullptr; - std::vector successors; }; @@ -167,7 +165,7 @@ class ReactiveGraph TopoQueue scheduledNodes_; IndexMap nodeData_; std::vector changedInputs_; - std::vector scheduledLinkNodes_; + LinkOutputMap scheduledLinkOutputs_; bool isTransactionActive_ = false; }; @@ -239,6 +237,9 @@ void ReactiveGraph::AddInput(NodeId nodeId, F&& inputCallback) if (! scheduledNodes_.IsEmpty()) Propagate(); + + if (! scheduledLinkOutputs_.empty()) + UpdateLinkNodes(); } } } @@ -274,7 +275,7 @@ void ReactiveGraph::DoTransaction(F&& transactionCallback) if (! scheduledNodes_.IsEmpty()) Propagate(); - if (!scheduledLinkNodes_.empty()) + if (! scheduledLinkOutputs_.empty()) UpdateLinkNodes(); } @@ -302,6 +303,13 @@ void ReactiveGraph::Propagate() continue; } + // Special handling for link output nodes. They have no successors and they don't have to be updated. + if (node.category == NodeCategory::linkoutput) + { + static_cast(node.nodePtr)->CollectOutput(scheduledLinkOutputs_); + continue; + } + int successorCount = node.successors.size(); if (nodePtr->Update(0, successorCount) == UpdateResult::changed) @@ -316,10 +324,35 @@ void ReactiveGraph::Propagate() void ReactiveGraph::UpdateLinkNodes() { - for (const auto& x : scheduledLinkNodes_) - { - - } + for (auto& e : scheduledLinkOutputs_) + { + e.first->EnqueueTransaction(TransactionFlags::none, + [inputs = std::move(e.second)] + { + for (auto& callback : inputs) + callback(); + }); + } + + scheduledLinkOutputs_.clear(); + + /*using ElementType = std::pair; + + auto from = scheduledLinkOutputs_.begin(); + auto to = scheduledLinkOutputs_.end(); + + for (auto it = from; it != to; ++it) + { + ReactiveGraph* graphPtr = it->first; + auto p = std::partition(it, to, [=] (ReactiveGraph* e) { return e == graphPtr }); + + for (auto it2 = p; it2 != to; ++it2) + { + graphPtr->EnqueueLinkTransaction(); + } + } + + scheduledLinkOutputs_.clear();*/ } void ReactiveGraph::ScheduleSuccessors(NodeData& node) @@ -331,11 +364,7 @@ void ReactiveGraph::ScheduleSuccessors(NodeData& node) if (!succ.queued) { succ.queued = true; - - if (node.category != NodeCategory::link) - scheduledNodes_.Push(succId, succ.level); - else - scheduledLinkNodes_.push_back(succId); + scheduledNodes_.Push(succId, succ.level); } } } @@ -400,7 +429,7 @@ size_t TransactionQueue::ProcessNextBatch() { if (!skipPop) { - if (transactions_.try_pop(curTransaction)) + if (!transactions_.try_pop(curTransaction)) return popCount; canMerge = IsBitmaskSet(curTransaction.flags, TransactionFlags::allow_merging); diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index 2bdd3eac..d084945d 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -267,7 +267,7 @@ class SignalSlotNode : public SignalNode { } virtual const char* GetNodeType() const override - { return "SignalSlotVirtualInput"; } + { return "SignalSlotInput"; } virtual int GetDependencyCount() const override { return 0; } @@ -301,7 +301,7 @@ class SignalSlotNode : public SignalNode }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalBridgeNode +/// SignalLinkNode /////////////////////////////////////////////////////////////////////////////////////////////////// template class SignalLinkNode : public SignalNode @@ -309,85 +309,95 @@ class SignalLinkNode : public SignalNode public: SignalLinkNode(const std::shared_ptr& graphPtr, const std::shared_ptr& srcGraphPtr, const std::shared_ptr>& dep) : SignalLinkNode::SignalNode( graphPtr, dep->Value() ), - linkOutput_( *this, srcGraphPtr, dep ) + linkOutput_( srcGraphPtr, dep ) { - slotInput_.nodeId = GraphPtr()->RegisterNode(&linkOutput_, NodeCategory::dyninput); - this->RegisterMe(); - - this->AttachToMe(slotInput_.nodeId); - this->AttachToMe(dep->GetNodeId()); + this->RegisterMe(NodeCategory::input); } ~SignalLinkNode() { - this->DetachFromMe(slotInput_.dep->GetNodeId()); - this->DetachFromMe(slotInput_.nodeId); - this->UnregisterMe(); - GraphPtr()->UnregisterNode(slotInput_.nodeId); } + void SetWeakSelfPtr(const std::weak_ptr& self) + { linkOutput_.parent = self; } + virtual const char* GetNodeType() const override { return "SignalLink"; } virtual int GetDependencyCount() const override - { return 2; } + { return 1; } virtual UpdateResult Update(TurnId turnId, int successorCount) override { - {// outputDataMutex - std::lock_guard lock(linkOutput_.outputDataMutex); - this->Value() = std::move(linkOutput_.outputData.front()); - linkOutput_.outputData.pop(); - }// ~outputDataMutex - return UpdateResult::changed; } + template + void SetValue(T&& newValue) + { + this->Value() = std::forward(newValue); + } + private: struct VirtualOutputNode : public ILinkOutputNode { - VirtualOutputNode(SignalSlotNode& parentIn, const std::shared_ptr& srcGraphPtrIn, const std::shared_ptr>& depIn) : - parent( parentIn ), - srcGraphPtr(srcGraphPtrIn), + VirtualOutputNode(const std::shared_ptr& srcGraphPtrIn, const std::shared_ptr>& depIn) : + parent( ), + srcGraphPtr( srcGraphPtrIn ), dep( depIn ) - { } + { + nodeId = srcGraphPtr->RegisterNode(this, NodeCategory::linkoutput); + srcGraphPtr->OnNodeAttach(nodeId, dep->GetNodeId()); + } + + ~VirtualOutputNode() + { + srcGraphPtr->OnNodeDetach(nodeId, dep->GetNodeId()); + srcGraphPtr->UnregisterNode(nodeId); + } virtual const char* GetNodeType() const override - { return "SignalLinkVirtualOutput"; } + { return "SignalLinkOutput"; } virtual int GetDependencyCount() const override { return 1; } virtual UpdateResult Update(TurnId turnId, int successorCount) override - { - {// outputDataMutex - std::lock_guard lock(outputDataMutex); - outputData.push(dep->Value()); - }// ~outputDataMutex - + { return UpdateResult::changed; } - virtual void CollectOutput(LinkOutputList& output) override - { output.emplace_back(srcGraphPtr.get(), &parent); } - - SignalSlotNode& parent; + virtual void CollectOutput(LinkOutputMap& output) override + { + if (auto p = parent.lock()) + { + auto* rawPtr = p->GraphPtr().get(); + output[rawPtr].push_back( + [storedParent = std::move(p), storedValue = dep->Value()] + { + NodeId nodeId = storedParent->GetNodeId(); + auto& graphPtr = storedParent->GraphPtr(); + + graphPtr->AddInput(nodeId, + [&storedParent, &storedValue] + { + storedParent->SetValue(std::move(storedValue)); + }); + }); + } + } + + std::weak_ptr parent; NodeId nodeId; std::shared_ptr> dep; - std::mutex outputDataMutex; - - std::queue> outputData; - std::shared_ptr srcGraphPtr; }; VirtualOutputNode linkOutput_; - - }; /****************************************/ REACT_IMPL_END /***************************************/ From 96302158e61ec39130db3082b048756417714a98 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 11 Dec 2016 23:16:34 +0100 Subject: [PATCH 228/266] group progress --- examples/src/Main.cpp | 28 ++-- include/react/API.h | 6 - include/react/Algorithm.h | 178 ++++++++++++++++++--- include/react/Event.h | 50 +++--- include/react/Group.h | 1 - include/react/Observer.h | 27 ++-- include/react/Signal.h | 52 +++--- include/react/detail/ReactiveInput.h | 7 +- include/react/detail/graph/EventNodes.h | 72 ++++----- include/react/detail/graph/ObserverNodes.h | 2 +- include/react/detail/graph/SignalNodes.h | 60 +++---- 11 files changed, 304 insertions(+), 179 deletions(-) diff --git a/examples/src/Main.cpp b/examples/src/Main.cpp index 62d5c079..e4fe6f5d 100644 --- a/examples/src/Main.cpp +++ b/examples/src/Main.cpp @@ -45,12 +45,20 @@ template void PrintEvents(EventRange evts) printf(" Event: %d\n", e); } +template void PrintSyncedEvents(EventRange evts, int a, int b) +{ + printf("Processing events...\n"); + + for (const auto& e : evts) + printf(" Event: %d, %d, %d\n", e, a, b); +} + template bool FilterFunc(T v) { return v > 10; } -int main2() +int main() { ReactiveGroup<> group; @@ -60,7 +68,7 @@ int main2() VarSignal y{ group, 0 }; VarSignal z{ group, 0 }; - Signal area{ Multiply, x, y }; + Signal area{ group, Multiply, x, y }; Signal volume{ Multiply, area, z }; Observer<> areaObs{ PrintArea, area }; @@ -136,15 +144,21 @@ int main2() ReactiveGroup<> group1; ReactiveGroup<> group2; + VarSignal s1{ group1, 10 }; + VarSignal s2{ group2, 11 }; + EventSource e1{ group1 }; EventSource e2{ group2 }; + auto hold = Hold(group1, 0, e1); + auto merged = Merge(group2, e1, e2); auto joined = Join(e1, e2); auto joined2 = Join(group1, e1, e2); Observer<> eventObs{ PrintEvents, merged }; + Observer<> eventObs2{ group2, PrintSyncedEvents, merged, s1, s2 }; e1.Emit(222); @@ -154,16 +168,6 @@ int main2() return 0; } -int main() -{ - ReactiveGroup<> group; - - VarSignal a{ }; - VarSignal b{ }; - -} - - diff --git a/include/react/API.h b/include/react/API.h index 79a44241..b1e86ad1 100644 --- a/include/react/API.h +++ b/include/react/API.h @@ -23,12 +23,6 @@ enum OwnershipPolicy shared }; -enum ReferencePolicy -{ - strong, - weak -}; - enum class WeightHint { automatic, diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index af6bce46..9a033559 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -18,47 +18,107 @@ #include "react/API.h" #include "react/detail/graph/AlgorithmNodes.h" +#include "Event.h" +#include "Signal.h" + /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Hold - Hold the most recent event in a signal /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Hold(T&& initialValue, const EventBase& events) -> Signal +auto Hold(const ReactiveGroupBase& group, T&& initialValue, const EventBase& evnt) -> Signal { using REACT_IMPL::HoldNode; + using REACT_IMPL::PrivateEventLinkNodeInterface; + using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::CtorTag; + + const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); + + return Signal( CtorTag{ }, std::make_shared>( + graphPtr, std::forward(initialValue), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)) ); +} + +template +auto Hold(T&& initialValue, const EventBase& evnt) -> Signal +{ + using REACT_IMPL::HoldNode; + using REACT_IMPL::PrivateEventLinkNodeInterface; using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::NodeCtorTag; + using REACT_IMPL::CtorTag; + + const auto& graphPtr = PrivateNodeInterface::GraphPtr(evnt); - return Signal( NodeCtorTag{ }, std::make_shared>( - PrivateNodeInterface::GraphPtr(events), std::forward(initialValue), PrivateNodeInterface::NodePtr(events)) ); + return Signal( CtorTag{ }, std::make_shared>( + graphPtr, std::forward(initialValue), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)) ); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Monitor - Emits value changes of target signal /////////////////////////////////////////////////////////////////////////////////////////////////// +template +auto Monitor(const ReactiveGroupBase& group, const SignalBase& signal) -> Event +{ + using REACT_IMPL::MonitorNode; + using REACT_IMPL::PrivateSignalLinkNodeInterface; + using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::CtorTag; + + const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); + + return Event( CtorTag{ }, std::make_shared>( + graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal)) ); +} + template auto Monitor(const SignalBase& signal) -> Event { using REACT_IMPL::MonitorNode; + using REACT_IMPL::PrivateSignalLinkNodeInterface; using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::NodeCtorTag; + using REACT_IMPL::CtorTag; + + const auto& graphPtr = PrivateNodeInterface::GraphPtr(evnt); - return Event( NodeCtorTag{ }, std::make_shared>( - PrivateNodeInterface::GraphPtr(signal), PrivateNodeInterface::NodePtr(signal)) ); + return Event( CtorTag{ }, std::make_shared>( + graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal)) ); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Iterate - Iteratively combines signal value with values from event stream (aka Fold) /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Iterate(T&& init, F&& func, const EventBase& events) -> Signal +auto Iterate(const ReactiveGroupBase& group, T&& initialValue, F&& func, const EventBase& evnt) -> Signal { using REACT_IMPL::IterateNode; using REACT_IMPL::IterateByRefNode; using REACT_IMPL::IsCallableWith; + using REACT_IMPL::PrivateEventLinkNodeInterface; + using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::CtorTag; + + using FuncType = typename std::decay::type; + using IterNodeType = typename std::conditional< + IsCallableWith,S>::value, + IterateNode, + IterateByRefNode>::type; + + const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); + + return Signal( CtorTag{ }, std::make_shared( + graphPtr, std::forward(initialValue), std::forward(func), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt) )); +} + +template +auto Iterate(T&& initialValue, F&& func, const EventBase& evnt) -> Signal +{ + using REACT_IMPL::IterateNode; + using REACT_IMPL::IterateByRefNode; + using REACT_IMPL::IsCallableWith; + using REACT_IMPL::PrivateEventLinkNodeInterface; using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::NodeCtorTag; + using REACT_IMPL::CtorTag; using FuncType = typename std::decay::type; using IterNodeType = typename std::conditional< @@ -66,22 +126,49 @@ auto Iterate(T&& init, F&& func, const EventBase& events) -> Signal, IterateByRefNode>::type; - return Signal( NodeCtorTag{ }, std::make_shared( - PrivateNodeInterface::GraphPtr(events), std::forward(init), std::forward(func), PrivateNodeInterface::NodePtr(events) )); + const auto& graphPtr = PrivateNodeInterface::GraphPtr(evnt); + + return Signal( CtorTag{ }, std::make_shared( + graphPtr, std::forward(initialValue), std::forward(func), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt) )); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Iterate - Synced /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Iterate(T&& init, F&& func, const EventBase& events, const SignalBase& ... signals) -> Signal +auto Iterate(const ReactiveGroupBase& group, T&& initialValue, F&& func, const EventBase& evnt, const SignalBase& ... signals) -> Signal { using REACT_IMPL::SyncedIterateNode; using REACT_IMPL::SyncedIterateByRefNode; using REACT_IMPL::IsCallableWith; - using REACT_IMPL::GetCheckedGraphPtr; + using REACT_IMPL::PrivateSignalLinkNodeInterface; + using REACT_IMPL::PrivateEventLinkNodeInterface; + using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::CtorTag; + + using FuncType = typename std::decay::type; + using IterNodeType = typename std::conditional< + IsCallableWith, S, Us ...>::value, + SyncedIterateNode, + SyncedIterateByRefNode>::type; + + const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); + + return Signal( CtorTag{ }, std::make_shared( + graphPtr, std::forward(initialValue), std::forward(func), + PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt), PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signals) ...)); +} + +template +auto Iterate(T&& initialValue, F&& func, const EventBase& evnt, const SignalBase& ... signals) -> Signal +{ + using REACT_IMPL::SyncedIterateNode; + using REACT_IMPL::SyncedIterateByRefNode; + using REACT_IMPL::IsCallableWith; + using REACT_IMPL::PrivateSignalLinkNodeInterface; + using REACT_IMPL::PrivateEventLinkNodeInterface;; using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::NodeCtorTag; + using REACT_IMPL::CtorTag; using FuncType = typename std::decay::type; using IterNodeType = typename std::conditional< @@ -89,38 +176,77 @@ auto Iterate(T&& init, F&& func, const EventBase& events, const SignalBase, SyncedIterateByRefNode>::type; - return Signal( NodeCtorTag{ }, std::make_shared( - GetCheckedGraphPtr(events, signals ...), - std::forward(init), std::forward(func), PrivateNodeInterface::NodePtr(events), PrivateNodeInterface::NodePtr(signals) ...)); + const auto& graphPtr = PrivateNodeInterface::GraphPtr(evnt); + + return Signal( CtorTag{ }, std::make_shared( + graphPtr, std::forward(initialValue), std::forward(func), + PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt), PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signals) ...)); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Snapshot - Sets signal value to value of other signal when event is received /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Snapshot(const SignalBase& signal, const EventBase& trigger) -> Signal +auto Snapshot(const ReactiveGroupBase& group, const SignalBase& signal, const EventBase& evnt) -> Signal { using REACT_IMPL::SnapshotNode; - using REACT_IMPL::GetCheckedGraphPtr; + using REACT_IMPL::PrivateSignalLinkNodeInterface; + using REACT_IMPL::PrivateEventLinkNodeInterface;; + using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::CtorTag; + + const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); + + return Event( CtorTag{ }, std::make_shared>( + graphPtr, PrivateNodeInterface::NodePtr(signal), PrivateNodeInterface::NodePtr(evnt)) ); +} + +template +auto Snapshot(const SignalBase& signal, const EventBase& evnt) -> Signal +{ + using REACT_IMPL::SnapshotNode; + using REACT_IMPL::PrivateSignalLinkNodeInterface; + using REACT_IMPL::PrivateEventLinkNodeInterface;; using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::NodeCtorTag; + using REACT_IMPL::CtorTag; + + const auto& graphPtr = PrivateNodeInterface::GraphPtr(signal); - return Events( NodeCtorTag{ }, std::make_shared>( - GetCheckedGraphPtr(signal, trigger), PrivateNodeInterface::NodePtr(signal), PrivateNodeInterface::NodePtr(trigger)) ); + return Event( CtorTag{ }, std::make_shared>( + graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)) ); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Pulse - Emits value of target signal when event is received /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Pulse(const SignalBase& signal, const EventBase& trigger) -> Event +auto Pulse(const ReactiveGroupBase& group, const SignalBase& signal, const EventBase& evnt) -> Event { using REACT_IMPL::PulseNode; - using REACT_IMPL::GetCheckedGraphPtr; + using REACT_IMPL::PrivateSignalLinkNodeInterface; + using REACT_IMPL::PrivateEventLinkNodeInterface;; + using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::CtorTag; + + const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); + + return Event( CtorTag{ }, std::make_shared>( + graphPtr, PrivateSignalLinkNodeInterface::NodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::NodePtr(graphPtr, evnt)) ); +} + +template +auto Pulse(const SignalBase& signal, const EventBase& evnt) -> Event +{ + using REACT_IMPL::PulseNode; + using REACT_IMPL::PrivateSignalLinkNodeInterface; + using REACT_IMPL::PrivateEventLinkNodeInterface;; using REACT_IMPL::PrivateNodeInterface; + using REACT_IMPL::CtorTag; + + const auto& graphPtr = PrivateNodeInterface::GraphPtr(signal); - return Event( NodeCtorTag{ }, std::make_shared>( - GetCheckedGraphPtr(signal, trigger), PrivateNodeInterface::NodePtr(signal), PrivateNodeInterface::NodePtr(trigger)) ); + return Event( CtorTag{ }, std::make_shared>( + graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)) ); } /******************************************/ REACT_END /******************************************/ diff --git a/include/react/Event.h b/include/react/Event.h index 89bcc0c8..bf8c0a05 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -230,11 +230,11 @@ class EventLinkBase : public EventBase using EventNodeType = REACT_IMPL::EventLinkNode; auto node = std::make_shared(graphPtr, PrivateNodeInterface::GraphPtr(input), PrivateNodeInterface::NodePtr(input)); - node->SetWeakSelfPtr(std::weak_ptr{ node }); - return node; + node->SetWeakSelfPtr(std::weak_ptr{ node }); + return node; } - friend struct REACT_IMPL::PrivateEventLinkNodeInterface; + friend struct REACT_IMPL::PrivateEventLinkNodeInterface; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -315,7 +315,7 @@ class Event : public EventBase Event::EventBase( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(std::forward(func), dep, signals ...) ) { } - template + template Event(const ReactiveGroupBase& group, F&& func, const EventBase& dep, const SignalBase& ... signals) : Event::EventBase( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), dep, signals ...) ) { } @@ -452,10 +452,10 @@ auto Merge(const ReactiveGroupBase& group, const EventBase& dep1, const Even typename std::common_type::type, T>::type; - const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); + const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); return Event( CtorTag{ }, std::make_shared>( - graphPtr, PrivateEventLinkNodeInterface::GetLocalEventNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalEventNodePtr(graphPtr, deps) ...)); + graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...)); } template @@ -477,7 +477,7 @@ auto Merge(const EventBase& dep1, const EventBase& ... deps) -> decltype const auto& graphPtr = PrivateNodeInterface::GraphPtr(dep1); return Event( CtorTag{ }, std::make_shared>( - graphPtr, PrivateEventLinkNodeInterface::GetLocalEventNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalEventNodePtr(graphPtr, deps) ...)); + graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...)); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -591,7 +591,7 @@ auto Join(const ReactiveGroupBase& group, const EventBase& dep1, const Event { using REACT_IMPL::EventJoinNode; using REACT_IMPL::PrivateReactiveGroupInterface; - using REACT_IMPL::PrivateEventLinkNodeInterface; + using REACT_IMPL::PrivateEventLinkNodeInterface; using REACT_IMPL::CtorTag; static_assert(sizeof...(Us) > 0, "Join requires at least 2 inputs."); @@ -600,7 +600,7 @@ auto Join(const ReactiveGroupBase& group, const EventBase& dep1, const Event const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); return Event, unique>( CtorTag{ }, std::make_shared>( - graphPtr, PrivateEventLinkNodeInterface::GetLocalEventNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalEventNodePtr(graphPtr, deps) ...)); + graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...)); } template @@ -608,7 +608,7 @@ auto Join(const EventBase& dep1, const EventBase& ... deps) -> Event 0, "Join requires at least 2 inputs."); @@ -617,7 +617,7 @@ auto Join(const EventBase& dep1, const EventBase& ... deps) -> Event, unique>( CtorTag{ }, std::make_shared>( - graphPtr, PrivateEventLinkNodeInterface::GetLocalEventNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalEventNodePtr(graphPtr, deps) ...)); + graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...)); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -649,20 +649,20 @@ bool Equals(const EventBase& lhs, const EventBase& rhs) struct PrivateEventLinkNodeInterface { - template - static auto GetLocalEventNodePtr(const std::shared_ptr& targetGraph, const EventBase& dep) -> std::shared_ptr> - { - const std::shared_ptr& sourceGraph = PrivateNodeInterface::GraphPtr(dep); - - if (sourceGraph == targetGraph) - { - return PrivateNodeInterface::NodePtr(dep); - } - else - { - return EventLinkBase::CreateLinkNode(targetGraph, dep); - } - } + template + static auto GetLocalNodePtr(const std::shared_ptr& targetGraph, const EventBase& dep) -> std::shared_ptr> + { + const std::shared_ptr& sourceGraph = PrivateNodeInterface::GraphPtr(dep); + + if (sourceGraph == targetGraph) + { + return PrivateNodeInterface::NodePtr(dep); + } + else + { + return EventLinkBase::CreateLinkNode(targetGraph, dep); + } + } }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/Group.h b/include/react/Group.h index ac5c1b91..fc5e49f1 100644 --- a/include/react/Group.h +++ b/include/react/Group.h @@ -22,7 +22,6 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ struct PrivateReactiveGroupInterface; -struct PrivateConcurrentReactiveGroupInterface; struct CtorTag { }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/Observer.h b/include/react/Observer.h index 590c5baa..eea9ce5f 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -58,13 +58,13 @@ class ObserverBase template auto CreateSignalObserverNode(const std::shared_ptr& graphPtr, F&& func, const SignalBase& dep1, const SignalBase& ... deps) -> decltype(auto) { - using REACT_IMPL::PrivateSignalLinkNodeInterface; + using REACT_IMPL::PrivateSignalLinkNodeInterface; using ObsNodeType = REACT_IMPL::SignalObserverNode::type, T1, Ts ...>; return std::make_shared( graphPtr, std::forward(func), - PrivateSignalLinkNodeInterface::GetLocalSignalNodePtr(graphPtr, dep1), PrivateSignalLinkNodeInterface::GetLocalSignalNodePtr(graphPtr, deps) ...); + PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...); } template @@ -77,30 +77,35 @@ class ObserverBase template auto CreateEventObserverNode(const std::shared_ptr& graphPtr, F&& func, const EventBase& dep) -> decltype(auto) { - using REACT_IMPL::PrivateEventLinkNodeInterface; + using REACT_IMPL::PrivateEventLinkNodeInterface; using ObsNodeType = REACT_IMPL::EventObserverNode::type, T>; - return std::make_shared(graphPtr, std::forward(func), PrivateEventLinkNodeInterface::GetLocalEventNodePtr(graphPtr, dep)); + return std::make_shared(graphPtr, std::forward(func), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep)); } template auto CreateEventObserverNode(F&& func, const EventBase& dep) -> decltype(auto) { using REACT_IMPL::PrivateNodeInterface; - return CreateSignalObserverNode(PrivateNodeInterface::GraphPtr(dep1), std::forward(func), dep1, deps ...); + return CreateEventObserverNode(PrivateNodeInterface::GraphPtr(dep), std::forward(func), dep); } template - auto CreateSyncedEventObserverNode(F&& func, const EventBase& dep, const SignalBase& ... syncs) -> decltype(auto) + auto CreateSyncedEventObserverNode(const std::shared_ptr& graphPtr, F&& func, const EventBase& dep, const SignalBase& ... syncs) -> decltype(auto) { - using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::PrivateSignalLinkNodeInterface; + using REACT_IMPL::PrivateEventLinkNodeInterface; + using REACT_IMPL::PrivateSignalLinkNodeInterface; using ObsNodeType = REACT_IMPL::SyncedEventObserverNode::type, T, Us ...>; - const auto& graphPtr = PrivateNodeInterface::GraphPtr(dep); - return std::make_shared( - graphPtr, std::forward(func), PrivateSignalLinkNodeInterface::GetLocalSignalNodePtr(graphPtr, dep), PrivateSignalLinkNodeInterface::GetLocalSignalNodePtr(graphPtr, syncs) ...); + graphPtr, std::forward(func), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep), PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, syncs) ...); + } + + template + auto CreateSyncedEventObserverNode(F&& func, const EventBase& dep, const SignalBase& ... syncs) -> decltype(auto) + { + using REACT_IMPL::PrivateNodeInterface; + return CreateSyncedEventObserverNode(PrivateNodeInterface::GraphPtr(dep), std::forward(func), dep, syncs ...); } private: diff --git a/include/react/Signal.h b/include/react/Signal.h index 4f733f47..bd5f62df 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -68,20 +68,20 @@ class SignalBase template auto CreateFuncNode(const std::shared_ptr& graphPtr, F&& func, const SignalBase& dep1, const SignalBase& ... deps) -> decltype(auto) { - using REACT_IMPL::PrivateSignalLinkNodeInterface; + using REACT_IMPL::PrivateSignalLinkNodeInterface; using FuncNodeType = REACT_IMPL::SignalFuncNode::type, T1, Ts ...>; return std::make_shared( graphPtr, std::forward(func), - PrivateSignalLinkNodeInterface::GetLocalSignalNodePtr(graphPtr, dep1), PrivateSignalLinkNodeInterface::GetLocalSignalNodePtr(graphPtr, deps) ...); + PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...); } template auto CreateFuncNode(F&& func, const SignalBase& dep1, const SignalBase& ... deps) -> decltype(auto) { - using REACT_IMPL::PrivateNodeInterface; - return CreateFuncNode(PrivateNodeInterface::GraphPtr(dep1), std::forward(func), dep1, deps ...); + using REACT_IMPL::PrivateNodeInterface; + return CreateFuncNode(PrivateNodeInterface::GraphPtr(dep1), std::forward(func), dep1, deps ...); } private: @@ -240,11 +240,11 @@ class SignalLinkBase : public SignalBase using LinkNodeType = REACT_IMPL::SignalLinkNode; auto node = std::make_shared(graphPtr, PrivateNodeInterface::GraphPtr(input), PrivateNodeInterface::NodePtr(input)); - node->SetWeakSelfPtr(std::weak_ptr{ node }); - return node; + node->SetWeakSelfPtr(std::weak_ptr{ node }); + return node; } - friend struct REACT_IMPL::PrivateSignalLinkNodeInterface; + friend struct REACT_IMPL::PrivateSignalLinkNodeInterface; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -272,7 +272,7 @@ class Signal : public SignalBase Signal::SignalBase( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), deps ...) ) { } - // Construct func signal with implicit group + // Construct func signal with implicit group template explicit Signal(F&& func, const SignalBase& ... deps) : Signal::SignalBase( REACT_IMPL::CtorTag{ }, CreateFuncNode(std::forward(func), deps ...) ) @@ -304,7 +304,7 @@ class Signal : public SignalBase Signal& operator=(Signal&& other) { Signal::SignalBase::operator=(std::move(other)); return *this; } - // Construct func signal with explicit group + // Construct func signal with explicit group template explicit Signal(const ReactiveGroupBase& group, F&& func, const SignalBase& ... deps) : Signal::SignalBase( REACT_IMPL::CtorTag{ }, CreateFuncNode(std::forward(func), deps ...) ) @@ -414,7 +414,7 @@ class SignalSlot : public SignalSlotBase SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) { } - // Construct with value + // Construct with value explicit SignalSlot(const SignalBase& input) : SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(input), input) ) { } @@ -455,7 +455,7 @@ class SignalSlot : public SignalSlotBase SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) { } - // Construct with value + // Construct with value explicit SignalSlot(const SignalBase& input) : SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(input), input) ) { } @@ -490,7 +490,7 @@ class SignalLink : public SignalLinkBase SignalLink::SignalLinkBase( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) { } - // Construct with value + // Construct with value explicit SignalLink(const SignalBase& input) : SignalLink::SignalLinkBase( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(input), input) ) { } @@ -544,20 +544,20 @@ bool Equals(const SignalBase& lhs, const SignalBase& rhs) struct PrivateSignalLinkNodeInterface { - template - static auto GetLocalSignalNodePtr(const std::shared_ptr& targetGraph, const SignalBase& sig) -> std::shared_ptr> - { - const std::shared_ptr& sourceGraph = PrivateNodeInterface::GraphPtr(sig); - - if (sourceGraph == targetGraph) - { - return PrivateNodeInterface::NodePtr(sig); - } - else - { - return SignalLinkBase::CreateLinkNode(targetGraph, sig); - } - } + template + static auto GetLocalNodePtr(const std::shared_ptr& targetGraph, const SignalBase& sig) -> std::shared_ptr> + { + const std::shared_ptr& sourceGraph = PrivateNodeInterface::GraphPtr(sig); + + if (sourceGraph == targetGraph) + { + return PrivateNodeInterface::NodePtr(sig); + } + else + { + return SignalLinkBase::CreateLinkNode(targetGraph, sig); + } + } }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h index dd2d101c..d156b9fb 100644 --- a/include/react/detail/ReactiveInput.h +++ b/include/react/detail/ReactiveInput.h @@ -654,8 +654,7 @@ class InputManager : } template - void AsyncTransaction(TransactionFlagsT flags, const WaitingStatePtrT& waitingStatePtr, - F&& func) + void AsyncTransaction(TransactionFlagsT flags, const WaitingStatePtrT& waitingStatePtr, F&& func) { if (waitingStatePtr != nullptr) waitingStatePtr->IncWaitCount(); @@ -693,9 +692,7 @@ class InputManager : } //IContinuationTarget - virtual void AsyncContinuation(TransactionFlagsT flags, - const WaitingStatePtrT& waitingStatePtr, - TransactionFuncT&& cont) override + virtual void AsyncContinuation(TransactionFlagsT flags, const WaitingStatePtrT& waitingStatePtr, TransactionFuncT&& cont) override { AsyncTransaction(flags, waitingStatePtr, std::move(cont)); } diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index f188e55c..7459af80 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -470,7 +470,7 @@ class EventJoinNode : public EventStreamNode> // All slots ready? apply( [this, &isReady] (Slot& ... slots) - { + { // Todo: combine return values instead REACT_EXPAND_PACK(CheckSlot(slots, isReady)); }, @@ -554,8 +554,8 @@ class EventLinkNode : public EventStreamNode this->UnregisterMe(); } - void SetWeakSelfPtr(const std::weak_ptr& self) - { linkOutput_.parent = self; } + void SetWeakSelfPtr(const std::weak_ptr& self) + { linkOutput_.parent = self; } virtual const char* GetNodeType() const override { return "EventLink"; } @@ -564,10 +564,10 @@ class EventLinkNode : public EventStreamNode { return 1; } virtual UpdateResult Update(TurnId turnId, int successorCount) override - { - this->SetPendingSuccessorCount(successorCount); - return UpdateResult::changed; - } + { + this->SetPendingSuccessorCount(successorCount); + return UpdateResult::changed; + } void SetEvents(EventLinkNode::EventStreamNode::StorageType&& events) { this->Events() = std::move(events); } @@ -576,19 +576,19 @@ class EventLinkNode : public EventStreamNode struct VirtualOutputNode : public ILinkOutputNode { VirtualOutputNode(const std::shared_ptr& srcGraphPtrIn, const std::shared_ptr>& depIn) : - parent( ), + parent( ), srcGraphPtr( srcGraphPtrIn ), dep( depIn ) { - nodeId = srcGraphPtr->RegisterNode(this, NodeCategory::linkoutput); - srcGraphPtr->OnNodeAttach(nodeId, dep->GetNodeId()); - } + nodeId = srcGraphPtr->RegisterNode(this, NodeCategory::linkoutput); + srcGraphPtr->OnNodeAttach(nodeId, dep->GetNodeId()); + } - ~VirtualOutputNode() - { - srcGraphPtr->OnNodeDetach(nodeId, dep->GetNodeId()); - srcGraphPtr->UnregisterNode(nodeId); - } + ~VirtualOutputNode() + { + srcGraphPtr->OnNodeDetach(nodeId, dep->GetNodeId()); + srcGraphPtr->UnregisterNode(nodeId); + } virtual const char* GetNodeType() const override { return "EventLinkOutput"; } @@ -598,30 +598,30 @@ class EventLinkNode : public EventStreamNode virtual UpdateResult Update(TurnId turnId, int successorCount) override { - return UpdateResult::changed; + return UpdateResult::changed; } virtual void CollectOutput(LinkOutputMap& output) override { - if (auto p = parent.lock()) - { - auto* rawPtr = p->GraphPtr().get(); - output[rawPtr].push_back( - [storedParent = std::move(p), storedEvents = dep->Events()] () mutable - { - NodeId nodeId = storedParent->GetNodeId(); - auto& graphPtr = storedParent->GraphPtr(); - - graphPtr->AddInput(nodeId, - [&storedParent, &storedEvents] - { - storedParent->SetEvents(std::move(storedEvents)); - }); - }); - } - } - - std::weak_ptr parent; + if (auto p = parent.lock()) + { + auto* rawPtr = p->GraphPtr().get(); + output[rawPtr].push_back( + [storedParent = std::move(p), storedEvents = dep->Events()] () mutable + { + NodeId nodeId = storedParent->GetNodeId(); + auto& graphPtr = storedParent->GraphPtr(); + + graphPtr->AddInput(nodeId, + [&storedParent, &storedEvents] + { + storedParent->SetEvents(std::move(storedEvents)); + }); + }); + } + } + + std::weak_ptr parent; NodeId nodeId; diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index f8887d8b..d4f3968a 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -158,7 +158,7 @@ class SyncedEventObserverNode : public ObserverNode virtual UpdateResult Update(TurnId turnId, int successorCount) override { // Updates might be triggered even if only sync nodes changed. Ignore those. - if (events_->Events().empty()) + if (subject_->Events().empty()) return UpdateResult::unchanged; apply([this] (const auto& ... syncs) { func_(EventRange( this->subject_->Events() ), syncs->Value() ...); }, syncHolder_); diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index d084945d..cb66531d 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -319,8 +319,8 @@ class SignalLinkNode : public SignalNode this->UnregisterMe(); } - void SetWeakSelfPtr(const std::weak_ptr& self) - { linkOutput_.parent = self; } + void SetWeakSelfPtr(const std::weak_ptr& self) + { linkOutput_.parent = self; } virtual const char* GetNodeType() const override { return "SignalLink"; } @@ -343,19 +343,19 @@ class SignalLinkNode : public SignalNode struct VirtualOutputNode : public ILinkOutputNode { VirtualOutputNode(const std::shared_ptr& srcGraphPtrIn, const std::shared_ptr>& depIn) : - parent( ), + parent( ), srcGraphPtr( srcGraphPtrIn ), dep( depIn ) { - nodeId = srcGraphPtr->RegisterNode(this, NodeCategory::linkoutput); - srcGraphPtr->OnNodeAttach(nodeId, dep->GetNodeId()); - } + nodeId = srcGraphPtr->RegisterNode(this, NodeCategory::linkoutput); + srcGraphPtr->OnNodeAttach(nodeId, dep->GetNodeId()); + } - ~VirtualOutputNode() - { - srcGraphPtr->OnNodeDetach(nodeId, dep->GetNodeId()); - srcGraphPtr->UnregisterNode(nodeId); - } + ~VirtualOutputNode() + { + srcGraphPtr->OnNodeDetach(nodeId, dep->GetNodeId()); + srcGraphPtr->UnregisterNode(nodeId); + } virtual const char* GetNodeType() const override { return "SignalLinkOutput"; } @@ -370,25 +370,25 @@ class SignalLinkNode : public SignalNode virtual void CollectOutput(LinkOutputMap& output) override { - if (auto p = parent.lock()) - { - auto* rawPtr = p->GraphPtr().get(); - output[rawPtr].push_back( - [storedParent = std::move(p), storedValue = dep->Value()] - { - NodeId nodeId = storedParent->GetNodeId(); - auto& graphPtr = storedParent->GraphPtr(); - - graphPtr->AddInput(nodeId, - [&storedParent, &storedValue] - { - storedParent->SetValue(std::move(storedValue)); - }); - }); - } - } - - std::weak_ptr parent; + if (auto p = parent.lock()) + { + auto* rawPtr = p->GraphPtr().get(); + output[rawPtr].push_back( + [storedParent = std::move(p), storedValue = dep->Value()] + { + NodeId nodeId = storedParent->GetNodeId(); + auto& graphPtr = storedParent->GraphPtr(); + + graphPtr->AddInput(nodeId, + [&storedParent, &storedValue] + { + storedParent->SetValue(std::move(storedValue)); + }); + }); + } + } + + std::weak_ptr parent; NodeId nodeId; From 33cab90c56aa3168720ea432b1ad2a95aa5e9d81 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 12 Dec 2016 18:35:46 +0100 Subject: [PATCH 229/266] fixes --- include/react/Algorithm.h | 10 ++-- include/react/detail/graph/PropagationST.h | 62 ++++++++-------------- 2 files changed, 27 insertions(+), 45 deletions(-) diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index 9a033559..6a0037a2 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -79,7 +79,7 @@ auto Monitor(const SignalBase& signal) -> Event using REACT_IMPL::PrivateNodeInterface; using REACT_IMPL::CtorTag; - const auto& graphPtr = PrivateNodeInterface::GraphPtr(evnt); + const auto& graphPtr = PrivateNodeInterface::GraphPtr(signal); return Event( CtorTag{ }, std::make_shared>( graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal)) ); @@ -197,8 +197,8 @@ auto Snapshot(const ReactiveGroupBase& group, const SignalBase& signal, const const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); - return Event( CtorTag{ }, std::make_shared>( - graphPtr, PrivateNodeInterface::NodePtr(signal), PrivateNodeInterface::NodePtr(evnt)) ); + return Signal( CtorTag{ }, std::make_shared>( + graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)) ); } template @@ -212,7 +212,7 @@ auto Snapshot(const SignalBase& signal, const EventBase& evnt) -> Signal( CtorTag{ }, std::make_shared>( + return Signal( CtorTag{ }, std::make_shared>( graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)) ); } @@ -231,7 +231,7 @@ auto Pulse(const ReactiveGroupBase& group, const SignalBase& signal, const Ev const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); return Event( CtorTag{ }, std::make_shared>( - graphPtr, PrivateSignalLinkNodeInterface::NodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::NodePtr(graphPtr, evnt)) ); + graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)) ); } template diff --git a/include/react/detail/graph/PropagationST.h b/include/react/detail/graph/PropagationST.h index 182cfcd0..4b8f2441 100644 --- a/include/react/detail/graph/PropagationST.h +++ b/include/react/detail/graph/PropagationST.h @@ -44,7 +44,7 @@ class TransactionQueue template void Push(TransactionFlags flags, F&& transaction) { - transactions_.push(StoredTransaction{ flags, std::forward(transaction) }); + transactions_.push(StoredTransaction{ flags, std::forward(transaction) }); if (count_.fetch_add(1, std::memory_order_relaxed) == 0) tbb::task::enqueue(*new(tbb::task::allocate_root()) WorkerTask(*this)); @@ -117,7 +117,7 @@ class ReactiveGraph NodeData(IReactiveNode* nodePtrIn, NodeCategory categoryIn) : nodePtr( nodePtrIn ), category(categoryIn) - { } + { } NodeCategory category = NodeCategory::normal; @@ -238,8 +238,8 @@ void ReactiveGraph::AddInput(NodeId nodeId, F&& inputCallback) if (! scheduledNodes_.IsEmpty()) Propagate(); - if (! scheduledLinkOutputs_.empty()) - UpdateLinkNodes(); + if (! scheduledLinkOutputs_.empty()) + UpdateLinkNodes(); } } } @@ -303,12 +303,12 @@ void ReactiveGraph::Propagate() continue; } - // Special handling for link output nodes. They have no successors and they don't have to be updated. - if (node.category == NodeCategory::linkoutput) - { - static_cast(node.nodePtr)->CollectOutput(scheduledLinkOutputs_); - continue; - } + // Special handling for link output nodes. They have no successors and they don't have to be updated. + if (node.category == NodeCategory::linkoutput) + { + static_cast(node.nodePtr)->CollectOutput(scheduledLinkOutputs_); + continue; + } int successorCount = node.successors.size(); @@ -324,35 +324,17 @@ void ReactiveGraph::Propagate() void ReactiveGraph::UpdateLinkNodes() { - for (auto& e : scheduledLinkOutputs_) - { - e.first->EnqueueTransaction(TransactionFlags::none, - [inputs = std::move(e.second)] - { - for (auto& callback : inputs) - callback(); - }); - } - - scheduledLinkOutputs_.clear(); - - /*using ElementType = std::pair; - - auto from = scheduledLinkOutputs_.begin(); - auto to = scheduledLinkOutputs_.end(); - - for (auto it = from; it != to; ++it) - { - ReactiveGraph* graphPtr = it->first; - auto p = std::partition(it, to, [=] (ReactiveGraph* e) { return e == graphPtr }); - - for (auto it2 = p; it2 != to; ++it2) - { - graphPtr->EnqueueLinkTransaction(); - } - } - - scheduledLinkOutputs_.clear();*/ + for (auto& e : scheduledLinkOutputs_) + { + e.first->EnqueueTransaction(TransactionFlags::none, + [inputs = std::move(e.second)] + { + for (auto& callback : inputs) + callback(); + }); + } + + scheduledLinkOutputs_.clear(); } void ReactiveGraph::ScheduleSuccessors(NodeData& node) @@ -364,7 +346,7 @@ void ReactiveGraph::ScheduleSuccessors(NodeData& node) if (!succ.queued) { succ.queued = true; - scheduledNodes_.Push(succId, succ.level); + scheduledNodes_.Push(succId, succ.level); } } } From f5454397a9e82ac131738e36e47ae57a45704b86 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 12 Dec 2016 23:01:18 +0100 Subject: [PATCH 230/266] example + benchmark fixes, wip --- benchmarks/src/BenchmarkBase.h | 15 +---- benchmarks/src/BenchmarkGrid.h | 60 +++++++++----------- benchmarks/src/Main.cpp | 17 +----- examples/src/BasicAlgorithms.cpp | 10 ++-- examples/src/BasicEvents.cpp | 38 +------------ examples/src/BasicObservers.cpp | 2 +- examples/src/BasicSignals.cpp | 50 +++-------------- examples/src/BasicSynchronization.cpp | 25 ++++++--- include/react/Group.h | 62 ++------------------- include/react/detail/IReactiveGraph.h | 2 +- include/react/detail/graph/AlgorithmNodes.h | 17 +++--- include/react/detail/graph/EventNodes.h | 22 ++++---- include/react/detail/graph/ObserverNodes.h | 6 +- include/react/detail/graph/PropagationST.h | 8 +-- include/react/detail/graph/SignalNodes.h | 12 ++-- 15 files changed, 103 insertions(+), 243 deletions(-) diff --git a/benchmarks/src/BenchmarkBase.h b/benchmarks/src/BenchmarkBase.h index 4b65306a..528a8489 100644 --- a/benchmarks/src/BenchmarkBase.h +++ b/benchmarks/src/BenchmarkBase.h @@ -55,18 +55,6 @@ inline const std::string CurrentDateTime() return buf; } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// BenchmarkBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class BenchmarkBase -{ -public: - typedef D Domain; - - double Run(); -}; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// RunBenchmark /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -118,8 +106,7 @@ template < int RUN_COUNT, template class TBenchmark, - typename TParams, - typename ... Ds + typename TParams > void RunBenchmarkClass(const char* name, std::ostream& out, const TParams& params) { diff --git a/benchmarks/src/BenchmarkGrid.h b/benchmarks/src/BenchmarkGrid.h index d719afaa..29afe312 100644 --- a/benchmarks/src/BenchmarkGrid.h +++ b/benchmarks/src/BenchmarkGrid.h @@ -21,47 +21,42 @@ using namespace react; /////////////////////////////////////////////////////////////////////////////////////////////////// /// GridGraphGenerator /////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename D, - typename TValue -> +template class GridGraphGenerator { public: - using MySignal = Signal; + using SignalType = Signal; - using Func1T = std::function; - using Func2T = std::function; + using Func1T = std::function; + using Func2T = std::function; - using SignalVectT = std::vector; - using WidthVectT = std::vector; + using SignalVectType = std::vector; - SignalVectT InputSignals; - SignalVectT OutputSignals; + SignalVectType inputSignals; + SignalVectType outputSignals; - Func1T Function1; - Func2T Function2; + Func1T function1; + Func2T function2; - WidthVectT Widths; + std::vector widths; void Generate() { - assert(InputSignals.size() >= 1); - assert(Widths.size() >= 1); + assert(inputSignals.size() >= 1); + assert(widths.size() >= 1); - SignalVectT buf1 = InputSignals; - SignalVectT buf2; + SignalVectType buf1 = std::move(inputSignals); + SignalVectType buf2; SignalVectT* curBuf = &buf1; SignalVectT* nextBuf = &buf2; - size_t curWidth = InputSignals.size(); + size_t curWidth = inputSignals.size(); size_t nodeCount = 1; nodeCount += curWidth; - for (auto targetWidth : Widths) + for (auto targetWidth : widths) { while (curWidth != targetWidth) { @@ -110,8 +105,8 @@ class GridGraphGenerator //printf ("NODE COUNT %d\n", nodeCount); - OutputSignals.clear(); - OutputSignals.insert(OutputSignals.begin(), curBuf->begin(), curBuf->end()); + outputSignals.clear(); + outputSignals.insert(outputSignals.begin(), curBuf->begin(), curBuf->end()); } }; @@ -135,22 +130,21 @@ struct BenchmarkParams_Grid const int K; }; -template -struct Benchmark_Grid : public BenchmarkBase +struct Benchmark_Grid { - double Run(const BenchmarkParams_Grid& params) + double Run(const BenchmarkParams_Grid& params, const ReactiveGroupBase& group) { - auto in = MakeVar(1); + VarSignal in{ group, 1 }; - GridGraphGenerator generator; + GridGraphGenerator generator; - generator.InputSignals.push_back(in); + generator.inputSignals.push_back(in); - generator.Widths.push_back(params.N); - generator.Widths.push_back(1); + generator.widths.push_back(params.N); + generator.widths.push_back(1); - generator.Function1 = [] (int a) { return a; }; - generator.Function2 = [] (int a, int b) { return a + b; }; + generator.function1 = [] (int a) { return a; }; + generator.function2 = [] (int a, int b) { return a + b; }; generator.Generate(); diff --git a/benchmarks/src/Main.cpp b/benchmarks/src/Main.cpp index 93a79d78..cbf5aaca 100644 --- a/benchmarks/src/Main.cpp +++ b/benchmarks/src/Main.cpp @@ -17,31 +17,16 @@ #include "BenchmarkSequence.h" #include "BenchmarkLifeSim.h" -#include "react/Domain.h" +#include "react/Group.h" #include "react/Signal.h" #include "react/Algorithm.h" #include "react/common/Util.h" -#include "react/logging/EventLog.h" - -#include "react/engine/ToposortEngine.h" -#include "react/engine/PulsecountEngine.h" -#include "react/engine/SubtreeEngine.h" /////////////////////////////////////////////////////////////////////////////////////////////////// namespace { using namespace react; -REACTIVE_DOMAIN(ToposortSTDomain, sequential, ToposortEngine) -REACTIVE_DOMAIN(ToposortDomain, parallel, ToposortEngine) -REACTIVE_DOMAIN(PulsecountDomain, parallel, PulsecountEngine) -REACTIVE_DOMAIN(SubtreeDomain, parallel, SubtreeEngine) - -REACTIVE_DOMAIN(ToposortSTDomainConc, sequential_concurrent, ToposortEngine) -REACTIVE_DOMAIN(ToposortDomainConc, parallel_concurrent, ToposortEngine) -REACTIVE_DOMAIN(PulsecountDomainConc, parallel_concurrent, PulsecountEngine) -REACTIVE_DOMAIN(SubtreeDomainConc, parallel_concurrent, SubtreeEngine) - void runBenchmarkGrid(std::ostream& out) { RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(20, 10000), diff --git a/examples/src/BasicAlgorithms.cpp b/examples/src/BasicAlgorithms.cpp index 1e84305d..a11dbdb9 100644 --- a/examples/src/BasicAlgorithms.cpp +++ b/examples/src/BasicAlgorithms.cpp @@ -66,8 +66,8 @@ namespace example2 struct Employee { - VarSignal name { string( "Bob" ), group }; - VarSignal salary { 3000, group }; + VarSignal name { group, string( "Bob" ) }; + VarSignal salary { group, 3000 }; }; void Run() @@ -213,8 +213,8 @@ namespace example5 public: EventSource update{ group }; - VarSignal delta{ 1, group }; - VarSignal start{ 0, group }; + VarSignal delta{ group, 1 }; + VarSignal start{ group, 0 }; Signal count{ Iterate(start.Value(), DoCounterLoop, update, delta, start) }; }; @@ -276,7 +276,7 @@ namespace example6 public: EventSource input{ group }; - VarSignal threshold{ 42, group }; + VarSignal threshold{ group, 42 }; Signal> allSamples{ Iterate>(vector{ }, DoIterateAllSamples, input) }; diff --git a/examples/src/BasicEvents.cpp b/examples/src/BasicEvents.cpp index 0f4f3f06..b9a7cfbd 100644 --- a/examples/src/BasicEvents.cpp +++ b/examples/src/BasicEvents.cpp @@ -237,46 +237,12 @@ namespace example5 /////////////////////////////////////////////////////////////////////////////////////////////////// int main() { - using namespace std; - using namespace react; - - ReactiveGroup<> group; - - auto ev1 = EventSource( group ); - auto ev2 = EventSource( group ); - - auto slot1 = EventSlot( ev1, group ); - auto slot2 = EventSlot( ev1, group ); - - Observer<> obs1( - [] (EventRange in) - { - for (int e : in) - cout << e << endl; - }, slot1); - - Observer<> obs2( - [] (EventRange in) - { - for (int e : in) - cout << e << endl; - }, slot2); - - ev1 << 10 << 20 << 30; - ev2 << 11 << 22 << 33; - - slot1.Set(ev2); - slot2.Set(ev2); - - ev1 << 10 << 20 << 30; - ev2 << 11 << 22 << 33; - - /*example1::v1::Run(); + example1::v1::Run(); example1::v2::Run(); example2::Run(); example3::Run(); example4::Run(); - example5::Run();*/ + example5::Run(); return 0; } \ No newline at end of file diff --git a/examples/src/BasicObservers.cpp b/examples/src/BasicObservers.cpp index 0a282528..c81cb372 100644 --- a/examples/src/BasicObservers.cpp +++ b/examples/src/BasicObservers.cpp @@ -22,7 +22,7 @@ namespace example1 ReactiveGroup<> group; - VarSignal x( 1, group ); + VarSignal x( group, 1 ); void Run() { diff --git a/examples/src/BasicSignals.cpp b/examples/src/BasicSignals.cpp index 44369987..fc52eb68 100644 --- a/examples/src/BasicSignals.cpp +++ b/examples/src/BasicSignals.cpp @@ -32,8 +32,8 @@ namespace example1 ReactiveGroup<> group; // The two words - VarSignal firstWord( string("Change"), group ); - VarSignal secondWord( string("me!"), group ); + VarSignal firstWord( group, string("Change") ); + VarSignal secondWord( group, string("me!") ); // A signal that concatenates both words Signal bothWords(ConcatFunc, firstWord, secondWord); @@ -66,7 +66,7 @@ namespace example2 ReactiveGroup<> group; - VarSignal x( 1, group ); + VarSignal x( group, 1 ); Signal xAbs( [] (int v) { return abs(v); }, x); @@ -100,8 +100,8 @@ namespace example3 ReactiveGroup<> group; - VarSignal a( 1, group ); - VarSignal b( 1, group ); + VarSignal a( group, 1 ); + VarSignal b( group, 1 ); Signal x( sumFunc, a, b ); Signal y( sumFunc, a, b ); @@ -186,8 +186,8 @@ namespace example5 } // Input operands - VarSignal a( 1, group ); - VarSignal b( 2, group ); + VarSignal a( group, 1 ); + VarSignal b( group, 2 ); // The expression vector Signal expressions( @@ -218,43 +218,11 @@ namespace example5 /////////////////////////////////////////////////////////////////////////////////////////////////// int main() { - using namespace std; - using namespace react; - - ReactiveGroup<> group; - - auto sig1 = VarSignal( 10, group ); - auto sig2 = VarSignal( 22, group ); - - auto slot1 = SignalSlot( sig1, group ); - auto slot2 = SignalSlot( sig1, group ); - - printf("%d\n", slot1.Value()); - printf("%d\n", slot2.Value()); - - slot1.Set(sig2); - slot2.Set(sig2); - - printf("%d\n", slot1.Value()); - printf("%d\n", slot2.Value()); - - group.DoTransaction([&] - { - slot1.Set(sig2); - slot2.Set(sig2); - }); - - group.EnqueueTransaction([&] - { - slot1.Set(sig2); - slot2.Set(sig2); - }); - - /*example1::Run(); + example1::Run(); example2::Run(); example3::Run(); example4::Run(); - example5::Run();*/ + example5::Run(); return 0; } \ No newline at end of file diff --git a/examples/src/BasicSynchronization.cpp b/examples/src/BasicSynchronization.cpp index 4a7f6529..a79f87ce 100644 --- a/examples/src/BasicSynchronization.cpp +++ b/examples/src/BasicSynchronization.cpp @@ -21,25 +21,36 @@ namespace example1 using namespace react; using namespace std; - REACTIVE_DOMAIN(D, sequential_concurrent) + ReactiveGroup<> group; class Sensor { public: - USING_REACTIVE_DOMAIN(D) + Sensor(const Sensor&) = default; + Sensor& operator=(const Sensor&) = default; + Sensor(Sensor&&) = default; + Sensor& operator=(Sensor&&) = default; - EventSourceT Samples = MakeEventSource(); + explicit Sensor(const ReactiveGroup<>& group) : + Samples( group ) + { } + + EventSource Samples; }; void Run() { cout << "Example 1 - Asynchronous transactions" << endl; - Sensor mySensor; + Sensor mySensor( group ); - Observe(mySensor.Samples, [] (int v) { - cout << v << endl; - }); + Observer<> obs( + [&] (EventRange<> in) + { + for (auto t : in) + cout << v << endl; + }, + mySensor.Samples); TransactionStatus status; diff --git a/include/react/Group.h b/include/react/Group.h index fc5e49f1..ca2c02f0 100644 --- a/include/react/Group.h +++ b/include/react/Group.h @@ -28,8 +28,6 @@ struct CtorTag { }; /*****************************************/ REACT_BEGIN /*****************************************/ - - #if 0 /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -84,54 +82,6 @@ class TransactionStatus friend void AsyncTransaction(TransactionFlagsT flags, TransactionStatus& status, F&& func); }; -/////////////////////////////////////////////////////////////////////////////////////////////// -/// AsyncTransaction -/////////////////////////////////////////////////////////////////////////////////////////////// -template -void AsyncTransaction(F&& func) -{ - static_assert(D::is_concurrent, - "AsyncTransaction: Domain does not support concurrent input."); - - using REACT_IMPL::DomainSpecificInputManager; - DomainSpecificInputManager::Instance() - .AsyncTransaction(0, nullptr, std::forward(func)); -} - -template -void AsyncTransaction(TransactionFlags flags, F&& func) -{ - static_assert(D::is_concurrent, - "AsyncTransaction: Domain does not support concurrent input."); - - using REACT_IMPL::DomainSpecificInputManager; - DomainSpecificInputManager::Instance() - .AsyncTransaction(flags, nullptr, std::forward(func)); -} - -template -void AsyncTransaction(TransactionStatus& status, F&& func) -{ - static_assert(D::is_concurrent, - "AsyncTransaction: Domain does not support concurrent input."); - - using REACT_IMPL::DomainSpecificInputManager; - - DomainSpecificInputManager::Instance() - .AsyncTransaction(0, status.statePtr_, std::forward(func)); -} - -template -void AsyncTransaction(TransactionFlagsT flags, TransactionStatus& status, F&& func) -{ - static_assert(D::is_concurrent, - "AsyncTransaction: Domain does not support concurrent input."); - - using REACT_IMPL::DomainSpecificInputManager; - DomainSpecificInputManager::Instance() - .AsyncTransaction(flags, status.statePtr_, std::forward(func)); -} - #endif /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -146,12 +96,6 @@ class ReactiveGroupBase graphPtr_( std::make_shared() ) { } - ReactiveGroupBase(const ReactiveGroupBase&) = default; - ReactiveGroupBase& operator=(const ReactiveGroupBase&) = default; - - ReactiveGroupBase(ReactiveGroupBase&& other) = default; - ReactiveGroupBase& operator=(ReactiveGroupBase&& other) = default; - ~ReactiveGroupBase() = default; template @@ -167,6 +111,12 @@ class ReactiveGroupBase { graphPtr_->EnqueueTransaction(flags, std::forward(func)); } protected: + ReactiveGroupBase(const ReactiveGroupBase&) = default; + ReactiveGroupBase& operator=(const ReactiveGroupBase&) = default; + + ReactiveGroupBase(ReactiveGroupBase&& other) = default; + ReactiveGroupBase& operator=(ReactiveGroupBase&& other) = default; + auto GraphPtr() -> std::shared_ptr& { return graphPtr_; } diff --git a/include/react/detail/IReactiveGraph.h b/include/react/detail/IReactiveGraph.h index 2ac8f291..85f3d253 100644 --- a/include/react/detail/IReactiveGraph.h +++ b/include/react/detail/IReactiveGraph.h @@ -59,7 +59,7 @@ struct IReactiveNode virtual const char* GetNodeType() const = 0; - virtual UpdateResult Update(TurnId turnId, int successorCount) = 0; + virtual UpdateResult Update(TurnId turnId, size_t successorCount) = 0; virtual int GetDependencyCount() const = 0; }; diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index 62ff5c9a..274581fd 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -101,7 +101,7 @@ class IterateNode : public SignalNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { S newValue = func_(EventRange( events_->Events() ), this->Value()); @@ -153,7 +153,7 @@ class IterateByRefNode : public SignalNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { func_(EventRange( events_->Events() ), this->Value()); @@ -201,7 +201,7 @@ class SyncedIterateNode : public SignalNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { // Updates might be triggered even if only sync nodes changed. Ignore those. if (events_->Events().empty()) @@ -267,7 +267,7 @@ class SyncedIterateByRefNode : public SignalNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { // Updates might be triggered even if only sync nodes changed. Ignore those. if (events_->Events().empty()) @@ -281,7 +281,6 @@ class SyncedIterateByRefNode : public SignalNode syncHolder_); events_->DecrementPendingSuccessorCount(); - this->SetPendingSuccessorCount(successorCount); return UpdateResult::changed; } @@ -325,7 +324,7 @@ class HoldNode : public SignalNode virtual const char* GetNodeType() const override { return "HoldNode"; } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { bool changed = false; @@ -379,7 +378,7 @@ class SnapshotNode : public SignalNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { bool changed = false; @@ -434,7 +433,7 @@ class MonitorNode : public EventStreamNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { this->Events().push_back(target_->Value()); @@ -477,7 +476,7 @@ class PulseNode : public EventStreamNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { for (size_t i=0; iEvents().size(); i++) this->Events().push_back(target_->Value()); diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 7459af80..374038fb 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -98,7 +98,7 @@ class EventStreamNode : public NodeBase { return events_; } - void SetPendingSuccessorCount(int count) + void SetPendingSuccessorCount(size_t count) { if (count == 0) { @@ -127,7 +127,7 @@ class EventStreamNode : public NodeBase private: StorageType events_; - std::atomic pendingSuccessorCount_ = 0; + std::atomic pendingSuccessorCount_ = 0; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -150,7 +150,7 @@ class EventSourceNode : public EventStreamNode virtual int GetDependencyCount() const override { return 0; } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { if (! this->Events().empty()) { @@ -189,7 +189,7 @@ class EventMergeNode : public EventStreamNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(MergeFromDep(deps)); }, depHolder_); @@ -254,7 +254,7 @@ class EventSlotNode : public EventStreamNode virtual int GetDependencyCount() const override { return 2; } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { this->Events().insert(this->Events().end(), slotInput_.dep->Events().begin(), slotInput_.dep->Events().end()); @@ -291,7 +291,7 @@ class EventSlotNode : public EventStreamNode virtual int GetDependencyCount() const override { return 0; } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { if (dep != newDep) { @@ -342,7 +342,7 @@ class EventProcessingNode : public EventStreamNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { func_(EventRange( dep_->Events() ), std::back_inserter(this->Events())); @@ -397,7 +397,7 @@ class SyncedEventProcessingNode : public EventStreamNode this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { // Updates might be triggered even if only sync nodes changed. Ignore those. if (dep_->Events().empty()) @@ -458,7 +458,7 @@ class EventJoinNode : public EventStreamNode> this->UnregisterMe(); } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { // Move events into buffers. apply([this, turnId] (Slot& ... slots) { REACT_EXPAND_PACK(FetchBuffer(turnId, slots)); }, slots_); @@ -563,7 +563,7 @@ class EventLinkNode : public EventStreamNode virtual int GetDependencyCount() const override { return 1; } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { this->SetPendingSuccessorCount(successorCount); return UpdateResult::changed; @@ -596,7 +596,7 @@ class EventLinkNode : public EventStreamNode virtual int GetDependencyCount() const override { return 1; } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { return UpdateResult::changed; } diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index d4f3968a..d383d341 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -69,7 +69,7 @@ class SignalObserverNode : public ObserverNode virtual int GetDependencyCount() const override { return sizeof...(TDeps); } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { apply([this] (const auto& ... deps) { this->func_(deps->Value() ...); }, depHolder_); return UpdateResult::unchanged; @@ -110,7 +110,7 @@ class EventObserverNode : public ObserverNode virtual int GetDependencyCount() const override { return 1; } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { func_(EventRange( subject_->Events() )); subject_->DecrementPendingSuccessorCount(); @@ -155,7 +155,7 @@ class SyncedEventObserverNode : public ObserverNode virtual int GetDependencyCount() const override { return 1 + sizeof...(TSyncs); } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { // Updates might be triggered even if only sync nodes changed. Ignore those. if (subject_->Events().empty()) diff --git a/include/react/detail/graph/PropagationST.h b/include/react/detail/graph/PropagationST.h index 4b8f2441..dbce7777 100644 --- a/include/react/detail/graph/PropagationST.h +++ b/include/react/detail/graph/PropagationST.h @@ -227,7 +227,7 @@ void ReactiveGraph::AddInput(NodeId nodeId, F&& inputCallback) } else { - int successorCount = node.successors.size(); + size_t successorCount = node.successors.size(); // Update the node. This applies the input buffer to the node value and checks if it changed. if (nodePtr->Update(0, successorCount) == UpdateResult::changed) @@ -258,7 +258,7 @@ void ReactiveGraph::DoTransaction(F&& transactionCallback) auto& node = nodeData_[nodeId]; auto* nodePtr = node.nodePtr; - int successorCount = node.successors.size(); + size_t successorCount = node.successors.size(); if (nodePtr->Update(0, successorCount) == UpdateResult::changed) { @@ -310,9 +310,9 @@ void ReactiveGraph::Propagate() continue; } - int successorCount = node.successors.size(); + size_t successorCount = node.successors.size(); - if (nodePtr->Update(0, successorCount) == UpdateResult::changed) + if (nodePtr->Update(0u, successorCount) == UpdateResult::changed) { ScheduleSuccessors(node); } diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index cb66531d..4dbd8574 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -87,7 +87,7 @@ class VarSignalNode : public SignalNode virtual int GetDependencyCount() const override { return 0; } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { if (isInputAdded_) { @@ -182,7 +182,7 @@ class SignalFuncNode : public SignalNode virtual int GetDependencyCount() const override { return sizeof...(TDeps); } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { bool changed = false; @@ -239,7 +239,7 @@ class SignalSlotNode : public SignalNode virtual int GetDependencyCount() const override { return 2; } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { if (! (this->Value() == slotInput_.dep->Value())) { @@ -272,7 +272,7 @@ class SignalSlotNode : public SignalNode virtual int GetDependencyCount() const override { return 0; } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { if (dep != newDep) { @@ -328,7 +328,7 @@ class SignalLinkNode : public SignalNode virtual int GetDependencyCount() const override { return 1; } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { return UpdateResult::changed; } @@ -363,7 +363,7 @@ class SignalLinkNode : public SignalNode virtual int GetDependencyCount() const override { return 1; } - virtual UpdateResult Update(TurnId turnId, int successorCount) override + virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { return UpdateResult::changed; } From 933016dce85c34ff712529bf2cac94a180e08341 Mon Sep 17 00:00:00 2001 From: schlangster Date: Fri, 16 Dec 2016 18:04:21 +0100 Subject: [PATCH 231/266] wip stuff --- benchmarks/src/BenchmarkBase.h | 13 +- benchmarks/src/BenchmarkFanout.h | 7 +- benchmarks/src/BenchmarkGrid.h | 33 ++-- benchmarks/src/BenchmarkLifeSim.h | 10 +- benchmarks/src/BenchmarkRandom.h | 9 +- benchmarks/src/BenchmarkSequence.h | 7 +- benchmarks/src/Main.cpp | 52 +++--- examples/src/Main.cpp | 191 ++++++++++++++++++++- include/react/Signal.h | 41 +++-- include/react/detail/graph/PropagationST.h | 2 +- 10 files changed, 269 insertions(+), 96 deletions(-) diff --git a/benchmarks/src/BenchmarkBase.h b/benchmarks/src/BenchmarkBase.h index 528a8489..7df36bbe 100644 --- a/benchmarks/src/BenchmarkBase.h +++ b/benchmarks/src/BenchmarkBase.h @@ -66,9 +66,6 @@ template > void RunBenchmark(std::ostream& logfile, TBenchmark b, const TParams& params) { - std::cout << "Engine: " << typeid(typename TBenchmark::Domain::Policy::Engine).name() << std::endl << std::endl; - logfile << "Engine: " << typeid(typename TBenchmark::Domain::Policy::Engine).name() << std::endl << std::endl; - double sum = 0; double min = DBL_MAX; double max = DBL_MIN; @@ -77,7 +74,7 @@ void RunBenchmark(std::ostream& logfile, TBenchmark b, const TParams& params) { double r = b.Run(params); std::cout << "\tRun " << i << ": " << r << std::endl; - logfile << "\tRun " << i << ": " << r << std::endl; + logfile << "\tRun " << i << ": " << r << std::endl; sum += r; @@ -105,7 +102,7 @@ void RunBenchmark(std::ostream& logfile, TBenchmark b, const TParams& params) template < int RUN_COUNT, - template class TBenchmark, + typename TBenchmark, typename TParams > void RunBenchmarkClass(const char* name, std::ostream& out, const TParams& params) @@ -118,8 +115,8 @@ void RunBenchmarkClass(const char* name, std::ostream& out, const TParams& param params.Print(out); out << ") =====" << std::endl << std::endl; - REACT_EXPAND_PACK(RunBenchmark(out, TBenchmark(), params)); + RunBenchmark(out, TBenchmark(), params); } -#define RUN_BENCHMARK(out, runCount, benchmarkClass, params, ...) \ - RunBenchmarkClass(#benchmarkClass, out, params) +#define RUN_BENCHMARK(out, runCount, benchmarkClass, params) \ + RunBenchmarkClass(#benchmarkClass, out, params) diff --git a/benchmarks/src/BenchmarkFanout.h b/benchmarks/src/BenchmarkFanout.h index 6feaf567..672b5f7c 100644 --- a/benchmarks/src/BenchmarkFanout.h +++ b/benchmarks/src/BenchmarkFanout.h @@ -16,7 +16,7 @@ #include "BenchmarkBase.h" #include "react/Signal.h" - +/* using namespace react; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -42,8 +42,7 @@ struct BenchmarkParams_Fanout const int Delay; }; -template -struct Benchmark_Fanout : public BenchmarkBase +struct Benchmark_Fanout { double Run(const BenchmarkParams_Fanout& params) { @@ -83,4 +82,6 @@ struct Benchmark_Fanout : public BenchmarkBase } }; +*/ + #endif // CPP_REACT_BENCHMARK_FANOUT_H \ No newline at end of file diff --git a/benchmarks/src/BenchmarkGrid.h b/benchmarks/src/BenchmarkGrid.h index 29afe312..6b691cc3 100644 --- a/benchmarks/src/BenchmarkGrid.h +++ b/benchmarks/src/BenchmarkGrid.h @@ -40,7 +40,7 @@ class GridGraphGenerator std::vector widths; - void Generate() + void Generate(const ReactiveGroupBase& group) { assert(inputSignals.size() >= 1); assert(widths.size() >= 1); @@ -48,8 +48,8 @@ class GridGraphGenerator SignalVectType buf1 = std::move(inputSignals); SignalVectType buf2; - SignalVectT* curBuf = &buf1; - SignalVectT* nextBuf = &buf2; + SignalVectType* curBuf = &buf1; + SignalVectType* nextBuf = &buf2; size_t curWidth = inputSignals.size(); @@ -70,36 +70,36 @@ class GridGraphGenerator if (shouldGrow) { - auto s = (*l) ->* Function1; - nextBuf->push_back(s); + auto s = SignalType{ group, function1, *l }; + nextBuf->push_back(std::move(s)); } while (r != curBuf->end()) { - auto s = (*l,*r) ->* Function2; - nextBuf->push_back(s); - nodeCount++; + auto s = SignalType{ group, function2, *l, *r }; + nextBuf->push_back(std::move(s)); + ++nodeCount; ++l; ++r; } if (shouldGrow) { - auto s = (*l) ->* Function1; - nextBuf->push_back(s); - nodeCount++; + auto s = SignalType{ group, function1, *l }; + nextBuf->push_back(std::move(s)); + ++nodeCount; } curBuf->clear(); // Swap buffer pointers - SignalVectT* t = curBuf; + SignalVectType* t = curBuf; curBuf = nextBuf; nextBuf = t; if (shouldGrow) - curWidth++; + ++curWidth; else - curWidth--; + --curWidth; } } @@ -135,10 +135,11 @@ struct Benchmark_Grid double Run(const BenchmarkParams_Grid& params, const ReactiveGroupBase& group) { VarSignal in{ group, 1 }; + Signal in2 = in; GridGraphGenerator generator; - generator.inputSignals.push_back(in); + generator.inputSignals.push_back(in2); generator.widths.push_back(params.N); generator.widths.push_back(1); @@ -146,7 +147,7 @@ struct Benchmark_Grid generator.function1 = [] (int a) { return a; }; generator.function2 = [] (int a, int b) { return a + b; }; - generator.Generate(); + generator.Generate(group); auto t0 = tbb::tick_count::now(); for (int i=0; i #include #include @@ -318,8 +318,10 @@ struct Benchmark_LifeSim : public BenchmarkBase logfile.open("log.txt"); D::Log().Write(logfile); - logfile.close()*/; + logfile.close()*//*; - return d; + //return d; } -}; \ No newline at end of file +}; + +*/ \ No newline at end of file diff --git a/benchmarks/src/BenchmarkRandom.h b/benchmarks/src/BenchmarkRandom.h index bf3ba9d1..e3ffe9ef 100644 --- a/benchmarks/src/BenchmarkRandom.h +++ b/benchmarks/src/BenchmarkRandom.h @@ -14,11 +14,11 @@ #include "BenchmarkBase.h" #include "react/common/Types.h" -#include "react/Domain.h" +#include "react/Group.h" #include "react/Signal.h" using namespace react; - +/* /////////////////////////////////////////////////////////////////////////////////////////////////// /// DiamondGraphGenerator /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -215,8 +215,7 @@ struct BenchmarkParams_Random const int SlowSeed; }; -template -struct Benchmark_Random : public BenchmarkBase +struct Benchmark_Random { double Run(const BenchmarkParams_Random& params) { @@ -321,4 +320,4 @@ struct Benchmark_Random : public BenchmarkBase return d; } -}; \ No newline at end of file +};*/ \ No newline at end of file diff --git a/benchmarks/src/BenchmarkSequence.h b/benchmarks/src/BenchmarkSequence.h index 5e96d3c7..bf87bc41 100644 --- a/benchmarks/src/BenchmarkSequence.h +++ b/benchmarks/src/BenchmarkSequence.h @@ -14,6 +14,8 @@ #include "react/Signal.h" +/* + using namespace react; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -39,8 +41,7 @@ struct BenchmarkParams_Sequence const int Delay; }; -template -struct Benchmark_Sequence : public BenchmarkBase +struct Benchmark_Sequence { double Run(const BenchmarkParams_Sequence& params) { @@ -76,4 +77,4 @@ struct Benchmark_Sequence : public BenchmarkBase return d; } -}; \ No newline at end of file +};*/ \ No newline at end of file diff --git a/benchmarks/src/Main.cpp b/benchmarks/src/Main.cpp index cbf5aaca..2af96fdd 100644 --- a/benchmarks/src/Main.cpp +++ b/benchmarks/src/Main.cpp @@ -27,7 +27,7 @@ namespace { using namespace react; -void runBenchmarkGrid(std::ostream& out) +/*void runBenchmarkGrid(std::ostream& out) { RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(20, 10000), ToposortSTDomain, ToposortDomain, PulsecountDomain); @@ -40,9 +40,9 @@ void runBenchmarkGrid(std::ostream& out) RUN_BENCHMARK(out, 5, Benchmark_Grid, BenchmarkParams_Grid(50, 10000), ToposortSTDomain, ToposortDomain, PulsecountDomain); -} +}*/ -void runBenchmarkRandom(std::ostream& out) +/*void runBenchmarkRandom(std::ostream& out) { const auto w = 20; const auto h = 11; @@ -67,7 +67,7 @@ void runBenchmarkRandom(std::ostream& out) seed1 *= 2; seed2 *= 2; } -} +}*/ void runBenchmarkFanout(std::ostream& out) { @@ -80,14 +80,14 @@ void runBenchmarkFanout(std::ostream& out) //RUN_BENCHMARK(out, 5, Benchmark_Fanout, BenchmarkParams_Fanout(1000, 10000, 0), // ToposortSTDomain, ToposortDomain, ELMDomain, PulsecountDomain, SourceSetDomain); - RUN_BENCHMARK(out, 3, Benchmark_Fanout, BenchmarkParams_Fanout(10, 10, 10), + /* RUN_BENCHMARK(out, 3, Benchmark_Fanout, BenchmarkParams_Fanout(10, 10, 10), ToposortSTDomain, ToposortDomain, PulsecountDomain); RUN_BENCHMARK(out, 3, Benchmark_Fanout, BenchmarkParams_Fanout(100, 10, 10), ToposortSTDomain, ToposortDomain, PulsecountDomain); RUN_BENCHMARK(out, 3, Benchmark_Fanout, BenchmarkParams_Fanout(1000, 10, 10), - ToposortSTDomain, ToposortDomain, PulsecountDomain); + ToposortSTDomain, ToposortDomain, PulsecountDomain);*/ } void runBenchmarkSequence(std::ostream& out) @@ -101,14 +101,14 @@ void runBenchmarkSequence(std::ostream& out) //RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(1000, 10000, 0), // ToposortSTDomain, ToposortDomain, ELMDomain, PulsecountDomain, SourceSetDomain); - RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(10, 10, 10), - ToposortSTDomain, ToposortDomain, PulsecountDomain); + //RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(10, 10, 10), + // ToposortSTDomain, ToposortDomain, PulsecountDomain); - RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(100, 10, 10), - ToposortSTDomain, ToposortDomain, PulsecountDomain); + //RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(100, 10, 10), + // ToposortSTDomain, ToposortDomain, PulsecountDomain); - RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(1000, 10, 10), - ToposortSTDomain, ToposortDomain, PulsecountDomain); + //RUN_BENCHMARK(out, 3, Benchmark_Sequence, BenchmarkParams_Sequence(1000, 10, 10), + // ToposortSTDomain, ToposortDomain, PulsecountDomain); } void runBenchmarkLifeSim(std::ostream& out) @@ -119,8 +119,8 @@ void runBenchmarkLifeSim(std::ostream& out) //RUN_BENCHMARK(std::cout, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(250, 30, 10000), // SourceSetDomain, PulsecountDomain); - RUN_BENCHMARK(out, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 15, 10000), - ToposortSTDomainConc, ToposortDomainConc, PulsecountDomainConc); + //RUN_BENCHMARK(out, 1, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 15, 10000), + // ToposortSTDomainConc, ToposortDomainConc, PulsecountDomainConc); //RUN_BENCHMARK(out, 3, Benchmark_LifeSim, BenchmarkParams_LifeSim(100, 50, 100), // PulsecountDomain, PulsecountDomain); @@ -130,8 +130,8 @@ void runBenchmarks() { std::ofstream logfile; - std::string path = "Benchmark Results/" + CurrentDateTime() + ".txt"; - logfile.open(path.c_str()); + //std::string path = "Benchmark Results/" + CurrentDateTime() + ".txt"; + //logfile.open(path.c_str()); // === GRID //runBenchmarkGrid(logfile); @@ -148,14 +148,14 @@ void runBenchmarks() //runBenchmarkSequence(logfile); // === LIFESIM - runBenchmarkLifeSim(logfile); + //runBenchmarkLifeSim(logfile); logfile.close(); } void debugBenchmarks() { - using TestDomain = PulsecountDomain; + //using TestDomain = PulsecountDomain; //RUN_BENCHMARK(std::cout, 1, Benchmark_Grid, BenchmarkParams_Grid(30, 1), // TestDomain); @@ -172,8 +172,8 @@ void debugBenchmarks() //RUN_BENCHMARK(std::cout, 1, Benchmark_Random, BenchmarkParams_Random(10, 25, 10, 0, 10, 25, 25, false), // ToposortDomain); - RUN_BENCHMARK(std::cout, 1, Benchmark_Random, BenchmarkParams_Random(40, 11, 2, 0, 1, 40, 40, false, 41556, 21624), - TestDomain); + //RUN_BENCHMARK(std::cout, 1, Benchmark_Random, BenchmarkParams_Random(40, 11, 2, 0, 1, 40, 40, false, 41556, 21624), + // TestDomain); //const auto w = 10; //const auto h = 11; @@ -194,21 +194,11 @@ void debugBenchmarks() //RunBenchmark<1>(Benchmark2()); //RunBenchmark<1>(Benchmark2()); //RunBenchmark<3>(Benchmark2()); - -#ifdef REACT_ENABLE_LOGGING - std::ofstream logfile; - logfile.open("log.txt"); - - TestDomain::Log().Write(logfile); - logfile.close(); -#endif } void profileBenchmark() { - RUN_BENCHMARK(std::cout, 3, Benchmark_Grid, BenchmarkParams_Grid(100, 10000), - //SubtreeDomain); - ToposortSTDomain, ToposortDomain, PulsecountDomain, SubtreeDomain); + RUN_BENCHMARK(std::cout, 3, Benchmark_Grid, BenchmarkParams_Grid(100, 10000)); //RUN_BENCHMARK(std::cout, 1, Benchmark_Grid, BenchmarkParams_Grid(30, 10000), //SourceSetDomain); diff --git a/examples/src/Main.cpp b/examples/src/Main.cpp index e4fe6f5d..47a9494c 100644 --- a/examples/src/Main.cpp +++ b/examples/src/Main.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "react/Signal.h" #include "react/Event.h" @@ -16,6 +17,97 @@ using namespace react; + +template +class GridGraphGenerator +{ +public: + using SignalType = Signal; + + using Func1T = std::function; + using Func2T = std::function; + + using SignalVectType = std::vector; + + SignalVectType inputSignals; + SignalVectType outputSignals; + + Func1T function1; + Func2T function2; + + std::vector widths; + + void Generate(const ReactiveGroupBase& group) + { + assert(inputSignals.size() >= 1); + assert(widths.size() >= 1); + + SignalVectType buf1 = std::move(inputSignals); + SignalVectType buf2; + + SignalVectType* curBuf = &buf1; + SignalVectType* nextBuf = &buf2; + + size_t curWidth = buf1.size(); + + size_t nodeCount = 1; + nodeCount += curWidth; + + for (auto targetWidth : widths) + { + while (curWidth != targetWidth) + { + // Grow or shrink? + bool shouldGrow = targetWidth > curWidth; + + auto l = curBuf->begin(); + auto r = curBuf->begin(); + if (r != curBuf->end()) + ++r; + + if (shouldGrow) + { + auto s = SignalType{ group, function1, *l }; + nextBuf->push_back(std::move(s)); + } + + while (r != curBuf->end()) + { + auto s = SignalType{ group, function2, *l, *r }; + nextBuf->push_back(std::move(s)); + ++nodeCount; + ++l; ++r; + } + + if (shouldGrow) + { + auto s = SignalType{ group, function1, *l }; + nextBuf->push_back(std::move(s)); + ++nodeCount; + } + + curBuf->clear(); + + // Swap buffer pointers + SignalVectType* t = curBuf; + curBuf = nextBuf; + nextBuf = t; + + if (shouldGrow) + ++curWidth; + else + --curWidth; + } + } + + //printf ("NODE COUNT %d\n", nodeCount); + + outputSignals.clear(); + outputSignals.insert(outputSignals.begin(), curBuf->begin(), curBuf->end()); + } +}; + + template T Multiply(T a, T b) { @@ -58,6 +150,16 @@ template bool FilterFunc(T v) return v > 10; } +template T IterFunc1(EventRange evts, T v) +{ + return v + 1; +} + +template T IterFunc2(EventRange evts, T v, T a1, T a2) +{ + return v + 1; +} + int main() { ReactiveGroup<> group; @@ -68,7 +170,7 @@ int main() VarSignal y{ group, 0 }; VarSignal z{ group, 0 }; - Signal area{ group, Multiply, x, y }; + Signal area{ Multiply, x, y }; Signal volume{ Multiply, area, z }; Observer<> areaObs{ PrintArea, area }; @@ -80,7 +182,7 @@ int main() group.DoTransaction([&] { - x <<= 100; + x.Set(100); y <<= 3; y <<= 4; }); @@ -124,6 +226,7 @@ int main() s2.Set(667); } + // Links { ReactiveGroup<> group1; ReactiveGroup<> group2; @@ -144,25 +247,68 @@ int main() ReactiveGroup<> group1; ReactiveGroup<> group2; - VarSignal s1{ group1, 10 }; + VarSignal s1{ group1, 10 }; + VarSignal s2{ group2, 11 }; + + EventSource e1{ group1 }; + EventSource e2{ group2 }; + + auto hold = Hold(group1, 0, e1); + + auto merged = Merge(group2, e1, e2); + + auto joined1 = Join(e1, e2); + auto joined2 = Join(group1, e1, e2); + + Observer<> eventObs1{ PrintEvents, merged }; + Observer<> eventObs2{ group2, PrintSyncedEvents, merged, s1, s2 }; + + e1.Emit(222); + + std::this_thread::sleep_for(std::chrono::seconds(5)); + } + + { + ReactiveGroup<> group1; + ReactiveGroup<> group2; + + VarSignal s1{ group1, 10 }; VarSignal s2{ group2, 11 }; EventSource e1{ group1 }; EventSource e2{ group2 }; - auto hold = Hold(group1, 0, e1); + auto hold1 = Hold(group1, 0, e1); + auto hold2 = Hold(0, e1); + + auto monitor1 = Monitor(group1, s1); + auto monitor2 = Monitor(s1); + + auto snapshot1 = Snapshot(group1, s1, e1); + auto snapshot2 = Snapshot(s1, e1); + + auto pulse1 = Pulse(group1, s1, e1); + auto pulse2 = Pulse(s1, e1); auto merged = Merge(group2, e1, e2); - auto joined = Join(e1, e2); + auto joined1 = Join(e1, e2); auto joined2 = Join(group1, e1, e2); + auto iter1 = Iterate(group, 0, IterFunc1, e1); + auto iter2 = Iterate(0, IterFunc1, e1); + + auto iter3 = Iterate(group, 0, IterFunc2, e1, s1, s2); + auto iter4 = Iterate(0, IterFunc2, e1, s1, s2); + Observer<> eventObs{ PrintEvents, merged }; - Observer<> eventObs2{ group2, PrintSyncedEvents, merged, s1, s2 }; + Observer<> eventObs2{ group2, PrintSyncedEvents, merged, s1, s2 }; e1.Emit(222); std::this_thread::sleep_for(std::chrono::seconds(5)); + + GridGraphGenerator grid; } return 0; @@ -178,7 +324,40 @@ int main() +int main() +{ + ReactiveGroup<> group; + + VarSignal in{ group, 1 }; + Signal in2 = in; + + GridGraphGenerator generator; + + generator.inputSignals.push_back(in2); + + generator.widths.push_back(100); + generator.widths.push_back(1); + + int updateCount = 0; + + generator.function1 = [&] (int a) { ++updateCount; return a; }; + generator.function2 = [&] (int a, int b) { ++updateCount; return a + b; }; + + generator.Generate(group); + + updateCount = 0; + auto t0 = tbb::tick_count::now(); + for (int i = 0; i < 10000; i++) + in <<= 10 + i; + auto t1 = tbb::tick_count::now(); + + double d = (t1 - t0).seconds(); + printf("updateCount %d\n", updateCount); + printf("Time %g\n", d); + + return 0; +} diff --git a/include/react/Signal.h b/include/react/Signal.h index bd5f62df..0bff5126 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -77,13 +77,6 @@ class SignalBase PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...); } - template - auto CreateFuncNode(F&& func, const SignalBase& dep1, const SignalBase& ... deps) -> decltype(auto) - { - using REACT_IMPL::PrivateNodeInterface; - return CreateFuncNode(PrivateNodeInterface::GraphPtr(dep1), std::forward(func), dep1, deps ...); - } - private: std::shared_ptr nodePtr_; @@ -266,16 +259,21 @@ class Signal : public SignalBase Signal(Signal&&) = default; Signal& operator=(Signal&&) = default; + // Construct from VarSignal + Signal(const VarSignalBase& other) : + Signal::SignalBase( other ) + { } + // Construct func signal with explicit group - template - explicit Signal(const ReactiveGroupBase& group, F&& func, const SignalBase& ... deps) : - Signal::SignalBase( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), deps ...) ) + template + explicit Signal(const ReactiveGroupBase& group, F&& func, const SignalBase& dep1, const SignalBase& ... deps) : + Signal::SignalBase( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), dep1, deps ...) ) { } // Construct func signal with implicit group - template - explicit Signal(F&& func, const SignalBase& ... deps) : - Signal::SignalBase( REACT_IMPL::CtorTag{ }, CreateFuncNode(std::forward(func), deps ...) ) + template + explicit Signal(F&& func, const SignalBase& dep1, const SignalBase& ... deps) : + Signal::SignalBase( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(dep1), std::forward(func), dep1, deps ...) ) { } }; @@ -295,6 +293,11 @@ class Signal : public SignalBase Signal(Signal&&) = default; Signal& operator=(Signal&&) = default; + // Construct from VarSignal + Signal(const VarSignalBase& other) : + Signal::SignalBase( other ) + { } + // Construct from unique Signal(Signal&& other) : Signal::SignalBase( std::move(other) ) @@ -305,15 +308,15 @@ class Signal : public SignalBase { Signal::SignalBase::operator=(std::move(other)); return *this; } // Construct func signal with explicit group - template - explicit Signal(const ReactiveGroupBase& group, F&& func, const SignalBase& ... deps) : - Signal::SignalBase( REACT_IMPL::CtorTag{ }, CreateFuncNode(std::forward(func), deps ...) ) + template + explicit Signal(const ReactiveGroupBase& group, F&& func, const SignalBase& dep1, const SignalBase& ... deps) : + Signal::SignalBase( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), dep1, deps ...) ) { } // Construct func signal with implicit group - template - explicit Signal(F&& func, const SignalBase& ... deps) : - Signal::SignalBase( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), deps ...) ) + template + explicit Signal(F&& func, const SignalBase& dep1, const SignalBase& ... deps) : + Signal::SignalBase( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(dep1), std::forward(func), dep1, deps ...) ) { } }; diff --git a/include/react/detail/graph/PropagationST.h b/include/react/detail/graph/PropagationST.h index dbce7777..814fc94c 100644 --- a/include/react/detail/graph/PropagationST.h +++ b/include/react/detail/graph/PropagationST.h @@ -216,7 +216,7 @@ void ReactiveGraph::AddInput(NodeId nodeId, F&& inputCallback) auto& node = nodeData_[nodeId]; auto* nodePtr = node.nodePtr; - // This write to the input buffer of the respective node. + // This writes to the input buffer of the respective node. inputCallback(); if (isTransactionActive_) From 2a802161125aea0eb0864aa54a04e3b0d12f07eb Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 18 Dec 2016 13:45:36 +0100 Subject: [PATCH 232/266] refactor wip --- include/react/API.h | 65 ++---- include/react/Algorithm.h | 24 +-- include/react/Event.h | 360 +++++++++---------------------- include/react/Group.h | 58 +---- include/react/Signal.h | 442 +++++++++----------------------------- 5 files changed, 235 insertions(+), 714 deletions(-) diff --git a/include/react/API.h b/include/react/API.h index b1e86ad1..dbd0e5df 100644 --- a/include/react/API.h +++ b/include/react/API.h @@ -17,12 +17,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// API constants /////////////////////////////////////////////////////////////////////////////////////////////////// -enum OwnershipPolicy -{ - unique, - shared -}; - enum class WeightHint { automatic, @@ -43,57 +37,34 @@ REACT_DEFINE_BITMASK_OPERATORS(TransactionFlags) /////////////////////////////////////////////////////////////////////////////////////////////////// // Groups -template class ReactiveGroup; // Signals template -class SignalBase; - -template -class VarSignalBase; - -template -class SignalSlotBase; - -template -class SignalLinkBase; - -template class Signal; -template +template class VarSignal; -template +template class SignalSlot; -template +template class SignalLink; // Events enum class Token; -template -class EventBase; - -template -class EventSourceBase; - -template -class EventSlotBase; - -template +template class Event; -template +template class EventSource; -template +template class EventSlot; // Observers -template class Observer; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -102,29 +73,29 @@ class Observer; template struct IsSignal { static const bool value = false; }; -template -struct IsSignal> { static const bool value = true; }; +template +struct IsSignal> { static const bool value = true; }; -template -struct IsSignal> { static const bool value = true; }; +template +struct IsSignal> { static const bool value = true; }; template struct IsEvent { static const bool value = false; }; -template -struct IsEvent> { static const bool value = true; }; +template +struct IsEvent> { static const bool value = true; }; -template -struct IsEvent> { static const bool value = true; }; +template +struct IsEvent> { static const bool value = true; }; template struct AsNonInputNode { using type = T; }; -template -struct AsNonInputNode> { using type = Signal; }; +template +struct AsNonInputNode> { using type = Signal; }; -template -struct AsNonInputNode> { using type = Event; }; +template +struct AsNonInputNode> { using type = Event; }; /******************************************/ REACT_END /******************************************/ diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index 6a0037a2..b7e8324d 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -27,7 +27,7 @@ /// Hold - Hold the most recent event in a signal /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Hold(const ReactiveGroupBase& group, T&& initialValue, const EventBase& evnt) -> Signal +auto Hold(const ReactiveGroup& group, T&& initialValue, const Event& evnt) -> Signal { using REACT_IMPL::HoldNode; using REACT_IMPL::PrivateEventLinkNodeInterface; @@ -41,7 +41,7 @@ auto Hold(const ReactiveGroupBase& group, T&& initialValue, const EventBase& } template -auto Hold(T&& initialValue, const EventBase& evnt) -> Signal +auto Hold(T&& initialValue, const Event& evnt) -> Signal { using REACT_IMPL::HoldNode; using REACT_IMPL::PrivateEventLinkNodeInterface; @@ -58,7 +58,7 @@ auto Hold(T&& initialValue, const EventBase& evnt) -> Signal /// Monitor - Emits value changes of target signal /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Monitor(const ReactiveGroupBase& group, const SignalBase& signal) -> Event +auto Monitor(const ReactiveGroup& group, const Signal& signal) -> Event { using REACT_IMPL::MonitorNode; using REACT_IMPL::PrivateSignalLinkNodeInterface; @@ -72,7 +72,7 @@ auto Monitor(const ReactiveGroupBase& group, const SignalBase& signal) -> Eve } template -auto Monitor(const SignalBase& signal) -> Event +auto Monitor(const Signal& signal) -> Event { using REACT_IMPL::MonitorNode; using REACT_IMPL::PrivateSignalLinkNodeInterface; @@ -89,7 +89,7 @@ auto Monitor(const SignalBase& signal) -> Event /// Iterate - Iteratively combines signal value with values from event stream (aka Fold) /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Iterate(const ReactiveGroupBase& group, T&& initialValue, F&& func, const EventBase& evnt) -> Signal +auto Iterate(const ReactiveGroup& group, T&& initialValue, F&& func, const Event& evnt) -> Signal { using REACT_IMPL::IterateNode; using REACT_IMPL::IterateByRefNode; @@ -111,7 +111,7 @@ auto Iterate(const ReactiveGroupBase& group, T&& initialValue, F&& func, const E } template -auto Iterate(T&& initialValue, F&& func, const EventBase& evnt) -> Signal +auto Iterate(T&& initialValue, F&& func, const Event& evnt) -> Signal { using REACT_IMPL::IterateNode; using REACT_IMPL::IterateByRefNode; @@ -136,7 +136,7 @@ auto Iterate(T&& initialValue, F&& func, const EventBase& evnt) -> Signal -auto Iterate(const ReactiveGroupBase& group, T&& initialValue, F&& func, const EventBase& evnt, const SignalBase& ... signals) -> Signal +auto Iterate(const ReactiveGroup& group, T&& initialValue, F&& func, const Event& evnt, const Signal& ... signals) -> Signal { using REACT_IMPL::SyncedIterateNode; using REACT_IMPL::SyncedIterateByRefNode; @@ -160,7 +160,7 @@ auto Iterate(const ReactiveGroupBase& group, T&& initialValue, F&& func, const E } template -auto Iterate(T&& initialValue, F&& func, const EventBase& evnt, const SignalBase& ... signals) -> Signal +auto Iterate(T&& initialValue, F&& func, const Event& evnt, const Signal& ... signals) -> Signal { using REACT_IMPL::SyncedIterateNode; using REACT_IMPL::SyncedIterateByRefNode; @@ -187,7 +187,7 @@ auto Iterate(T&& initialValue, F&& func, const EventBase& evnt, const SignalB /// Snapshot - Sets signal value to value of other signal when event is received /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Snapshot(const ReactiveGroupBase& group, const SignalBase& signal, const EventBase& evnt) -> Signal +auto Snapshot(const ReactiveGroup& group, const Signal& signal, const Event& evnt) -> Signal { using REACT_IMPL::SnapshotNode; using REACT_IMPL::PrivateSignalLinkNodeInterface; @@ -202,7 +202,7 @@ auto Snapshot(const ReactiveGroupBase& group, const SignalBase& signal, const } template -auto Snapshot(const SignalBase& signal, const EventBase& evnt) -> Signal +auto Snapshot(const Signal& signal, const Event& evnt) -> Signal { using REACT_IMPL::SnapshotNode; using REACT_IMPL::PrivateSignalLinkNodeInterface; @@ -220,7 +220,7 @@ auto Snapshot(const SignalBase& signal, const EventBase& evnt) -> Signal -auto Pulse(const ReactiveGroupBase& group, const SignalBase& signal, const EventBase& evnt) -> Event +auto Pulse(const ReactiveGroup& group, const Signal& signal, const Event& evnt) -> Event { using REACT_IMPL::PulseNode; using REACT_IMPL::PrivateSignalLinkNodeInterface; @@ -235,7 +235,7 @@ auto Pulse(const ReactiveGroupBase& group, const SignalBase& signal, const Ev } template -auto Pulse(const SignalBase& signal, const EventBase& evnt) -> Event +auto Pulse(const Signal& signal, const Event& evnt) -> Event { using REACT_IMPL::PulseNode; using REACT_IMPL::PrivateSignalLinkNodeInterface; diff --git a/include/react/Event.h b/include/react/Event.h index bf8c0a05..da63eb35 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -28,18 +28,41 @@ struct PrivateEventLinkNodeInterface; /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Events +/// Event /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EventBase +template +class Event { private: using NodeType = REACT_IMPL::EventStreamNode; public: - // Private node ctor - EventBase(REACT_IMPL::CtorTag, std::shared_ptr&& nodePtr) : - nodePtr_( std::move(nodePtr) ) + Event() = default; + + Event(const Event&) = default; + Event& operator=(const Event&) = default; + + Event(Event&&) = default; + Event& operator=(Event&&) = default; + + template + Event(F&& func, const Event& dep) : + Event::Event( REACT_IMPL::CtorTag{ }, CreateProcessingNode(std::forward(func), dep) ) + { } + + template + Event(const ReactiveGroup& group, F&& func, const Event& dep) : + Event::Event( REACT_IMPL::CtorTag{ }, CreateProcessingNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), dep) ) + { } + + template + Event(F&& func, const Event& dep, const Signal& ... signals) : + Event::Event( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(std::forward(func), dep, signals ...) ) + { } + + template + Event(const ReactiveGroup& group, F&& func, const Event& dep, const Signal& ... signals) : + Event::Event( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), dep, signals ...) ) { } auto Tokenize() const -> decltype(auto) @@ -58,13 +81,10 @@ class EventBase { return REACT::Transform(*this, std::forward(f)); }*/ protected: - EventBase() = default; - - EventBase(const EventBase&) = default; - EventBase& operator=(const EventBase&) = default; - - EventBase(EventBase&&) = default; - EventBase& operator=(EventBase&&) = default; + // Internal node ctor + Event(REACT_IMPL::CtorTag, std::shared_ptr&& nodePtr) : + nodePtr_( std::move(nodePtr) ) + { } auto NodePtr() -> std::shared_ptr& { return nodePtr_; } @@ -73,7 +93,7 @@ class EventBase { return nodePtr_; } template - auto CreateProcessingNode(F&& func, const EventBase& dep) -> decltype(auto) + auto CreateProcessingNode(F&& func, const Event& dep) -> decltype(auto) { using REACT_IMPL::PrivateNodeInterface; using EventNodeType = REACT_IMPL::EventProcessingNode::type>; @@ -82,7 +102,7 @@ class EventBase } template - auto CreateSyncedProcessingNode(F&& func, const EventBase& dep, const SignalBase& ... syncs) -> decltype(auto) + auto CreateSyncedProcessingNode(F&& func, const Event& dep, const Signal& ... syncs) -> decltype(auto) { using REACT_IMPL::GetCheckedGraphPtr; using REACT_IMPL::PrivateNodeInterface; @@ -103,11 +123,22 @@ class EventBase /////////////////////////////////////////////////////////////////////////////////////////////////// /// EventSource /////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EventSourceBase : public EventBase +template +class EventSource : public Event { public: - using EventBase::EventBase; + EventSource() = default; + + EventSource(const EventSource&) = default; + EventSource& operator=(const EventSource&) = default; + + EventSource(EventSource&& other) = default; + EventSource& operator=(EventSource&& other) = default; + + // Construct event source + explicit EventSource(const ReactiveGroup& group) : + EventSource::Event( REACT_IMPL::CtorTag{ }, CreateSourceNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) + { } void Emit(const E& value) { EmitValue(value); } @@ -119,20 +150,14 @@ class EventSourceBase : public EventBase void Emit() { EmitValue(Token::value); } - EventSourceBase& operator<<(const E& value) + EventSource& operator<<(const E& value) { EmitValue(e); return *this; } - EventSourceBase& operator<<(E&& value) + EventSource& operator<<(E&& value) { EmitValue(std::move(value)); return *this; } protected: - EventSourceBase() = default; - - EventSourceBase(const EventSourceBase&) = default; - EventSourceBase& operator=(const EventSourceBase&) = default; - EventSourceBase(EventSourceBase&& other) = default; - EventSourceBase& operator=(EventSourceBase&& other) = default; auto CreateSourceNode(const std::shared_ptr& graphPtr) -> decltype(auto) { @@ -160,27 +185,30 @@ class EventSourceBase : public EventBase /// EventSlotBase /////////////////////////////////////////////////////////////////////////////////////////////////// template -class EventSlotBase : public EventBase +class EventSlot : public Event { public: - using EventBase::EventBase; + EventSlot() = default; - void Set(const EventBase& newInput) - { SetInput(newInput); } + EventSlot(const EventSlot&) = default; + EventSlot& operator=(const EventSlot&) = default; - void operator<<=(const EventBase& newInput) - { SetInput(newInput); } + EventSlot(EventSlot&&) = default; + EventSlot& operator=(EventSlot&&) = default; -protected: - EventSlotBase() = default; + // Construct with value + EventSlot(const ReactiveGroup& group, const Event& input) : + EventSlot::EventSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) + { } - EventSlotBase(const EventSlotBase&) = default; - EventSlotBase& operator=(const EventSlotBase&) = default; + void Set(const Event& newInput) + { SetInput(newInput); } - EventSlotBase(EventSlotBase&&) = default; - EventSlotBase& operator=(EventSlotBase&&) = default; + void operator<<=(const Event& newInput) + { SetInput(newInput); } - auto CreateSlotNode(const std::shared_ptr& graphPtr, const EventBase& input) -> decltype(auto) +protected: + auto CreateSlotNode(const std::shared_ptr& graphPtr, const Event& input) -> decltype(auto) { using REACT_IMPL::PrivateNodeInterface; using REACT_IMPL::PrivateReactiveGroupInterface; @@ -190,7 +218,7 @@ class EventSlotBase : public EventBase } private: - void SetInput(const EventBase& newInput) + void SetInput(const Event& newInput) { using REACT_IMPL::PrivateNodeInterface; using REACT_IMPL::NodeId; @@ -206,24 +234,32 @@ class EventSlotBase : public EventBase }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventLinkBase +/// EventLink /////////////////////////////////////////////////////////////////////////////////////////////////// template -class EventLinkBase : public EventBase +class EventLink : public Event { public: - using EventBase::EventBase; + EventLink() = default; -protected: - EventLinkBase() = default; + EventLink(const EventLink&) = default; + EventLink& operator=(const EventLink&) = default; - EventLinkBase(const EventLinkBase&) = default; - EventLinkBase& operator=(const EventLinkBase&) = default; + EventLink(EventLink&&) = default; + EventLink& operator=(EventLink&&) = default; - EventLinkBase(EventLinkBase&&) = default; - EventLinkBase& operator=(EventLinkBase&&) = default; + // Construct with explicit group + EventLink(const ReactiveGroup& group, const Event& input) : + SignalLink::Signal( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) + { } + + // Construct with implicit group + explicit EventLink(const Event& input) : + SignalLink::Signal( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(input), input) ) + { } - static auto CreateLinkNode(const std::shared_ptr& graphPtr, const EventBase& input) -> decltype(auto) +protected: + static auto CreateLinkNode(const std::shared_ptr& graphPtr, const Event& input) -> decltype(auto) { using REACT_IMPL::PrivateNodeInterface; using REACT_IMPL::PrivateReactiveGroupInterface; @@ -237,207 +273,11 @@ class EventLinkBase : public EventBase friend struct REACT_IMPL::PrivateEventLinkNodeInterface; }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Event -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Event : public EventBase -{ -public: - using EventBase::EventBase; - - using ValueType = E; - - Event() = delete; - - Event(const Event&) = delete; - Event& operator=(const Event&) = delete; - - Event(Event&&) = default; - Event& operator=(Event&&) = default; - - template - Event(F&& func, const EventBase& dep) : - Event::EventBase( REACT_IMPL::CtorTag{ }, CreateProcessingNode(std::forward(func), dep) ) - { } - - template - Event(const ReactiveGroupBase& group, F&& func, const EventBase& dep) : - Event::EventBase( REACT_IMPL::CtorTag{ }, CreateProcessingNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), dep) ) - { } - - template - Event(F&& func, const EventBase& dep, const SignalBase& ... signals) : - Event::EventBase( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(std::forward(func), dep, signals ...) ) - { } - - template - Event(const ReactiveGroupBase& group, F&& func, const EventBase& dep, const SignalBase& ... signals) : - Event::EventBase( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), dep, signals ...) ) - { } -}; - -template -class Event : public EventBase -{ -public: - using EventBase::EventBase; - - using ValueType = E; - - Event() = delete; - - Event(const Event&) = default; - Event& operator=(const Event&) = default; - - Event(Event&&) = default; - Event& operator=(Event&&) = default; - - Event(Event&& other) : - Event::EventBase( std::move(other) ) - { } - - Event& operator=(Event&& other) - { Event::EventBase::operator=(std::move(other)); return *this; } - - template - Event(F&& func, const EventBase& dep) : - Event::EventBase( REACT_IMPL::CtorTag{ }, CreateProcessingNode(std::forward(func), dep) ) - { } - - template - Event(const ReactiveGroupBase& group, F&& func, const EventBase& dep) : - Event::EventBase( REACT_IMPL::CtorTag{ }, CreateProcessingNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), dep) ) - { } - - template - Event(F&& func, const EventBase& dep, const SignalBase& ... signals) : - Event::EventBase( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(std::forward(func), dep, signals ...) ) - { } - - template - Event(const ReactiveGroupBase& group, F&& func, const EventBase& dep, const SignalBase& ... signals) : - Event::EventBase( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), dep, signals ...) ) - { } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventSource -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EventSource : public EventSourceBase -{ -public: - using EventSourceBase::EventSourceBase; - - using ValueType = E; - - EventSource() = delete; - - EventSource(const EventSource&) = delete; - EventSource& operator=(const EventSource&) = delete; - - EventSource(EventSource&&) = default; - EventSource& operator=(EventSource&&) = default; - - // Construct event source - explicit EventSource(const ReactiveGroupBase& group) : - EventSource::EventSourceBase( REACT_IMPL::CtorTag{ }, CreateSourceNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) - { } -}; - -template -class EventSource : public EventSourceBase -{ -public: - using EventSourceBase::EventSourceBase; - - using ValueType = E; - - EventSource() = delete; - - EventSource(const EventSource&) = default; - EventSource& operator=(const EventSource&) = default; - - EventSource(EventSource&&) = default; - EventSource& operator=(EventSource&&) = default; - - // Construct event source - explicit EventSource(const ReactiveGroupBase& group) : - EventSource::EventSourceBase( REACT_IMPL::CtorTag{ }, CreateSourceNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) - { } - - // Construct from unique - EventSource(EventSource&& other) : - EventSource::EventSourceBase( std::move(other) ) - { } - - // Assign from unique - EventSource& operator=(EventSource&& other) - { EventSource::EventSourceBase::operator=(std::move(other)); return *this; } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventSlot -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EventSlot : public EventSlotBase -{ -public: - using EventSlotBase::EventSlotBase; - - using ValueType = E; - - EventSlot() = delete; - - EventSlot(const EventSlot&) = delete; - EventSlot& operator=(const EventSlot&) = delete; - - EventSlot(EventSlot&&) = default; - EventSlot& operator=(EventSlot&&) = default; - - // Construct with value - EventSlot(const ReactiveGroupBase& group, const EventBase& input) : - EventSlot::EventSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) - { } -}; - -template -class EventSlot : public EventSlotBase -{ -public: - using EventSlotBase::EventSlotBase; - - using ValueType = E; - - EventSlot() = delete; - - EventSlot(const EventSlot&) = default; - EventSlot& operator=(const EventSlot&) = default; - - EventSlot(EventSlot&&) = default; - EventSlot& operator=(EventSlot&&) = default; - - // Construct from unique - EventSlot(EventSlot&& other) : - EventSlot::EventSlotBase( std::move(other) ) - { } - - // Assign from unique - EventSlot& operator=(EventSlot&& other) - { EventSlot::EventSlotBase::operator=(std::move(other)); return *this; } - - // Construct with value - EventSlot(const ReactiveGroupBase& group, const SignalBase& input) : - EventSlot::EventSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) - { } -}; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Merge /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Merge(const ReactiveGroupBase& group, const EventBase& dep1, const EventBase& ... deps) -> decltype(auto) +auto Merge(const ReactiveGroup& group, const Event& dep1, const Event& ... deps) -> decltype(auto) { using REACT_IMPL::EventMergeNode; using REACT_IMPL::PrivateEventLinkNodeInterface; @@ -459,7 +299,7 @@ auto Merge(const ReactiveGroupBase& group, const EventBase& dep1, const Even } template -auto Merge(const EventBase& dep1, const EventBase& ... deps) -> decltype(auto) +auto Merge(const Event& dep1, const Event& ... deps) -> decltype(auto) { using REACT_IMPL::EventMergeNode; using REACT_IMPL::PrivateEventLinkNodeInterface; @@ -484,7 +324,7 @@ auto Merge(const EventBase& dep1, const EventBase& ... deps) -> decltype /// Filter /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Filter(const ReactiveGroupBase& group, F&& pred, const EventBase& dep) -> Event +auto Filter(const ReactiveGroup& group, F&& pred, const Event& dep) -> Event { auto filterFunc = [capturedPred = std::forward(pred)] (EventRange inRange, EventSink out) { std::copy_if(inRange.begin(), inRange.end(), out, capturedPred); }; @@ -493,7 +333,7 @@ auto Filter(const ReactiveGroupBase& group, F&& pred, const EventBase& dep) - } template -auto Filter(F&& pred, const EventBase& dep) -> Event +auto Filter(F&& pred, const Event& dep) -> Event { auto filterFunc = [capturedPred = std::forward(pred)] (EventRange inRange, EventSink out) { std::copy_if(inRange.begin(), inRange.end(), out, capturedPred); }; @@ -502,7 +342,7 @@ auto Filter(F&& pred, const EventBase& dep) -> Event } template -auto Filter(const ReactiveGroupBase& group, F&& pred, const EventBase& dep, const SignalBase& ... signals) -> Event +auto Filter(const ReactiveGroup& group, F&& pred, const Event& dep, const Signal& ... signals) -> Event { auto filterFunc = [capturedPred = std::forward(pred)] (EventRange inRange, EventSink out, const Us& ... values) { @@ -515,7 +355,7 @@ auto Filter(const ReactiveGroupBase& group, F&& pred, const EventBase& dep, c } template -auto Filter(F&& pred, const EventBase& dep, const SignalBase& ... signals) -> Event +auto Filter(F&& pred, const Event& dep, const Signal& ... signals) -> Event { auto filterFunc = [capturedPred = std::forward(pred)] (EventRange inRange, EventSink out, const Us& ... values) { @@ -531,7 +371,7 @@ auto Filter(F&& pred, const EventBase& dep, const SignalBase& ... signals /// Transform /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Transform(const ReactiveGroupBase& group, F&& op, const EventBase& dep) -> Event +auto Transform(const ReactiveGroup& group, F&& op, const Event& dep) -> Event { auto transformFunc = [capturedOp = std::forward(op)] (EventRange inRange, EventSink out) { std::transform(inRange.begin(), inRange.end(), out, capturedOp); }; @@ -540,7 +380,7 @@ auto Transform(const ReactiveGroupBase& group, F&& op, const EventBase& dep) } template -auto Transform(F&& op, const EventBase& dep) -> Event +auto Transform(F&& op, const Event& dep) -> Event { auto transformFunc = [capturedOp = std::forward(op)] (EventRange inRange, EventSink out) { std::transform(inRange.begin(), inRange.end(), out, capturedOp); }; @@ -549,7 +389,7 @@ auto Transform(F&& op, const EventBase& dep) -> Event } template -auto Transform(const ReactiveGroupBase& group, F&& op, const EventBase& dep, const SignalBase& ... signals) -> Event +auto Transform(const ReactiveGroup& group, F&& op, const Event& dep, const Signal& ... signals) -> Event { auto transformFunc = [capturedOp = std::forward(pred)] (EventRange inRange, EventSink out, const Vs& ... values) { @@ -561,7 +401,7 @@ auto Transform(const ReactiveGroupBase& group, F&& op, const EventBase& dep, } template -auto Transform(F&& op, const EventBase& dep, const SignalBase& ... signals) -> Event +auto Transform(F&& op, const Event& dep, const Signal& ... signals) -> Event { auto transformFunc = [capturedOp = std::forward(pred)] (EventRange inRange, EventSink out, const Vs& ... values) { @@ -587,7 +427,7 @@ auto Flatten(const Signal>& outer) -> Events /// Join /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Join(const ReactiveGroupBase& group, const EventBase& dep1, const EventBase& ... deps) -> Event, unique> +auto Join(const ReactiveGroup& group, const Event& dep1, const Event& ... deps) -> Event> { using REACT_IMPL::EventJoinNode; using REACT_IMPL::PrivateReactiveGroupInterface; @@ -604,7 +444,7 @@ auto Join(const ReactiveGroupBase& group, const EventBase& dep1, const Event } template -auto Join(const EventBase& dep1, const EventBase& ... deps) -> Event, unique> +auto Join(const Event& dep1, const Event& ... deps) -> Event> { using REACT_IMPL::EventJoinNode; using REACT_IMPL::PrivateNodeInterface; @@ -642,7 +482,7 @@ auto Tokenize(T&& source) -> decltype(auto) /***************************************/ REACT_IMPL_BEGIN /**************************************/ template -bool Equals(const EventBase& lhs, const EventBase& rhs) +bool Equals(const Event& lhs, const Event& rhs) { return lhs.Equals(rhs); } @@ -650,7 +490,7 @@ bool Equals(const EventBase& lhs, const EventBase& rhs) struct PrivateEventLinkNodeInterface { template - static auto GetLocalNodePtr(const std::shared_ptr& targetGraph, const EventBase& dep) -> std::shared_ptr> + static auto GetLocalNodePtr(const std::shared_ptr& targetGraph, const Event& dep) -> std::shared_ptr> { const std::shared_ptr& sourceGraph = PrivateNodeInterface::GraphPtr(dep); diff --git a/include/react/Group.h b/include/react/Group.h index ca2c02f0..837a2189 100644 --- a/include/react/Group.h +++ b/include/react/Group.h @@ -87,16 +87,20 @@ class TransactionStatus /////////////////////////////////////////////////////////////////////////////////////////////////// /// ReactiveGroupBase /////////////////////////////////////////////////////////////////////////////////////////////////// -class ReactiveGroupBase +class ReactiveGroup { using GraphType = REACT_IMPL::ReactiveGraph; public: - ReactiveGroupBase() : + ReactiveGroup() : graphPtr_( std::make_shared() ) { } - ~ReactiveGroupBase() = default; + ReactiveGroup(const ReactiveGroup&) = default; + ReactiveGroup& operator=(const ReactiveGroup&) = default; + + ReactiveGroup(ReactiveGroup&& other) = default; + ReactiveGroup& operator=(ReactiveGroup&& other) = default; template void DoTransaction(F&& func) @@ -111,12 +115,6 @@ class ReactiveGroupBase { graphPtr_->EnqueueTransaction(flags, std::forward(func)); } protected: - ReactiveGroupBase(const ReactiveGroupBase&) = default; - ReactiveGroupBase& operator=(const ReactiveGroupBase&) = default; - - ReactiveGroupBase(ReactiveGroupBase&& other) = default; - ReactiveGroupBase& operator=(ReactiveGroupBase&& other) = default; - auto GraphPtr() -> std::shared_ptr& { return graphPtr_; } @@ -129,44 +127,6 @@ class ReactiveGroupBase friend struct REACT_IMPL::PrivateReactiveGroupInterface; }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Signal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template <> -class ReactiveGroup : public ReactiveGroupBase -{ -public: - ReactiveGroup() = default; - - ReactiveGroup(const ReactiveGroup&) = delete; - ReactiveGroup& operator=(const ReactiveGroup&) = delete; - - ReactiveGroup(ReactiveGroup&& other) = default; - ReactiveGroup& operator=(ReactiveGroup&& other) = default; -}; - -template <> -class ReactiveGroup : public ReactiveGroupBase -{ -public: - ReactiveGroup() = default; - - ReactiveGroup(const ReactiveGroup&) = default; - ReactiveGroup& operator=(const ReactiveGroup&) = default; - - ReactiveGroup(ReactiveGroup&& other) = default; - ReactiveGroup& operator=(ReactiveGroup&& other) = default; - - // Construct from unique - ReactiveGroup(ReactiveGroup&& other) : - ReactiveGroup::ReactiveGroupBase( std::move(other) ) - { } - - // Assign from unique - ReactiveGroup& operator=(ReactiveGroup&& other) - { ReactiveGroup::ReactiveGroupBase::operator=(std::move(other)); return *this; } -}; - /******************************************/ REACT_END /******************************************/ /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -192,10 +152,10 @@ struct PrivateNodeInterface struct PrivateReactiveGroupInterface { - static auto GraphPtr(const ReactiveGroupBase& group) -> const std::shared_ptr& + static auto GraphPtr(const ReactiveGroup& group) -> const std::shared_ptr& { return group.GraphPtr(); } - static auto GraphPtr(ReactiveGroupBase& group) -> std::shared_ptr& + static auto GraphPtr(ReactiveGroup& group) -> std::shared_ptr& { return group.GraphPtr(); } }; diff --git a/include/react/Signal.h b/include/react/Signal.h index 0bff5126..786710d3 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -33,31 +33,46 @@ struct PrivateSignalLinkNodeInterface; /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalBase +/// Signal /////////////////////////////////////////////////////////////////////////////////////////////////// template -class SignalBase +class Signal { private: using NodeType = REACT_IMPL::SignalNode; public: - // Private node ctor - SignalBase(REACT_IMPL::CtorTag, std::shared_ptr&& nodePtr) : - nodePtr_( std::move(nodePtr) ) - { } - const S& Value() const { return nodePtr_->Value(); } -protected: - SignalBase() = default; + // Empty signal + Signal() = default; - SignalBase(const SignalBase&) = default; - SignalBase& operator=(const SignalBase&) = default; + // Copy ctor & assignment + Signal(const Signal&) = default; + Signal& operator=(const Signal&) = default; - SignalBase(SignalBase&&) = default; - SignalBase& operator=(SignalBase&&) = default; + // Move ctor & assignment + Signal(Signal&&) = default; + Signal& operator=(Signal&&) = default; + + // Construct func signal with explicit group + template + explicit Signal(const ReactiveGroup& group, F&& func, const Signal& dep1, const Signal& ... deps) : + Signal::Signal( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), dep1, deps ...) ) + { } + + // Construct func signal with implicit group + template + explicit Signal(F&& func, const Signal& dep1, const Signal& ... deps) : + Signal::Signal( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(dep1), std::forward(func), dep1, deps ...) ) + { } + +protected: + // Private node ctor + Signal(REACT_IMPL::CtorTag, std::shared_ptr&& nodePtr) : + nodePtr_( std::move(nodePtr) ) + { } auto NodePtr() -> std::shared_ptr& { return nodePtr_; } @@ -66,7 +81,7 @@ class SignalBase { return nodePtr_; } template - auto CreateFuncNode(const std::shared_ptr& graphPtr, F&& func, const SignalBase& dep1, const SignalBase& ... deps) -> decltype(auto) + auto CreateFuncNode(const std::shared_ptr& graphPtr, F&& func, const Signal& dep1, const Signal& ... deps) -> decltype(auto) { using REACT_IMPL::PrivateSignalLinkNodeInterface; using FuncNodeType = REACT_IMPL::SignalFuncNode::type, T1, Ts ...>; @@ -85,13 +100,32 @@ class SignalBase /////////////////////////////////////////////////////////////////////////////////////////////////// -/// VarSignalBase +/// VarSignal /////////////////////////////////////////////////////////////////////////////////////////////////// template -class VarSignalBase : public SignalBase +class VarSignal : public Signal { public: - using SignalBase::SignalBase; + using Signal::Signal; + + VarSignal() = default; + + VarSignal(const VarSignal&) = default; + VarSignal& operator=(const VarSignal&) = default; + + VarSignal(VarSignal&&) = default; + VarSignal& operator=(VarSignal&&) = default; + + // Construct with group + default + explicit VarSignal(const ReactiveGroup& group) : + VarSignal::Signal( REACT_IMPL::CtorTag{ }, CreateVarNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) + { } + + // Construct with group + value + template + VarSignal(const ReactiveGroup& group, T&& value) : + VarSignal::Signal( REACT_IMPL::CtorTag{ }, CreateVarNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(value)) ) + { } void Set(const S& newValue) { SetValue(newValue); } @@ -110,14 +144,6 @@ class VarSignalBase : public SignalBase { ModifyValue(func); } protected: - VarSignalBase() = default; - - VarSignalBase(const VarSignalBase&) = default; - VarSignalBase& operator=(const VarSignalBase&) = default; - - VarSignalBase(VarSignalBase&&) = default; - VarSignalBase& operator=(VarSignalBase&&) = default; - static auto CreateVarNode(const std::shared_ptr& graphPtr) -> decltype(auto) { using VarNodeType = REACT_IMPL::VarSignalNode; @@ -165,27 +191,38 @@ class VarSignalBase : public SignalBase /// SignalSlotBase /////////////////////////////////////////////////////////////////////////////////////////////////// template -class SignalSlotBase : public SignalBase +class SignalSlot : public Signal { public: - using SignalBase::SignalBase; + SignalSlot(const SignalSlot&) = default; + SignalSlot& operator=(const SignalSlot&) = default; - void Set(const SignalBase& newInput) - { SetInput(newInput); } + SignalSlot(SignalSlot&&) = default; + SignalSlot& operator=(SignalSlot&&) = default; - void operator<<=(const SignalBase& newInput) - { SetInput(newInput); } + // Construct with group + default + explicit SignalSlot(const ReactiveGroup& group) : + SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) + { } -protected: - SignalSlotBase() = default; + // Construct with group + value + SignalSlot(const ReactiveGroup& group, const Signal& input) : + SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) + { } + + // Construct with value + explicit SignalSlot(const Signal& input) : + SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(input), input) ) + { } - SignalSlotBase(const SignalSlotBase&) = default; - SignalSlotBase& operator=(const SignalSlotBase&) = default; + void Set(const Signal& newInput) + { SetInput(newInput); } - SignalSlotBase(SignalSlotBase&&) = default; - SignalSlotBase& operator=(SignalSlotBase&&) = default; + void operator<<=(const Signal& newInput) + { SetInput(newInput); } - static auto CreateSlotNode(const std::shared_ptr& graphPtr, const SignalBase& input) -> decltype(auto) +protected: + static auto CreateSlotNode(const std::shared_ptr& graphPtr, const Signal& input) -> decltype(auto) { using REACT_IMPL::PrivateNodeInterface; using SlotNodeType = REACT_IMPL::SignalSlotNode; @@ -194,7 +231,7 @@ class SignalSlotBase : public SignalBase } private: - void SetInput(const SignalBase& newInput) + void SetInput(const Signal& newInput) { using REACT_IMPL::PrivateNodeInterface; using REACT_IMPL::NodeId; @@ -209,24 +246,32 @@ class SignalSlotBase : public SignalBase }; /////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalLinkBase +/// SignalLink /////////////////////////////////////////////////////////////////////////////////////////////////// template -class SignalLinkBase : public SignalBase +class SignalLink : public Signal { public: - using SignalBase::SignalBase; + SignalLink() = default; -protected: - SignalLinkBase() = default; + SignalLink(const SignalLink&) = default; + SignalLink& operator=(const SignalLink&) = default; - SignalLinkBase(const SignalLinkBase&) = default; - SignalLinkBase& operator=(const SignalLinkBase&) = default; + SignalLink(SignalLink&&) = default; + SignalLink& operator=(SignalLink&&) = default; - SignalLinkBase(SignalLinkBase&&) = default; - SignalLinkBase& operator=(SignalLinkBase&&) = default; + // Construct with explicit group + SignalLink(const ReactiveGroup& group, const Signal& input) : + SignalLink::Signal( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) + { } + + // Construct with implicit group + explicit SignalLink(const Signal& input) : + SignalLink::Signal( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(input), input) ) + { } - static auto CreateLinkNode(const std::shared_ptr& graphPtr, const SignalBase& input) -> decltype(auto) +protected: + static auto CreateLinkNode(const std::shared_ptr& graphPtr, const Signal& input) -> decltype(auto) { using REACT_IMPL::PrivateNodeInterface; using REACT_IMPL::PrivateReactiveGroupInterface; @@ -240,307 +285,12 @@ class SignalLinkBase : public SignalBase friend struct REACT_IMPL::PrivateSignalLinkNodeInterface; }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Signal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class Signal : public SignalBase -{ -public: - using SignalBase::SignalBase; - - using ValueType = S; - - Signal() = delete; - - Signal(const Signal&) = delete; - Signal& operator=(const Signal&) = delete; - - Signal(Signal&&) = default; - Signal& operator=(Signal&&) = default; - - // Construct from VarSignal - Signal(const VarSignalBase& other) : - Signal::SignalBase( other ) - { } - - // Construct func signal with explicit group - template - explicit Signal(const ReactiveGroupBase& group, F&& func, const SignalBase& dep1, const SignalBase& ... deps) : - Signal::SignalBase( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), dep1, deps ...) ) - { } - - // Construct func signal with implicit group - template - explicit Signal(F&& func, const SignalBase& dep1, const SignalBase& ... deps) : - Signal::SignalBase( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(dep1), std::forward(func), dep1, deps ...) ) - { } -}; - -template -class Signal : public SignalBase -{ -public: - using SignalBase::SignalBase; - - using ValueType = S; - - Signal() = delete; - - Signal(const Signal&) = default; - Signal& operator=(const Signal&) = default; - - Signal(Signal&&) = default; - Signal& operator=(Signal&&) = default; - - // Construct from VarSignal - Signal(const VarSignalBase& other) : - Signal::SignalBase( other ) - { } - - // Construct from unique - Signal(Signal&& other) : - Signal::SignalBase( std::move(other) ) - { } - - // Assign from unique - Signal& operator=(Signal&& other) - { Signal::SignalBase::operator=(std::move(other)); return *this; } - - // Construct func signal with explicit group - template - explicit Signal(const ReactiveGroupBase& group, F&& func, const SignalBase& dep1, const SignalBase& ... deps) : - Signal::SignalBase( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), dep1, deps ...) ) - { } - - // Construct func signal with implicit group - template - explicit Signal(F&& func, const SignalBase& dep1, const SignalBase& ... deps) : - Signal::SignalBase( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(dep1), std::forward(func), dep1, deps ...) ) - { } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// VarSignal -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class VarSignal : public VarSignalBase -{ -public: - using VarSignalBase::VarSignalBase; - - using ValueType = S; - - VarSignal() = delete; - - VarSignal(const VarSignal&) = delete; - VarSignal& operator=(const VarSignal&) = delete; - - VarSignal(VarSignal&&) = default; - VarSignal& operator=(VarSignal&&) = default; - - // Construct with group + default - explicit VarSignal(const ReactiveGroupBase& group) : - VarSignal::VarSignalBase( REACT_IMPL::CtorTag{ }, CreateVarNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) - { } - - // Construct with group + value - template - VarSignal(const ReactiveGroupBase& group, T&& value) : - VarSignal::VarSignalBase( REACT_IMPL::CtorTag{ }, CreateVarNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(value)) ) - { } -}; - -template -class VarSignal : public VarSignalBase -{ -public: - using VarSignalBase::VarSignalBase; - - using ValueType = S; - - VarSignal() = delete; - - VarSignal(const VarSignal&) = default; - VarSignal& operator=(const VarSignal&) = default; - - VarSignal(VarSignal&&) = default; - VarSignal& operator=(VarSignal&&) = default; - - // Construct from unique - VarSignal(VarSignal&& other) : - VarSignal::VarSignalBase( std::move(other) ) - { } - - // Assign from unique - VarSignal& operator=(VarSignal&& other) - { VarSignal::VarSignalBase::operator=(std::move(other)); return *this; } - - // Construct with default - explicit VarSignal(const ReactiveGroupBase& group) : - VarSignal::VarSignalBase( REACT_IMPL::CtorTag{ }, CreateVarNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) - { } - - // Construct with value - template - VarSignal(const ReactiveGroupBase& group, T&& value) : - VarSignal::VarSignalBase( REACT_IMPL::CtorTag{ }, CreateVarNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(value)) ) - { } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalSlot -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class SignalSlot : public SignalSlotBase -{ -public: - using SignalSlotBase::SignalSlotBase; - - using ValueType = S; - - SignalSlot() = delete; - - SignalSlot(const SignalSlot&) = delete; - SignalSlot& operator=(const SignalSlot&) = delete; - - SignalSlot(SignalSlot&&) = default; - SignalSlot& operator=(SignalSlot&&) = default; - - // Construct with group + default - explicit SignalSlot(const ReactiveGroupBase& group) : - SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) - { } - - // Construct with group + value - SignalSlot(const ReactiveGroupBase& group, const SignalBase& input) : - SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) - { } - - // Construct with value - explicit SignalSlot(const SignalBase& input) : - SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(input), input) ) - { } -}; - -template -class SignalSlot : public SignalSlotBase -{ -public: - using SignalSlotBase::SignalSlotBase; - - using ValueType = S; - - SignalSlot() = delete; - - SignalSlot(const SignalSlot&) = default; - SignalSlot& operator=(const SignalSlot&) = default; - - SignalSlot(SignalSlot&&) = default; - SignalSlot& operator=(SignalSlot&&) = default; - - // Construct from unique - SignalSlot(SignalSlot&& other) : - SignalSlot::SignalSlotBase( std::move(other) ) - { } - - // Assign from unique - SignalSlot& operator=(SignalSlot&& other) - { SignalSlot::SignalSlotBase::operator=(std::move(other)); return *this; } - - // Construct with group + default - explicit SignalSlot(const ReactiveGroupBase& group) : - SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) - { } - - // Construct with group + value - SignalSlot(const ReactiveGroupBase& group, const SignalBase& input) : - SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) - { } - - // Construct with value - explicit SignalSlot(const SignalBase& input) : - SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(input), input) ) - { } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SignalLink -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class SignalLink : public SignalLinkBase -{ -public: - using SignalLinkBase::SignalLinkBase; - - using ValueType = S; - - SignalLink() = delete; - - SignalLink(const SignalLink&) = delete; - SignalLink& operator=(const SignalLink&) = delete; - - SignalLink(SignalLink&&) = default; - SignalLink& operator=(SignalLink&&) = default; - - // Construct with default - explicit SignalLink(const ReactiveGroupBase& group) : - SignalLink::SignalLinkBase( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) - { } - - // Construct with group + value - SignalLink(const ReactiveGroupBase& group, const SignalBase& input) : - SignalLink::SignalLinkBase( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) - { } - - // Construct with value - explicit SignalLink(const SignalBase& input) : - SignalLink::SignalLinkBase( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(input), input) ) - { } -}; - -template -class SignalLink : public SignalLinkBase -{ -public: - using SignalLinkBase::SignalLinkBase; - - using ValueType = S; - - SignalLink() = delete; - - SignalLink(const SignalLink&) = default; - SignalLink& operator=(const SignalLink&) = default; - - SignalLink(SignalLink&&) = default; - SignalLink& operator=(SignalLink&&) = default; - - // Construct from unique - SignalLink(SignalLink&& other) : - SignalLink::SignalLinkBase( std::move(other) ) - { } - - // Assign from unique - SignalLink& operator=(SignalSlot&& other) - { SignalLink::SignalLinkBase::operator=(std::move(other)); return *this; } - - // Construct with default - explicit SignalLink(const ReactiveGroupBase& group) : - SignalLink::SignalLinkBase( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) - { } - - // Construct with value - SignalLink(const ReactiveGroupBase& group, const SignalBase& input) : - SignalLink::SignalLinkBase( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) - { } -}; - /******************************************/ REACT_END /******************************************/ /***************************************/ REACT_IMPL_BEGIN /**************************************/ template -bool Equals(const SignalBase& lhs, const SignalBase& rhs) +bool Equals(const Signal& lhs, const Signal& rhs) { return lhs.Equals(rhs); } @@ -548,7 +298,7 @@ bool Equals(const SignalBase& lhs, const SignalBase& rhs) struct PrivateSignalLinkNodeInterface { template - static auto GetLocalNodePtr(const std::shared_ptr& targetGraph, const SignalBase& sig) -> std::shared_ptr> + static auto GetLocalNodePtr(const std::shared_ptr& targetGraph, const Signal& sig) -> std::shared_ptr> { const std::shared_ptr& sourceGraph = PrivateNodeInterface::GraphPtr(sig); From 6ce444005409811361a8be2270b297881e753b89 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 18 Dec 2016 16:45:44 +0100 Subject: [PATCH 233/266] refactor wip --- examples/src/Main.cpp | 56 ++++++------ include/react/Event.h | 4 +- include/react/Observer.h | 186 ++++++++++++--------------------------- 3 files changed, 84 insertions(+), 162 deletions(-) diff --git a/examples/src/Main.cpp b/examples/src/Main.cpp index 47a9494c..653d9d5e 100644 --- a/examples/src/Main.cpp +++ b/examples/src/Main.cpp @@ -22,7 +22,7 @@ template class GridGraphGenerator { public: - using SignalType = Signal; + using SignalType = Signal; using Func1T = std::function; using Func2T = std::function; @@ -37,7 +37,7 @@ class GridGraphGenerator std::vector widths; - void Generate(const ReactiveGroupBase& group) + void Generate(const ReactiveGroup& group) { assert(inputSignals.size() >= 1); assert(widths.size() >= 1); @@ -160,9 +160,9 @@ template T IterFunc2(EventRange evts, T v, T a1, T a2) return v + 1; } -int main() +int main2() { - ReactiveGroup<> group; + ReactiveGroup group; { // Signals @@ -173,8 +173,8 @@ int main() Signal area{ Multiply, x, y }; Signal volume{ Multiply, area, z }; - Observer<> areaObs{ PrintArea, area }; - Observer<> volumeObs{ PrintVolume, volume }; + Observer areaObs{ PrintArea, area }; + Observer volumeObs{ PrintVolume, volume }; x.Set(2); // a: 0, v: 0 y.Set(2); // a: 4, v: 0 @@ -198,7 +198,7 @@ int main() Event anyButton = Merge(button1, button2); Event filtered = Filter(FilterFunc, anyButton); - Observer<> eventObs{ PrintEvents, anyButton }; + Observer eventObs{ PrintEvents, anyButton }; button1.Emit(1); button2.Emit(2); @@ -217,7 +217,7 @@ int main() SignalSlot slot{ s1 }; - Observer<> areaObs{ PrintValue, slot }; + Observer areaObs{ PrintValue, slot }; s1.Set(42); @@ -228,15 +228,15 @@ int main() // Links { - ReactiveGroup<> group1; - ReactiveGroup<> group2; + ReactiveGroup group1; + ReactiveGroup group2; VarSignal s1{ group1, 10 }; VarSignal s2{ group2, 11 }; Signal v{ Multiply, s1, s2 }; - Observer<> obs{ PrintValue, v }; + Observer obs{ PrintValue, v }; s1.Set(555); @@ -244,8 +244,8 @@ int main() } { - ReactiveGroup<> group1; - ReactiveGroup<> group2; + ReactiveGroup group1; + ReactiveGroup group2; VarSignal s1{ group1, 10 }; VarSignal s2{ group2, 11 }; @@ -260,8 +260,8 @@ int main() auto joined1 = Join(e1, e2); auto joined2 = Join(group1, e1, e2); - Observer<> eventObs1{ PrintEvents, merged }; - Observer<> eventObs2{ group2, PrintSyncedEvents, merged, s1, s2 }; + Observer eventObs1{ PrintEvents, merged }; + Observer eventObs2{ group2, PrintSyncedEvents, merged, s1, s2 }; e1.Emit(222); @@ -269,8 +269,8 @@ int main() } { - ReactiveGroup<> group1; - ReactiveGroup<> group2; + ReactiveGroup group1; + ReactiveGroup group2; VarSignal s1{ group1, 10 }; VarSignal s2{ group2, 11 }; @@ -301,8 +301,8 @@ int main() auto iter3 = Iterate(group, 0, IterFunc2, e1, s1, s2); auto iter4 = Iterate(0, IterFunc2, e1, s1, s2); - Observer<> eventObs{ PrintEvents, merged }; - Observer<> eventObs2{ group2, PrintSyncedEvents, merged, s1, s2 }; + Observer eventObs{ PrintEvents, merged }; + Observer eventObs2{ group2, PrintSyncedEvents, merged, s1, s2 }; e1.Emit(222); @@ -326,10 +326,10 @@ int main() int main() { - ReactiveGroup<> group; + ReactiveGroup group; - VarSignal in{ group, 1 }; - Signal in2 = in; + VarSignal in{ group, 1 }; + Signal in2 = in; GridGraphGenerator generator; @@ -376,9 +376,9 @@ int main() /*int main2() { - ReactiveGroup<> group1; - ReactiveGroup<> group2; - ReactiveGroup<> group3; + ReactiveGroup group1; + ReactiveGroup group2; + ReactiveGroup group3; VarSignal x{ 0, group1 }; VarSignal y{ 0, group2 }; @@ -387,7 +387,7 @@ int main() Signal area{ Multiply, x, y }; Signal volume{ Multiply, area, z }; - Observer<> obs{ PrintAreaAndVolume, area, volume }; + Observer obs{ PrintAreaAndVolume, area, volume }; Signal> volumeHistory = Iterate>( vector{ }, PushToVector, Monitor(volume)); @@ -420,8 +420,8 @@ int main3() using namespace std; using namespace react; - ReactiveGroup<> group1; - ReactiveGroup<> group2; + ReactiveGroup group1; + ReactiveGroup group2; auto sig1 = VarSignal( 10, group1 ); diff --git a/include/react/Event.h b/include/react/Event.h index da63eb35..54d1ec85 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -294,7 +294,7 @@ auto Merge(const ReactiveGroup& group, const Event& dep1, const Event& . const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); - return Event( CtorTag{ }, std::make_shared>( + return Event( CtorTag{ }, std::make_shared>( graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...)); } @@ -316,7 +316,7 @@ auto Merge(const Event& dep1, const Event& ... deps) -> decltype(auto) const auto& graphPtr = PrivateNodeInterface::GraphPtr(dep1); - return Event( CtorTag{ }, std::make_shared>( + return Event( CtorTag{ }, std::make_shared>( graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...)); } diff --git a/include/react/Observer.h b/include/react/Observer.h index eea9ce5f..8d2be2a9 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -23,16 +23,55 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// /// ObserverBase /////////////////////////////////////////////////////////////////////////////////////////////////// -class ObserverBase +class Observer { private: using NodeType = REACT_IMPL::ObserverNode; public: - // Private node ctor - explicit ObserverBase(std::shared_ptr&& nodePtr) : - nodePtr_( std::move(nodePtr) ) - { } + Observer() = default; + + Observer(const Observer&) = default; + Observer& operator=(const Observer&) = default; + + Observer(Observer&&) = default; + Observer& operator=(Observer&&) = default; + + // Construct signal observer with implicit group + template + Observer(F&& func, const Signal& ... subjects) : + Observer::Observer(CreateSignalObserverNode(std::forward(func), subjects ...)) + { } + + // Construct signal observer with explicit group + template + Observer(const ReactiveGroup& group, F&& func, const Signal& ... subjects) : + Observer::Observer(CreateSignalObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subjects ...)) + { } + + // Construct event observer with implicit group + template + Observer(F&& func, const Event& subject) : + Observer::Observer(CreateEventObserverNode(std::forward(func), subject)) + { } + + // Construct event observer with explicit group + template + Observer(const ReactiveGroup& group, F&& func, const Event& subject) : + Observer::Observer(CreateEventObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subject)) + { } + + // Constructed synced event observer with implicit group + template + Observer(F&& func, const Event& subject, const Signal& ... signals) : + Observer::Observer(CreateSyncedEventObserverNode(std::forward(func), subject, signals ...)) + { } + + // Constructed synced event observer with explicit group + template + Observer(const ReactiveGroup& group, F&& func, const Event& subject, const Signal& ... signals) : + Observer::Observer(CreateSyncedEventObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subject, signals ...)) + { } void Cancel() { nodePtr_.reset(); } @@ -41,13 +80,10 @@ class ObserverBase { return nodePtr_ != nullptr; } protected: - ObserverBase() = default; - - ObserverBase(const ObserverBase&) = default; - ObserverBase& operator=(const ObserverBase&) = default; - - ObserverBase(ObserverBase&&) = default; - ObserverBase& operator=(ObserverBase&&) = default; + // Private node ctor + explicit Observer(std::shared_ptr&& nodePtr) : + nodePtr_(std::move(nodePtr)) + { } auto NodePtr() -> std::shared_ptr& { return nodePtr_; } @@ -56,7 +92,7 @@ class ObserverBase { return nodePtr_; } template - auto CreateSignalObserverNode(const std::shared_ptr& graphPtr, F&& func, const SignalBase& dep1, const SignalBase& ... deps) -> decltype(auto) + auto CreateSignalObserverNode(const std::shared_ptr& graphPtr, F&& func, const Signal& dep1, const Signal& ... deps) -> decltype(auto) { using REACT_IMPL::PrivateSignalLinkNodeInterface; using ObsNodeType = REACT_IMPL::SignalObserverNode::type, T1, Ts ...>; @@ -68,14 +104,14 @@ class ObserverBase } template - auto CreateSignalObserverNode(F&& func, const SignalBase& dep1, const SignalBase& ... deps) -> decltype(auto) + auto CreateSignalObserverNode(F&& func, const Signal& dep1, const Signal& ... deps) -> decltype(auto) { using REACT_IMPL::PrivateNodeInterface; return CreateSignalObserverNode(PrivateNodeInterface::GraphPtr(dep1), std::forward(func), dep1, deps ...); } template - auto CreateEventObserverNode(const std::shared_ptr& graphPtr, F&& func, const EventBase& dep) -> decltype(auto) + auto CreateEventObserverNode(const std::shared_ptr& graphPtr, F&& func, const Event& dep) -> decltype(auto) { using REACT_IMPL::PrivateEventLinkNodeInterface; using ObsNodeType = REACT_IMPL::EventObserverNode::type, T>; @@ -84,14 +120,14 @@ class ObserverBase } template - auto CreateEventObserverNode(F&& func, const EventBase& dep) -> decltype(auto) + auto CreateEventObserverNode(F&& func, const Event& dep) -> decltype(auto) { using REACT_IMPL::PrivateNodeInterface; return CreateEventObserverNode(PrivateNodeInterface::GraphPtr(dep), std::forward(func), dep); } template - auto CreateSyncedEventObserverNode(const std::shared_ptr& graphPtr, F&& func, const EventBase& dep, const SignalBase& ... syncs) -> decltype(auto) + auto CreateSyncedEventObserverNode(const std::shared_ptr& graphPtr, F&& func, const Event& dep, const Signal& ... syncs) -> decltype(auto) { using REACT_IMPL::PrivateEventLinkNodeInterface; using REACT_IMPL::PrivateSignalLinkNodeInterface; @@ -102,7 +138,7 @@ class ObserverBase } template - auto CreateSyncedEventObserverNode(F&& func, const EventBase& dep, const SignalBase& ... syncs) -> decltype(auto) + auto CreateSyncedEventObserverNode(F&& func, const Event& dep, const Signal& ... syncs) -> decltype(auto) { using REACT_IMPL::PrivateNodeInterface; return CreateSyncedEventObserverNode(PrivateNodeInterface::GraphPtr(dep), std::forward(func), dep, syncs ...); @@ -114,120 +150,6 @@ class ObserverBase friend struct REACT_IMPL::PrivateNodeInterface; }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Observer -/////////////////////////////////////////////////////////////////////////////////////////////////// -template <> -class Observer : public ObserverBase -{ -public: - using ObserverBase::ObserverBase; - - Observer() = delete; - - Observer(const Observer&) = delete; - Observer& operator=(const Observer&) = delete; - - Observer(Observer&&) = default; - Observer& operator=(Observer&&) = default; - - // Construct signal observer with implicit group - template - Observer(F&& func, const SignalBase& ... subjects) : - Observer::ObserverBase( CreateSignalObserverNode(std::forward(func), subjects ...) ) - { } - - // Construct signal observer with explicit group - template - Observer(const ReactiveGroupBase& group, F&& func, const SignalBase& ... subjects) : - Observer::ObserverBase( CreateSignalObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subjects ...) ) - { } - - // Construct event observer with implicit group - template - Observer(F&& func, const EventBase& subject) : - Observer::ObserverBase( CreateEventObserverNode(std::forward(func), subject) ) - { } - - // Construct event observer with explicit group - template - Observer(const ReactiveGroupBase& group, F&& func, const EventBase& subject) : - Observer::ObserverBase( CreateEventObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subject) ) - { } - - // Constructed synced event observer with implicit group - template - Observer(F&& func, const EventBase& subject, const SignalBase& ... signals) : - Observer::ObserverBase( CreateSyncedEventObserverNode(std::forward(func), subject, signals ...) ) - { } - - // Constructed synced event observer with explicit group - template - Observer(const ReactiveGroupBase& group, F&& func, const EventBase& subject, const SignalBase& ... signals) : - Observer::ObserverBase( CreateSyncedEventObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subject, signals ...) ) - { } -}; - -template <> -class Observer : public ObserverBase -{ -public: - using ObserverBase::ObserverBase; - - Observer() = delete; - - Observer(const Observer&) = default; - Observer& operator=(const Observer&) = default; - - Observer(Observer&&) = default; - Observer& operator=(Observer&&) = default; - - // Construct from unique - Observer(Observer&& other) : - Observer::ObserverBase( std::move(other) ) - { } - - // Assign from unique - Observer& operator=(Observer&& other) - { Observer::ObserverBase::operator=(std::move(other)); return *this; } - - // Construct signal observer with implicit group - template - Observer(F&& func, const SignalBase& ... subjects) : - Observer::ObserverBase( CreateSignalObserverNode(std::forward(func), subjects ...) ) - { } - - // Construct signal observer with explicit group - template - Observer(const ReactiveGroupBase& group, F&& func, const SignalBase& ... subjects) : - Observer::ObserverBase( CreateSignalObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subjects ...) ) - { } - - // Construct event observer with implicit group - template - Observer(F&& func, const EventBase& subject) : - Observer::ObserverBase( CreateEventObserverNode(std::forward(func), subject) ) - { } - - // Construct event observer with explicit group - template - Observer(const ReactiveGroupBase& group, F&& func, const EventBase& subject) : - Observer::ObserverBase( CreateEventObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subject) ) - { } - - // Constructed synced event observer with implicit group - template - Observer(F&& func, const EventBase& subject, const SignalBase& ... signals) : - Observer::ObserverBase( CreateSyncedEventObserverNode(std::forward(func), subject, signals ...) ) - { } - - // Constructed synced event observer with explicit group - template - Observer(const ReactiveGroupBase& group, F&& func, const EventBase& subject, const SignalBase& ... signals) : - Observer::ObserverBase( CreateSyncedEventObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subject, signals ...) ) - { } -}; - /******************************************/ REACT_END /******************************************/ #endif // REACT_OBSERVER_H_INCLUDED \ No newline at end of file From b7ec9da43ede547eaa0b42d61b79047cc168d722 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 18 Dec 2016 22:31:10 +0100 Subject: [PATCH 234/266] done getting rid of unique/shared wrapper types. everything is shared by default now. --- examples/src/BasicAlgorithms.cpp | 16 +++---- examples/src/BasicEvents.cpp | 22 +++++----- examples/src/BasicObservers.cpp | 8 ++-- examples/src/BasicSignals.cpp | 16 +++---- include/react/Algorithm.h | 62 +++++++++++++-------------- include/react/Event.h | 42 +++++++++--------- include/react/Group.h | 6 +++ include/react/Observer.h | 14 +++--- include/react/Signal.h | 8 ++-- project/msvc/CppReact.vcxproj | 1 - project/msvc/CppReact.vcxproj.filters | 3 -- 11 files changed, 99 insertions(+), 99 deletions(-) diff --git a/examples/src/BasicAlgorithms.cpp b/examples/src/BasicAlgorithms.cpp index a11dbdb9..e804ec81 100644 --- a/examples/src/BasicAlgorithms.cpp +++ b/examples/src/BasicAlgorithms.cpp @@ -22,7 +22,7 @@ namespace example1 using namespace std; using namespace react; - ReactiveGroup<> group; + ReactiveGroup group; struct Sensor { @@ -36,7 +36,7 @@ namespace example1 Sensor mySensor; - Observer<> obs( + Observer obs( [] (int v) { cout << v << endl; @@ -62,7 +62,7 @@ namespace example2 using namespace std; using namespace react; - ReactiveGroup<> group; + ReactiveGroup group; struct Employee { @@ -76,7 +76,7 @@ namespace example2 Employee bob; - Observer<> obs( + Observer obs( [] (EventRange in, const string& name) { for (int newSalary : in) @@ -96,7 +96,7 @@ namespace example3 using namespace std; using namespace react; - ReactiveGroup<> group; + ReactiveGroup group; struct Counter { @@ -137,7 +137,7 @@ namespace example4 using namespace std; using namespace react; - ReactiveGroup<> group; + ReactiveGroup group; struct Sensor { @@ -188,7 +188,7 @@ namespace example5 using namespace std; using namespace react; - ReactiveGroup<> group; + ReactiveGroup group; enum ECmd { increment, decrement, reset }; @@ -255,7 +255,7 @@ namespace example6 using namespace std; using namespace react; - ReactiveGroup<> group; + ReactiveGroup group; class Sensor { diff --git a/examples/src/BasicEvents.cpp b/examples/src/BasicEvents.cpp index b9a7cfbd..97b795c2 100644 --- a/examples/src/BasicEvents.cpp +++ b/examples/src/BasicEvents.cpp @@ -22,7 +22,7 @@ namespace example1 // Defines a group. // Each group represents a separate dependency graph. // Reactives from different groups can not be mixed. - ReactiveGroup<> group; + ReactiveGroup group; // An event source that emits values of type string namespace v1 @@ -33,7 +33,7 @@ namespace example1 { cout << "Example 1 - Hello world (string source)" << endl; - Observer<> obs( + Observer obs( [] (EventRange in) { for (const auto& s : in) @@ -61,7 +61,7 @@ namespace example1 int count = 0; - Observer<> obs( + Observer obs( [&] (EventRange<> in) { for (auto t : in) @@ -87,7 +87,7 @@ namespace example2 using namespace std; using namespace react; - ReactiveGroup<> group; + ReactiveGroup group; // An event stream that merges both sources EventSource<> leftClick( group ); @@ -101,7 +101,7 @@ namespace example2 int count = 0; - Observer<> obs( + Observer obs( [&] (EventRange<> in) { for (auto t : in) @@ -124,7 +124,7 @@ namespace example3 using namespace std; using namespace react; - ReactiveGroup<> group; + ReactiveGroup group; EventSource numbers( group ); @@ -134,7 +134,7 @@ namespace example3 { cout << "Example 3 - Filtering events" << endl; - Observer<> obs( + Observer obs( [&] (EventRange in) { for (auto n : in) @@ -156,7 +156,7 @@ namespace example4 using namespace std; using namespace react; - ReactiveGroup<> group; + ReactiveGroup group; // Data types enum class Tag { normal, critical }; @@ -178,7 +178,7 @@ namespace example4 { cout << "Example 4 - Transforming events" << endl; - Observer<> obs( + Observer obs( [] (EventRange in) { for (TaggedNum e : in) @@ -206,7 +206,7 @@ namespace example5 using namespace std; using namespace react; - ReactiveGroup<> group; + ReactiveGroup group; EventSource src( group ); @@ -214,7 +214,7 @@ namespace example5 { cout << "Example 5 - Queuing multiple inputs" << endl; - Observer<> obs( + Observer obs( [] (EventRange in) { for (int e : in) diff --git a/examples/src/BasicObservers.cpp b/examples/src/BasicObservers.cpp index c81cb372..261e5b88 100644 --- a/examples/src/BasicObservers.cpp +++ b/examples/src/BasicObservers.cpp @@ -20,7 +20,7 @@ namespace example1 using namespace std; using namespace react; - ReactiveGroup<> group; + ReactiveGroup group; VarSignal x( group, 1 ); @@ -31,7 +31,7 @@ namespace example1 { Signal mySignal( [] (int x) { return x; }, x ); - Observer<> obs( [] (int mySignal) { cout << mySignal << endl; }, mySignal ); + Observer obs( [] (int mySignal) { cout << mySignal << endl; }, mySignal ); x <<= 2; // output: 2 } @@ -50,7 +50,7 @@ namespace example2 using namespace std; using namespace react; - ReactiveGroup<> group; + ReactiveGroup group; EventSource<> trigger( group ); @@ -58,7 +58,7 @@ namespace example2 { cout << "Example 2 - Detaching observers manually" << endl; - Observer<> obs( + Observer obs( [] (EventRange<> in) { for (auto _ : in) diff --git a/examples/src/BasicSignals.cpp b/examples/src/BasicSignals.cpp index fc52eb68..995872dd 100644 --- a/examples/src/BasicSignals.cpp +++ b/examples/src/BasicSignals.cpp @@ -29,7 +29,7 @@ namespace example1 // Defines a group. // Each group represents a separate dependency graph. // Reactives from different groups can not be mixed. - ReactiveGroup<> group; + ReactiveGroup group; // The two words VarSignal firstWord( group, string("Change") ); @@ -64,7 +64,7 @@ namespace example2 using namespace std; using namespace react; - ReactiveGroup<> group; + ReactiveGroup group; VarSignal x( group, 1 ); @@ -74,7 +74,7 @@ namespace example2 { cout << "Example 2 - Reacting to value changes" << endl; - Observer<> obs( + Observer obs( [] (int newValue) { cout << "xAbs changed to " << newValue << endl; }, xAbs ); @@ -98,7 +98,7 @@ namespace example3 int sumFunc(int a, int b) { return a + b; } - ReactiveGroup<> group; + ReactiveGroup group; VarSignal a( group, 1 ); VarSignal b( group, 1 ); @@ -111,7 +111,7 @@ namespace example3 { cout << "Example 3 - Changing multiple inputs" << endl; - Observer<> obs( + Observer obs( [] (int newValue) { cout << "z changed to " << newValue << endl; }, z ); @@ -136,7 +136,7 @@ namespace example4 using namespace std; using namespace react; - ReactiveGroup<> group; + ReactiveGroup group; VarSignal> data( group ); @@ -167,7 +167,7 @@ namespace example5 using namespace std; using namespace react; - ReactiveGroup<> group; + ReactiveGroup group; // Helpers using ExprPairType = pair; @@ -204,7 +204,7 @@ namespace example5 { cout << "Example 5 - Complex signals (v3)" << endl; - Observer<> obs(PrintExpressions, expressions); + Observer obs(PrintExpressions, expressions); a <<= 50; b <<= 60; diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index b7e8324d..f9ab8bb7 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -32,12 +32,13 @@ auto Hold(const ReactiveGroup& group, T&& initialValue, const Event& evnt) -> using REACT_IMPL::HoldNode; using REACT_IMPL::PrivateEventLinkNodeInterface; using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::PrivateNodeInterface; using REACT_IMPL::CtorTag; const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); - return Signal( CtorTag{ }, std::make_shared>( - graphPtr, std::forward(initialValue), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)) ); + return PrivateNodeInterface::CreateNodeHelper, HoldNode>( + graphPtr, std::forward(initialValue), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); } template @@ -50,8 +51,8 @@ auto Hold(T&& initialValue, const Event& evnt) -> Signal const auto& graphPtr = PrivateNodeInterface::GraphPtr(evnt); - return Signal( CtorTag{ }, std::make_shared>( - graphPtr, std::forward(initialValue), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)) ); + return PrivateNodeInterface::CreateNodeHelper, HoldNode>( + graphPtr, std::forward(initialValue), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -63,12 +64,12 @@ auto Monitor(const ReactiveGroup& group, const Signal& signal) -> Event using REACT_IMPL::MonitorNode; using REACT_IMPL::PrivateSignalLinkNodeInterface; using REACT_IMPL::PrivateReactiveGroupInterface; - using REACT_IMPL::CtorTag; + using REACT_IMPL::PrivateNodeInterface; const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); - return Event( CtorTag{ }, std::make_shared>( - graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal)) ); + return PrivateNodeInterface::CreateNodeHelper, MonitorNode>( + graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal)); } template @@ -77,12 +78,11 @@ auto Monitor(const Signal& signal) -> Event using REACT_IMPL::MonitorNode; using REACT_IMPL::PrivateSignalLinkNodeInterface; using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::CtorTag; const auto& graphPtr = PrivateNodeInterface::GraphPtr(signal); - return Event( CtorTag{ }, std::make_shared>( - graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal)) ); + return PrivateNodeInterface::CreateNodeHelper, MonitorNode>( + graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal)); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -96,7 +96,7 @@ auto Iterate(const ReactiveGroup& group, T&& initialValue, F&& func, const Event using REACT_IMPL::IsCallableWith; using REACT_IMPL::PrivateEventLinkNodeInterface; using REACT_IMPL::PrivateReactiveGroupInterface; - using REACT_IMPL::CtorTag; + using REACT_IMPL::PrivateNodeInterface; using FuncType = typename std::decay::type; using IterNodeType = typename std::conditional< @@ -106,8 +106,8 @@ auto Iterate(const ReactiveGroup& group, T&& initialValue, F&& func, const Event const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); - return Signal( CtorTag{ }, std::make_shared( - graphPtr, std::forward(initialValue), std::forward(func), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt) )); + return PrivateNodeInterface::CreateNodeHelper, IterNodeType>( + graphPtr, std::forward(initialValue), std::forward(func), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); } template @@ -128,8 +128,8 @@ auto Iterate(T&& initialValue, F&& func, const Event& evnt) -> Signal const auto& graphPtr = PrivateNodeInterface::GraphPtr(evnt); - return Signal( CtorTag{ }, std::make_shared( - graphPtr, std::forward(initialValue), std::forward(func), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt) )); + return PrivateNodeInterface::CreateNodeHelper, IterNodeType>( + graphPtr, std::forward(initialValue), std::forward(func), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -144,7 +144,7 @@ auto Iterate(const ReactiveGroup& group, T&& initialValue, F&& func, const Event using REACT_IMPL::PrivateSignalLinkNodeInterface; using REACT_IMPL::PrivateEventLinkNodeInterface; using REACT_IMPL::PrivateReactiveGroupInterface; - using REACT_IMPL::CtorTag; + using REACT_IMPL::PrivateNodeInterface; using FuncType = typename std::decay::type; using IterNodeType = typename std::conditional< @@ -154,9 +154,9 @@ auto Iterate(const ReactiveGroup& group, T&& initialValue, F&& func, const Event const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); - return Signal( CtorTag{ }, std::make_shared( + return PrivateNodeInterface::CreateNodeHelper, IterNodeType>( graphPtr, std::forward(initialValue), std::forward(func), - PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt), PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signals) ...)); + PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt), PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signals) ...); } template @@ -168,7 +168,6 @@ auto Iterate(T&& initialValue, F&& func, const Event& evnt, const Signal& using REACT_IMPL::PrivateSignalLinkNodeInterface; using REACT_IMPL::PrivateEventLinkNodeInterface;; using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::CtorTag; using FuncType = typename std::decay::type; using IterNodeType = typename std::conditional< @@ -178,9 +177,9 @@ auto Iterate(T&& initialValue, F&& func, const Event& evnt, const Signal& const auto& graphPtr = PrivateNodeInterface::GraphPtr(evnt); - return Signal( CtorTag{ }, std::make_shared( + return PrivateNodeInterface::CreateNodeHelper, IterNodeType>( graphPtr, std::forward(initialValue), std::forward(func), - PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt), PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signals) ...)); + PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt), PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signals) ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -193,12 +192,12 @@ auto Snapshot(const ReactiveGroup& group, const Signal& signal, const Event( CtorTag{ }, std::make_shared>( - graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)) ); + return PrivateNodeInterface::CreateNodeHelper, SnapshotNode>( + graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); } template @@ -208,12 +207,11 @@ auto Snapshot(const Signal& signal, const Event& evnt) -> Signal using REACT_IMPL::PrivateSignalLinkNodeInterface; using REACT_IMPL::PrivateEventLinkNodeInterface;; using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::CtorTag; const auto& graphPtr = PrivateNodeInterface::GraphPtr(signal); - return Signal( CtorTag{ }, std::make_shared>( - graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)) ); + return PrivateNodeInterface::CreateNodeHelper, SnapshotNode>( + graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -226,12 +224,12 @@ auto Pulse(const ReactiveGroup& group, const Signal& signal, const Event& using REACT_IMPL::PrivateSignalLinkNodeInterface; using REACT_IMPL::PrivateEventLinkNodeInterface;; using REACT_IMPL::PrivateReactiveGroupInterface; - using REACT_IMPL::CtorTag; + using REACT_IMPL::PrivateNodeInterface; const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); - return Event( CtorTag{ }, std::make_shared>( - graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)) ); + return PrivateNodeInterface::CreateNodeHelper, PulseNode>( + graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); } template @@ -245,8 +243,8 @@ auto Pulse(const Signal& signal, const Event& evnt) -> Event const auto& graphPtr = PrivateNodeInterface::GraphPtr(signal); - return Event( CtorTag{ }, std::make_shared>( - graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)) ); + return PrivateNodeInterface::CreateNodeHelper, PulseNode>( + graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); } /******************************************/ REACT_END /******************************************/ diff --git a/include/react/Event.h b/include/react/Event.h index 54d1ec85..9151bc14 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -282,6 +282,7 @@ auto Merge(const ReactiveGroup& group, const Event& dep1, const Event& . using REACT_IMPL::EventMergeNode; using REACT_IMPL::PrivateEventLinkNodeInterface; using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::PrivateNodeInterface; using REACT_IMPL::CtorTag; static_assert(sizeof...(Us) > 0, "Merge requires at least 2 inputs."); @@ -293,9 +294,9 @@ auto Merge(const ReactiveGroup& group, const Event& dep1, const Event& . T>::type; const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); - - return Event( CtorTag{ }, std::make_shared>( - graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...)); + + return PrivateNodeInterface::CreateNodeHelper, EventMergeNode>( + graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...); } template @@ -316,8 +317,8 @@ auto Merge(const Event& dep1, const Event& ... deps) -> decltype(auto) const auto& graphPtr = PrivateNodeInterface::GraphPtr(dep1); - return Event( CtorTag{ }, std::make_shared>( - graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...)); + return PrivateNodeInterface::CreateNodeHelper, EventMergeNode>( + graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -329,7 +330,7 @@ auto Filter(const ReactiveGroup& group, F&& pred, const Event& dep) -> Event< auto filterFunc = [capturedPred = std::forward(pred)] (EventRange inRange, EventSink out) { std::copy_if(inRange.begin(), inRange.end(), out, capturedPred); }; - return Event(group, std::move(filterFunc), dep); + return Event(group, std::move(filterFunc), dep); } template @@ -338,7 +339,7 @@ auto Filter(F&& pred, const Event& dep) -> Event auto filterFunc = [capturedPred = std::forward(pred)] (EventRange inRange, EventSink out) { std::copy_if(inRange.begin(), inRange.end(), out, capturedPred); }; - return Event(std::move(filterFunc), dep); + return Event(std::move(filterFunc), dep); } template @@ -351,7 +352,7 @@ auto Filter(const ReactiveGroup& group, F&& pred, const Event& dep, const Sig *out++ = v; }; - return Event(group, std::move(filterFunc), dep, signals ...); + return Event(group, std::move(filterFunc), dep, signals ...); } template @@ -364,7 +365,7 @@ auto Filter(F&& pred, const Event& dep, const Signal& ... signals) -> Eve *out++ = v; }; - return Event(std::move(filterFunc), dep, signals ...); + return Event(std::move(filterFunc), dep, signals ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -376,7 +377,7 @@ auto Transform(const ReactiveGroup& group, F&& op, const Event& dep) -> Event auto transformFunc = [capturedOp = std::forward(op)] (EventRange inRange, EventSink out) { std::transform(inRange.begin(), inRange.end(), out, capturedOp); }; - return Event(group, std::move(transformFunc), dep); + return Event(group, std::move(transformFunc), dep); } template @@ -385,7 +386,7 @@ auto Transform(F&& op, const Event& dep) -> Event auto transformFunc = [capturedOp = std::forward(op)] (EventRange inRange, EventSink out) { std::transform(inRange.begin(), inRange.end(), out, capturedOp); }; - return Event(std::move(transformFunc), dep); + return Event(std::move(transformFunc), dep); } template @@ -397,7 +398,7 @@ auto Transform(const ReactiveGroup& group, F&& op, const Event& dep, const Si *out++ = capturedPred(v, values ...); }; - return Event(group, std::move(transformFunc), dep, signals ...); + return Event(group, std::move(transformFunc), dep, signals ...); } template @@ -409,7 +410,7 @@ auto Transform(F&& op, const Event& dep, const Signal& ... signals) -> Ev *out++ = capturedPred(v, values ...); }; - return Event(std::move(transformFunc), dep, signals ...); + return Event(std::move(transformFunc), dep, signals ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -432,32 +433,31 @@ auto Join(const ReactiveGroup& group, const Event& dep1, const Event& .. using REACT_IMPL::EventJoinNode; using REACT_IMPL::PrivateReactiveGroupInterface; using REACT_IMPL::PrivateEventLinkNodeInterface; - using REACT_IMPL::CtorTag; + using REACT_IMPL::PrivateNodeInterface; static_assert(sizeof...(Us) > 0, "Join requires at least 2 inputs."); // If supplied, use merge type, otherwise default to common type. const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); - return Event, unique>( CtorTag{ }, std::make_shared>( - graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...)); + return PrivateNodeInterface::CreateNodeHelper>, EventJoinNode>( + graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...); } template auto Join(const Event& dep1, const Event& ... deps) -> Event> { using REACT_IMPL::EventJoinNode; - using REACT_IMPL::PrivateNodeInterface; using REACT_IMPL::PrivateEventLinkNodeInterface; - using REACT_IMPL::CtorTag; + using REACT_IMPL::PrivateNodeInterface; static_assert(sizeof...(Us) > 0, "Join requires at least 2 inputs."); // If supplied, use merge type, otherwise default to common type. const auto& graphPtr = PrivateNodeInterface::GraphPtr(dep1); - return Event, unique>( CtorTag{ }, std::make_shared>( - graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...)); + return PrivateNodeInterface::CreateNodeHelper>, EventJoinNode>( + graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -500,7 +500,7 @@ struct PrivateEventLinkNodeInterface } else { - return EventLinkBase::CreateLinkNode(targetGraph, dep); + return EventLink::CreateLinkNode(targetGraph, dep); } } }; diff --git a/include/react/Group.h b/include/react/Group.h index 837a2189..050d070f 100644 --- a/include/react/Group.h +++ b/include/react/Group.h @@ -133,6 +133,12 @@ class ReactiveGroup struct PrivateNodeInterface { + // Free functions may have to access the private node constructor (i.e. Merge). + // Instead of making all of them friends, this helper function is used. + template + static auto CreateNodeHelper(TArgs&& ... args) -> decltype(auto) + { return TResult( CtorTag{ }, std::make_shared(std::forward(args) ...)); } + template static auto NodePtr(const TBase& base) -> const std::shared_ptr& { return base.NodePtr(); } diff --git a/include/react/Observer.h b/include/react/Observer.h index 8d2be2a9..c9d33072 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -40,37 +40,37 @@ class Observer // Construct signal observer with implicit group template Observer(F&& func, const Signal& ... subjects) : - Observer::Observer(CreateSignalObserverNode(std::forward(func), subjects ...)) + Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSignalObserverNode(std::forward(func), subjects ...)) { } // Construct signal observer with explicit group template Observer(const ReactiveGroup& group, F&& func, const Signal& ... subjects) : - Observer::Observer(CreateSignalObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subjects ...)) + Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSignalObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subjects ...)) { } // Construct event observer with implicit group template Observer(F&& func, const Event& subject) : - Observer::Observer(CreateEventObserverNode(std::forward(func), subject)) + Observer::Observer(REACT_IMPL::CtorTag{ }, CreateEventObserverNode(std::forward(func), subject)) { } // Construct event observer with explicit group template Observer(const ReactiveGroup& group, F&& func, const Event& subject) : - Observer::Observer(CreateEventObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subject)) + Observer::Observer(REACT_IMPL::CtorTag{ }, CreateEventObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subject)) { } // Constructed synced event observer with implicit group template Observer(F&& func, const Event& subject, const Signal& ... signals) : - Observer::Observer(CreateSyncedEventObserverNode(std::forward(func), subject, signals ...)) + Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSyncedEventObserverNode(std::forward(func), subject, signals ...)) { } // Constructed synced event observer with explicit group template Observer(const ReactiveGroup& group, F&& func, const Event& subject, const Signal& ... signals) : - Observer::Observer(CreateSyncedEventObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subject, signals ...)) + Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSyncedEventObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subject, signals ...)) { } void Cancel() @@ -81,7 +81,7 @@ class Observer protected: // Private node ctor - explicit Observer(std::shared_ptr&& nodePtr) : + Observer(REACT_IMPL::CtorTag, std::shared_ptr&& nodePtr) : nodePtr_(std::move(nodePtr)) { } diff --git a/include/react/Signal.h b/include/react/Signal.h index 786710d3..79789527 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -202,17 +202,17 @@ class SignalSlot : public Signal // Construct with group + default explicit SignalSlot(const ReactiveGroup& group) : - SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) + SignalSlot::Signal( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) { } // Construct with group + value SignalSlot(const ReactiveGroup& group, const Signal& input) : - SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) + SignalSlot::Signal( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) { } // Construct with value explicit SignalSlot(const Signal& input) : - SignalSlot::SignalSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(input), input) ) + SignalSlot::Signal( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(input), input) ) { } void Set(const Signal& newInput) @@ -308,7 +308,7 @@ struct PrivateSignalLinkNodeInterface } else { - return SignalLinkBase::CreateLinkNode(targetGraph, sig); + return SignalLink::CreateLinkNode(targetGraph, sig); } } }; diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index 7ef4b0dc..ead65969 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -183,7 +183,6 @@ - diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index c5384429..76abe5da 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -122,8 +122,5 @@ Source Files\engine - - Source Files - \ No newline at end of file From fc17e99dc8ff7d1fbe3a66c7ac1ae2465defc321 Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 28 Dec 2016 14:01:31 +0100 Subject: [PATCH 235/266] refactor --- benchmarks/src/BenchmarkGrid.h | 4 +- examples/src/BasicAlgorithms.cpp | 12 ++-- examples/src/BasicEvents.cpp | 10 ++-- examples/src/BasicObservers.cpp | 4 +- examples/src/BasicSignals.cpp | 10 ++-- examples/src/BasicSynchronization.cpp | 4 +- examples/src/Main.cpp | 28 ++++----- include/react/API.h | 2 +- include/react/Algorithm.h | 36 ++++++------ include/react/Event.h | 54 +++++++++--------- include/react/Group.h | 79 +++++--------------------- include/react/Observer.h | 12 ++-- include/react/Signal.h | 61 ++++++++------------ include/react/detail/graph/GraphBase.h | 25 ++++---- 14 files changed, 138 insertions(+), 203 deletions(-) diff --git a/benchmarks/src/BenchmarkGrid.h b/benchmarks/src/BenchmarkGrid.h index 6b691cc3..d7ed0bb8 100644 --- a/benchmarks/src/BenchmarkGrid.h +++ b/benchmarks/src/BenchmarkGrid.h @@ -40,7 +40,7 @@ class GridGraphGenerator std::vector widths; - void Generate(const ReactiveGroupBase& group) + void Generate(const GroupBase& group) { assert(inputSignals.size() >= 1); assert(widths.size() >= 1); @@ -132,7 +132,7 @@ struct BenchmarkParams_Grid struct Benchmark_Grid { - double Run(const BenchmarkParams_Grid& params, const ReactiveGroupBase& group) + double Run(const BenchmarkParams_Grid& params, const GroupBase& group) { VarSignal in{ group, 1 }; Signal in2 = in; diff --git a/examples/src/BasicAlgorithms.cpp b/examples/src/BasicAlgorithms.cpp index e804ec81..c2c5d123 100644 --- a/examples/src/BasicAlgorithms.cpp +++ b/examples/src/BasicAlgorithms.cpp @@ -22,7 +22,7 @@ namespace example1 using namespace std; using namespace react; - ReactiveGroup group; + Group group; struct Sensor { @@ -62,7 +62,7 @@ namespace example2 using namespace std; using namespace react; - ReactiveGroup group; + Group group; struct Employee { @@ -96,7 +96,7 @@ namespace example3 using namespace std; using namespace react; - ReactiveGroup group; + Group group; struct Counter { @@ -137,7 +137,7 @@ namespace example4 using namespace std; using namespace react; - ReactiveGroup group; + Group group; struct Sensor { @@ -188,7 +188,7 @@ namespace example5 using namespace std; using namespace react; - ReactiveGroup group; + Group group; enum ECmd { increment, decrement, reset }; @@ -255,7 +255,7 @@ namespace example6 using namespace std; using namespace react; - ReactiveGroup group; + Group group; class Sensor { diff --git a/examples/src/BasicEvents.cpp b/examples/src/BasicEvents.cpp index 97b795c2..da3fa4f2 100644 --- a/examples/src/BasicEvents.cpp +++ b/examples/src/BasicEvents.cpp @@ -22,7 +22,7 @@ namespace example1 // Defines a group. // Each group represents a separate dependency graph. // Reactives from different groups can not be mixed. - ReactiveGroup group; + Group group; // An event source that emits values of type string namespace v1 @@ -87,7 +87,7 @@ namespace example2 using namespace std; using namespace react; - ReactiveGroup group; + Group group; // An event stream that merges both sources EventSource<> leftClick( group ); @@ -124,7 +124,7 @@ namespace example3 using namespace std; using namespace react; - ReactiveGroup group; + Group group; EventSource numbers( group ); @@ -156,7 +156,7 @@ namespace example4 using namespace std; using namespace react; - ReactiveGroup group; + Group group; // Data types enum class Tag { normal, critical }; @@ -206,7 +206,7 @@ namespace example5 using namespace std; using namespace react; - ReactiveGroup group; + Group group; EventSource src( group ); diff --git a/examples/src/BasicObservers.cpp b/examples/src/BasicObservers.cpp index 261e5b88..ad263a2e 100644 --- a/examples/src/BasicObservers.cpp +++ b/examples/src/BasicObservers.cpp @@ -20,7 +20,7 @@ namespace example1 using namespace std; using namespace react; - ReactiveGroup group; + Group group; VarSignal x( group, 1 ); @@ -50,7 +50,7 @@ namespace example2 using namespace std; using namespace react; - ReactiveGroup group; + Group group; EventSource<> trigger( group ); diff --git a/examples/src/BasicSignals.cpp b/examples/src/BasicSignals.cpp index 995872dd..6bd6771d 100644 --- a/examples/src/BasicSignals.cpp +++ b/examples/src/BasicSignals.cpp @@ -29,7 +29,7 @@ namespace example1 // Defines a group. // Each group represents a separate dependency graph. // Reactives from different groups can not be mixed. - ReactiveGroup group; + Group group; // The two words VarSignal firstWord( group, string("Change") ); @@ -64,7 +64,7 @@ namespace example2 using namespace std; using namespace react; - ReactiveGroup group; + Group group; VarSignal x( group, 1 ); @@ -98,7 +98,7 @@ namespace example3 int sumFunc(int a, int b) { return a + b; } - ReactiveGroup group; + Group group; VarSignal a( group, 1 ); VarSignal b( group, 1 ); @@ -136,7 +136,7 @@ namespace example4 using namespace std; using namespace react; - ReactiveGroup group; + Group group; VarSignal> data( group ); @@ -167,7 +167,7 @@ namespace example5 using namespace std; using namespace react; - ReactiveGroup group; + Group group; // Helpers using ExprPairType = pair; diff --git a/examples/src/BasicSynchronization.cpp b/examples/src/BasicSynchronization.cpp index a79f87ce..8eacfa6e 100644 --- a/examples/src/BasicSynchronization.cpp +++ b/examples/src/BasicSynchronization.cpp @@ -21,7 +21,7 @@ namespace example1 using namespace react; using namespace std; - ReactiveGroup<> group; + Group<> group; class Sensor { @@ -31,7 +31,7 @@ namespace example1 Sensor(Sensor&&) = default; Sensor& operator=(Sensor&&) = default; - explicit Sensor(const ReactiveGroup<>& group) : + explicit Sensor(const Group<>& group) : Samples( group ) { } diff --git a/examples/src/Main.cpp b/examples/src/Main.cpp index 653d9d5e..cc5fdc31 100644 --- a/examples/src/Main.cpp +++ b/examples/src/Main.cpp @@ -37,7 +37,7 @@ class GridGraphGenerator std::vector widths; - void Generate(const ReactiveGroup& group) + void Generate(const Group& group) { assert(inputSignals.size() >= 1); assert(widths.size() >= 1); @@ -162,7 +162,7 @@ template T IterFunc2(EventRange evts, T v, T a1, T a2) int main2() { - ReactiveGroup group; + Group group; { // Signals @@ -228,8 +228,8 @@ int main2() // Links { - ReactiveGroup group1; - ReactiveGroup group2; + Group group1; + Group group2; VarSignal s1{ group1, 10 }; VarSignal s2{ group2, 11 }; @@ -244,8 +244,8 @@ int main2() } { - ReactiveGroup group1; - ReactiveGroup group2; + Group group1; + Group group2; VarSignal s1{ group1, 10 }; VarSignal s2{ group2, 11 }; @@ -269,8 +269,8 @@ int main2() } { - ReactiveGroup group1; - ReactiveGroup group2; + Group group1; + Group group2; VarSignal s1{ group1, 10 }; VarSignal s2{ group2, 11 }; @@ -326,7 +326,7 @@ int main2() int main() { - ReactiveGroup group; + Group group; VarSignal in{ group, 1 }; Signal in2 = in; @@ -376,9 +376,9 @@ int main() /*int main2() { - ReactiveGroup group1; - ReactiveGroup group2; - ReactiveGroup group3; + Group group1; + Group group2; + Group group3; VarSignal x{ 0, group1 }; VarSignal y{ 0, group2 }; @@ -420,8 +420,8 @@ int main3() using namespace std; using namespace react; - ReactiveGroup group1; - ReactiveGroup group2; + Group group1; + Group group2; auto sig1 = VarSignal( 10, group1 ); diff --git a/include/react/API.h b/include/react/API.h index dbd0e5df..da892da8 100644 --- a/include/react/API.h +++ b/include/react/API.h @@ -37,7 +37,7 @@ REACT_DEFINE_BITMASK_OPERATORS(TransactionFlags) /////////////////////////////////////////////////////////////////////////////////////////////////// // Groups -class ReactiveGroup; +class Group; // Signals template diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index f9ab8bb7..e5d6e076 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -27,15 +27,15 @@ /// Hold - Hold the most recent event in a signal /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Hold(const ReactiveGroup& group, T&& initialValue, const Event& evnt) -> Signal +auto Hold(const Group& group, T&& initialValue, const Event& evnt) -> Signal { using REACT_IMPL::HoldNode; using REACT_IMPL::PrivateEventLinkNodeInterface; - using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::PrivateGroupInterface; using REACT_IMPL::PrivateNodeInterface; using REACT_IMPL::CtorTag; - const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); + const auto& graphPtr = PrivateGroupInterface::GraphPtr(group); return PrivateNodeInterface::CreateNodeHelper, HoldNode>( graphPtr, std::forward(initialValue), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); @@ -59,14 +59,14 @@ auto Hold(T&& initialValue, const Event& evnt) -> Signal /// Monitor - Emits value changes of target signal /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Monitor(const ReactiveGroup& group, const Signal& signal) -> Event +auto Monitor(const Group& group, const Signal& signal) -> Event { using REACT_IMPL::MonitorNode; using REACT_IMPL::PrivateSignalLinkNodeInterface; - using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::PrivateGroupInterface; using REACT_IMPL::PrivateNodeInterface; - const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); + const auto& graphPtr = PrivateGroupInterface::GraphPtr(group); return PrivateNodeInterface::CreateNodeHelper, MonitorNode>( graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal)); @@ -89,13 +89,13 @@ auto Monitor(const Signal& signal) -> Event /// Iterate - Iteratively combines signal value with values from event stream (aka Fold) /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Iterate(const ReactiveGroup& group, T&& initialValue, F&& func, const Event& evnt) -> Signal +auto Iterate(const Group& group, T&& initialValue, F&& func, const Event& evnt) -> Signal { using REACT_IMPL::IterateNode; using REACT_IMPL::IterateByRefNode; using REACT_IMPL::IsCallableWith; using REACT_IMPL::PrivateEventLinkNodeInterface; - using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::PrivateGroupInterface; using REACT_IMPL::PrivateNodeInterface; using FuncType = typename std::decay::type; @@ -104,7 +104,7 @@ auto Iterate(const ReactiveGroup& group, T&& initialValue, F&& func, const Event IterateNode, IterateByRefNode>::type; - const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); + const auto& graphPtr = PrivateGroupInterface::GraphPtr(group); return PrivateNodeInterface::CreateNodeHelper, IterNodeType>( graphPtr, std::forward(initialValue), std::forward(func), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); @@ -136,14 +136,14 @@ auto Iterate(T&& initialValue, F&& func, const Event& evnt) -> Signal /// Iterate - Synced /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Iterate(const ReactiveGroup& group, T&& initialValue, F&& func, const Event& evnt, const Signal& ... signals) -> Signal +auto Iterate(const Group& group, T&& initialValue, F&& func, const Event& evnt, const Signal& ... signals) -> Signal { using REACT_IMPL::SyncedIterateNode; using REACT_IMPL::SyncedIterateByRefNode; using REACT_IMPL::IsCallableWith; using REACT_IMPL::PrivateSignalLinkNodeInterface; using REACT_IMPL::PrivateEventLinkNodeInterface; - using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::PrivateGroupInterface; using REACT_IMPL::PrivateNodeInterface; using FuncType = typename std::decay::type; @@ -152,7 +152,7 @@ auto Iterate(const ReactiveGroup& group, T&& initialValue, F&& func, const Event SyncedIterateNode, SyncedIterateByRefNode>::type; - const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); + const auto& graphPtr = PrivateGroupInterface::GraphPtr(group); return PrivateNodeInterface::CreateNodeHelper, IterNodeType>( graphPtr, std::forward(initialValue), std::forward(func), @@ -186,15 +186,15 @@ auto Iterate(T&& initialValue, F&& func, const Event& evnt, const Signal& /// Snapshot - Sets signal value to value of other signal when event is received /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Snapshot(const ReactiveGroup& group, const Signal& signal, const Event& evnt) -> Signal +auto Snapshot(const Group& group, const Signal& signal, const Event& evnt) -> Signal { using REACT_IMPL::SnapshotNode; using REACT_IMPL::PrivateSignalLinkNodeInterface; using REACT_IMPL::PrivateEventLinkNodeInterface;; - using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::PrivateGroupInterface; using REACT_IMPL::PrivateNodeInterface; - const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); + const auto& graphPtr = PrivateGroupInterface::GraphPtr(group); return PrivateNodeInterface::CreateNodeHelper, SnapshotNode>( graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); @@ -218,15 +218,15 @@ auto Snapshot(const Signal& signal, const Event& evnt) -> Signal /// Pulse - Emits value of target signal when event is received /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Pulse(const ReactiveGroup& group, const Signal& signal, const Event& evnt) -> Event +auto Pulse(const Group& group, const Signal& signal, const Event& evnt) -> Event { using REACT_IMPL::PulseNode; using REACT_IMPL::PrivateSignalLinkNodeInterface; using REACT_IMPL::PrivateEventLinkNodeInterface;; - using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::PrivateGroupInterface; using REACT_IMPL::PrivateNodeInterface; - const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); + const auto& graphPtr = PrivateGroupInterface::GraphPtr(group); return PrivateNodeInterface::CreateNodeHelper, PulseNode>( graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); diff --git a/include/react/Event.h b/include/react/Event.h index 9151bc14..58e6ac4d 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -51,8 +51,8 @@ class Event { } template - Event(const ReactiveGroup& group, F&& func, const Event& dep) : - Event::Event( REACT_IMPL::CtorTag{ }, CreateProcessingNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), dep) ) + Event(const Group& group, F&& func, const Event& dep) : + Event::Event( REACT_IMPL::CtorTag{ }, CreateProcessingNode(REACT_IMPL::PrivateGroupInterface::GraphPtr(group), std::forward(func), dep) ) { } template @@ -61,8 +61,8 @@ class Event { } template - Event(const ReactiveGroup& group, F&& func, const Event& dep, const Signal& ... signals) : - Event::Event( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), dep, signals ...) ) + Event(const Group& group, F&& func, const Event& dep, const Signal& ... signals) : + Event::Event( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(REACT_IMPL::PrivateGroupInterface::GraphPtr(group), std::forward(func), dep, signals ...) ) { } auto Tokenize() const -> decltype(auto) @@ -136,8 +136,8 @@ class EventSource : public Event EventSource& operator=(EventSource&& other) = default; // Construct event source - explicit EventSource(const ReactiveGroup& group) : - EventSource::Event( REACT_IMPL::CtorTag{ }, CreateSourceNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) + explicit EventSource(const Group& group) : + EventSource::Event( REACT_IMPL::CtorTag{ }, CreateSourceNode(REACT_IMPL::PrivateGroupInterface::GraphPtr(group)) ) { } void Emit(const E& value) @@ -159,7 +159,7 @@ class EventSource : public Event protected: - auto CreateSourceNode(const std::shared_ptr& graphPtr) -> decltype(auto) + auto CreateSourceNode(const Group& group) -> decltype(auto) { using SrcNodeType = REACT_IMPL::EventSourceNode; return std::make_shared(graphPtr); @@ -197,8 +197,8 @@ class EventSlot : public Event EventSlot& operator=(EventSlot&&) = default; // Construct with value - EventSlot(const ReactiveGroup& group, const Event& input) : - EventSlot::EventSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) + EventSlot(const Group& group, const Event& input) : + EventSlot::EventSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(group, input) ) { } void Set(const Event& newInput) @@ -208,13 +208,13 @@ class EventSlot : public Event { SetInput(newInput); } protected: - auto CreateSlotNode(const std::shared_ptr& graphPtr, const Event& input) -> decltype(auto) + auto CreateSlotNode(const Group& group, const Event& input) -> decltype(auto) { using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::PrivateGroupInterface; using SlotNodeType = REACT_IMPL::EventSlotNode; - return std::make_shared(PrivateReactiveGroupInterface::GraphPtr(group), PrivateNodeInterface::NodePtr(input)); + return std::make_shared(group, input); } private: @@ -249,8 +249,8 @@ class EventLink : public Event EventLink& operator=(EventLink&&) = default; // Construct with explicit group - EventLink(const ReactiveGroup& group, const Event& input) : - SignalLink::Signal( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) + EventLink(const Group& group, const Event& input) : + SignalLink::Signal( REACT_IMPL::CtorTag{ }, CreateLinkNode(group, input) ) { } // Construct with implicit group @@ -259,13 +259,13 @@ class EventLink : public Event { } protected: - static auto CreateLinkNode(const std::shared_ptr& graphPtr, const Event& input) -> decltype(auto) + static auto CreateLinkNode(const Group& group, const Event& input) -> decltype(auto) { using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::PrivateGroupInterface; using EventNodeType = REACT_IMPL::EventLinkNode; - auto node = std::make_shared(graphPtr, PrivateNodeInterface::GraphPtr(input), PrivateNodeInterface::NodePtr(input)); + auto node = std::make_shared(group, PrivateNodeInterface::GraphPtr(input), PrivateNodeInterface::NodePtr(input)); node->SetWeakSelfPtr(std::weak_ptr{ node }); return node; } @@ -277,11 +277,11 @@ class EventLink : public Event /// Merge /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Merge(const ReactiveGroup& group, const Event& dep1, const Event& ... deps) -> decltype(auto) +auto Merge(const Group& group, const Event& dep1, const Event& ... deps) -> decltype(auto) { using REACT_IMPL::EventMergeNode; using REACT_IMPL::PrivateEventLinkNodeInterface; - using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::PrivateGroupInterface; using REACT_IMPL::PrivateNodeInterface; using REACT_IMPL::CtorTag; @@ -293,7 +293,7 @@ auto Merge(const ReactiveGroup& group, const Event& dep1, const Event& . typename std::common_type::type, T>::type; - const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); + const auto& graphPtr = PrivateGroupInterface::GraphPtr(group); return PrivateNodeInterface::CreateNodeHelper, EventMergeNode>( graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...); @@ -325,7 +325,7 @@ auto Merge(const Event& dep1, const Event& ... deps) -> decltype(auto) /// Filter /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Filter(const ReactiveGroup& group, F&& pred, const Event& dep) -> Event +auto Filter(const Group& group, F&& pred, const Event& dep) -> Event { auto filterFunc = [capturedPred = std::forward(pred)] (EventRange inRange, EventSink out) { std::copy_if(inRange.begin(), inRange.end(), out, capturedPred); }; @@ -343,7 +343,7 @@ auto Filter(F&& pred, const Event& dep) -> Event } template -auto Filter(const ReactiveGroup& group, F&& pred, const Event& dep, const Signal& ... signals) -> Event +auto Filter(const Group& group, F&& pred, const Event& dep, const Signal& ... signals) -> Event { auto filterFunc = [capturedPred = std::forward(pred)] (EventRange inRange, EventSink out, const Us& ... values) { @@ -372,7 +372,7 @@ auto Filter(F&& pred, const Event& dep, const Signal& ... signals) -> Eve /// Transform /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Transform(const ReactiveGroup& group, F&& op, const Event& dep) -> Event +auto Transform(const Group& group, F&& op, const Event& dep) -> Event { auto transformFunc = [capturedOp = std::forward(op)] (EventRange inRange, EventSink out) { std::transform(inRange.begin(), inRange.end(), out, capturedOp); }; @@ -390,7 +390,7 @@ auto Transform(F&& op, const Event& dep) -> Event } template -auto Transform(const ReactiveGroup& group, F&& op, const Event& dep, const Signal& ... signals) -> Event +auto Transform(const Group& group, F&& op, const Event& dep, const Signal& ... signals) -> Event { auto transformFunc = [capturedOp = std::forward(pred)] (EventRange inRange, EventSink out, const Vs& ... values) { @@ -428,17 +428,17 @@ auto Flatten(const Signal>& outer) -> Events /// Join /////////////////////////////////////////////////////////////////////////////////////////////////// template -auto Join(const ReactiveGroup& group, const Event& dep1, const Event& ... deps) -> Event> +auto Join(const Group& group, const Event& dep1, const Event& ... deps) -> Event> { using REACT_IMPL::EventJoinNode; - using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::PrivateGroupInterface; using REACT_IMPL::PrivateEventLinkNodeInterface; using REACT_IMPL::PrivateNodeInterface; static_assert(sizeof...(Us) > 0, "Join requires at least 2 inputs."); // If supplied, use merge type, otherwise default to common type. - const auto& graphPtr = PrivateReactiveGroupInterface::GraphPtr(group); + const auto& graphPtr = PrivateGroupInterface::GraphPtr(group); return PrivateNodeInterface::CreateNodeHelper>, EventJoinNode>( graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...); diff --git a/include/react/Group.h b/include/react/Group.h index 050d070f..6e9a645d 100644 --- a/include/react/Group.h +++ b/include/react/Group.h @@ -4,8 +4,8 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#ifndef REACT_DOMAIN_H_INCLUDED -#define REACT_DOMAIN_H_INCLUDED +#ifndef REACT_GROUP_H_INCLUDED +#define REACT_GROUP_H_INCLUDED #pragma once @@ -21,7 +21,7 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -struct PrivateReactiveGroupInterface; +struct PrivateGroupInterface; struct CtorTag { }; /****************************************/ REACT_IMPL_END /***************************************/ @@ -85,22 +85,22 @@ class TransactionStatus #endif /////////////////////////////////////////////////////////////////////////////////////////////////// -/// ReactiveGroupBase +/// GroupBase /////////////////////////////////////////////////////////////////////////////////////////////////// -class ReactiveGroup +class Group { using GraphType = REACT_IMPL::ReactiveGraph; public: - ReactiveGroup() : + Group() : graphPtr_( std::make_shared() ) { } - ReactiveGroup(const ReactiveGroup&) = default; - ReactiveGroup& operator=(const ReactiveGroup&) = default; + Group(const Group&) = default; + Group& operator=(const Group&) = default; - ReactiveGroup(ReactiveGroup&& other) = default; - ReactiveGroup& operator=(ReactiveGroup&& other) = default; + Group(Group&& other) = default; + Group& operator=(Group&& other) = default; template void DoTransaction(F&& func) @@ -114,7 +114,8 @@ class ReactiveGroup void EnqueueTransaction(TransactionFlags flags, F&& func) { graphPtr_->EnqueueTransaction(flags, std::forward(func)); } -protected: + +public: // Internal auto GraphPtr() -> std::shared_ptr& { return graphPtr_; } @@ -123,62 +124,8 @@ class ReactiveGroup private: std::shared_ptr graphPtr_; - - friend struct REACT_IMPL::PrivateReactiveGroupInterface; }; /******************************************/ REACT_END /******************************************/ -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -struct PrivateNodeInterface -{ - // Free functions may have to access the private node constructor (i.e. Merge). - // Instead of making all of them friends, this helper function is used. - template - static auto CreateNodeHelper(TArgs&& ... args) -> decltype(auto) - { return TResult( CtorTag{ }, std::make_shared(std::forward(args) ...)); } - - template - static auto NodePtr(const TBase& base) -> const std::shared_ptr& - { return base.NodePtr(); } - - template - static auto NodePtr(TBase& base) -> std::shared_ptr& - { return base.NodePtr(); } - - template - static auto GraphPtr(const TBase& base) -> const std::shared_ptr& - { return base.NodePtr()->GraphPtr(); } - - template - static auto GraphPtr(TBase& base) -> std::shared_ptr& - { return base.NodePtr()->GraphPtr(); } -}; - -struct PrivateReactiveGroupInterface -{ - static auto GraphPtr(const ReactiveGroup& group) -> const std::shared_ptr& - { return group.GraphPtr(); } - - static auto GraphPtr(ReactiveGroup& group) -> std::shared_ptr& - { return group.GraphPtr(); } -}; - -template -static auto GetCheckedGraphPtr(const TBase1& dep1, const TBases& ... deps) -> const std::shared_ptr& -{ - const std::shared_ptr& graphPtr1 = PrivateNodeInterface::GraphPtr(dep1); - - std::initializer_list rawGraphPtrs = { PrivateNodeInterface::GraphPtr(deps).get() ... }; - - bool isSameGraphForAllDeps = std::all_of(rawGraphPtrs.begin(), rawGraphPtrs.end(), [&] (ReactiveGraph* p) { return p == graphPtr1.get(); }); - - REACT_ASSERT(isSameGraphForAllDeps, "All dependencies must belong to the same group."); - - return graphPtr1; -} - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_DOMAIN_H_INCLUDED \ No newline at end of file +#endif // REACT_GROUP_H_INCLUDED \ No newline at end of file diff --git a/include/react/Observer.h b/include/react/Observer.h index c9d33072..0d2a82e0 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -45,8 +45,8 @@ class Observer // Construct signal observer with explicit group template - Observer(const ReactiveGroup& group, F&& func, const Signal& ... subjects) : - Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSignalObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subjects ...)) + Observer(const Group& group, F&& func, const Signal& ... subjects) : + Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSignalObserverNode(REACT_IMPL::PrivateGroupInterface::GraphPtr(group), std::forward(func), subjects ...)) { } // Construct event observer with implicit group @@ -57,8 +57,8 @@ class Observer // Construct event observer with explicit group template - Observer(const ReactiveGroup& group, F&& func, const Event& subject) : - Observer::Observer(REACT_IMPL::CtorTag{ }, CreateEventObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subject)) + Observer(const Group& group, F&& func, const Event& subject) : + Observer::Observer(REACT_IMPL::CtorTag{ }, CreateEventObserverNode(REACT_IMPL::PrivateGroupInterface::GraphPtr(group), std::forward(func), subject)) { } // Constructed synced event observer with implicit group @@ -69,8 +69,8 @@ class Observer // Constructed synced event observer with explicit group template - Observer(const ReactiveGroup& group, F&& func, const Event& subject, const Signal& ... signals) : - Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSyncedEventObserverNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), subject, signals ...)) + Observer(const Group& group, F&& func, const Event& subject, const Signal& ... signals) : + Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSyncedEventObserverNode(REACT_IMPL::PrivateGroupInterface::GraphPtr(group), std::forward(func), subject, signals ...)) { } void Cancel() diff --git a/include/react/Signal.h b/include/react/Signal.h index 79789527..bc607a9f 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -42,24 +42,16 @@ class Signal using NodeType = REACT_IMPL::SignalNode; public: - const S& Value() const - { return nodePtr_->Value(); } - - // Empty signal - Signal() = default; - - // Copy ctor & assignment Signal(const Signal&) = default; Signal& operator=(const Signal&) = default; - // Move ctor & assignment Signal(Signal&&) = default; Signal& operator=(Signal&&) = default; // Construct func signal with explicit group template - explicit Signal(const ReactiveGroup& group, F&& func, const Signal& dep1, const Signal& ... deps) : - Signal::Signal( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(func), dep1, deps ...) ) + explicit Signal(const Group& group, F&& func, const Signal& dep1, const Signal& ... deps) : + Signal::Signal( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateGroupInterface::GraphPtr(group), std::forward(func), dep1, deps ...) ) { } // Construct func signal with implicit group @@ -68,7 +60,10 @@ class Signal Signal::Signal( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(dep1), std::forward(func), dep1, deps ...) ) { } -protected: + const Group& GetGroup() const + { return nodePtr_->GetGroup(); } + +public: // Internal // Private node ctor Signal(REACT_IMPL::CtorTag, std::shared_ptr&& nodePtr) : nodePtr_( std::move(nodePtr) ) @@ -80,6 +75,7 @@ class Signal auto NodePtr() const -> const std::shared_ptr& { return nodePtr_; } +protected: template auto CreateFuncNode(const std::shared_ptr& graphPtr, F&& func, const Signal& dep1, const Signal& ... deps) -> decltype(auto) { @@ -94,11 +90,8 @@ class Signal private: std::shared_ptr nodePtr_; - - friend struct REACT_IMPL::PrivateNodeInterface; }; - /////////////////////////////////////////////////////////////////////////////////////////////////// /// VarSignal /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -106,10 +99,6 @@ template class VarSignal : public Signal { public: - using Signal::Signal; - - VarSignal() = default; - VarSignal(const VarSignal&) = default; VarSignal& operator=(const VarSignal&) = default; @@ -117,14 +106,14 @@ class VarSignal : public Signal VarSignal& operator=(VarSignal&&) = default; // Construct with group + default - explicit VarSignal(const ReactiveGroup& group) : - VarSignal::Signal( REACT_IMPL::CtorTag{ }, CreateVarNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) + explicit VarSignal(const Group& group) : + VarSignal::Signal( REACT_IMPL::CtorTag{ }, CreateVarNode(group) ) { } // Construct with group + value template - VarSignal(const ReactiveGroup& group, T&& value) : - VarSignal::Signal( REACT_IMPL::CtorTag{ }, CreateVarNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), std::forward(value)) ) + VarSignal(const Group& group, T&& value) : + VarSignal::Signal( REACT_IMPL::CtorTag{ }, CreateVarNode(group, std::forward(value)) ) { } void Set(const S& newValue) @@ -144,14 +133,14 @@ class VarSignal : public Signal { ModifyValue(func); } protected: - static auto CreateVarNode(const std::shared_ptr& graphPtr) -> decltype(auto) + static auto CreateVarNode(const Group& group) -> decltype(auto) { using VarNodeType = REACT_IMPL::VarSignalNode; return std::make_shared(graphPtr); } template - static auto CreateVarNode(const std::shared_ptr& graphPtr, T&& value) -> decltype(auto) + static auto CreateVarNode(const Group& group, T&& value) -> decltype(auto) { using VarNodeType = REACT_IMPL::VarSignalNode; return std::make_shared(graphPtr, std::forward(value)); @@ -162,7 +151,6 @@ class VarSignal : public Signal void SetValue(T&& newValue) { using REACT_IMPL::NodeId; - using REACT_IMPL::ReactiveGraph; using VarNodeType = REACT_IMPL::VarSignalNode; VarNodeType* castedPtr = static_cast(this->NodePtr().get()); @@ -176,7 +164,6 @@ class VarSignal : public Signal void ModifyValue(const F& func) { using REACT_IMPL::NodeId; - using REACT_IMPL::ReactiveGraph; using VarNodeType = REACT_IMPL::VarSignalNode; VarNodeType* castedPtr = static_cast(this->NodePtr().get()); @@ -201,13 +188,13 @@ class SignalSlot : public Signal SignalSlot& operator=(SignalSlot&&) = default; // Construct with group + default - explicit SignalSlot(const ReactiveGroup& group) : - SignalSlot::Signal( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group)) ) + explicit SignalSlot(const Group& group) : + SignalSlot::Signal( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateGroupInterface::GraphPtr(group)) ) { } // Construct with group + value - SignalSlot(const ReactiveGroup& group, const Signal& input) : - SignalSlot::Signal( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) + SignalSlot(const Group& group, const Signal& input) : + SignalSlot::Signal( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateGroupInterface::GraphPtr(group), input) ) { } // Construct with value @@ -222,7 +209,7 @@ class SignalSlot : public Signal { SetInput(newInput); } protected: - static auto CreateSlotNode(const std::shared_ptr& graphPtr, const Signal& input) -> decltype(auto) + static auto CreateSlotNode(const Group& group, const Signal& input) -> decltype(auto) { using REACT_IMPL::PrivateNodeInterface; using SlotNodeType = REACT_IMPL::SignalSlotNode; @@ -252,8 +239,6 @@ template class SignalLink : public Signal { public: - SignalLink() = default; - SignalLink(const SignalLink&) = default; SignalLink& operator=(const SignalLink&) = default; @@ -261,20 +246,20 @@ class SignalLink : public Signal SignalLink& operator=(SignalLink&&) = default; // Construct with explicit group - SignalLink(const ReactiveGroup& group, const Signal& input) : - SignalLink::Signal( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateReactiveGroupInterface::GraphPtr(group), input) ) + SignalLink(const Group& group, const Signal& input) : + SignalLink::Signal( REACT_IMPL::CtorTag{ }, CreateLinkNode(group, input) ) { } // Construct with implicit group explicit SignalLink(const Signal& input) : - SignalLink::Signal( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(input), input) ) + SignalLink::Signal( REACT_IMPL::CtorTag{ }, CreateLinkNode(input.GetGroup(), input) ) { } protected: - static auto CreateLinkNode(const std::shared_ptr& graphPtr, const Signal& input) -> decltype(auto) + static auto CreateLinkNode(const Group& group, const Signal& input) -> decltype(auto) { using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::PrivateReactiveGroupInterface; + using REACT_IMPL::PrivateGroupInterface; using LinkNodeType = REACT_IMPL::SignalLinkNode; auto node = std::make_shared(graphPtr, PrivateNodeInterface::GraphPtr(input), PrivateNodeInterface::NodePtr(input)); diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index d39809bb..3305ca40 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -29,8 +29,8 @@ class ReactiveGraph; class NodeBase : public IReactiveNode { public: - NodeBase(const std::shared_ptr& graphPtr) : - graphPtr_( graphPtr ) + NodeBase(const Group& group) : + group_( group ) { } NodeBase(const NodeBase&) = delete; @@ -58,35 +58,38 @@ class NodeBase : public IReactiveNode NodeId GetNodeId() const { return nodeId_; } + const Group& GetGroup() const + { return group_; } + auto GraphPtr() const -> const std::shared_ptr& - { return graphPtr_; } + { return GroupInternals::GraphPtr(group_); } auto GraphPtr() -> std::shared_ptr& - { return graphPtr_; } + { return GroupInternals::GraphPtr(group_); } protected: void RegisterMe(NodeCategory category = NodeCategory::normal) - { nodeId_ = graphPtr_->RegisterNode(this, category); } + { nodeId_ = GraphPtr()->RegisterNode(this, category); } void UnregisterMe() - { graphPtr_->UnregisterNode(nodeId_); } + { GraphPtr()->UnregisterNode(nodeId_); } void AttachToMe(NodeId otherNodeId) - { graphPtr_->OnNodeAttach(nodeId_, otherNodeId); } + { GraphPtr()->OnNodeAttach(nodeId_, otherNodeId); } void DetachFromMe(NodeId otherNodeId) - { graphPtr_->OnNodeDetach(nodeId_, otherNodeId); } + { GraphPtr()->OnNodeDetach(nodeId_, otherNodeId); } void DynamicAttachToMe(NodeId otherNodeId, TurnId turnId) - { graphPtr_->OnDynamicNodeAttach(nodeId_, otherNodeId, turnId); } + { GraphPtr()->OnDynamicNodeAttach(nodeId_, otherNodeId, turnId); } void DynamicDetachFromMe(NodeId otherNodeId, TurnId turnId) - { graphPtr_->OnDynamicNodeDetach(nodeId_, otherNodeId, turnId); } + { GraphPtr()->OnDynamicNodeDetach(nodeId_, otherNodeId, turnId); } private: NodeId nodeId_; - std::shared_ptr graphPtr_; + Group group_; }; /****************************************/ REACT_IMPL_END /***************************************/ From 1116f3a55ad263ebe21a48bbc9fc34e2662de7ed Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 28 Dec 2016 14:46:47 +0100 Subject: [PATCH 236/266] progress --- include/react/Event.h | 21 ++++-------------- include/react/Observer.h | 46 +++++++++++++++------------------------- 2 files changed, 21 insertions(+), 46 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index 58e6ac4d..d0dec01b 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -37,8 +37,6 @@ class Event using NodeType = REACT_IMPL::EventStreamNode; public: - Event() = default; - Event(const Event&) = default; Event& operator=(const Event&) = default; @@ -52,7 +50,7 @@ class Event template Event(const Group& group, F&& func, const Event& dep) : - Event::Event( REACT_IMPL::CtorTag{ }, CreateProcessingNode(REACT_IMPL::PrivateGroupInterface::GraphPtr(group), std::forward(func), dep) ) + Event::Event( REACT_IMPL::CtorTag{ }, CreateProcessingNode(group, std::forward(func), dep) ) { } template @@ -62,7 +60,7 @@ class Event template Event(const Group& group, F&& func, const Event& dep, const Signal& ... signals) : - Event::Event( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(REACT_IMPL::PrivateGroupInterface::GraphPtr(group), std::forward(func), dep, signals ...) ) + Event::Event( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(group, std::forward(func), dep, signals ...) ) { } auto Tokenize() const -> decltype(auto) @@ -127,8 +125,6 @@ template class EventSource : public Event { public: - EventSource() = default; - EventSource(const EventSource&) = default; EventSource& operator=(const EventSource&) = default; @@ -137,7 +133,7 @@ class EventSource : public Event // Construct event source explicit EventSource(const Group& group) : - EventSource::Event( REACT_IMPL::CtorTag{ }, CreateSourceNode(REACT_IMPL::PrivateGroupInterface::GraphPtr(group)) ) + EventSource::Event( REACT_IMPL::CtorTag{ }, CreateSourceNode(group) ) { } void Emit(const E& value) @@ -157,12 +153,10 @@ class EventSource : public Event { EmitValue(std::move(value)); return *this; } protected: - - auto CreateSourceNode(const Group& group) -> decltype(auto) { using SrcNodeType = REACT_IMPL::EventSourceNode; - return std::make_shared(graphPtr); + return std::make_shared(group); } private: @@ -188,8 +182,6 @@ template class EventSlot : public Event { public: - EventSlot() = default; - EventSlot(const EventSlot&) = default; EventSlot& operator=(const EventSlot&) = default; @@ -210,8 +202,6 @@ class EventSlot : public Event protected: auto CreateSlotNode(const Group& group, const Event& input) -> decltype(auto) { - using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::PrivateGroupInterface; using SlotNodeType = REACT_IMPL::EventSlotNode; return std::make_shared(group, input); @@ -220,7 +210,6 @@ class EventSlot : public Event private: void SetInput(const Event& newInput) { - using REACT_IMPL::PrivateNodeInterface; using REACT_IMPL::NodeId; using REACT_IMPL::ReactiveGraph; using SlotNodeType = REACT_IMPL::EventSlotNode; @@ -240,8 +229,6 @@ template class EventLink : public Event { public: - EventLink() = default; - EventLink(const EventLink&) = default; EventLink& operator=(const EventLink&) = default; diff --git a/include/react/Observer.h b/include/react/Observer.h index 0d2a82e0..b0b64c8e 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -29,8 +29,6 @@ class Observer using NodeType = REACT_IMPL::ObserverNode; public: - Observer() = default; - Observer(const Observer&) = default; Observer& operator=(const Observer&) = default; @@ -41,45 +39,39 @@ class Observer template Observer(F&& func, const Signal& ... subjects) : Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSignalObserverNode(std::forward(func), subjects ...)) - { } + { } // Construct signal observer with explicit group template Observer(const Group& group, F&& func, const Signal& ... subjects) : - Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSignalObserverNode(REACT_IMPL::PrivateGroupInterface::GraphPtr(group), std::forward(func), subjects ...)) - { } + Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSignalObserverNode(group, std::forward(func), subjects ...)) + { } // Construct event observer with implicit group template Observer(F&& func, const Event& subject) : Observer::Observer(REACT_IMPL::CtorTag{ }, CreateEventObserverNode(std::forward(func), subject)) - { } + { } // Construct event observer with explicit group template Observer(const Group& group, F&& func, const Event& subject) : - Observer::Observer(REACT_IMPL::CtorTag{ }, CreateEventObserverNode(REACT_IMPL::PrivateGroupInterface::GraphPtr(group), std::forward(func), subject)) - { } + Observer::Observer(REACT_IMPL::CtorTag{ }, CreateEventObserverNode(group, std::forward(func), subject)) + { } // Constructed synced event observer with implicit group template Observer(F&& func, const Event& subject, const Signal& ... signals) : Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSyncedEventObserverNode(std::forward(func), subject, signals ...)) - { } + { } // Constructed synced event observer with explicit group template Observer(const Group& group, F&& func, const Event& subject, const Signal& ... signals) : - Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSyncedEventObserverNode(REACT_IMPL::PrivateGroupInterface::GraphPtr(group), std::forward(func), subject, signals ...)) - { } - - void Cancel() - { nodePtr_.reset(); } - - bool IsCancelled() const - { return nodePtr_ != nullptr; } + Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSyncedEventObserverNode(group, std::forward(func), subject, signals ...)) + { } -protected: +public: //Internal // Private node ctor Observer(REACT_IMPL::CtorTag, std::shared_ptr&& nodePtr) : nodePtr_(std::move(nodePtr)) @@ -91,14 +83,15 @@ class Observer auto NodePtr() const -> const std::shared_ptr& { return nodePtr_; } +protected: template - auto CreateSignalObserverNode(const std::shared_ptr& graphPtr, F&& func, const Signal& dep1, const Signal& ... deps) -> decltype(auto) + auto CreateSignalObserverNode(const Group& group, F&& func, const Signal& dep1, const Signal& ... deps) -> decltype(auto) { using REACT_IMPL::PrivateSignalLinkNodeInterface; using ObsNodeType = REACT_IMPL::SignalObserverNode::type, T1, Ts ...>; return std::make_shared( - graphPtr, + group, std::forward(func), PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...); } @@ -106,28 +99,26 @@ class Observer template auto CreateSignalObserverNode(F&& func, const Signal& dep1, const Signal& ... deps) -> decltype(auto) { - using REACT_IMPL::PrivateNodeInterface; return CreateSignalObserverNode(PrivateNodeInterface::GraphPtr(dep1), std::forward(func), dep1, deps ...); } template - auto CreateEventObserverNode(const std::shared_ptr& graphPtr, F&& func, const Event& dep) -> decltype(auto) + auto CreateEventObserverNode(const Group& group, F&& func, const Event& dep) -> decltype(auto) { using REACT_IMPL::PrivateEventLinkNodeInterface; using ObsNodeType = REACT_IMPL::EventObserverNode::type, T>; - return std::make_shared(graphPtr, std::forward(func), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep)); + return std::make_shared(group, std::forward(func), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep)); } template auto CreateEventObserverNode(F&& func, const Event& dep) -> decltype(auto) { - using REACT_IMPL::PrivateNodeInterface; return CreateEventObserverNode(PrivateNodeInterface::GraphPtr(dep), std::forward(func), dep); } template - auto CreateSyncedEventObserverNode(const std::shared_ptr& graphPtr, F&& func, const Event& dep, const Signal& ... syncs) -> decltype(auto) + auto CreateSyncedEventObserverNode(const Group& group, F&& func, const Event& dep, const Signal& ... syncs) -> decltype(auto) { using REACT_IMPL::PrivateEventLinkNodeInterface; using REACT_IMPL::PrivateSignalLinkNodeInterface; @@ -140,14 +131,11 @@ class Observer template auto CreateSyncedEventObserverNode(F&& func, const Event& dep, const Signal& ... syncs) -> decltype(auto) { - using REACT_IMPL::PrivateNodeInterface; return CreateSyncedEventObserverNode(PrivateNodeInterface::GraphPtr(dep), std::forward(func), dep, syncs ...); } private: - std::shared_ptr nodePtr_; - - friend struct REACT_IMPL::PrivateNodeInterface; + std::shared_ptr nodePtr_;s }; /******************************************/ REACT_END /******************************************/ From d7c2e2dde4439c0196d8d141b1c5b3f947008bba Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 1 Jan 2017 23:49:19 +0100 Subject: [PATCH 237/266] wip refactoring --- include/react/Algorithm.h | 147 +---------- include/react/Event.h | 266 +++++++++----------- include/react/Group.h | 62 +++-- include/react/Observer.h | 80 ++---- include/react/Signal.h | 217 ++++++++-------- include/react/detail/Defs.h | 18 +- include/react/detail/graph/AlgorithmNodes.h | 54 ++-- include/react/detail/graph/EventNodes.h | 92 +++---- include/react/detail/graph/GraphBase.h | 27 +- include/react/detail/graph/ObserverNodes.h | 51 ++-- include/react/detail/graph/SignalNodes.h | 99 ++++---- 11 files changed, 476 insertions(+), 637 deletions(-) diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index e5d6e076..babc4c8b 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -30,30 +30,12 @@ template auto Hold(const Group& group, T&& initialValue, const Event& evnt) -> Signal { using REACT_IMPL::HoldNode; - using REACT_IMPL::PrivateEventLinkNodeInterface; - using REACT_IMPL::PrivateGroupInterface; - using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::CtorTag; - - const auto& graphPtr = PrivateGroupInterface::GraphPtr(group); - - return PrivateNodeInterface::CreateNodeHelper, HoldNode>( - graphPtr, std::forward(initialValue), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); + return Signal::CreateWithNode>(group, std::forward(initialValue), SameGroupOrLink(group, evnt)); } template auto Hold(T&& initialValue, const Event& evnt) -> Signal -{ - using REACT_IMPL::HoldNode; - using REACT_IMPL::PrivateEventLinkNodeInterface; - using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::CtorTag; - - const auto& graphPtr = PrivateNodeInterface::GraphPtr(evnt); - - return PrivateNodeInterface::CreateNodeHelper, HoldNode>( - graphPtr, std::forward(initialValue), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); -} + { return Hold(evnt.GetGroup(), std::forward(initialValue), evnt); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Monitor - Emits value changes of target signal @@ -62,28 +44,12 @@ template auto Monitor(const Group& group, const Signal& signal) -> Event { using REACT_IMPL::MonitorNode; - using REACT_IMPL::PrivateSignalLinkNodeInterface; - using REACT_IMPL::PrivateGroupInterface; - using REACT_IMPL::PrivateNodeInterface; - - const auto& graphPtr = PrivateGroupInterface::GraphPtr(group); - - return PrivateNodeInterface::CreateNodeHelper, MonitorNode>( - graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal)); + return Event::CreateWithNode>(group, SameGroupOrLink(group, signal)); } template auto Monitor(const Signal& signal) -> Event -{ - using REACT_IMPL::MonitorNode; - using REACT_IMPL::PrivateSignalLinkNodeInterface; - using REACT_IMPL::PrivateNodeInterface; - - const auto& graphPtr = PrivateNodeInterface::GraphPtr(signal); - - return PrivateNodeInterface::CreateNodeHelper, MonitorNode>( - graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal)); -} + { return Monitor(signal.GetGroup(), signal); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Iterate - Iteratively combines signal value with values from event stream (aka Fold) @@ -94,9 +60,6 @@ auto Iterate(const Group& group, T&& initialValue, F&& func, const Event& evn using REACT_IMPL::IterateNode; using REACT_IMPL::IterateByRefNode; using REACT_IMPL::IsCallableWith; - using REACT_IMPL::PrivateEventLinkNodeInterface; - using REACT_IMPL::PrivateGroupInterface; - using REACT_IMPL::PrivateNodeInterface; using FuncType = typename std::decay::type; using IterNodeType = typename std::conditional< @@ -104,33 +67,12 @@ auto Iterate(const Group& group, T&& initialValue, F&& func, const Event& evn IterateNode, IterateByRefNode>::type; - const auto& graphPtr = PrivateGroupInterface::GraphPtr(group); - - return PrivateNodeInterface::CreateNodeHelper, IterNodeType>( - graphPtr, std::forward(initialValue), std::forward(func), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); + return Signal::CreateWithNode(group, std::forward(initialValue), std::forward(func), SameGroupOrLink(group, evnt)); } template auto Iterate(T&& initialValue, F&& func, const Event& evnt) -> Signal -{ - using REACT_IMPL::IterateNode; - using REACT_IMPL::IterateByRefNode; - using REACT_IMPL::IsCallableWith; - using REACT_IMPL::PrivateEventLinkNodeInterface; - using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::CtorTag; - - using FuncType = typename std::decay::type; - using IterNodeType = typename std::conditional< - IsCallableWith,S>::value, - IterateNode, - IterateByRefNode>::type; - - const auto& graphPtr = PrivateNodeInterface::GraphPtr(evnt); - - return PrivateNodeInterface::CreateNodeHelper, IterNodeType>( - graphPtr, std::forward(initialValue), std::forward(func), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); -} + { return Iterate(evnt.GetGroup(), std::forward(initialValue), std::forward(func), evnt); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Iterate - Synced @@ -141,10 +83,6 @@ auto Iterate(const Group& group, T&& initialValue, F&& func, const Event& evn using REACT_IMPL::SyncedIterateNode; using REACT_IMPL::SyncedIterateByRefNode; using REACT_IMPL::IsCallableWith; - using REACT_IMPL::PrivateSignalLinkNodeInterface; - using REACT_IMPL::PrivateEventLinkNodeInterface; - using REACT_IMPL::PrivateGroupInterface; - using REACT_IMPL::PrivateNodeInterface; using FuncType = typename std::decay::type; using IterNodeType = typename std::conditional< @@ -152,35 +90,13 @@ auto Iterate(const Group& group, T&& initialValue, F&& func, const Event& evn SyncedIterateNode, SyncedIterateByRefNode>::type; - const auto& graphPtr = PrivateGroupInterface::GraphPtr(group); - - return PrivateNodeInterface::CreateNodeHelper, IterNodeType>( - graphPtr, std::forward(initialValue), std::forward(func), - PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt), PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signals) ...); + return Signal::CreateWithNode( + group, std::forward(initialValue), std::forward(func), SameGroupOrLink(group, evnt), SameGroupOrLink(group, signals) ...); } template auto Iterate(T&& initialValue, F&& func, const Event& evnt, const Signal& ... signals) -> Signal -{ - using REACT_IMPL::SyncedIterateNode; - using REACT_IMPL::SyncedIterateByRefNode; - using REACT_IMPL::IsCallableWith; - using REACT_IMPL::PrivateSignalLinkNodeInterface; - using REACT_IMPL::PrivateEventLinkNodeInterface;; - using REACT_IMPL::PrivateNodeInterface; - - using FuncType = typename std::decay::type; - using IterNodeType = typename std::conditional< - IsCallableWith, S, Us ...>::value, - SyncedIterateNode, - SyncedIterateByRefNode>::type; - - const auto& graphPtr = PrivateNodeInterface::GraphPtr(evnt); - - return PrivateNodeInterface::CreateNodeHelper, IterNodeType>( - graphPtr, std::forward(initialValue), std::forward(func), - PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt), PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signals) ...); -} + { return Iterate(evnt.GetGroup(), std::forward(initialValue), std::forward(func), evnt, signals ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Snapshot - Sets signal value to value of other signal when event is received @@ -189,30 +105,12 @@ template auto Snapshot(const Group& group, const Signal& signal, const Event& evnt) -> Signal { using REACT_IMPL::SnapshotNode; - using REACT_IMPL::PrivateSignalLinkNodeInterface; - using REACT_IMPL::PrivateEventLinkNodeInterface;; - using REACT_IMPL::PrivateGroupInterface; - using REACT_IMPL::PrivateNodeInterface; - - const auto& graphPtr = PrivateGroupInterface::GraphPtr(group); - - return PrivateNodeInterface::CreateNodeHelper, SnapshotNode>( - graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); + return Signal::CreateWithNode>(group, SameGroupOrLink(group, signal), SameGroupOrLink(group, evnt)); } template auto Snapshot(const Signal& signal, const Event& evnt) -> Signal -{ - using REACT_IMPL::SnapshotNode; - using REACT_IMPL::PrivateSignalLinkNodeInterface; - using REACT_IMPL::PrivateEventLinkNodeInterface;; - using REACT_IMPL::PrivateNodeInterface; - - const auto& graphPtr = PrivateNodeInterface::GraphPtr(signal); - - return PrivateNodeInterface::CreateNodeHelper, SnapshotNode>( - graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); -} + { return Snapshot(signal.GetGroup(), signal, evnt); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Pulse - Emits value of target signal when event is received @@ -221,31 +119,12 @@ template auto Pulse(const Group& group, const Signal& signal, const Event& evnt) -> Event { using REACT_IMPL::PulseNode; - using REACT_IMPL::PrivateSignalLinkNodeInterface; - using REACT_IMPL::PrivateEventLinkNodeInterface;; - using REACT_IMPL::PrivateGroupInterface; - using REACT_IMPL::PrivateNodeInterface; - - const auto& graphPtr = PrivateGroupInterface::GraphPtr(group); - - return PrivateNodeInterface::CreateNodeHelper, PulseNode>( - graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); + return Event::CreateWithNode>(group, SameGroupOrLink(group, signal), SameGroupOrLink(group, evnt)); } template auto Pulse(const Signal& signal, const Event& evnt) -> Event -{ - using REACT_IMPL::PulseNode; - using REACT_IMPL::PrivateSignalLinkNodeInterface; - using REACT_IMPL::PrivateEventLinkNodeInterface;; - using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::CtorTag; - - const auto& graphPtr = PrivateNodeInterface::GraphPtr(signal); - - return PrivateNodeInterface::CreateNodeHelper, PulseNode>( - graphPtr, PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, signal), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, evnt)); -} + { return Pulse(signal.GetGroup(), signal, evnt); } /******************************************/ REACT_END /******************************************/ diff --git a/include/react/Event.h b/include/react/Event.h index d0dec01b..e4d2a987 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -21,7 +21,48 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -struct PrivateEventLinkNodeInterface; +template +class EventInternals +{ +protected: + using NodeType = EventStreamNode; + using StorageType = typename NodeType::StorageType; + +public: + EventInternals(const EventInternals&) = default; + EventInternals& operator=(const EventInternals&) = default; + + EventInternals(EventInternals&&) = default; + EventInternals& operator=(EventInternals&&) = default; + + EventInternals(std::shared_ptr&& nodePtr) : + nodePtr_( std::move(nodePtr) ) + { } + + auto GetNodePtr() -> std::shared_ptr& + { return nodePtr_; } + + auto GetNodePtr() const -> const std::shared_ptr& + { return nodePtr_; } + + NodeId GetNodeId() const + { return nodePtr_->GetNodeId(); } + + StorageType& Events() + { return nodePtr->Events(); } + + const StorageType& Events() const + { return nodePtr->Events(); } + + void SetPendingSuccessorCount(size_t count) + { nodePtr_->SetPendingSuccessorCount(count); } + + void DecrementPendingSuccessorCount() + { nodePtr_->DecrementPendingSuccessorCount(); } + +private: + std::shared_ptr nodePtr_; +}; /****************************************/ REACT_IMPL_END /***************************************/ @@ -31,11 +72,8 @@ struct PrivateEventLinkNodeInterface; /// Event /////////////////////////////////////////////////////////////////////////////////////////////////// template -class Event +class Event : protected REACT_IMPL::EventInternals { -private: - using NodeType = REACT_IMPL::EventStreamNode; - public: Event(const Event&) = default; Event& operator=(const Event&) = default; @@ -45,7 +83,7 @@ class Event template Event(F&& func, const Event& dep) : - Event::Event( REACT_IMPL::CtorTag{ }, CreateProcessingNode(std::forward(func), dep) ) + Event::Event( REACT_IMPL::CtorTag{ }, CreateProcessingNode(dep.GetGroup(), std::forward(func), dep) ) { } template @@ -55,7 +93,7 @@ class Event template Event(F&& func, const Event& dep, const Signal& ... signals) : - Event::Event( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(std::forward(func), dep, signals ...) ) + Event::Event( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(dep.GetGroup(), std::forward(func), dep, signals ...) ) { } template @@ -78,44 +116,56 @@ class Event auto Transform(F&& pred) const -> decltype(auto) { return REACT::Transform(*this, std::forward(f)); }*/ -protected: - // Internal node ctor - Event(REACT_IMPL::CtorTag, std::shared_ptr&& nodePtr) : - nodePtr_( std::move(nodePtr) ) - { } + auto GetGroup() const -> const Group& + { return GetNodePtr()->GetGroup(); } - auto NodePtr() -> std::shared_ptr& - { return nodePtr_; } + auto GetGroup() -> Group& + { return GetNodePtr()->GetGroup(); } - auto NodePtr() const -> const std::shared_ptr& - { return nodePtr_; } + friend bool operator==(const Event& a, const Event& b) + { return a.GetNodePtr() == b.GetNodePtr(); } + + friend bool operator!=(const Event& a, const Event& b) + { return !(a == b); } + + friend auto GetInternals(Event& e) -> REACT_IMPL::EventInternals& + { return e; } + + friend auto GetInternals(const Event& e) -> const REACT_IMPL::EventInternals& + { return e; } + +public: // Internal + template + static Event CreateWithNode(TArgs&& ... args) + { + return Event( REACT_IMPL::CtorTag{ }, std::make_shared(std::forward(args) ...) ); + } + +protected: + // Private node ctor + Event(REACT_IMPL::CtorTag, std::shared_ptr>&& nodePtr) : + Event::EventInternals( std::move(nodePtr) ) + { } template - auto CreateProcessingNode(F&& func, const Event& dep) -> decltype(auto) + auto CreateProcessingNode(const Group& group, F&& func, const Event& dep) -> decltype(auto) { - using REACT_IMPL::PrivateNodeInterface; - using EventNodeType = REACT_IMPL::EventProcessingNode::type>; + using REACT_IMPL::EventProcessingNode; + using REACT_IMPL::SameGroupOrLink; - return std::make_shared(PrivateNodeInterface::GraphPtr(dep), std::forward(func), PrivateNodeInterface::NodePtr(dep)); + return std::make_shared::type>>( + group, std::forward(func), SameGroupOrLink(group, dep)); } template - auto CreateSyncedProcessingNode(F&& func, const Event& dep, const Signal& ... syncs) -> decltype(auto) + auto CreateSyncedProcessingNode(const Group& group, F&& func, const Event& dep, const Signal& ... syncs) -> decltype(auto) { - using REACT_IMPL::GetCheckedGraphPtr; - using REACT_IMPL::PrivateNodeInterface; - using EventNodeType = REACT_IMPL::SyncedEventProcessingNode::type, Us ...>; - - return std::make_shared( - GetCheckedGraphPtr(dep, syncs ...), - std::forward(func), - PrivateNodeInterface::NodePtr(dep), PrivateNodeInterface::NodePtr(syncs) ...); - } - -private: - std::shared_ptr nodePtr_; + using REACT_IMPL::SyncedEventProcessingNode; + using REACT_IMPL::SameGroupOrLink; - friend struct REACT_IMPL::PrivateNodeInterface; + return std::make_shared::type, Us ...>>( + group, std::forward(func), SameGroupOrLink(group, dep), SameGroupOrLink(group, syncs) ...); + } }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -155,8 +205,8 @@ class EventSource : public Event protected: auto CreateSourceNode(const Group& group) -> decltype(auto) { - using SrcNodeType = REACT_IMPL::EventSourceNode; - return std::make_shared(group); + using REACT_IMPL::EventSourceNode; + return std::make_shared>(group); } private: @@ -167,10 +217,11 @@ class EventSource : public Event using REACT_IMPL::ReactiveGraph; using SrcNodeType = REACT_IMPL::EventSourceNode; - SrcNodeType* castedPtr = static_cast(this->NodePtr().get()); + SrcNodeType* castedPtr = static_cast(this->GetNodePtr().get()); NodeId nodeId = castedPtr->GetNodeId(); - auto& graphPtr = NodePtr()->GraphPtr(); + auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); + graphPtr->AddInput(nodeId, [castedPtr, &value] { castedPtr->EmitValue(std::forward(value)); }); } }; @@ -188,9 +239,14 @@ class EventSlot : public Event EventSlot(EventSlot&&) = default; EventSlot& operator=(EventSlot&&) = default; - // Construct with value + // Construct with explicit group EventSlot(const Group& group, const Event& input) : - EventSlot::EventSlotBase( REACT_IMPL::CtorTag{ }, CreateSlotNode(group, input) ) + EventSlot::Event( REACT_IMPL::CtorTag{ }, CreateSlotNode(group, input) ) + { } + + // Construct with implicit group + EventSlot(const Event& input) : + EventSlot::Event( REACT_IMPL::CtorTag{ }, CreateSlotNode(input.GetGroup(), input) ) { } void Set(const Event& newInput) @@ -202,9 +258,8 @@ class EventSlot : public Event protected: auto CreateSlotNode(const Group& group, const Event& input) -> decltype(auto) { - using SlotNodeType = REACT_IMPL::EventSlotNode; - - return std::make_shared(group, input); + using REACT_IMPL::EventSlotNode; + return std::make_shared>(group, SameGroupOrLink(input)); } private: @@ -237,27 +292,23 @@ class EventLink : public Event // Construct with explicit group EventLink(const Group& group, const Event& input) : - SignalLink::Signal( REACT_IMPL::CtorTag{ }, CreateLinkNode(group, input) ) + EventLink::Event( REACT_IMPL::CtorTag{ }, CreateLinkNode(group, input) ) { } // Construct with implicit group explicit EventLink(const Event& input) : - SignalLink::Signal( REACT_IMPL::CtorTag{ }, CreateLinkNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(input), input) ) + EventLink::Event( REACT_IMPL::CtorTag{ }, CreateLinkNode(input.GetGroup(), input) ) { } protected: static auto CreateLinkNode(const Group& group, const Event& input) -> decltype(auto) { - using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::PrivateGroupInterface; - using EventNodeType = REACT_IMPL::EventLinkNode; + using REACT_IMPL::EventLinkNode; - auto node = std::make_shared(group, PrivateNodeInterface::GraphPtr(input), PrivateNodeInterface::NodePtr(input)); - node->SetWeakSelfPtr(std::weak_ptr{ node }); + auto node = std::make_shared>(group, input); + node->SetWeakSelfPtr(std::weak_ptr>{ node }); return node; } - - friend struct REACT_IMPL::PrivateEventLinkNodeInterface; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -267,9 +318,7 @@ template auto Merge(const Group& group, const Event& dep1, const Event& ... deps) -> decltype(auto) { using REACT_IMPL::EventMergeNode; - using REACT_IMPL::PrivateEventLinkNodeInterface; - using REACT_IMPL::PrivateGroupInterface; - using REACT_IMPL::PrivateNodeInterface; + using REACT_IMPL::SameGroupOrLink; using REACT_IMPL::CtorTag; static_assert(sizeof...(Us) > 0, "Merge requires at least 2 inputs."); @@ -280,33 +329,12 @@ auto Merge(const Group& group, const Event& dep1, const Event& ... deps) typename std::common_type::type, T>::type; - const auto& graphPtr = PrivateGroupInterface::GraphPtr(group); - - return PrivateNodeInterface::CreateNodeHelper, EventMergeNode>( - graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...); + return Event::CreateWithNode>(group, SameGroupOrLink(group, dep1), SameGroupOrLink(group, deps) ...); } template auto Merge(const Event& dep1, const Event& ... deps) -> decltype(auto) -{ - using REACT_IMPL::EventMergeNode; - using REACT_IMPL::PrivateEventLinkNodeInterface; - using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::CtorTag; - - static_assert(sizeof...(Us) > 0, "Merge requires at least 2 inputs."); - - // If supplied, use merge type, otherwise default to common type. - using E = typename std::conditional< - std::is_same::value, - typename std::common_type::type, - T>::type; - - const auto& graphPtr = PrivateNodeInterface::GraphPtr(dep1); - - return PrivateNodeInterface::CreateNodeHelper, EventMergeNode>( - graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...); -} + { return Merge(dep1.GetGroup(), dep1, deps ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Filter @@ -322,12 +350,7 @@ auto Filter(const Group& group, F&& pred, const Event& dep) -> Event template auto Filter(F&& pred, const Event& dep) -> Event -{ - auto filterFunc = [capturedPred = std::forward(pred)] (EventRange inRange, EventSink out) - { std::copy_if(inRange.begin(), inRange.end(), out, capturedPred); }; - - return Event(std::move(filterFunc), dep); -} + { return Filter(dep.GetGroup(), std::forward(pred), dep); } template auto Filter(const Group& group, F&& pred, const Event& dep, const Signal& ... signals) -> Event @@ -344,16 +367,7 @@ auto Filter(const Group& group, F&& pred, const Event& dep, const Signal& template auto Filter(F&& pred, const Event& dep, const Signal& ... signals) -> Event -{ - auto filterFunc = [capturedPred = std::forward(pred)] (EventRange inRange, EventSink out, const Us& ... values) - { - for (const auto& v : inRange) - if (capturedPred(v, values ...)) - *out++ = v; - }; - - return Event(std::move(filterFunc), dep, signals ...); -} + { return Filter(dep.GetGroup(), std::forward(pred), dep); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Transform @@ -369,12 +383,7 @@ auto Transform(const Group& group, F&& op, const Event& dep) -> Event template auto Transform(F&& op, const Event& dep) -> Event -{ - auto transformFunc = [capturedOp = std::forward(op)] (EventRange inRange, EventSink out) - { std::transform(inRange.begin(), inRange.end(), out, capturedOp); }; - - return Event(std::move(transformFunc), dep); -} + { return Transform(dep1.GetGroup(), std::forward(op), dep); } template auto Transform(const Group& group, F&& op, const Event& dep, const Signal& ... signals) -> Event @@ -390,15 +399,7 @@ auto Transform(const Group& group, F&& op, const Event& dep, const Signal template auto Transform(F&& op, const Event& dep, const Signal& ... signals) -> Event -{ - auto transformFunc = [capturedOp = std::forward(pred)] (EventRange inRange, EventSink out, const Vs& ... values) - { - for (const auto& v : inRange) - *out++ = capturedPred(v, values ...); - }; - - return Event(std::move(transformFunc), dep, signals ...); -} + { return Transform(dep.GetGroup(), std::forward(op), dep, signals ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Flatten @@ -418,34 +419,16 @@ template auto Join(const Group& group, const Event& dep1, const Event& ... deps) -> Event> { using REACT_IMPL::EventJoinNode; - using REACT_IMPL::PrivateGroupInterface; - using REACT_IMPL::PrivateEventLinkNodeInterface; - using REACT_IMPL::PrivateNodeInterface; static_assert(sizeof...(Us) > 0, "Join requires at least 2 inputs."); - // If supplied, use merge type, otherwise default to common type. - const auto& graphPtr = PrivateGroupInterface::GraphPtr(group); - - return PrivateNodeInterface::CreateNodeHelper>, EventJoinNode>( - graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...); + return Event>::CreateWithNode>( + group, SameGroupOrLink(group, dep1), SameGroupOrLink(group, deps) ...); } template auto Join(const Event& dep1, const Event& ... deps) -> Event> -{ - using REACT_IMPL::EventJoinNode; - using REACT_IMPL::PrivateEventLinkNodeInterface; - using REACT_IMPL::PrivateNodeInterface; - - static_assert(sizeof...(Us) > 0, "Join requires at least 2 inputs."); - - // If supplied, use merge type, otherwise default to common type. - const auto& graphPtr = PrivateNodeInterface::GraphPtr(dep1); - - return PrivateNodeInterface::CreateNodeHelper>, EventJoinNode>( - graphPtr, PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...); -} + { return Join(dep1.GetGroup(), dep1, deps ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Token @@ -474,23 +457,14 @@ bool Equals(const Event& lhs, const Event& rhs) return lhs.Equals(rhs); } -struct PrivateEventLinkNodeInterface +template +static Event SameGroupOrLink(const Group& targetGroup, const Event& dep) { - template - static auto GetLocalNodePtr(const std::shared_ptr& targetGraph, const Event& dep) -> std::shared_ptr> - { - const std::shared_ptr& sourceGraph = PrivateNodeInterface::GraphPtr(dep); - - if (sourceGraph == targetGraph) - { - return PrivateNodeInterface::NodePtr(dep); - } - else - { - return EventLink::CreateLinkNode(targetGraph, dep); - } - } -}; + if (dep.GetGroup() == targetGroup) + return dep; + else + return EventLink( targetGroup, dep ); +} /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/Group.h b/include/react/Group.h index 6e9a645d..d68526df 100644 --- a/include/react/Group.h +++ b/include/react/Group.h @@ -21,8 +21,33 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -struct PrivateGroupInterface; -struct CtorTag { }; +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// GroupInternals +/////////////////////////////////////////////////////////////////////////////////////////////////// +class GroupInternals +{ + using GraphType = REACT_IMPL::ReactiveGraph; + +public: + GroupInternals() : + graphPtr_( std::make_shared() ) + { } + + GroupInternals(const GroupInternals&) = default; + GroupInternals& operator=(const GroupInternals&) = default; + + GroupInternals(GroupInternals&&) = default; + GroupInternals& operator=(GroupInternals&&) = default; + + auto GetGraphPtr() -> std::shared_ptr& + { return graphPtr_; } + + auto GetGraphPtr() const -> const std::shared_ptr& + { return graphPtr_; } + +private: + std::shared_ptr graphPtr_; +}; /****************************************/ REACT_IMPL_END /***************************************/ @@ -85,26 +110,22 @@ class TransactionStatus #endif /////////////////////////////////////////////////////////////////////////////////////////////////// -/// GroupBase +/// Group /////////////////////////////////////////////////////////////////////////////////////////////////// -class Group +class Group : protected REACT_IMPL::GroupInternals { - using GraphType = REACT_IMPL::ReactiveGraph; - public: - Group() : - graphPtr_( std::make_shared() ) - { } + Group() = default; Group(const Group&) = default; Group& operator=(const Group&) = default; - Group(Group&& other) = default; - Group& operator=(Group&& other) = default; + Group(Group&&) = default; + Group& operator=(Group&&) = default; template void DoTransaction(F&& func) - { graphPtr_->DoTransaction(std::forward(func)); } + { GetGraphPtr()->DoTransaction(std::forward(func)); } template void EnqueueTransaction(F&& func) @@ -112,18 +133,19 @@ class Group template void EnqueueTransaction(TransactionFlags flags, F&& func) - { graphPtr_->EnqueueTransaction(flags, std::forward(func)); } + { GetGraphPtr()->EnqueueTransaction(flags, std::forward(func)); } + friend bool operator==(const Group& a, const Group& b) + { return a.GetGraphPtr() == b.GetGraphPtr(); } -public: // Internal - auto GraphPtr() -> std::shared_ptr& - { return graphPtr_; } + friend bool operator!=(const Group& a, const Group& b) + { return !(a == b); } - auto GraphPtr() const -> const std::shared_ptr& - { return graphPtr_; } + friend auto GetInternals(Group& g) -> REACT_IMPL::GroupInternals& + { return g; } -private: - std::shared_ptr graphPtr_; + friend auto GetInternals(const Group& g) -> const REACT_IMPL::GroupInternals& + { return g; } }; /******************************************/ REACT_END /******************************************/ diff --git a/include/react/Observer.h b/include/react/Observer.h index b0b64c8e..a2ff497a 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -35,22 +35,16 @@ class Observer Observer(Observer&&) = default; Observer& operator=(Observer&&) = default; - // Construct signal observer with implicit group - template - Observer(F&& func, const Signal& ... subjects) : - Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSignalObserverNode(std::forward(func), subjects ...)) - { } - // Construct signal observer with explicit group - template - Observer(const Group& group, F&& func, const Signal& ... subjects) : - Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSignalObserverNode(group, std::forward(func), subjects ...)) + template + Observer(const Group& group, F&& func, const Signal& subject1, const Signal& ... subjects) : + Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSignalObserverNode(group, std::forward(func), subject1, subjects ...)) { } - // Construct event observer with implicit group - template - Observer(F&& func, const Event& subject) : - Observer::Observer(REACT_IMPL::CtorTag{ }, CreateEventObserverNode(std::forward(func), subject)) + // Construct signal observer with implicit group + template + Observer(F&& func, const Signal& subject1, const Signal& ... subjects) : + Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSignalObserverNode(subject1.GetGroup(), std::forward(func), subject1, subjects ...)) { } // Construct event observer with explicit group @@ -59,10 +53,10 @@ class Observer Observer::Observer(REACT_IMPL::CtorTag{ }, CreateEventObserverNode(group, std::forward(func), subject)) { } - // Constructed synced event observer with implicit group - template - Observer(F&& func, const Event& subject, const Signal& ... signals) : - Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSyncedEventObserverNode(std::forward(func), subject, signals ...)) + // Construct event observer with implicit group + template + Observer(F&& func, const Event& subject) : + Observer::Observer(REACT_IMPL::CtorTag{ }, CreateEventObserverNode(subject.GetGroup(), std::forward(func), subject)) { } // Constructed synced event observer with explicit group @@ -71,6 +65,12 @@ class Observer Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSyncedEventObserverNode(group, std::forward(func), subject, signals ...)) { } + // Constructed synced event observer with implicit group + template + Observer(F&& func, const Event& subject, const Signal& ... signals) : + Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSyncedEventObserverNode(subject.GetGroup(), std::forward(func), subject, signals ...)) + { } + public: //Internal // Private node ctor Observer(REACT_IMPL::CtorTag, std::shared_ptr&& nodePtr) : @@ -87,55 +87,29 @@ class Observer template auto CreateSignalObserverNode(const Group& group, F&& func, const Signal& dep1, const Signal& ... deps) -> decltype(auto) { - using REACT_IMPL::PrivateSignalLinkNodeInterface; - using ObsNodeType = REACT_IMPL::SignalObserverNode::type, T1, Ts ...>; - - return std::make_shared( - group, - std::forward(func), - PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...); - } - - template - auto CreateSignalObserverNode(F&& func, const Signal& dep1, const Signal& ... deps) -> decltype(auto) - { - return CreateSignalObserverNode(PrivateNodeInterface::GraphPtr(dep1), std::forward(func), dep1, deps ...); + using REACT_IMPL::SignalObserverNode; + return std::make_shared::type, T1, Ts ...>>( + group, std::forward(func), SameGroupOrLink(group, dep1), SameGroupOrLink(group, deps) ...); } template auto CreateEventObserverNode(const Group& group, F&& func, const Event& dep) -> decltype(auto) { - using REACT_IMPL::PrivateEventLinkNodeInterface; - using ObsNodeType = REACT_IMPL::EventObserverNode::type, T>; - - return std::make_shared(group, std::forward(func), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep)); - } - - template - auto CreateEventObserverNode(F&& func, const Event& dep) -> decltype(auto) - { - return CreateEventObserverNode(PrivateNodeInterface::GraphPtr(dep), std::forward(func), dep); + using REACT_IMPL::EventObserverNode; + return std::make_shared::type, T>>( + group, std::forward(func), SameGroupOrLink(group, dep)); } template auto CreateSyncedEventObserverNode(const Group& group, F&& func, const Event& dep, const Signal& ... syncs) -> decltype(auto) { - using REACT_IMPL::PrivateEventLinkNodeInterface; - using REACT_IMPL::PrivateSignalLinkNodeInterface; - using ObsNodeType = REACT_IMPL::SyncedEventObserverNode::type, T, Us ...>; - - return std::make_shared( - graphPtr, std::forward(func), PrivateEventLinkNodeInterface::GetLocalNodePtr(graphPtr, dep), PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, syncs) ...); - } - - template - auto CreateSyncedEventObserverNode(F&& func, const Event& dep, const Signal& ... syncs) -> decltype(auto) - { - return CreateSyncedEventObserverNode(PrivateNodeInterface::GraphPtr(dep), std::forward(func), dep, syncs ...); + using REACT_IMPL::SyncedEventObserverNode; + return std::make_shared::type, T, Us ...>>( + group, std::forward(func), SameGroupOrLink(group, dep), SameGroupOrLink(group, syncs) ...); } private: - std::shared_ptr nodePtr_;s + std::shared_ptr nodePtr_; }; /******************************************/ REACT_END /******************************************/ diff --git a/include/react/Signal.h b/include/react/Signal.h index bc607a9f..58e5da37 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -9,10 +9,6 @@ #pragma once -#if _MSC_VER && !__INTEL_COMPILER - #pragma warning(disable : 4503) -#endif - #include "react/detail/Defs.h" #include "react/API.h" #include "react/Group.h" @@ -26,7 +22,38 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ -struct PrivateSignalLinkNodeInterface; +template +class SignalInternals +{ +public: + SignalInternals(const SignalInternals&) = default; + SignalInternals& operator=(const SignalInternals&) = default; + + SignalInternals(SignalInternals&&) = default; + SignalInternals& operator=(SignalInternals&&) = default; + + SignalInternals(std::shared_ptr>&& nodePtr) : + nodePtr_( std::move(nodePtr) ) + { } + + auto GetNodePtr() -> std::shared_ptr>& + { return nodePtr_; } + + auto GetNodePtr() const -> const std::shared_ptr>& + { return nodePtr_; } + + NodeId GetNodeId() const + { return nodePtr_->GetNodeId(); } + + S& Value() + { return nodePtr_->Value(); } + + const S& Value() const + { return nodePtr_->Value(); } + +private: + std::shared_ptr> nodePtr_; +}; /****************************************/ REACT_IMPL_END /***************************************/ @@ -36,11 +63,8 @@ struct PrivateSignalLinkNodeInterface; /// Signal /////////////////////////////////////////////////////////////////////////////////////////////////// template -class Signal +class Signal : protected REACT_IMPL::SignalInternals { -private: - using NodeType = REACT_IMPL::SignalNode; - public: Signal(const Signal&) = default; Signal& operator=(const Signal&) = default; @@ -48,48 +72,53 @@ class Signal Signal(Signal&&) = default; Signal& operator=(Signal&&) = default; - // Construct func signal with explicit group + // Construct with explicit group template explicit Signal(const Group& group, F&& func, const Signal& dep1, const Signal& ... deps) : - Signal::Signal( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateGroupInterface::GraphPtr(group), std::forward(func), dep1, deps ...) ) + Signal::SignalInternals( CreateFuncNode(group, std::forward(func), dep1, deps ...) ) { } - // Construct func signal with implicit group + // Construct with implicit group template explicit Signal(F&& func, const Signal& dep1, const Signal& ... deps) : - Signal::Signal( REACT_IMPL::CtorTag{ }, CreateFuncNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(dep1), std::forward(func), dep1, deps ...) ) + Signal::SignalInternals( CreateFuncNode(dep1.GetGroup(), std::forward(func), dep1, deps ...) ) { } - const Group& GetGroup() const - { return nodePtr_->GetGroup(); } + auto GetGroup() const -> const Group& + { return this->GetNodePtr()->GetGroup(); } -public: // Internal - // Private node ctor - Signal(REACT_IMPL::CtorTag, std::shared_ptr&& nodePtr) : - nodePtr_( std::move(nodePtr) ) - { } + auto GetGroup() -> Group& + { return this->GetNodePtr()->GetGroup(); } - auto NodePtr() -> std::shared_ptr& - { return nodePtr_; } + friend bool operator==(const Signal& a, const Signal& b) + { return a.GetNodePtr() == b.GetNodePtr(); } - auto NodePtr() const -> const std::shared_ptr& - { return nodePtr_; } + friend bool operator!=(const Signal& a, const Signal& b) + { return !(a == b); } + + friend auto GetInternals(Signal& s) -> REACT_IMPL::SignalInternals& + { return s; } + + friend auto GetInternals(const Signal& s) -> const REACT_IMPL::SignalInternals& + { return s; } + + template + static Signal CreateWithNode(TArgs&& ... args) + { return Signal( REACT_IMPL::CtorTag{ }, std::make_shared(std::forward(args) ...) ); } protected: + Signal(REACT_IMPL::CtorTag, std::shared_ptr>&& nodePtr) : + Signal::SignalInternals( std::move(nodePtr) ) + { } + template - auto CreateFuncNode(const std::shared_ptr& graphPtr, F&& func, const Signal& dep1, const Signal& ... deps) -> decltype(auto) + auto CreateFuncNode(const Group& group, F&& func, const Signal& dep1, const Signal& ... deps) -> decltype(auto) { - using REACT_IMPL::PrivateSignalLinkNodeInterface; - using FuncNodeType = REACT_IMPL::SignalFuncNode::type, T1, Ts ...>; + using REACT_IMPL::SignalFuncNode; - return std::make_shared( - graphPtr, - std::forward(func), - PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, dep1), PrivateSignalLinkNodeInterface::GetLocalNodePtr(graphPtr, deps) ...); + return std::make_shared::type, T1, Ts ...>>( + group, std::forward(func), SameGroupOrLink(group, dep1), SameGroupOrLink(group, deps) ...); } - -private: - std::shared_ptr nodePtr_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -132,18 +161,24 @@ class VarSignal : public Signal void Modify(const F& func) { ModifyValue(func); } + friend bool operator==(const VarSignal& a, VarSignal& b) + { return a.GetNodePtr() == b.GetNodePtr(); } + + friend bool operator!=(const VarSignal& a, VarSignal& b) + { return !(a == b); } + protected: static auto CreateVarNode(const Group& group) -> decltype(auto) { - using VarNodeType = REACT_IMPL::VarSignalNode; - return std::make_shared(graphPtr); + using REACT_IMPL::VarSignalNode; + return std::make_shared>(group); } template static auto CreateVarNode(const Group& group, T&& value) -> decltype(auto) { - using VarNodeType = REACT_IMPL::VarSignalNode; - return std::make_shared(graphPtr, std::forward(value)); + using REACT_IMPL::VarSignalNode; + return std::make_shared>(group, std::forward(value)); } private: @@ -153,10 +188,11 @@ class VarSignal : public Signal using REACT_IMPL::NodeId; using VarNodeType = REACT_IMPL::VarSignalNode; - VarNodeType* castedPtr = static_cast(this->NodePtr().get()); + VarNodeType* castedPtr = static_cast(this->GetNodePtr().get()); NodeId nodeId = castedPtr->GetNodeId(); - auto& graphPtr = NodePtr()->GraphPtr(); + auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); + graphPtr->AddInput(nodeId, [castedPtr, &newValue] { castedPtr->SetValue(std::forward(newValue)); }); } @@ -166,10 +202,11 @@ class VarSignal : public Signal using REACT_IMPL::NodeId; using VarNodeType = REACT_IMPL::VarSignalNode; - VarNodeType* castedPtr = static_cast(this->NodePtr().get()); + VarNodeType* castedPtr = static_cast(this->GetNodePtr().get()); NodeId nodeId = castedPtr->GetNodeId(); - auto& graphPtr = castedPtr->GraphPtr(); + auto& graphPtr = BaseCast(this->GetGroup()).GetGraphPtr(); + graphPtr->AddInput(nodeId, [castedPtr, &func] { castedPtr->ModifyValue(func); }); } }; @@ -189,17 +226,17 @@ class SignalSlot : public Signal // Construct with group + default explicit SignalSlot(const Group& group) : - SignalSlot::Signal( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateGroupInterface::GraphPtr(group)) ) + SignalSlot::Signal( REACT_IMPL::CtorTag{ }, CreateSlotNode(group) ) { } // Construct with group + value SignalSlot(const Group& group, const Signal& input) : - SignalSlot::Signal( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateGroupInterface::GraphPtr(group), input) ) + SignalSlot::Signal( REACT_IMPL::CtorTag{ }, CreateSlotNode(group, input) ) { } // Construct with value explicit SignalSlot(const Signal& input) : - SignalSlot::Signal( REACT_IMPL::CtorTag{ }, CreateSlotNode(REACT_IMPL::PrivateNodeInterface::GraphPtr(input), input) ) + SignalSlot::Signal( REACT_IMPL::CtorTag{ }, CreateSlotNode(input.GetGroup(), input) ) { } void Set(const Signal& newInput) @@ -209,26 +246,30 @@ class SignalSlot : public Signal { SetInput(newInput); } protected: - static auto CreateSlotNode(const Group& group, const Signal& input) -> decltype(auto) + static auto CreateSlotNode(const Group& group) -> decltype(auto) { - using REACT_IMPL::PrivateNodeInterface; - using SlotNodeType = REACT_IMPL::SignalSlotNode; + using REACT_IMPL::SignalSlotNode; + return std::make_shared>(group); + } - return std::make_shared(graphPtr, PrivateNodeInterface::NodePtr(input)); + static auto CreateSlotNode(const Group& group, const Signal& input) -> decltype(auto) + { + using REACT_IMPL::SignalSlotNode; + return std::make_shared>(group, input); } private: void SetInput(const Signal& newInput) { - using REACT_IMPL::PrivateNodeInterface; using REACT_IMPL::NodeId; using SlotNodeType = REACT_IMPL::SignalSlotNode; - SlotNodeType* castedPtr = static_cast(this->NodePtr().get()); + SlotNodeType* castedPtr = static_cast(this->GetNodePtr().get()); NodeId nodeId = castedPtr->GetInputNodeId(); - auto& graphPtr = NodePtr()->GraphPtr(); - graphPtr->AddInput(nodeId, [castedPtr, &newInput] { castedPtr->SetInput(PrivateNodeInterface::NodePtr(newInput)); }); + auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); + + graphPtr->AddInput(nodeId, [castedPtr, &newInput] { castedPtr->SetInput(newInput); }); } }; @@ -258,16 +299,12 @@ class SignalLink : public Signal protected: static auto CreateLinkNode(const Group& group, const Signal& input) -> decltype(auto) { - using REACT_IMPL::PrivateNodeInterface; - using REACT_IMPL::PrivateGroupInterface; - using LinkNodeType = REACT_IMPL::SignalLinkNode; + using REACT_IMPL::SignalLinkNode; - auto node = std::make_shared(graphPtr, PrivateNodeInterface::GraphPtr(input), PrivateNodeInterface::NodePtr(input)); - node->SetWeakSelfPtr(std::weak_ptr{ node }); + auto node = std::make_shared>(group, PrivateNodeInterface::GraphPtr(input), PrivateNodeInterface::NodePtr(input)); + node->SetWeakSelfPtr(std::weak_ptr>{ node }); return node; } - - friend struct REACT_IMPL::PrivateSignalLinkNodeInterface; }; /******************************************/ REACT_END /******************************************/ @@ -280,61 +317,15 @@ bool Equals(const Signal& lhs, const Signal& rhs) return lhs.Equals(rhs); } -struct PrivateSignalLinkNodeInterface +template +static Signal SameGroupOrLink(const Group& targetGroup, const Signal& dep) { - template - static auto GetLocalNodePtr(const std::shared_ptr& targetGraph, const Signal& sig) -> std::shared_ptr> - { - const std::shared_ptr& sourceGraph = PrivateNodeInterface::GraphPtr(sig); - - if (sourceGraph == targetGraph) - { - return PrivateNodeInterface::NodePtr(sig); - } - else - { - return SignalLink::CreateLinkNode(targetGraph, sig); - } - } -}; + if (dep.GetGroup() == targetGroup) + return dep; + else + return SignalLink( group, dep ); +} /****************************************/ REACT_IMPL_END /***************************************/ -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Flatten macros -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Note: Using static_cast rather than -> return type, because when using lambda for inline -// class initialization, decltype did not recognize the parameter r -// Note2: MSVC doesn't like typename in the lambda -#if _MSC_VER && !__INTEL_COMPILER - #define REACT_MSVC_NO_TYPENAME -#else - #define REACT_MSVC_NO_TYPENAME typename -#endif - -#define REACTIVE_REF(obj, name) \ - Flatten( \ - MakeSignal( \ - obj, \ - [] (const REACT_MSVC_NO_TYPENAME \ - REACT_IMPL::Identity::Type::ValueT& r) \ - { \ - using T = decltype(r.name); \ - using S = REACT_MSVC_NO_TYPENAME REACT::DecayInput::Type; \ - return static_cast(r.name); \ - })) - -#define REACTIVE_PTR(obj, name) \ - Flatten( \ - MakeSignal( \ - obj, \ - [] (REACT_MSVC_NO_TYPENAME \ - REACT_IMPL::Identity::Type::ValueT r) \ - { \ - assert(r != nullptr); \ - using T = decltype(r->name); \ - using S = REACT_MSVC_NO_TYPENAME REACT::DecayInput::Type; \ - return static_cast(r->name); \ - })) - #endif // REACT_SIGNAL_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/Defs.h b/include/react/detail/Defs.h index 24f4cd97..6bdda2e0 100644 --- a/include/react/detail/Defs.h +++ b/include/react/detail/Defs.h @@ -21,22 +21,6 @@ #define REACT_IMPL_END REACT_END } #define REACT_IMPL REACT ::impl -// Assert with message -#define REACT_ASSERT(condition, ...) for (; !(condition); assert(condition)) printf(__VA_ARGS__) -#define REACT_ERROR(...) REACT_ASSERT(false, __VA_ARGS__) - -// Thread local storage -#if _WIN32 || _WIN64 - // MSVC - #define REACT_TLS __declspec(thread) -#elif __GNUC__ - // GCC - #define REACT_TLS __thread -#else - // Standard C++11 - #define REACT_TLS thread_local -#endif - /***************************************/ REACT_IMPL_BEGIN /**************************************/ // Type aliases @@ -44,6 +28,8 @@ using uint = unsigned int; using uchar = unsigned char; using std::size_t; +struct CtorTag { }; + /****************************************/ REACT_IMPL_END /***************************************/ #endif // REACT_DETAIL_DEFS_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index 274581fd..0287a816 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -86,8 +86,8 @@ class IterateNode : public SignalNode { public: template - IterateNode(const std::shared_ptr& graphPtr, T&& init, FIn&& func, const std::shared_ptr>& events) : - IterateNode::SignalNode( graphPtr, std::forward(init) ), + IterateNode(const Group& group, T&& init, FIn&& func, const Event& events) : + IterateNode::SignalNode( group, std::forward(init) ), func_( std::forward(func) ), events_( events ) { @@ -125,7 +125,7 @@ class IterateNode : public SignalNode { return 1; } private: - std::shared_ptr> events_; + Event events_; F func_; }; @@ -138,8 +138,8 @@ class IterateByRefNode : public SignalNode { public: template - IterateByRefNode(const std::shared_ptr& graphPtr, T&& init, FIn&& func, const std::shared_ptr>& events) : - IterateByRefNode::SignalNode( graphPtr, std::forward(init) ), + IterateByRefNode(const Group& group, T&& init, FIn&& func, const Event& events) : + IterateByRefNode::SignalNode( group, std::forward(init) ), func_( std::forward(func) ), events_( events ) { @@ -172,7 +172,7 @@ class IterateByRefNode : public SignalNode protected: F func_; - std::shared_ptr> events_; + Event events_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -183,8 +183,8 @@ class SyncedIterateNode : public SignalNode { public: template - SyncedIterateNode(const std::shared_ptr& graphPtr, T&& init, FIn&& func, const std::shared_ptr>& events, const std::shared_ptr>& ... syncs) : - SyncedIterateNode::SignalNode( graphPtr, std::forward(init) ), + SyncedIterateNode(const Group& group, T&& init, FIn&& func, const Event& events, const Signal& ... syncs) : + SyncedIterateNode::SignalNode( group, std::forward(init) ), func_( std::forward(func) ), events_( events ), syncHolder_( syncs ... ) @@ -236,7 +236,7 @@ class SyncedIterateNode : public SignalNode private: F func_; - std::shared_ptr> events_; + Event events_; std::tuple>...> syncHolder_; }; @@ -249,8 +249,8 @@ class SyncedIterateByRefNode : public SignalNode { public: template - SyncedIterateByRefNode(const std::shared_ptr& graphPtr, T&& init, FIn&& func, const std::shared_ptr>& events, const std::shared_ptr>& ... syncs) : - SyncedIterateByRefNode::SignalNode( graphPtr, std::forward(init) ), + SyncedIterateByRefNode(const Group& group, T&& init, FIn&& func, const Event& events, const Signal& ... syncs) : + SyncedIterateByRefNode::SignalNode( group, std::forward(init) ), func_( std::forward(func) ), events_( events ), syncHolder_( syncs ... ) @@ -294,9 +294,9 @@ class SyncedIterateByRefNode : public SignalNode private: F func_; - std::shared_ptr> events_; + Event events_; - std::tuple>...> syncHolder_; + std::tuple ...> syncHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -307,8 +307,8 @@ class HoldNode : public SignalNode { public: template - HoldNode(const std::shared_ptr& graphPtr, T&& init, const std::shared_ptr>& events) : - HoldNode::SignalNode( graphPtr, std::forward(init) ), + HoldNode(const Group& group, T&& init, const Event& events) : + HoldNode::SignalNode( group, std::forward(init) ), events_( events ) { this->RegisterMe(); @@ -351,7 +351,7 @@ class HoldNode : public SignalNode { return 1; } private: - const std::shared_ptr> events_; + const Event events_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -361,8 +361,8 @@ template class SnapshotNode : public SignalNode { public: - SnapshotNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& target, const std::shared_ptr>& trigger) : - SnapshotNode::SignalNode( graphPtr, target->Value() ), + SnapshotNode(const Group& group, const Signal& target, const Event& trigger) : + SnapshotNode::SignalNode( group, target->Value() ), target_( target ), trigger_( trigger ) { @@ -408,8 +408,8 @@ class SnapshotNode : public SignalNode { return 2; } private: - std::shared_ptr> target_; - std::shared_ptr> trigger_; + Signal target_; + Event trigger_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -419,8 +419,8 @@ template class MonitorNode : public EventStreamNode { public: - MonitorNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& target) : - MonitorNode::EventStreamNode( graphPtr ), + MonitorNode(const Group& group, const Signal& target) : + MonitorNode::EventStreamNode( group ), target_( target ) { this->RegisterMe(); @@ -449,7 +449,7 @@ class MonitorNode : public EventStreamNode { return 1; } private: - std::shared_ptr> target_; + Signal target_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -459,8 +459,8 @@ template class PulseNode : public EventStreamNode { public: - PulseNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& target, const std::shared_ptr>& trigger) : - PulseNode::EventStreamNode( graphPtr ), + PulseNode(const Group& group, const Signal& target, const Event& trigger) : + PulseNode::EventStreamNode( group ), target_( target ), trigger_( trigger ) { @@ -501,8 +501,8 @@ class PulseNode : public EventStreamNode { return 2; } private: - const std::shared_ptr> target_; - const std::shared_ptr> trigger_; + Signal target_; + Event trigger_; }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 374038fb..c8d91af0 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -87,8 +87,8 @@ class EventStreamNode : public NodeBase EventStreamNode(const EventStreamNode&) = delete; EventStreamNode& operator=(const EventStreamNode&) = delete; - explicit EventStreamNode(const std::shared_ptr& graphPtr) : - NodeBase( graphPtr ) + explicit EventStreamNode(const Group& group) : + NodeBase( group ) { } StorageType& Events() @@ -122,7 +122,7 @@ class EventStreamNode : public NodeBase // Last successor to arrive clears the buffer. if (pendingSuccessorCount_-- == 1) events_.clear(); - }; + } private: StorageType events_; @@ -137,12 +137,16 @@ template class EventSourceNode : public EventStreamNode { public: - EventSourceNode(const std::shared_ptr& graphPtr) : - EventSourceNode::EventStreamNode( graphPtr ) - { this->RegisterMe(NodeCategory::input); } + EventSourceNode(const Group& group) : + EventSourceNode::EventStreamNode( group ) + { + this->RegisterMe(NodeCategory::input); + } ~EventSourceNode() - { this->UnregisterMe(); } + { + this->UnregisterMe(); + } virtual const char* GetNodeType() const override { return "EventSource"; } @@ -175,8 +179,8 @@ template class EventMergeNode : public EventStreamNode { public: - EventMergeNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& ... deps) : - EventMergeNode::EventStreamNode( graphPtr ), + EventMergeNode(const Group& group, const Event& ... deps) : + EventMergeNode::EventStreamNode( group ), depHolder_( deps ... ) { this->RegisterMe(); @@ -185,7 +189,7 @@ class EventMergeNode : public EventStreamNode ~EventMergeNode() { - apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(this->DetachFromMe(deps->GetNodeId())); }, depHolder_); + apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(deps).GetNodeId())); }, depHolder_); this->UnregisterMe(); } @@ -212,13 +216,15 @@ class EventMergeNode : public EventStreamNode private: template - void MergeFromDep(const std::shared_ptr>& other) + void MergeFromDep(const Event& dep) { - this->Events().insert(this->Events().end(), other->Events().begin(), other->Events().end()); - other->DecrementPendingSuccessorCount(); + auto& depNodePtr = BaseCast(dep).GetNodePtr(); + + this->Events().insert(this->Events().end(), depNodePtr->Events().begin(), depNodePtr->Events().end()); + depNodePtr->DecrementPendingSuccessorCount(); } - std::tuple> ...> depHolder_; + std::tuple ...> depHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -228,8 +234,8 @@ template class EventSlotNode : public EventStreamNode { public: - EventSlotNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& dep) : - EventSlotNode::EventStreamNode( graphPtr ), + EventSlotNode(const Group& group, const Event& dep) : + EventSlotNode::EventStreamNode( group ), slotInput_( *this, dep ) { slotInput_.nodeId = GraphPtr()->RegisterNode(&slotInput_, NodeCategory::dyninput); @@ -280,7 +286,7 @@ class EventSlotNode : public EventStreamNode private: struct VirtualInputNode : public IReactiveNode { - VirtualInputNode(EventSlotNode& parentIn, const std::shared_ptr>& depIn) : + VirtualInputNode(EventSlotNode& parentIn, const Event& depIn) : parent( parentIn ), dep( depIn ) { } @@ -312,8 +318,8 @@ class EventSlotNode : public EventStreamNode NodeId nodeId; - std::shared_ptr> dep; - std::shared_ptr> newDep; + Event dep; + Event newDep; }; VirtualInputNode slotInput_; @@ -327,8 +333,8 @@ class EventProcessingNode : public EventStreamNode { public: template - EventProcessingNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& dep) : - EventProcessingNode::EventStreamNode( graphPtr ), + EventProcessingNode(const Group& group, FIn&& func, const Event& dep) : + EventProcessingNode::EventStreamNode( group ), func_( std::forward(func) ), dep_( dep ) { @@ -368,7 +374,7 @@ class EventProcessingNode : public EventStreamNode private: F func_; - std::shared_ptr> dep_; + Event dep_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -379,8 +385,8 @@ class SyncedEventProcessingNode : public EventStreamNode { public: template - SyncedEventProcessingNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& dep, const std::shared_ptr>& ... syncs) : - SyncedEventProcessingNode::EventStreamNode( graphPtr ), + SyncedEventProcessingNode(const Group& group, FIn&& func, const Event& dep, const Signal& ... syncs) : + SyncedEventProcessingNode::EventStreamNode( group ), func_( std::forward(func) ), dep_( dep ), syncHolder_( syncs ... ) @@ -432,9 +438,9 @@ class SyncedEventProcessingNode : public EventStreamNode private: F func_; - std::shared_ptr> dep_; + Event dep_; - std::tuple>...> syncHolder_; + std::tuple ...> syncHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -444,8 +450,8 @@ template class EventJoinNode : public EventStreamNode> { public: - EventJoinNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& ... deps) : - EventJoinNode::EventStreamNode( graphPtr ), + EventJoinNode(const Group& group, const Event& ... deps) : + EventJoinNode::EventStreamNode( group ), slots_( deps ... ) { this->RegisterMe(); @@ -510,12 +516,12 @@ class EventJoinNode : public EventStreamNode> template struct Slot { - Slot(const std::shared_ptr>& src) : + Slot(const Event& src) : source( src ) { } - std::shared_ptr> source; - std::deque buffer; + Event source; + std::deque buffer; }; template @@ -542,9 +548,9 @@ template class EventLinkNode : public EventStreamNode { public: - EventLinkNode(const std::shared_ptr& graphPtr, const std::shared_ptr& srcGraphPtr, const std::shared_ptr>& dep) : - EventLinkNode::EventStreamNode( graphPtr ), - linkOutput_( srcGraphPtr, dep ) + EventLinkNode(const Group& group, const Event& dep) : + EventLinkNode::EventStreamNode( group ), + linkOutput_( dep ) { this->RegisterMe(NodeCategory::input); } @@ -575,10 +581,10 @@ class EventLinkNode : public EventStreamNode private: struct VirtualOutputNode : public ILinkOutputNode { - VirtualOutputNode(const std::shared_ptr& srcGraphPtrIn, const std::shared_ptr>& depIn) : + VirtualOutputNode(const Event& depIn) : parent( ), - srcGraphPtr( srcGraphPtrIn ), - dep( depIn ) + dep( depIn ), + srcGroup( depIn.GetGroup() ) { nodeId = srcGraphPtr->RegisterNode(this, NodeCategory::linkoutput); srcGraphPtr->OnNodeAttach(nodeId, dep->GetNodeId()); @@ -597,9 +603,7 @@ class EventLinkNode : public EventStreamNode { return 1; } virtual UpdateResult Update(TurnId turnId, size_t successorCount) override - { - return UpdateResult::changed; - } + { return UpdateResult::changed; } virtual void CollectOutput(LinkOutputMap& output) override { @@ -623,11 +627,9 @@ class EventLinkNode : public EventStreamNode std::weak_ptr parent; - NodeId nodeId; - - std::shared_ptr> dep; - - std::shared_ptr srcGraphPtr; + NodeId nodeId; + Event dep; + Group srcGroup; }; VirtualOutputNode linkOutput_; diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index 3305ca40..a3769ce3 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -58,33 +58,36 @@ class NodeBase : public IReactiveNode NodeId GetNodeId() const { return nodeId_; } - const Group& GetGroup() const + auto GetGroup() const -> const Group& { return group_; } - auto GraphPtr() const -> const std::shared_ptr& - { return GroupInternals::GraphPtr(group_); } - - auto GraphPtr() -> std::shared_ptr& - { return GroupInternals::GraphPtr(group_); } + auto GetGroup() -> Group& + { return group_; } protected: + auto GetGraphPtr() const -> const std::shared_ptr& + { GetInternals(group_).GetGraphPtr(); } + + auto GetGraphPtr() -> std::shared_ptr& + { GetInternals(group_).GetGraphPtr(); } + void RegisterMe(NodeCategory category = NodeCategory::normal) - { nodeId_ = GraphPtr()->RegisterNode(this, category); } + { nodeId_ = GetGraphPtr()->RegisterNode(this, category); } void UnregisterMe() - { GraphPtr()->UnregisterNode(nodeId_); } + { GetGraphPtr()->UnregisterNode(nodeId_); } void AttachToMe(NodeId otherNodeId) - { GraphPtr()->OnNodeAttach(nodeId_, otherNodeId); } + { GetGraphPtr()->OnNodeAttach(nodeId_, otherNodeId); } void DetachFromMe(NodeId otherNodeId) - { GraphPtr()->OnNodeDetach(nodeId_, otherNodeId); } + { GetGraphPtr()->OnNodeDetach(nodeId_, otherNodeId); } void DynamicAttachToMe(NodeId otherNodeId, TurnId turnId) - { GraphPtr()->OnDynamicNodeAttach(nodeId_, otherNodeId, turnId); } + { GetGraphPtr()->OnDynamicNodeAttach(nodeId_, otherNodeId, turnId); } void DynamicDetachFromMe(NodeId otherNodeId, TurnId turnId) - { GraphPtr()->OnDynamicNodeDetach(nodeId_, otherNodeId, turnId); } + { GetGraphPtr()->OnDynamicNodeDetach(nodeId_, otherNodeId, turnId); } private: NodeId nodeId_; diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index d383d341..c84e06d0 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -10,6 +10,7 @@ #pragma once #include "react/detail/Defs.h" +#include "react/API.h" #include #include @@ -35,8 +36,8 @@ class EventStreamNode; class ObserverNode : public NodeBase { public: - ObserverNode(const std::shared_ptr& graphPtr) : - ObserverNode::NodeBase( graphPtr ) + ObserverNode(const Group& group) : + ObserverNode::NodeBase( group ) { } }; @@ -48,18 +49,18 @@ class SignalObserverNode : public ObserverNode { public: template - SignalObserverNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& ... deps) : - SignalObserverNode::ObserverNode( graphPtr ), + SignalObserverNode(const Group& group, FIn&& func, const Signal& ... deps) : + SignalObserverNode::ObserverNode( group ), func_( std::forward(func) ), depHolder_( deps ... ) { this->RegisterMe(NodeCategory::output); - REACT_EXPAND_PACK(this->AttachToMe(deps->GetNodeId())); + REACT_EXPAND_PACK(this->AttachToMe(GetInternals(deps).GetNodeId())); } ~SignalObserverNode() { - apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(this->DetachFromMe(deps->GetNodeId())); }, depHolder_); + apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(deps).GetNodeId())); }, depHolder_); this->UnregisterMe(); } @@ -71,14 +72,14 @@ class SignalObserverNode : public ObserverNode virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { - apply([this] (const auto& ... deps) { this->func_(deps->Value() ...); }, depHolder_); + apply([this] (const auto& ... deps) { this->func_(GetInternals(deps).Value() ...); }, depHolder_); return UpdateResult::unchanged; } private: F func_; - std::tuple> ...> depHolder_; + std::tuple ...> depHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -89,18 +90,18 @@ class EventObserverNode : public ObserverNode { public: template - EventObserverNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& subject) : - EventObserverNode::ObserverNode( graphPtr ), + EventObserverNode(const Group& group, FIn&& func, const Event& subject) : + EventObserverNode::ObserverNode( group ), func_( std::forward(func) ), subject_( subject ) { this->RegisterMe(NodeCategory::output); - this->AttachToMe(subject->GetNodeId()); + this->AttachToMe(GetInternals(subject).GetNodeId()); } ~EventObserverNode() { - this->DetachFromMe(subject_->GetNodeId()); + this->DetachFromMe(GetInternals(subject_).GetNodeId()); this->UnregisterMe(); } @@ -112,15 +113,15 @@ class EventObserverNode : public ObserverNode virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { - func_(EventRange( subject_->Events() )); - subject_->DecrementPendingSuccessorCount(); + func_(EventRange( GetInternals(subject_).Events() )); + GetInternals(subject_).DecrementPendingSuccessorCount(); return UpdateResult::unchanged; } private: F func_; - std::shared_ptr> subject_; + Event subject_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -131,21 +132,21 @@ class SyncedEventObserverNode : public ObserverNode { public: template - SyncedEventObserverNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& subject, const std::shared_ptr>& ... syncs) : - SyncedEventObserverNode::ObserverNode( graphPtr ), + SyncedEventObserverNode(const Group& group, FIn&& func, const Event& subject, const Signal& ... syncs) : + SyncedEventObserverNode::ObserverNode( group ), func_( std::forward(func) ), subject_( subject ), syncHolder_( syncs ... ) { this->RegisterMe(NodeCategory::output); this->AttachToMe(subject->GetNodeId()); - REACT_EXPAND_PACK(this->AttachToMe(syncs->GetNodeId())); + REACT_EXPAND_PACK(this->AttachToMe(GetInternals(syncs).GetNodeId())); } ~SyncedEventObserverNode() { - apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(syncs->GetNodeId())); }, syncHolder_); - this->DetachFromMe(subject_->GetNodeId()); + apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(syncs).GetNodeId())); }, syncHolder_); + this->DetachFromMe(GetInternals(subject_).GetNodeId()); this->UnregisterMe(); } @@ -158,12 +159,12 @@ class SyncedEventObserverNode : public ObserverNode virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { // Updates might be triggered even if only sync nodes changed. Ignore those. - if (subject_->Events().empty()) + if (GetInternals(this->subject_).Events().empty()) return UpdateResult::unchanged; - apply([this] (const auto& ... syncs) { func_(EventRange( this->subject_->Events() ), syncs->Value() ...); }, syncHolder_); + apply([this] (const auto& ... syncs) { func_(EventRange( GetInternals(this->subject_).Events() ), GetInternals(syncs).Value() ...); }, syncHolder_); - subject_->DecrementPendingSuccessorCount(); + GetInternals(subject_).DecrementPendingSuccessorCount(); return UpdateResult::unchanged; } @@ -171,9 +172,9 @@ class SyncedEventObserverNode : public ObserverNode private: F func_; - std::shared_ptr> subject_; + Event subject_; - std::tuple>...> syncHolder_; + std::tuple ...> syncHolder_; }; /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index 4dbd8574..b25b46d9 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -39,14 +39,14 @@ class SignalNode : public NodeBase SignalNode(const SignalNode&) = delete; SignalNode& operator=(const SignalNode&) = delete; - explicit SignalNode(const std::shared_ptr& graphPtr) : - SignalNode::NodeBase( graphPtr ), + explicit SignalNode(const Group& group) : + SignalNode::NodeBase( group ), value_( ) { } template - SignalNode(const std::shared_ptr& graphPtr, T&& value) : - SignalNode::NodeBase( graphPtr ), + SignalNode(const Group& group, T&& value) : + SignalNode::NodeBase( group ), value_( std::forward(value) ) { } @@ -67,19 +67,25 @@ template class VarSignalNode : public SignalNode { public: - explicit VarSignalNode(const std::shared_ptr& graphPtr) : - VarSignalNode::SignalNode( graphPtr ), + explicit VarSignalNode(const Group& group) : + VarSignalNode::SignalNode( group ), newValue_( ) - { this->RegisterMe(NodeCategory::input); } + { + this->RegisterMe(NodeCategory::input); + } template - VarSignalNode(const std::shared_ptr& graphPtr, T&& value) : - VarSignalNode::SignalNode( graphPtr, std::forward(value) ), + VarSignalNode(const Group& group, T&& value) : + VarSignalNode::SignalNode( group, std::forward(value) ), newValue_( value ) - { this->RegisterMe(); } + { + this->RegisterMe(); + } ~VarSignalNode() - { this->UnregisterMe(); } + { + this->UnregisterMe(); + } virtual const char* GetNodeType() const override { return "VarSignal"; } @@ -161,8 +167,8 @@ class SignalFuncNode : public SignalNode { public: template - SignalFuncNode(const std::shared_ptr& graphPtr, FIn&& func, const std::shared_ptr>& ... deps) : - SignalFuncNode::SignalNode( graphPtr, func(deps->Value() ...) ), + SignalFuncNode(const Group& group, FIn&& func, const Signal& ... deps) : + SignalFuncNode::SignalNode( group, func(deps->Value() ...) ), func_( std::forward(func) ), depHolder_( deps ... ) { @@ -172,7 +178,7 @@ class SignalFuncNode : public SignalNode ~SignalFuncNode() { - apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(this->DetachFromMe(deps->GetNodeId())); }, depHolder_); + apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(deps).GetNodePtr()->GetNodeId())); }, depHolder_); this->UnregisterMe(); } @@ -186,7 +192,7 @@ class SignalFuncNode : public SignalNode { bool changed = false; - S newValue = apply([this] (const auto& ... deps) { return this->func_(deps->Value() ...); }, depHolder_); + S newValue = apply([this] (const auto& ... deps) { return this->func_(GetInternals(deps).Value() ...); }, depHolder_); if (! (this->Value() == newValue)) { @@ -203,7 +209,7 @@ class SignalFuncNode : public SignalNode private: F func_; - std::tuple> ...> depHolder_; + std::tuple ...> depHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -213,8 +219,8 @@ template class SignalSlotNode : public SignalNode { public: - SignalSlotNode(const std::shared_ptr& graphPtr, const std::shared_ptr>& dep) : - SignalSlotNode::SignalNode( graphPtr, dep->Value() ), + SignalSlotNode(const Group& group, const Signal& dep) : + SignalSlotNode::SignalNode( group, dep->Value() ), slotInput_( *this, dep ) { slotInput_.nodeId = GraphPtr()->RegisterNode(&slotInput_, NodeCategory::dyninput); @@ -226,11 +232,13 @@ class SignalSlotNode : public SignalNode ~SignalSlotNode() { - this->DetachFromMe(slotInput_.dep->GetNodeId()); + const auto& depNodePtr = GetInternals(slotInput_.dep).GetNodePtr(); + + this->DetachFromMe(depNodePtr->GetNodeId()); this->DetachFromMe(slotInput_.nodeId); this->UnregisterMe(); - GraphPtr()->UnregisterNode(slotInput_.nodeId); + GetGraphPtr()->UnregisterNode(slotInput_.nodeId); } virtual const char* GetNodeType() const override @@ -241,9 +249,11 @@ class SignalSlotNode : public SignalNode virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { - if (! (this->Value() == slotInput_.dep->Value())) + const auto& depNodePtr = GetInternals(slotInput_.dep).GetNodePtr(); + + if (! (this->Value() == depNodePtr->Value())) { - this->Value() = slotInput_.dep->Value(); + this->Value() = depNodePtr->Value(); return UpdateResult::changed; } else @@ -252,7 +262,7 @@ class SignalSlotNode : public SignalNode } } - void SetInput(const std::shared_ptr>& newInput) + void SetInput(const Signal& newInput) { slotInput_.newDep = newInput; } NodeId GetInputNodeId() const @@ -261,9 +271,10 @@ class SignalSlotNode : public SignalNode private: struct VirtualInputNode : public IReactiveNode { - VirtualInputNode(SignalSlotNode& parentIn, const std::shared_ptr>& depIn) : + VirtualInputNode(SignalSlotNode& parentIn, const Signal& depIn) : parent( parentIn ), - dep( depIn ) + dep( depIn ), + newDep( depIn ), { } virtual const char* GetNodeType() const override @@ -276,15 +287,17 @@ class SignalSlotNode : public SignalNode { if (dep != newDep) { - parent.DynamicDetachFromMe(dep->GetNodeId(), 0); - parent.DynamicAttachToMe(newDep->GetNodeId(), 0); + const auto& depNodePtr = GetInternals(dep).GetNodePtr(); + const auto& newDepNodePtr = GetInternals(newDep).GetNodePtr(); + + parent.DynamicDetachFromMe(depNodePtr->GetNodeId(), 0); + parent.DynamicAttachToMe(newDepNodePtr->GetNodeId(), 0); dep = std::move(newDep); return UpdateResult::changed; } else { - newDep.reset(); return UpdateResult::unchanged; } } @@ -293,8 +306,8 @@ class SignalSlotNode : public SignalNode NodeId nodeId; - std::shared_ptr> dep; - std::shared_ptr> newDep; + Signal dep; + Signal newDep; }; VirtualInputNode slotInput_; @@ -307,9 +320,9 @@ template class SignalLinkNode : public SignalNode { public: - SignalLinkNode(const std::shared_ptr& graphPtr, const std::shared_ptr& srcGraphPtr, const std::shared_ptr>& dep) : - SignalLinkNode::SignalNode( graphPtr, dep->Value() ), - linkOutput_( srcGraphPtr, dep ) + SignalLinkNode(const Group& group, const Group& srcGroup, const Signal& dep) : + SignalLinkNode::SignalNode( group, dep->Value() ), + linkOutput_( srcGroup, dep ) { this->RegisterMe(NodeCategory::input); } @@ -329,22 +342,18 @@ class SignalLinkNode : public SignalNode { return 1; } virtual UpdateResult Update(TurnId turnId, size_t successorCount) override - { - return UpdateResult::changed; - } + { return UpdateResult::changed; } template void SetValue(T&& newValue) - { - this->Value() = std::forward(newValue); - } + { this->Value() = std::forward(newValue); } private: struct VirtualOutputNode : public ILinkOutputNode { - VirtualOutputNode(const std::shared_ptr& srcGraphPtrIn, const std::shared_ptr>& depIn) : + VirtualOutputNode(const Group& srcGroupIn, const Signal& depIn) : parent( ), - srcGraphPtr( srcGraphPtrIn ), + srcGroup( srcGroupIn ), dep( depIn ) { nodeId = srcGraphPtr->RegisterNode(this, NodeCategory::linkoutput); @@ -364,9 +373,7 @@ class SignalLinkNode : public SignalNode { return 1; } virtual UpdateResult Update(TurnId turnId, size_t successorCount) override - { - return UpdateResult::changed; - } + { return UpdateResult::changed; } virtual void CollectOutput(LinkOutputMap& output) override { @@ -392,9 +399,9 @@ class SignalLinkNode : public SignalNode NodeId nodeId; - std::shared_ptr> dep; + Signal dep; - std::shared_ptr srcGraphPtr; + Group srcGroup; }; VirtualOutputNode linkOutput_; From b70012f98e26479af24eb2ce49b2593c80b5e4eb Mon Sep 17 00:00:00 2001 From: schlangster Date: Wed, 4 Jan 2017 22:17:46 +0100 Subject: [PATCH 238/266] progress --- include/react/Event.h | 14 +- include/react/Signal.h | 27 +--- include/react/detail/graph/AlgorithmNodes.h | 134 ++++++++++---------- include/react/detail/graph/EventNodes.h | 94 ++++++++++---- include/react/detail/graph/GraphBase.h | 4 +- include/react/detail/graph/ObserverNodes.h | 2 +- include/react/detail/graph/SignalNodes.h | 52 ++++---- 7 files changed, 171 insertions(+), 156 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index e4d2a987..01c284c6 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -49,10 +49,10 @@ class EventInternals { return nodePtr_->GetNodeId(); } StorageType& Events() - { return nodePtr->Events(); } + { return nodePtr_->Events(); } const StorageType& Events() const - { return nodePtr->Events(); } + { return nodePtr_->Events(); } void SetPendingSuccessorCount(size_t count) { nodePtr_->SetPendingSuccessorCount(count); } @@ -383,7 +383,7 @@ auto Transform(const Group& group, F&& op, const Event& dep) -> Event template auto Transform(F&& op, const Event& dep) -> Event - { return Transform(dep1.GetGroup(), std::forward(op), dep); } + { return Transform(dep.GetGroup(), std::forward(op), dep); } template auto Transform(const Group& group, F&& op, const Event& dep, const Signal& ... signals) -> Event @@ -399,7 +399,7 @@ auto Transform(const Group& group, F&& op, const Event& dep, const Signal template auto Transform(F&& op, const Event& dep, const Signal& ... signals) -> Event - { return Transform(dep.GetGroup(), std::forward(op), dep, signals ...); } + { return Transform(dep.GetGroup(), std::forward(op), dep, signals ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Flatten @@ -451,12 +451,6 @@ auto Tokenize(T&& source) -> decltype(auto) /***************************************/ REACT_IMPL_BEGIN /**************************************/ -template -bool Equals(const Event& lhs, const Event& rhs) -{ - return lhs.Equals(rhs); -} - template static Event SameGroupOrLink(const Group& targetGroup, const Event& dep) { diff --git a/include/react/Signal.h b/include/react/Signal.h index 58e5da37..10ed71f8 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -205,7 +205,7 @@ class VarSignal : public Signal VarNodeType* castedPtr = static_cast(this->GetNodePtr().get()); NodeId nodeId = castedPtr->GetNodeId(); - auto& graphPtr = BaseCast(this->GetGroup()).GetGraphPtr(); + auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); graphPtr->AddInput(nodeId, [castedPtr, &func] { castedPtr->ModifyValue(func); }); } @@ -224,17 +224,12 @@ class SignalSlot : public Signal SignalSlot(SignalSlot&&) = default; SignalSlot& operator=(SignalSlot&&) = default; - // Construct with group + default - explicit SignalSlot(const Group& group) : - SignalSlot::Signal( REACT_IMPL::CtorTag{ }, CreateSlotNode(group) ) - { } - - // Construct with group + value + // Construct with explicit group SignalSlot(const Group& group, const Signal& input) : SignalSlot::Signal( REACT_IMPL::CtorTag{ }, CreateSlotNode(group, input) ) { } - // Construct with value + // Construct with implicit group explicit SignalSlot(const Signal& input) : SignalSlot::Signal( REACT_IMPL::CtorTag{ }, CreateSlotNode(input.GetGroup(), input) ) { } @@ -246,12 +241,6 @@ class SignalSlot : public Signal { SetInput(newInput); } protected: - static auto CreateSlotNode(const Group& group) -> decltype(auto) - { - using REACT_IMPL::SignalSlotNode; - return std::make_shared>(group); - } - static auto CreateSlotNode(const Group& group, const Signal& input) -> decltype(auto) { using REACT_IMPL::SignalSlotNode; @@ -301,7 +290,7 @@ class SignalLink : public Signal { using REACT_IMPL::SignalLinkNode; - auto node = std::make_shared>(group, PrivateNodeInterface::GraphPtr(input), PrivateNodeInterface::NodePtr(input)); + auto node = std::make_shared>(group, input); node->SetWeakSelfPtr(std::weak_ptr>{ node }); return node; } @@ -311,19 +300,13 @@ class SignalLink : public Signal /***************************************/ REACT_IMPL_BEGIN /**************************************/ -template -bool Equals(const Signal& lhs, const Signal& rhs) -{ - return lhs.Equals(rhs); -} - template static Signal SameGroupOrLink(const Group& targetGroup, const Signal& dep) { if (dep.GetGroup() == targetGroup) return dep; else - return SignalLink( group, dep ); + return SignalLink( targetGroup, dep ); } /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/detail/graph/AlgorithmNodes.h b/include/react/detail/graph/AlgorithmNodes.h index 0287a816..fdb925e1 100644 --- a/include/react/detail/graph/AlgorithmNodes.h +++ b/include/react/detail/graph/AlgorithmNodes.h @@ -86,26 +86,26 @@ class IterateNode : public SignalNode { public: template - IterateNode(const Group& group, T&& init, FIn&& func, const Event& events) : + IterateNode(const Group& group, T&& init, FIn&& func, const Event& evnt) : IterateNode::SignalNode( group, std::forward(init) ), func_( std::forward(func) ), - events_( events ) + evnt_( evnt ) { this->RegisterMe(); - this->AttachToMe(events->GetNodeId()); + this->AttachToMe(GetInternals(evnt).GetNodeId()); } ~IterateNode() { - this->DetachFromMe(events_->GetNodeId()); + this->DetachFromMe(GetInternals(evnt_).GetNodeId()); this->UnregisterMe(); } virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { - S newValue = func_(EventRange( events_->Events() ), this->Value()); + S newValue = func_(EventRange( GetInternals(evnt_).Events() ), this->Value()); - events_->DecrementPendingSuccessorCount(); + GetInternals(evnt_).DecrementPendingSuccessorCount(); if (! (newValue == this->Value())) { @@ -125,9 +125,8 @@ class IterateNode : public SignalNode { return 1; } private: - Event events_; - - F func_; + F func_; + Event evnt_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -138,26 +137,26 @@ class IterateByRefNode : public SignalNode { public: template - IterateByRefNode(const Group& group, T&& init, FIn&& func, const Event& events) : + IterateByRefNode(const Group& group, T&& init, FIn&& func, const Event& evnt) : IterateByRefNode::SignalNode( group, std::forward(init) ), func_( std::forward(func) ), - events_( events ) + evnt_( evnt ) { this->RegisterMe(); - this->AttachToMe(events->GetNodeId()); + this->AttachToMe(GetInternals(evnt_).GetNodeId()); } ~IterateByRefNode() { - this->DetachFromMe(events_->GetNodeId()); + this->DetachFromMe(GetInternals(evnt).GetNodeId()); this->UnregisterMe(); } virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { - func_(EventRange( events_->Events() ), this->Value()); + func_(EventRange( GetInternals(evnt_).Events() ), this->Value()); - events_->DecrementPendingSuccessorCount(); + GetInternals(evnt_).DecrementPendingSuccessorCount(); // Always assume change return UpdateResult::changed; @@ -170,9 +169,8 @@ class IterateByRefNode : public SignalNode { return 1; } protected: - F func_; - - Event events_; + F func_; + Event evnt_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -183,38 +181,38 @@ class SyncedIterateNode : public SignalNode { public: template - SyncedIterateNode(const Group& group, T&& init, FIn&& func, const Event& events, const Signal& ... syncs) : + SyncedIterateNode(const Group& group, T&& init, FIn&& func, const Event& evnt, const Signal& ... syncs) : SyncedIterateNode::SignalNode( group, std::forward(init) ), func_( std::forward(func) ), - events_( events ), + evnt_( evnt ), syncHolder_( syncs ... ) { this->RegisterMe(); - this->AttachToMe(events->GetNodeId()); - REACT_EXPAND_PACK(this->AttachToMe(syncs->GetNodeId())); + this->AttachToMe(GetInternals(evnt).GetNodeId()); + REACT_EXPAND_PACK(this->AttachToMe(GetInternals(syncs).GetNodeId())); } ~SyncedIterateNode() { - apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(syncs->GetNodeId())); }, syncHolder_); - this->DetachFromMe(events_->GetNodeId()); + apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(syncs).GetNodeId())); }, syncHolder_); + this->DetachFromMe(GetInternals(evnt_).GetNodeId()); this->UnregisterMe(); } virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { // Updates might be triggered even if only sync nodes changed. Ignore those. - if (events_->Events().empty()) + if (GetInternals(evnt_).Events().empty()) return UpdateResult::unchanged; S newValue = apply( [this] (const auto& ... syncs) { - return func_(EventRange( events_->Events() ), this->Value(), syncs->Value() ...); + return func_(EventRange( GetInternals(evnt_).Events() ), this->Value(), GetInternals(syncs).Value() ...); }, syncHolder_); - events_->DecrementPendingSuccessorCount(); + GetInternals(evnt_).DecrementPendingSuccessorCount(); if (! (newValue == this->Value())) { @@ -234,11 +232,10 @@ class SyncedIterateNode : public SignalNode { return 1 + sizeof...(TSyncs); } private: - F func_; - - Event events_; + F func_; + Event evnt_; - std::tuple>...> syncHolder_; + std::tuple ...> syncHolder_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -249,38 +246,38 @@ class SyncedIterateByRefNode : public SignalNode { public: template - SyncedIterateByRefNode(const Group& group, T&& init, FIn&& func, const Event& events, const Signal& ... syncs) : + SyncedIterateByRefNode(const Group& group, T&& init, FIn&& func, const Event& evnt, const Signal& ... syncs) : SyncedIterateByRefNode::SignalNode( group, std::forward(init) ), func_( std::forward(func) ), - events_( events ), + evnt_( evnt ), syncHolder_( syncs ... ) { this->RegisterMe(); - this->AttachToMe(events->GetNodeId()); - REACT_EXPAND_PACK(this->AttachToMe(syncs->GetNodeId())); + this->AttachToMe(GetInternals(evnt).GetNodeId()); + REACT_EXPAND_PACK(this->AttachToMe(GetInternals(syncs).GetNodeId())); } ~SyncedIterateByRefNode() { - apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(syncs->GetNodeId())); }, syncHolder_); - this->DetachFromMe(events_->GetNodeId()); + apply([this] (const auto& ... syncs) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(syncs).GetNodeId())); }, syncHolder_); + this->DetachFromMe(GetInternals(evnt_).GetNodeId()); this->UnregisterMe(); } virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { // Updates might be triggered even if only sync nodes changed. Ignore those. - if (events_->Events().empty()) + if (GetInternals(evnt_).Events().empty()) return UpdateResult::unchanged; apply( [this] (const auto& ... args) { - func_(EventRange( events_->Events() ), this->Value(), args->Value() ...); + func_(EventRange( GetInternals(evnt_).Events() ), this->Value(), GetInternals(args).Value() ...); }, syncHolder_); - events_->DecrementPendingSuccessorCount(); + GetInternals(evnt_).DecrementPendingSuccessorCount(); return UpdateResult::changed; } @@ -292,9 +289,8 @@ class SyncedIterateByRefNode : public SignalNode { return 1 + sizeof...(TSyncs); } private: - F func_; - - Event events_; + F func_; + Event events_; std::tuple ...> syncHolder_; }; @@ -307,17 +303,17 @@ class HoldNode : public SignalNode { public: template - HoldNode(const Group& group, T&& init, const Event& events) : + HoldNode(const Group& group, T&& init, const Event& evnt) : HoldNode::SignalNode( group, std::forward(init) ), - events_( events ) + evnt_( evnt ) { this->RegisterMe(); - this->AttachToMe(events->GetNodeId()); + this->AttachToMe(GetInternals(evnt).GetNodeId()); } ~HoldNode() { - this->DetachFromMe(events_->GetNodeId()); + this->DetachFromMe(GetInternals(evnt_).GetNodeId()); this->UnregisterMe(); } @@ -328,9 +324,9 @@ class HoldNode : public SignalNode { bool changed = false; - if (! events_->Events().empty()) + if (! GetInternals(evnt_).Events().empty()) { - const S& newValue = events_->Events().back(); + const S& newValue = GetInternals(evnt_).Events().back(); if (! (newValue == this->Value())) { @@ -338,7 +334,7 @@ class HoldNode : public SignalNode this->Value() = newValue; } - events_->DecrementPendingSuccessorCount(); + GetInternals(evnt_).DecrementPendingSuccessorCount(); } if (changed) @@ -351,7 +347,7 @@ class HoldNode : public SignalNode { return 1; } private: - const Event events_; + Event evnt_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -362,19 +358,19 @@ class SnapshotNode : public SignalNode { public: SnapshotNode(const Group& group, const Signal& target, const Event& trigger) : - SnapshotNode::SignalNode( group, target->Value() ), + SnapshotNode::SignalNode( group, GetInternals(target).Value() ), target_( target ), trigger_( trigger ) { this->RegisterMe(); - this->AttachToMe(target->GetNodeId()); - this->AttachToMe(trigger->GetNodeId()); + this->AttachToMe(GetInternals(target).GetNodeId()); + this->AttachToMe(GetInternals(trigger).GetNodeId()); } ~SnapshotNode() { - this->DetachFromMe(trigger_->GetNodeId()); - this->DetachFromMe(target_->GetNodeId()); + this->DetachFromMe(GetInternals(trigger_).GetNodeId()); + this->DetachFromMe(GetInternals(target_).GetNodeId()); this->UnregisterMe(); } @@ -382,9 +378,9 @@ class SnapshotNode : public SignalNode { bool changed = false; - if (! trigger_->Events().empty()) + if (! GetInternals(trigger_).Events().empty()) { - const S& newValue = target_->Value(); + const S& newValue = GetInternals(target_).Value(); if (! (newValue == this->Value())) { @@ -392,7 +388,7 @@ class SnapshotNode : public SignalNode this->Value() = newValue; } - trigger_->DecrementPendingSuccessorCount(); + GetInternals(trigger_).DecrementPendingSuccessorCount(); } if (changed) @@ -424,18 +420,18 @@ class MonitorNode : public EventStreamNode target_( target ) { this->RegisterMe(); - this->AttachToMe(target->GetNodeId()); + this->AttachToMe(GetInternals(target).GetNodeId()); } ~MonitorNode() { - this->DetachFromMe(target_->GetNodeId()); + this->DetachFromMe(GetInternals(target_).GetNodeId()); this->UnregisterMe(); } virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { - this->Events().push_back(target_->Value()); + this->Events().push_back(GetInternals(target_).Value()); this->SetPendingSuccessorCount(successorCount); @@ -465,23 +461,23 @@ class PulseNode : public EventStreamNode trigger_( trigger ) { this->RegisterMe(); - this->AttachToMe(target->GetNodeId()); - this->AttachToMe(trigger->GetNodeId()); + this->AttachToMe(GetInternals(target).GetNodeId()); + this->AttachToMe(GetInternals(trigger).GetNodeId()); } ~PulseNode() { - this->DetachFromMe(trigger_->GetNodeId()); - this->DetachFromMe(target_->GetNodeId()); + this->DetachFromMe(GetInternals(trigger_).GetNodeId()); + this->DetachFromMe(GetInternals(target_).GetNodeId()); this->UnregisterMe(); } virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { - for (size_t i=0; iEvents().size(); i++) - this->Events().push_back(target_->Value()); + for (size_t i = 0; i < GetInternals(trigger_).Events().size(); i++) + this->Events().push_back(GetInternals(target_).Value()); - trigger_->DecrementPendingSuccessorCount(); + GetInternals(trigger_).DecrementPendingSuccessorCount(); if (! this->Events().empty()) { diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index c8d91af0..f720fc18 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -184,7 +184,7 @@ class EventMergeNode : public EventStreamNode depHolder_( deps ... ) { this->RegisterMe(); - REACT_EXPAND_PACK(this->AttachToMe(deps->GetNodeId())); + REACT_EXPAND_PACK(this->AttachToMe(GetInternals(deps).GetNodeId())); } ~EventMergeNode() @@ -195,7 +195,7 @@ class EventMergeNode : public EventStreamNode virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { - apply([this] (const auto& ... deps) { REACT_EXPAND_PACK(MergeFromDep(deps)); }, depHolder_); + apply([this] (auto& ... deps) { REACT_EXPAND_PACK(MergeFromDep(deps)); }, depHolder_); if (! this->Events().empty()) { @@ -216,12 +216,13 @@ class EventMergeNode : public EventStreamNode private: template - void MergeFromDep(const Event& dep) + void MergeFromDep(Event& dep) { - auto& depNodePtr = BaseCast(dep).GetNodePtr(); + auto& depInternals = GetInternals(dep); - this->Events().insert(this->Events().end(), depNodePtr->Events().begin(), depNodePtr->Events().end()); - depNodePtr->DecrementPendingSuccessorCount(); + this->Events().insert(this->Events().end(), depInternals.Events().begin(), depInternals.Events().end()); + + depInternals.DecrementPendingSuccessorCount(); } std::tuple ...> depHolder_; @@ -234,20 +235,20 @@ template class EventSlotNode : public EventStreamNode { public: - EventSlotNode(const Group& group, const Event& dep) : + EventSlotNode(const Group& group) : EventSlotNode::EventStreamNode( group ), - slotInput_( *this, dep ) + slotInput_( *this ) { slotInput_.nodeId = GraphPtr()->RegisterNode(&slotInput_, NodeCategory::dyninput); this->RegisterMe(); this->AttachToMe(slotInput_.nodeId); - this->AttachToMe(dep->GetNodeId()); + //this->AttachToMe(GetInternals(dep).GetNodeId()); } ~EventSlotNode() { - this->DetachFromMe(slotInput_.dep->GetNodeId()); + this->DetachFromMe(GetInternals(slotInput_.dep).GetNodeId()); this->DetachFromMe(slotInput_.nodeId); this->UnregisterMe(); @@ -277,8 +278,45 @@ class EventSlotNode : public EventStreamNode } } - void SetInput(const std::shared_ptr>& newInput) - { slotInput_.newDep = newInput; } + void AddInput(const Event>& input) + { + auto& newDeps = slotInput_.newDeps; + + for (auto& e : newDeps) + { + if (e.second != input) + continue; + + // Earlier remove is overridden by later add. + if (e.first == false) + e.first = true; + + // Either element was already added, or remove has been overridden. Nothing more to do. + return; + } + + newDeps.emplace_back(true, input); + } + + void RemoveInput(const Event& input) + { + auto& newDeps = slotInput_.newDeps; + + for (auto& e : newDeps) + { + if (e.second != input) + continue; + + // Earlier add is overridden by later remove. + if (e.first == true) + e.first = false; + + // Either element was already removed, or add has been overridden. Nothing more to do. + return; + } + + newDeps.emplace_back(false, input); + } NodeId GetInputNodeId() const { return slotInput_.nodeId; } @@ -318,8 +356,8 @@ class EventSlotNode : public EventStreamNode NodeId nodeId; - Event dep; - Event newDep; + std::vector> deps; + std::vector>> newDeps; }; VirtualInputNode slotInput_; @@ -339,20 +377,20 @@ class EventProcessingNode : public EventStreamNode dep_( dep ) { this->RegisterMe(); - this->AttachToMe(dep->GetNodeId()); + this->AttachToMe(GetInternals(dep).GetNodeId()); } ~EventProcessingNode() { - this->DetachFromMe(dep_->GetNodeId()); + this->DetachFromMe(GetInternals(dep_).GetNodeId()); this->UnregisterMe(); } virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { - func_(EventRange( dep_->Events() ), std::back_inserter(this->Events())); + func_(EventRange( GetInternals(dep_).Events() ), std::back_inserter(this->Events())); - dep_->DecrementPendingSuccessorCount(); + GetInternals(dep_).DecrementPendingSuccessorCount(); if (! this->Events().empty()) { @@ -455,12 +493,12 @@ class EventJoinNode : public EventStreamNode> slots_( deps ... ) { this->RegisterMe(); - REACT_EXPAND_PACK(this->AttachToMe(deps->GetNodeId())); + REACT_EXPAND_PACK(this->AttachToMe(GetInternals(deps).GetNodeId())); } ~EventJoinNode() { - apply([this] (const auto& ... slots) { REACT_EXPAND_PACK(this->DetachFromMe(slots.source->GetNodeId())); }, slots_); + apply([this] (const auto& ... slots) { REACT_EXPAND_PACK(this->DetachFromMe(GetInternals(slots.source).GetNodeId())); }, slots_); this->UnregisterMe(); } @@ -527,8 +565,8 @@ class EventJoinNode : public EventStreamNode> template static void FetchBuffer(TurnId turnId, Slot& slot) { - slot.buffer.insert(slot.buffer.end(), slot.source->Events().begin(), slot.source->Events().end()); - slot.source->DecrementPendingSuccessorCount(); + slot.buffer.insert(slot.buffer.end(), GetInternals(slot.source).Events().begin(), GetInternals(slot.source).Events().end()); + GetInternals(slot.source).DecrementPendingSuccessorCount(); } template @@ -586,13 +624,15 @@ class EventLinkNode : public EventStreamNode dep( depIn ), srcGroup( depIn.GetGroup() ) { + auto& srcGraphPtr = GetInternals(srcGroup).GetGraphPtr(); nodeId = srcGraphPtr->RegisterNode(this, NodeCategory::linkoutput); - srcGraphPtr->OnNodeAttach(nodeId, dep->GetNodeId()); + srcGraphPtr->OnNodeAttach(nodeId, GetInternals(dep).GetNodeId()); } ~VirtualOutputNode() { - srcGraphPtr->OnNodeDetach(nodeId, dep->GetNodeId()); + auto& srcGraphPtr = GetInternals(srcGroup).GetGraphPtr(); + srcGraphPtr->OnNodeDetach(nodeId, GetInternals(dep).GetNodeId()); srcGraphPtr->UnregisterNode(nodeId); } @@ -609,12 +649,12 @@ class EventLinkNode : public EventStreamNode { if (auto p = parent.lock()) { - auto* rawPtr = p->GraphPtr().get(); + auto* rawPtr = p->GetGraphPtr().get(); output[rawPtr].push_back( - [storedParent = std::move(p), storedEvents = dep->Events()] () mutable + [storedParent = std::move(p), storedEvents = GetInternals(dep).Events()] () mutable { NodeId nodeId = storedParent->GetNodeId(); - auto& graphPtr = storedParent->GraphPtr(); + auto& graphPtr = storedParent->GetGraphPtr(); graphPtr->AddInput(nodeId, [&storedParent, &storedEvents] diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index a3769ce3..43f58f89 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -66,10 +66,10 @@ class NodeBase : public IReactiveNode protected: auto GetGraphPtr() const -> const std::shared_ptr& - { GetInternals(group_).GetGraphPtr(); } + { return GetInternals(group_).GetGraphPtr(); } auto GetGraphPtr() -> std::shared_ptr& - { GetInternals(group_).GetGraphPtr(); } + { return GetInternals(group_).GetGraphPtr(); } void RegisterMe(NodeCategory category = NodeCategory::normal) { nodeId_ = GetGraphPtr()->RegisterNode(this, category); } diff --git a/include/react/detail/graph/ObserverNodes.h b/include/react/detail/graph/ObserverNodes.h index c84e06d0..695c0792 100644 --- a/include/react/detail/graph/ObserverNodes.h +++ b/include/react/detail/graph/ObserverNodes.h @@ -139,7 +139,7 @@ class SyncedEventObserverNode : public ObserverNode syncHolder_( syncs ... ) { this->RegisterMe(NodeCategory::output); - this->AttachToMe(subject->GetNodeId()); + this->AttachToMe(GetInternals(subject).GetNodeId()); REACT_EXPAND_PACK(this->AttachToMe(GetInternals(syncs).GetNodeId())); } diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index b25b46d9..d137c506 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -168,12 +168,12 @@ class SignalFuncNode : public SignalNode public: template SignalFuncNode(const Group& group, FIn&& func, const Signal& ... deps) : - SignalFuncNode::SignalNode( group, func(deps->Value() ...) ), + SignalFuncNode::SignalNode( group, func(GetInternals(deps).Value() ...) ), func_( std::forward(func) ), depHolder_( deps ... ) { this->RegisterMe(); - REACT_EXPAND_PACK(this->AttachToMe(deps->GetNodeId())); + REACT_EXPAND_PACK(this->AttachToMe(GetInternals(deps).GetNodeId())); } ~SignalFuncNode() @@ -220,14 +220,14 @@ class SignalSlotNode : public SignalNode { public: SignalSlotNode(const Group& group, const Signal& dep) : - SignalSlotNode::SignalNode( group, dep->Value() ), + SignalSlotNode::SignalNode( group, GetInternals(dep).Value() ), slotInput_( *this, dep ) { - slotInput_.nodeId = GraphPtr()->RegisterNode(&slotInput_, NodeCategory::dyninput); + slotInput_.nodeId = GetGraphPtr()->RegisterNode(&slotInput_, NodeCategory::dyninput); this->RegisterMe(); this->AttachToMe(slotInput_.nodeId); - this->AttachToMe(dep->GetNodeId()); + this->AttachToMe(GetInternals(dep).GetNodeId()); } ~SignalSlotNode() @@ -263,6 +263,9 @@ class SignalSlotNode : public SignalNode } void SetInput(const Signal& newInput) + { + slotInput_.isChanged = true; + } { slotInput_.newDep = newInput; } NodeId GetInputNodeId() const @@ -273,8 +276,7 @@ class SignalSlotNode : public SignalNode { VirtualInputNode(SignalSlotNode& parentIn, const Signal& depIn) : parent( parentIn ), - dep( depIn ), - newDep( depIn ), + dep( depIn ) { } virtual const char* GetNodeType() const override @@ -306,8 +308,8 @@ class SignalSlotNode : public SignalNode NodeId nodeId; - Signal dep; - Signal newDep; + Signal dep; + bool isChanged = false; }; VirtualInputNode slotInput_; @@ -320,9 +322,9 @@ template class SignalLinkNode : public SignalNode { public: - SignalLinkNode(const Group& group, const Group& srcGroup, const Signal& dep) : - SignalLinkNode::SignalNode( group, dep->Value() ), - linkOutput_( srcGroup, dep ) + SignalLinkNode(const Group& group, const Signal& dep) : + SignalLinkNode::SignalNode( group, GetInternals(dep).Value() ), + linkOutput_( dep ) { this->RegisterMe(NodeCategory::input); } @@ -351,18 +353,20 @@ class SignalLinkNode : public SignalNode private: struct VirtualOutputNode : public ILinkOutputNode { - VirtualOutputNode(const Group& srcGroupIn, const Signal& depIn) : + VirtualOutputNode(const Signal& depIn) : parent( ), - srcGroup( srcGroupIn ), - dep( depIn ) + dep( depIn ), + srcGroup( depIn.GetGroup() ) { + auto& srcGraphPtr = GetInternals(srcGroup).GetGraphPtr(); nodeId = srcGraphPtr->RegisterNode(this, NodeCategory::linkoutput); - srcGraphPtr->OnNodeAttach(nodeId, dep->GetNodeId()); + srcGraphPtr->OnNodeAttach(nodeId, GetInternals(dep).GetNodeId()); } ~VirtualOutputNode() { - srcGraphPtr->OnNodeDetach(nodeId, dep->GetNodeId()); + auto& srcGraphPtr = GetInternals(srcGroup).GetGraphPtr(); + srcGraphPtr->OnNodeDetach(nodeId, GetInternals(dep).GetNodeId()); srcGraphPtr->UnregisterNode(nodeId); } @@ -379,12 +383,12 @@ class SignalLinkNode : public SignalNode { if (auto p = parent.lock()) { - auto* rawPtr = p->GraphPtr().get(); + auto* rawPtr = p->GetGraphPtr().get(); output[rawPtr].push_back( - [storedParent = std::move(p), storedValue = dep->Value()] + [storedParent = std::move(p), storedValue = GetInternals(dep).Value()] { NodeId nodeId = storedParent->GetNodeId(); - auto& graphPtr = storedParent->GraphPtr(); + auto& graphPtr = storedParent->GetGraphPtr(); graphPtr->AddInput(nodeId, [&storedParent, &storedValue] @@ -397,11 +401,9 @@ class SignalLinkNode : public SignalNode std::weak_ptr parent; - NodeId nodeId; - - Signal dep; - - Group srcGroup; + NodeId nodeId; + Signal dep; + Group srcGroup; }; VirtualOutputNode linkOutput_; From 88090f769aab1aa086af9d4b3346be1ffb36cba0 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sat, 7 Jan 2017 10:23:46 +0100 Subject: [PATCH 239/266] progress --- examples/src/BasicObservers.cpp | 22 +- examples/src/BasicSignals.cpp | 15 +- examples/src/Main.cpp | 355 +++++++++++---------- include/react/Event.h | 51 +-- include/react/Signal.h | 2 +- include/react/detail/graph/EventNodes.h | 101 ++---- include/react/detail/graph/PropagationST.h | 30 +- include/react/detail/graph/SignalNodes.h | 67 ++-- 8 files changed, 313 insertions(+), 330 deletions(-) diff --git a/examples/src/BasicObservers.cpp b/examples/src/BasicObservers.cpp index ad263a2e..fede6fd0 100644 --- a/examples/src/BasicObservers.cpp +++ b/examples/src/BasicObservers.cpp @@ -58,17 +58,17 @@ namespace example2 { cout << "Example 2 - Detaching observers manually" << endl; - Observer obs( - [] (EventRange<> in) - { - for (auto _ : in) - cout << "Triggered!" << endl; - }, - trigger); - - trigger.Emit(); // output: Triggered! - - obs.Cancel(); // Remove the observer + { + Observer obs( + [] (EventRange<> in) + { + for (auto _ : in) + cout << "Triggered!" << endl; + }, + trigger); + + trigger.Emit(); // output: Triggered! + } trigger.Emit(); // no output diff --git a/examples/src/BasicSignals.cpp b/examples/src/BasicSignals.cpp index 6bd6771d..a8b6a60a 100644 --- a/examples/src/BasicSignals.cpp +++ b/examples/src/BasicSignals.cpp @@ -26,6 +26,9 @@ namespace example1 string ConcatFunc(string first, string second) { return first + string(" ") + second; } + void PrintFunc(const string& s) + { cout << s << endl; } + // Defines a group. // Each group represents a separate dependency graph. // Reactives from different groups can not be mixed. @@ -40,18 +43,14 @@ namespace example1 void Run() { - cout << "Example 1 - Hello world" << endl; + Observer obs{ PrintFunc, bothWords }; - cout << bothWords.Value() << endl; + cout << "Example 1 - Hello world" << endl; firstWord <<= string("Hello"); - cout << bothWords.Value() << endl; - secondWord <<= string("World"); - cout << bothWords.Value() << endl; - cout << endl; } } @@ -150,8 +149,8 @@ namespace example4 data.Modify([] (vector& data) { data.push_back("World"); }); - for (const auto& s : data.Value()) - cout << s << " "; +// for (const auto& s : data.Value()) +// cout << s << " "; cout << endl; // output: Hello World diff --git a/examples/src/Main.cpp b/examples/src/Main.cpp index cc5fdc31..1ab7a14f 100644 --- a/examples/src/Main.cpp +++ b/examples/src/Main.cpp @@ -17,7 +17,6 @@ using namespace react; - template class GridGraphGenerator { @@ -67,13 +66,13 @@ class GridGraphGenerator if (shouldGrow) { - auto s = SignalType{ group, function1, *l }; + auto s = SignalType{ group, function1, *l }; nextBuf->push_back(std::move(s)); } while (r != curBuf->end()) { - auto s = SignalType{ group, function2, *l, *r }; + auto s = SignalType{ group, function2, *l, *r }; nextBuf->push_back(std::move(s)); ++nodeCount; ++l; ++r; @@ -111,38 +110,38 @@ class GridGraphGenerator template T Multiply(T a, T b) { - return a * b; + return a * b; } template void PrintValue(T v) { - printf("Value: %d\n", v); + printf("Value: %d\n", v); } template void PrintArea(T v) { - printf("Area: %d\n", v); + printf("Area: %d\n", v); } template void PrintVolume(T v) { - printf("Volume: %d\n", v); + printf("Volume: %d\n", v); } template void PrintEvents(EventRange evts) { printf("Processing events...\n"); - for (const auto& e : evts) - printf(" Event: %d\n", e); + for (const auto& e : evts) + printf(" Event: %d\n", e); } template void PrintSyncedEvents(EventRange evts, int a, int b) { - printf("Processing events...\n"); + printf("Processing events...\n"); - for (const auto& e : evts) - printf(" Event: %d, %d, %d\n", e, a, b); + for (const auto& e : evts) + printf(" Event: %d, %d, %d\n", e, a, b); } template bool FilterFunc(T v) @@ -152,166 +151,179 @@ template bool FilterFunc(T v) template T IterFunc1(EventRange evts, T v) { - return v + 1; + return v + 1; } template T IterFunc2(EventRange evts, T v, T a1, T a2) { - return v + 1; + return v + 1; } -int main2() +int main() { - Group group; + Group group; + + { + // Signals + VarSignal x{ group, 0 }; + VarSignal y{ group, 0 }; + VarSignal z{ group, 0 }; + + Signal area{ Multiply, x, y }; + Signal volume{ Multiply, area, z }; + + Observer areaObs{ PrintArea, area }; + Observer volumeObs{ PrintVolume, volume }; - { - // Signals - VarSignal x{ group, 0 }; - VarSignal y{ group, 0 }; - VarSignal z{ group, 0 }; + x.Set(2); // a: 0, v: 0 + y.Set(2); // a: 4, v: 0 + z.Set(2); // a: 4, v: 8 - Signal area{ Multiply, x, y }; - Signal volume{ Multiply, area, z }; + group.DoTransaction([&] + { + x.Set(100); + y <<= 3; + y <<= 4; + }); - Observer areaObs{ PrintArea, area }; - Observer volumeObs{ PrintVolume, volume }; + // a: 400, v: 800 + } - x.Set(2); // a: 0, v: 0 - y.Set(2); // a: 4, v: 0 - z.Set(2); // a: 4, v: 8 + { + // Events + EventSource button1{ group }; + EventSource button2{ group }; - group.DoTransaction([&] - { - x.Set(100); - y <<= 3; - y <<= 4; - }); + Event anyButton = Merge(button1, button2); + Event filtered = Filter(FilterFunc, anyButton); - // a: 400, v: 800 - } + Observer eventObs{ PrintEvents, anyButton }; - { - // Events - EventSource button1{ group }; - EventSource button2{ group }; + button1.Emit(1); + button2.Emit(2); - Event anyButton = Merge(button1, button2); - Event filtered = Filter(FilterFunc, anyButton); + group.DoTransaction([&] + { + for (int i=0; i<10; ++i) + button1.Emit(42); + }); + } - Observer eventObs{ PrintEvents, anyButton }; + { + // Dynamic signals + VarSignal s1{ group, 10 }; + VarSignal s2{ group, 22 }; + + SignalSlot slot{ s1 }; - button1.Emit(1); - button2.Emit(2); + Observer areaObs{ PrintValue, slot }; - group.DoTransaction([&] - { - for (int i=0; i<10; ++i) - button1.Emit(42); - }); - } + s1.Set(42); - { - // Dynamic signals - VarSignal s1{ group, 10 }; - VarSignal s2{ group, 22 }; + slot.Set(s2); - SignalSlot slot{ s1 }; + s2.Set(667); + } - Observer areaObs{ PrintValue, slot }; + { + // Dynamic events + EventSource s1{ group }; + EventSource s2{ group }; - s1.Set(42); + EventSlot slot{ group }; - slot.Set(s2); + Observer eventObs{ PrintEvents, anyButton }; - s2.Set(667); - } + slot.AddInput(s1); + slot.AddInput(s2); + } - // Links - { - Group group1; - Group group2; + // Links + { + Group group1; + Group group2; - VarSignal s1{ group1, 10 }; - VarSignal s2{ group2, 11 }; + VarSignal s1{ group1, 10 }; + VarSignal s2{ group2, 11 }; - Signal v{ Multiply, s1, s2 }; + Signal v{ Multiply, s1, s2 }; - Observer obs{ PrintValue, v }; + Observer obs{ PrintValue, v }; - s1.Set(555); + s1.Set(555); - std::this_thread::sleep_for(std::chrono::seconds(5)); - } + std::this_thread::sleep_for(std::chrono::seconds(5)); + } - { - Group group1; - Group group2; + { + Group group1; + Group group2; - VarSignal s1{ group1, 10 }; - VarSignal s2{ group2, 11 }; + VarSignal s1{ group1, 10 }; + VarSignal s2{ group2, 11 }; - EventSource e1{ group1 }; - EventSource e2{ group2 }; + EventSource e1{ group1 }; + EventSource e2{ group2 }; - auto hold = Hold(group1, 0, e1); + auto hold = Hold(group1, 0, e1); - auto merged = Merge(group2, e1, e2); + auto merged = Merge(group2, e1, e2); - auto joined1 = Join(e1, e2); - auto joined2 = Join(group1, e1, e2); + auto joined1 = Join(e1, e2); + auto joined2 = Join(group1, e1, e2); - Observer eventObs1{ PrintEvents, merged }; - Observer eventObs2{ group2, PrintSyncedEvents, merged, s1, s2 }; + Observer eventObs1{ PrintEvents, merged }; + Observer eventObs2{ group2, PrintSyncedEvents, merged, s1, s2 }; - e1.Emit(222); + e1.Emit(222); - std::this_thread::sleep_for(std::chrono::seconds(5)); - } + std::this_thread::sleep_for(std::chrono::seconds(5)); + } - { - Group group1; - Group group2; + { + Group group1; + Group group2; - VarSignal s1{ group1, 10 }; - VarSignal s2{ group2, 11 }; + VarSignal s1{ group1, 10 }; + VarSignal s2{ group2, 11 }; - EventSource e1{ group1 }; - EventSource e2{ group2 }; + EventSource e1{ group1 }; + EventSource e2{ group2 }; - auto hold1 = Hold(group1, 0, e1); - auto hold2 = Hold(0, e1); + auto hold1 = Hold(group1, 0, e1); + auto hold2 = Hold(0, e1); - auto monitor1 = Monitor(group1, s1); - auto monitor2 = Monitor(s1); + auto monitor1 = Monitor(group1, s1); + auto monitor2 = Monitor(s1); - auto snapshot1 = Snapshot(group1, s1, e1); - auto snapshot2 = Snapshot(s1, e1); + auto snapshot1 = Snapshot(group1, s1, e1); + auto snapshot2 = Snapshot(s1, e1); - auto pulse1 = Pulse(group1, s1, e1); - auto pulse2 = Pulse(s1, e1); + auto pulse1 = Pulse(group1, s1, e1); + auto pulse2 = Pulse(s1, e1); - auto merged = Merge(group2, e1, e2); + auto merged = Merge(group2, e1, e2); - auto joined1 = Join(e1, e2); - auto joined2 = Join(group1, e1, e2); + auto joined1 = Join(e1, e2); + auto joined2 = Join(group1, e1, e2); - auto iter1 = Iterate(group, 0, IterFunc1, e1); - auto iter2 = Iterate(0, IterFunc1, e1); + auto iter1 = Iterate(group, 0, IterFunc1, e1); + auto iter2 = Iterate(0, IterFunc1, e1); - auto iter3 = Iterate(group, 0, IterFunc2, e1, s1, s2); - auto iter4 = Iterate(0, IterFunc2, e1, s1, s2); + auto iter3 = Iterate(group, 0, IterFunc2, e1, s1, s2); + auto iter4 = Iterate(0, IterFunc2, e1, s1, s2); - Observer eventObs{ PrintEvents, merged }; - Observer eventObs2{ group2, PrintSyncedEvents, merged, s1, s2 }; + Observer eventObs{ PrintEvents, merged }; + Observer eventObs2{ group2, PrintSyncedEvents, merged, s1, s2 }; - e1.Emit(222); + e1.Emit(222); - std::this_thread::sleep_for(std::chrono::seconds(5)); + std::this_thread::sleep_for(std::chrono::seconds(5)); - GridGraphGenerator grid; - } + GridGraphGenerator grid; + } - return 0; + return 0; } @@ -324,39 +336,39 @@ int main2() -int main() +int main2() { - Group group; + Group group; - VarSignal in{ group, 1 }; - Signal in2 = in; + VarSignal in{ group, 1 }; + Signal in2 = in; - GridGraphGenerator generator; + GridGraphGenerator generator; - generator.inputSignals.push_back(in2); + generator.inputSignals.push_back(in2); - generator.widths.push_back(100); - generator.widths.push_back(1); + generator.widths.push_back(100); + generator.widths.push_back(1); - int updateCount = 0; + int updateCount = 0; - generator.function1 = [&] (int a) { ++updateCount; return a; }; - generator.function2 = [&] (int a, int b) { ++updateCount; return a + b; }; + generator.function1 = [&] (int a) { ++updateCount; return a; }; + generator.function2 = [&] (int a, int b) { ++updateCount; return a + b; }; - generator.Generate(group); + generator.Generate(group); - updateCount = 0; + updateCount = 0; - auto t0 = tbb::tick_count::now(); - for (int i = 0; i < 10000; i++) - in <<= 10 + i; - auto t1 = tbb::tick_count::now(); + auto t0 = tbb::tick_count::now(); + for (int i = 0; i < 10000; i++) + in <<= 10 + i; + auto t1 = tbb::tick_count::now(); - double d = (t1 - t0).seconds(); - printf("updateCount %d\n", updateCount); - printf("Time %g\n", d); + double d = (t1 - t0).seconds(); + printf("updateCount %d\n", updateCount); + printf("Time %g\n", d); - return 0; + return 0; } @@ -371,47 +383,46 @@ int main() +/* - - -/*int main2() +int main7() { - Group group1; - Group group2; - Group group3; + Group group1; + Group group2; + Group group3; - VarSignal x{ 0, group1 }; - VarSignal y{ 0, group2 }; - VarSignal z{ 0, group3 }; + VarSignal x{ group1, 0 }; + VarSignal y{ group2, 0 }; + VarSignal z{ group3, 0 }; - Signal area{ Multiply, x, y }; - Signal volume{ Multiply, area, z }; + Signal area{ Multiply, x, y }; + Signal volume{ Multiply, area, z }; - Observer obs{ PrintAreaAndVolume, area, volume }; + Observer obs{ PrintAreaAndVolume, area, volume }; - Signal> volumeHistory = Iterate>( vector{ }, PushToVector, Monitor(volume)); + Signal> volumeHistory = Iterate>( vector{ }, PushToVector, Monitor(volume)); - x <<= 2; - y <<= 2; - z <<= 2; + x <<= 2; + y <<= 2; + z <<= 2; - group.DoTransaction([&] - { - x <<= 100; - y <<= 200; - z <<= 300; - }); + group.DoTransaction([&] + { + x <<= 100; + y <<= 200; + z <<= 300; + }); - obs.Cancel(); + obs.Cancel(); - x <<= 42; + x <<= 42; - printf("History:\n"); - for (auto t : volumeHistory.Value()) - printf("%d ", t); - printf("\n"); + printf("History:\n"); + for (auto t : volumeHistory.Value()) + printf("%d ", t); + printf("\n"); - return 0; + return 0; } @@ -421,14 +432,16 @@ int main3() using namespace react; Group group1; - Group group2; + Group group2; auto sig1 = VarSignal( 10, group1 ); auto link1 = SignalLink( sig1, group2 ); - auto link2 = SignalLink( sig1, group2 ); + auto link2 = SignalLink( sig1, group2 ); - sig1.Set(10); + sig1.Set(10); return 0; -}*/ \ No newline at end of file +} + +*/ \ No newline at end of file diff --git a/include/react/Event.h b/include/react/Event.h index 01c284c6..5a7fc42e 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -239,37 +239,37 @@ class EventSlot : public Event EventSlot(EventSlot&&) = default; EventSlot& operator=(EventSlot&&) = default; - // Construct with explicit group - EventSlot(const Group& group, const Event& input) : - EventSlot::Event( REACT_IMPL::CtorTag{ }, CreateSlotNode(group, input) ) + // Construct emtpy slot + EventSlot(const Group& group) : + EventSlot::Event( REACT_IMPL::CtorTag{ }, CreateSlotNode(group) ) { } - // Construct with implicit group - EventSlot(const Event& input) : - EventSlot::Event( REACT_IMPL::CtorTag{ }, CreateSlotNode(input.GetGroup(), input) ) - { } + void Add(const Event& input) + { AddInput(input); } - void Set(const Event& newInput) - { SetInput(newInput); } + void Remove(const Event& input) + { RemoveInput(input); } - void operator<<=(const Event& newInput) - { SetInput(newInput); } + void RemoveAll() + { RemoveAllInputs(); } protected: - auto CreateSlotNode(const Group& group, const Event& input) -> decltype(auto) + auto CreateSlotNode(const Group& group) -> decltype(auto) { using REACT_IMPL::EventSlotNode; - return std::make_shared>(group, SameGroupOrLink(input)); + return std::make_shared>(group); } private: - void SetInput(const Event& newInput) + void AddInput(const Event& input) { + SameGroupOrLink(input) + using REACT_IMPL::NodeId; using REACT_IMPL::ReactiveGraph; using SlotNodeType = REACT_IMPL::EventSlotNode; - SlotNodeType* castedPtr = static_cast(this->NodePtr().get()); + SlotNodeType* castedPtr = static_cast(this->GetNodePtr().get()); NodeId nodeId = castedPtr->GetInputNodeId(); auto& graphPtr = NodePtr()->GraphPtr(); @@ -292,21 +292,24 @@ class EventLink : public Event // Construct with explicit group EventLink(const Group& group, const Event& input) : - EventLink::Event( REACT_IMPL::CtorTag{ }, CreateLinkNode(group, input) ) - { } - - // Construct with implicit group - explicit EventLink(const Event& input) : - EventLink::Event( REACT_IMPL::CtorTag{ }, CreateLinkNode(input.GetGroup(), input) ) + EventLink::Event( REACT_IMPL::CtorTag{ }, GetOrCreateLinkNode(group, input) ) { } protected: - static auto CreateLinkNode(const Group& group, const Event& input) -> decltype(auto) + static auto GetOrCreateLinkNode(const Group& group, const Event& input) -> decltype(auto) { using REACT_IMPL::EventLinkNode; - auto node = std::make_shared>(group, input); - node->SetWeakSelfPtr(std::weak_ptr>{ node }); + const void* inputPtr = GetInternals(input.GetGroup()).GetGraphPtr().get(); + const void* targetPtr = GetInternals(group).GetGraphPtr().get(); + + { + + } + + auto nodePtr = std::make_shared>(group, input); + auto weakPtr = + nodePtr->SetWeakSelfPtr(std::weak_ptr>{ nodePtr }); return node; } }; diff --git a/include/react/Signal.h b/include/react/Signal.h index 10ed71f8..22e237fc 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -244,7 +244,7 @@ class SignalSlot : public Signal static auto CreateSlotNode(const Group& group, const Signal& input) -> decltype(auto) { using REACT_IMPL::SignalSlotNode; - return std::make_shared>(group, input); + return std::make_shared>(group, SameGroupOrLink(group, input)); } private: diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index f720fc18..27d81a41 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -236,23 +236,21 @@ class EventSlotNode : public EventStreamNode { public: EventSlotNode(const Group& group) : - EventSlotNode::EventStreamNode( group ), - slotInput_( *this ) + EventSlotNode::EventStreamNode( group ) { - slotInput_.nodeId = GraphPtr()->RegisterNode(&slotInput_, NodeCategory::dyninput); + inputNodeId_ = GetGraphPtr()->RegisterNode(&slotInput_, NodeCategory::dyninput); this->RegisterMe(); - this->AttachToMe(slotInput_.nodeId); - //this->AttachToMe(GetInternals(dep).GetNodeId()); + this->AttachToMe(inputNodeId_); } ~EventSlotNode() { - this->DetachFromMe(GetInternals(slotInput_.dep).GetNodeId()); - this->DetachFromMe(slotInput_.nodeId); + RemoveAllInputs(); + this->DetachFromMe(inputNodeId_); this->UnregisterMe(); - GraphPtr()->UnregisterNode(slotInput_.nodeId); + GetGraphPtr()->UnregisterNode(inputNodeId_); } virtual const char* GetNodeType() const override @@ -263,7 +261,11 @@ class EventSlotNode : public EventStreamNode virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { - this->Events().insert(this->Events().end(), slotInput_.dep->Events().begin(), slotInput_.dep->Events().end()); + for (const auto& e : deps_) + { + const auto& events = GetInternals(e).Events(); + this->Events().insert(this->Events().end(), events.begin(), events.end()); + } slotInput_.dep->DecrementPendingSuccessorCount(); @@ -278,89 +280,54 @@ class EventSlotNode : public EventStreamNode } } - void AddInput(const Event>& input) + void AddInput(const Event& input) { - auto& newDeps = slotInput_.newDeps; - - for (auto& e : newDeps) + auto it = std::find(inputs_.begin(), inputs_.end(), input); + if (it == inputs_.end()) { - if (e.second != input) - continue; - - // Earlier remove is overridden by later add. - if (e.first == false) - e.first = true; - - // Either element was already added, or remove has been overridden. Nothing more to do. - return; + inputs_.push_back(input); + this->AttachToMe(GetInternals(input).GetNodeId()); } - - newDeps.emplace_back(true, input); } void RemoveInput(const Event& input) { - auto& newDeps = slotInput_.newDeps; - - for (auto& e : newDeps) + auto it = std::find(inputs_.begin(), inputs_.end(), input); + if (it != inputs_.end()) { - if (e.second != input) - continue; - - // Earlier add is overridden by later remove. - if (e.first == true) - e.first = false; - - // Either element was already removed, or add has been overridden. Nothing more to do. - return; + inputs_.erase(it); + this->DetachFromMe(GetInternals(input).GetNodeId()); } + } + + void RemoveAllInputs() + { + for (const auto& e : inputs_) + this->DetachFromMe(GetInternals(e).GetNodeId()); - newDeps.emplace_back(false, input); + inputs_.clear(); } NodeId GetInputNodeId() const - { return slotInput_.nodeId; } + { return inputNodeId_; } private: struct VirtualInputNode : public IReactiveNode { - VirtualInputNode(EventSlotNode& parentIn, const Event& depIn) : - parent( parentIn ), - dep( depIn ) - { } - virtual const char* GetNodeType() const override - { return "EventSlotVirtualInput"; } + { return "EventSlotInput"; } virtual int GetDependencyCount() const override { return 0; } virtual UpdateResult Update(TurnId turnId, size_t successorCount) override - { - if (dep != newDep) - { - parent.DynamicDetachFromMe(dep->GetNodeId(), 0); - parent.DynamicAttachToMe(newDep->GetNodeId(), 0); - - dep = std::move(newDep); - return UpdateResult::changed; - } - else - { - newDep.reset(); - return UpdateResult::unchanged; - } - } - - EventSlotNode& parent; - - NodeId nodeId; - - std::vector> deps; - std::vector>> newDeps; + { return UpdateResult::changed; } }; - VirtualInputNode slotInput_; + std::vector> inputs_; + + NodeId inputNodeId_; + VirtualInputNode slotInput_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/detail/graph/PropagationST.h b/include/react/detail/graph/PropagationST.h index 814fc94c..c9cb6dfc 100644 --- a/include/react/detail/graph/PropagationST.h +++ b/include/react/detail/graph/PropagationST.h @@ -26,6 +26,33 @@ /***************************************/ REACT_IMPL_BEGIN /**************************************/ +template +class PtrCache +{ +public: + void Add(const K& key, const std::shared_ptr& ptr) + { + auto res = map1_.insert(key, ptr); + if (res.first == true) + { + map2_.insert(ptr , res.second); + return true; + } + + } + + void Remove(const std::shared_ptr& ptr) + { + map2_.remove(); + } + +private: + std::mutex lock_; + + std::map> map1_; + std::unordered_map map2_; +}; + class ReactiveGraph; class TransactionQueue @@ -94,9 +121,6 @@ class ReactiveGraph void OnNodeAttach(NodeId node, NodeId parentId); void OnNodeDetach(NodeId node, NodeId parentId); - void OnDynamicNodeAttach(NodeId node, NodeId parentId, TurnId turnId); - void OnDynamicNodeDetach(NodeId node, NodeId parentId, TurnId turnId); - template void AddInput(NodeId nodeId, F&& inputCallback); diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index d137c506..607746ea 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -221,24 +221,22 @@ class SignalSlotNode : public SignalNode public: SignalSlotNode(const Group& group, const Signal& dep) : SignalSlotNode::SignalNode( group, GetInternals(dep).Value() ), - slotInput_( *this, dep ) + input_( dep ) { - slotInput_.nodeId = GetGraphPtr()->RegisterNode(&slotInput_, NodeCategory::dyninput); + inputNodeId_ = GetGraphPtr()->RegisterNode(&slotInput_, NodeCategory::dyninput); this->RegisterMe(); - this->AttachToMe(slotInput_.nodeId); + this->AttachToMe(inputNodeId_); this->AttachToMe(GetInternals(dep).GetNodeId()); } ~SignalSlotNode() { - const auto& depNodePtr = GetInternals(slotInput_.dep).GetNodePtr(); - - this->DetachFromMe(depNodePtr->GetNodeId()); - this->DetachFromMe(slotInput_.nodeId); + this->DetachFromMe(GetInternals(input_).GetNodeId()); + this->DetachFromMe(inputNodeId_); this->UnregisterMe(); - GetGraphPtr()->UnregisterNode(slotInput_.nodeId); + GetGraphPtr()->UnregisterNode(inputNodeId_); } virtual const char* GetNodeType() const override @@ -249,11 +247,9 @@ class SignalSlotNode : public SignalNode virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { - const auto& depNodePtr = GetInternals(slotInput_.dep).GetNodePtr(); - - if (! (this->Value() == depNodePtr->Value())) + if (! (this->Value() == GetInternals(input_).Value())) { - this->Value() = depNodePtr->Value(); + this->Value() = GetInternals(input_).Value(); return UpdateResult::changed; } else @@ -264,21 +260,21 @@ class SignalSlotNode : public SignalNode void SetInput(const Signal& newInput) { - slotInput_.isChanged = true; + if (newInput == input_) + return; + + this->DetachFromMe(GetInternals(input_).GetNodeId()); + this->AttachToMe(GetInternals(newInput).GetNodeId()); + + input_ = newInput; } - { slotInput_.newDep = newInput; } NodeId GetInputNodeId() const - { return slotInput_.nodeId; } + { return inputNodeId_; } private: struct VirtualInputNode : public IReactiveNode { - VirtualInputNode(SignalSlotNode& parentIn, const Signal& depIn) : - parent( parentIn ), - dep( depIn ) - { } - virtual const char* GetNodeType() const override { return "SignalSlotInput"; } @@ -286,33 +282,14 @@ class SignalSlotNode : public SignalNode { return 0; } virtual UpdateResult Update(TurnId turnId, size_t successorCount) override - { - if (dep != newDep) - { - const auto& depNodePtr = GetInternals(dep).GetNodePtr(); - const auto& newDepNodePtr = GetInternals(newDep).GetNodePtr(); - - parent.DynamicDetachFromMe(depNodePtr->GetNodeId(), 0); - parent.DynamicAttachToMe(newDepNodePtr->GetNodeId(), 0); - - dep = std::move(newDep); - return UpdateResult::changed; - } - else - { - return UpdateResult::unchanged; - } - } - - SignalSlotNode& parent; - - NodeId nodeId; - - Signal dep; - bool isChanged = false; + { return UpdateResult::changed; } }; - VirtualInputNode slotInput_; + Signal input_; + + NodeId inputNodeId_; + VirtualInputNode slotInput_; + }; /////////////////////////////////////////////////////////////////////////////////////////////////// From d24b490af8f866ad954e1d3413c1b7a00791d58e Mon Sep 17 00:00:00 2001 From: schlangster Date: Sat, 7 Jan 2017 22:17:52 +0100 Subject: [PATCH 240/266] progress --- include/react/Event.h | 59 +++++++++++++++---- include/react/detail/graph/EventNodes.h | 6 +- include/react/detail/graph/GraphBase.h | 6 -- include/react/detail/graph/PropagationST.h | 66 ++++++++++++++-------- 4 files changed, 93 insertions(+), 44 deletions(-) diff --git a/include/react/Event.h b/include/react/Event.h index 5a7fc42e..7020a638 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -263,17 +263,41 @@ class EventSlot : public Event private: void AddInput(const Event& input) { - SameGroupOrLink(input) + using REACT_IMPL::NodeId; + using SlotNodeType = REACT_IMPL::EventSlotNode; + + SlotNodeType* castedPtr = static_cast(this->GetNodePtr().get()); + + NodeId nodeId = castedPtr->GetInputNodeId(); + auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); + graphPtr->AddInput(nodeId, [castedPtr, &input] { castedPtr->AddInput(input); }); + } + + void RemoveInput(const Event& input) + { using REACT_IMPL::NodeId; - using REACT_IMPL::ReactiveGraph; using SlotNodeType = REACT_IMPL::EventSlotNode; SlotNodeType* castedPtr = static_cast(this->GetNodePtr().get()); NodeId nodeId = castedPtr->GetInputNodeId(); - auto& graphPtr = NodePtr()->GraphPtr(); - graphPtr->AddInput(nodeId, [castedPtr, &newInput] { castedPtr->SetInput(PrivateNodeInterface::NodePtr(newInput)); }); + auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); + + graphPtr->AddInput(nodeId, [castedPtr, &input] { castedPtr->RemoveInput(input); }); + } + + void RemoveAllInputs() + { + using REACT_IMPL::NodeId; + using SlotNodeType = REACT_IMPL::EventSlotNode; + + SlotNodeType* castedPtr = static_cast(this->GetNodePtr().get()); + + NodeId nodeId = castedPtr->GetInputNodeId(); + auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); + + graphPtr->AddInput(nodeId, [castedPtr] { castedPtr->RemoveAllInputs(); }); } }; @@ -295,22 +319,33 @@ class EventLink : public Event EventLink::Event( REACT_IMPL::CtorTag{ }, GetOrCreateLinkNode(group, input) ) { } + ~EventLink() + { + auto nodePtr = GetNodePtr(); + } + protected: static auto GetOrCreateLinkNode(const Group& group, const Event& input) -> decltype(auto) { using REACT_IMPL::EventLinkNode; - const void* inputPtr = GetInternals(input.GetGroup()).GetGraphPtr().get(); - const void* targetPtr = GetInternals(group).GetGraphPtr().get(); + auto targetGraphPtr = GetInternals(group).GetGraphPtr(); + + void* k1 = GetInternals(input.GetGroup()).GetGraphPtr().get(); + void* k2 = GetInternals(input).GetNodePtr().get(); - { + auto& linkCache = targetGraphPtr->GetLinkCache(); - } + auto nodePtr = linkCache.LookupOrCreate>( + { k1, k2 }, + [&] + { + auto nodePtr = std::make_shared>(group, input); + nodePtr->SetWeakSelfPtr(std::weak_ptr>{ nodePtr }); + return nodePtr; + }); - auto nodePtr = std::make_shared>(group, input); - auto weakPtr = - nodePtr->SetWeakSelfPtr(std::weak_ptr>{ nodePtr }); - return node; + return nodePtr; } }; diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 27d81a41..2ccccca3 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -261,13 +261,13 @@ class EventSlotNode : public EventStreamNode virtual UpdateResult Update(TurnId turnId, size_t successorCount) override { - for (const auto& e : deps_) + for (auto& e : inputs_) { const auto& events = GetInternals(e).Events(); this->Events().insert(this->Events().end(), events.begin(), events.end()); - } - slotInput_.dep->DecrementPendingSuccessorCount(); + GetInternals(e).DecrementPendingSuccessorCount(); + } if (! this->Events().empty()) { diff --git a/include/react/detail/graph/GraphBase.h b/include/react/detail/graph/GraphBase.h index 43f58f89..65f8d1f7 100644 --- a/include/react/detail/graph/GraphBase.h +++ b/include/react/detail/graph/GraphBase.h @@ -83,12 +83,6 @@ class NodeBase : public IReactiveNode void DetachFromMe(NodeId otherNodeId) { GetGraphPtr()->OnNodeDetach(nodeId_, otherNodeId); } - void DynamicAttachToMe(NodeId otherNodeId, TurnId turnId) - { GetGraphPtr()->OnDynamicNodeAttach(nodeId_, otherNodeId, turnId); } - - void DynamicDetachFromMe(NodeId otherNodeId, TurnId turnId) - { GetGraphPtr()->OnDynamicNodeDetach(nodeId_, otherNodeId, turnId); } - private: NodeId nodeId_; diff --git a/include/react/detail/graph/PropagationST.h b/include/react/detail/graph/PropagationST.h index c9cb6dfc..fd2640fd 100644 --- a/include/react/detail/graph/PropagationST.h +++ b/include/react/detail/graph/PropagationST.h @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include @@ -30,27 +32,49 @@ template class PtrCache { public: - void Add(const K& key, const std::shared_ptr& ptr) + template + std::shared_ptr LookupOrCreate(const K& key, F&& createFunc) { - auto res = map1_.insert(key, ptr); - if (res.first == true) + std::lock_guard scopedLock(mutex_); + + auto it = map1_.find(key); + + if (it != map1_.end()) { - map2_.insert(ptr , res.second); - return true; + if (auto ptr = it->second.lock()) + { + return std::static_pointer_cast(ptr); + } } - + + std::shared_ptr v = createFunc(); + auto res = map1_.insert({ key, std::weak_ptr{ v } }); + map2_[v.get()] = res.first; + return v; } - void Remove(const std::shared_ptr& ptr) + template + void Erase(const std::shared_ptr& ptr) { - map2_.remove(); + std::lock_guard scopedLock(mutex_); + + auto it = map2_.find((void*)ptr.get()); + + if (it != map2_.end()) + { + map1_.erase(it->second); + map2_.erase(it); + } } private: - std::mutex lock_; + std::mutex mutex_; + + using Map1Type = std::map>; + using Map2Type = std::unordered_map; - std::map> map1_; - std::unordered_map map2_; + Map1Type map1_; + Map2Type map2_; }; class ReactiveGraph; @@ -115,6 +139,8 @@ class TransactionQueue class ReactiveGraph { public: + using LinkCacheType = PtrCache>; + NodeId RegisterNode(IReactiveNode* nodePtr, NodeCategory category); void UnregisterNode(NodeId nodeId); @@ -129,6 +155,9 @@ class ReactiveGraph template void EnqueueTransaction(TransactionFlags flags, F&& transactionCallback); + + LinkCacheType& GetLinkCache() + { return linkCache_; } private: struct NodeData @@ -189,7 +218,9 @@ class ReactiveGraph TopoQueue scheduledNodes_; IndexMap nodeData_; std::vector changedInputs_; - LinkOutputMap scheduledLinkOutputs_; + LinkOutputMap scheduledLinkOutputs_; + + LinkCacheType linkCache_; bool isTransactionActive_ = false; }; @@ -202,7 +233,6 @@ NodeId ReactiveGraph::RegisterNode(IReactiveNode* nodePtr, NodeCategory category void ReactiveGraph::UnregisterNode(NodeId nodeId) { nodeData_.Remove(nodeId); - } void ReactiveGraph::OnNodeAttach(NodeId nodeId, NodeId parentId) @@ -224,16 +254,6 @@ void ReactiveGraph::OnNodeDetach(NodeId nodeId, NodeId parentId) successors.erase(std::find(successors.begin(), successors.end(), nodeId)); } -void ReactiveGraph::OnDynamicNodeAttach(NodeId nodeId, NodeId parentId, TurnId turnId) -{ - OnNodeAttach(nodeId, parentId); -} - -void ReactiveGraph::OnDynamicNodeDetach(NodeId nodeId, NodeId parentId, TurnId turnId) -{ - OnNodeDetach(nodeId, parentId); -} - template void ReactiveGraph::AddInput(NodeId nodeId, F&& inputCallback) { From 20f0149bdee34cc3fa68a1908b4b950842f45e40 Mon Sep 17 00:00:00 2001 From: schlangster Date: Sun, 8 Jan 2017 01:07:01 +0100 Subject: [PATCH 241/266] done with link cache --- examples/src/Main.cpp | 48 ++++++++++++++++++++-- include/react/Event.h | 18 +++----- include/react/Signal.h | 33 +++++++++------ include/react/detail/graph/EventNodes.h | 3 ++ include/react/detail/graph/PropagationST.h | 4 +- include/react/detail/graph/SignalNodes.h | 3 ++ 6 files changed, 79 insertions(+), 30 deletions(-) diff --git a/examples/src/Main.cpp b/examples/src/Main.cpp index 1ab7a14f..5720dcf1 100644 --- a/examples/src/Main.cpp +++ b/examples/src/Main.cpp @@ -159,7 +159,7 @@ template T IterFunc2(EventRange evts, T v, T a1, T a2) return v + 1; } -int main() +int main1() { Group group; @@ -232,10 +232,13 @@ int main() EventSlot slot{ group }; - Observer eventObs{ PrintEvents, anyButton }; + Observer eventObs{ PrintEvents, slot }; + + slot.Add(s1); + slot.Add(s2); - slot.AddInput(s1); - slot.AddInput(s2); + slot.Remove(s1); + slot.RemoveAll(); } // Links @@ -327,7 +330,44 @@ int main() } +int main() +{ + Group group; + + Group extGroup; + + // Dynamic events + EventSource s1{ group }; + EventSource s2{ group }; + EventSource s3{ extGroup }; + //EventSource s4{ extGroup }; + + EventSlot slot{ group }; + + Observer eventObs{ PrintEvents, slot }; + //Observer extObs{ PrintEvents, link1 }; + + //slot.Add(s1); + //slot.Add(s2); + group.DoTransaction([&] + { + s1.Emit(1); + s2.Emit(2); + + + slot.Add(s3); + slot.Add(s3); + slot.Add(s3); + slot.Add(s3); + }); + + s3.Emit(3); + + std::this_thread::sleep_for(std::chrono::seconds(5)); + + return 0; +} diff --git a/include/react/Event.h b/include/react/Event.h index 7020a638..5d97dfff 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -271,7 +271,7 @@ class EventSlot : public Event NodeId nodeId = castedPtr->GetInputNodeId(); auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); - graphPtr->AddInput(nodeId, [castedPtr, &input] { castedPtr->AddInput(input); }); + graphPtr->AddInput(nodeId, [this, castedPtr, &input] { castedPtr->AddInput(SameGroupOrLink(GetGroup(), input)); }); } void RemoveInput(const Event& input) @@ -284,7 +284,7 @@ class EventSlot : public Event NodeId nodeId = castedPtr->GetInputNodeId(); auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); - graphPtr->AddInput(nodeId, [castedPtr, &input] { castedPtr->RemoveInput(input); }); + graphPtr->AddInput(nodeId, [this, castedPtr, &input] { castedPtr->RemoveInput(SameGroupOrLink(GetGroup(), input)); }); } void RemoveAllInputs() @@ -314,28 +314,22 @@ class EventLink : public Event EventLink(EventLink&&) = default; EventLink& operator=(EventLink&&) = default; - // Construct with explicit group + // Construct with group EventLink(const Group& group, const Event& input) : EventLink::Event( REACT_IMPL::CtorTag{ }, GetOrCreateLinkNode(group, input) ) { } - ~EventLink() - { - auto nodePtr = GetNodePtr(); - } - protected: static auto GetOrCreateLinkNode(const Group& group, const Event& input) -> decltype(auto) { using REACT_IMPL::EventLinkNode; - auto targetGraphPtr = GetInternals(group).GetGraphPtr(); + auto& targetGraphPtr = GetInternals(group).GetGraphPtr(); + auto& linkCache = targetGraphPtr->GetLinkCache(); void* k1 = GetInternals(input.GetGroup()).GetGraphPtr().get(); void* k2 = GetInternals(input).GetNodePtr().get(); - auto& linkCache = targetGraphPtr->GetLinkCache(); - auto nodePtr = linkCache.LookupOrCreate>( { k1, k2 }, [&] @@ -495,7 +489,7 @@ static Event SameGroupOrLink(const Group& targetGroup, const Event& dep) if (dep.GetGroup() == targetGroup) return dep; else - return EventLink( targetGroup, dep ); + return EventLink{ targetGroup, dep }; } /****************************************/ REACT_IMPL_END /***************************************/ diff --git a/include/react/Signal.h b/include/react/Signal.h index 22e237fc..a429f34b 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -258,7 +258,7 @@ class SignalSlot : public Signal NodeId nodeId = castedPtr->GetInputNodeId(); auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); - graphPtr->AddInput(nodeId, [castedPtr, &newInput] { castedPtr->SetInput(newInput); }); + graphPtr->AddInput(nodeId, [this, castedPtr, &newInput] { castedPtr->SetInput(SameGroupOrLink(GetGroup(), newInput)); }); } }; @@ -275,24 +275,33 @@ class SignalLink : public Signal SignalLink(SignalLink&&) = default; SignalLink& operator=(SignalLink&&) = default; - // Construct with explicit group + // Construct with group SignalLink(const Group& group, const Signal& input) : - SignalLink::Signal( REACT_IMPL::CtorTag{ }, CreateLinkNode(group, input) ) - { } - - // Construct with implicit group - explicit SignalLink(const Signal& input) : - SignalLink::Signal( REACT_IMPL::CtorTag{ }, CreateLinkNode(input.GetGroup(), input) ) + SignalLink::Signal( REACT_IMPL::CtorTag{ }, GetOrCreateLinkNode(group, input) ) { } protected: - static auto CreateLinkNode(const Group& group, const Signal& input) -> decltype(auto) + static auto GetOrCreateLinkNode(const Group& group, const Signal& input) -> decltype(auto) { using REACT_IMPL::SignalLinkNode; - auto node = std::make_shared>(group, input); - node->SetWeakSelfPtr(std::weak_ptr>{ node }); - return node; + auto targetGraphPtr = GetInternals(group).GetGraphPtr(); + + void* k1 = GetInternals(input.GetGroup()).GetGraphPtr().get(); + void* k2 = GetInternals(input).GetNodePtr().get(); + + auto& linkCache = targetGraphPtr->GetLinkCache(); + + auto nodePtr = linkCache.LookupOrCreate>( + { k1, k2 }, + [&] + { + auto nodePtr = std::make_shared>(group, input); + nodePtr->SetWeakSelfPtr(std::weak_ptr>{ nodePtr }); + return nodePtr; + }); + + return nodePtr; } }; diff --git a/include/react/detail/graph/EventNodes.h b/include/react/detail/graph/EventNodes.h index 2ccccca3..e216b4b5 100644 --- a/include/react/detail/graph/EventNodes.h +++ b/include/react/detail/graph/EventNodes.h @@ -562,6 +562,9 @@ class EventLinkNode : public EventStreamNode ~EventLinkNode() { + auto& linkCache = GetGraphPtr()->GetLinkCache(); + linkCache.Erase(this); + this->UnregisterMe(); } diff --git a/include/react/detail/graph/PropagationST.h b/include/react/detail/graph/PropagationST.h index fd2640fd..d1cead73 100644 --- a/include/react/detail/graph/PropagationST.h +++ b/include/react/detail/graph/PropagationST.h @@ -54,11 +54,11 @@ class PtrCache } template - void Erase(const std::shared_ptr& ptr) + void Erase(V* ptr) { std::lock_guard scopedLock(mutex_); - auto it = map2_.find((void*)ptr.get()); + auto it = map2_.find((void*)ptr); if (it != map2_.end()) { diff --git a/include/react/detail/graph/SignalNodes.h b/include/react/detail/graph/SignalNodes.h index 607746ea..268faf7c 100644 --- a/include/react/detail/graph/SignalNodes.h +++ b/include/react/detail/graph/SignalNodes.h @@ -308,6 +308,9 @@ class SignalLinkNode : public SignalNode ~SignalLinkNode() { + auto& linkCache = GetGraphPtr()->GetLinkCache(); + linkCache.Erase(this); + this->UnregisterMe(); } From 32fe91e6bcaa01556ddde119e5fc70e2c5f65756 Mon Sep 17 00:00:00 2001 From: schlangster Date: Mon, 23 Jan 2017 20:37:48 +0100 Subject: [PATCH 242/266] progress --- include/react/Group.h | 6 +- include/react/common/Containers.h | 267 +++++---------------- include/react/detail/graph/PropagationST.h | 5 +- include/react/engine/PulsecountEngine.h | 151 ------------ include/react/engine/SubtreeEngine.h | 198 --------------- include/react/engine/ToposortEngine.h | 214 ----------------- include/react/logging/EventLog.h | 100 -------- include/react/logging/EventRecords.h | 263 -------------------- include/react/logging/Logging.h | 31 --- project/msvc/CppReact.vcxproj | 3 - project/msvc/CppReact.vcxproj.filters | 12 - 11 files changed, 71 insertions(+), 1179 deletions(-) delete mode 100644 include/react/engine/PulsecountEngine.h delete mode 100644 include/react/engine/SubtreeEngine.h delete mode 100644 include/react/engine/ToposortEngine.h delete mode 100644 include/react/logging/EventLog.h delete mode 100644 include/react/logging/EventRecords.h delete mode 100644 include/react/logging/Logging.h diff --git a/include/react/Group.h b/include/react/Group.h index d68526df..dd62b012 100644 --- a/include/react/Group.h +++ b/include/react/Group.h @@ -65,9 +65,9 @@ class TransactionStatus public: // Default ctor - inline TransactionStatus() : + TransactionStatus() : statePtr_( StateT::Create() ) - {} + { } // Move ctor TransactionStatus(TransactionStatus&& other) : @@ -91,7 +91,7 @@ class TransactionStatus TransactionStatus(const TransactionStatus&) = delete; TransactionStatus& operator=(const TransactionStatus&) = delete; - inline void Wait() + void Wait() { assert(statePtr_.Get() != nullptr); statePtr_->Wait(); diff --git a/include/react/common/Containers.h b/include/react/common/Containers.h index a03257db..79980acf 100644 --- a/include/react/common/Containers.h +++ b/include/react/common/Containers.h @@ -14,59 +14,41 @@ #include #include #include +#include #include -#include /***************************************/ REACT_IMPL_BEGIN /**************************************/ -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EnumFlags -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EnumFlags -{ -public: - using FlagsT = typename std::underlying_type::type; - - template - void Set() { flags_ |= 1 << x; } - - template - void Clear() { flags_ &= ~(1 << x); } - - template - bool Test() const { return (flags_ & (1 << x)) != 0; } - -private: - FlagsT flags_ = 0; -}; - - /////////////////////////////////////////////////////////////////////////////////////////////////// /// IndexMap /////////////////////////////////////////////////////////////////////////////////////////////////// template -class IndexMap +class IndexedStorage { static const size_t initial_capacity = 8; static const size_t grow_factor = 2; + using StorageType = typename std::aligned_storage::type; + public: using ValueType = T; - IndexMap() = default; + IndexedStorage() = default; - IndexMap(const IndexMap&) = default; - IndexMap& operator=(const IndexMap&) = default; + IndexedStorage(IndexedStorage&&) = default; + IndexedStorage& operator=(IndexedStorage&&) = default; - IndexMap(IndexMap&&) = default; - IndexMap& operator=(IndexMap&&) = default; + IndexedStorage(const IndexedStorage&) = delete; + IndexedStorage& operator=(const IndexedStorage&) = delete; + + ~IndexedStorage() + { Reset(); } T& operator[](size_t index) - { return data_[index]; } + { return reinterpret_cast(data_[index]); } const T& operator[](size_t index) const - { return data_[index]; } + { return reinterpret_cast(data_[index]); } size_t Insert(T value) { @@ -85,61 +67,73 @@ class IndexMap } } - void Remove(size_t index) + void Erase(size_t index) { - for (size_t index : freeIndices_) - data_[index].~T(); + // If we erased something other than the last element, save in free index list. + if (index != (size_ - 1)) + { + freeIndices_[freeSize_++] = index; + } + + reinterpret_cast(data_[index]).~T(); --size_; - freeIndices_.push_back(index); } void Clear() { - // Sort free indexes so we can remove check for them in linear time - std::sort(freeIndices_.begin(), freeIndices_.end()); + // Sort free indexes so we can remove check for them in linear time. + std::sort(&freeIndices_[0], &freeIndices_[freeSize_]); - const size_t totalSize = size_ + freeIndices_.size(); - size_t i = 0; + const size_t totalSize = size_ + freeSize_; + size_t index = 0; - // Skip over free indices - for (auto freeIndex : freeIndices_) + // Skip over free indices. + for (size_t j = 0; j < freeSize_; ++j) { - for (; i < totalSize; ++i) + for (; index < totalSize; ++index) { - if (i == freeIndex) + if (j == freeIndex_) + { + ++index; break; + } else - data_[i].~T(); + { + data_[index].~T(); + } } } // Rest - for (; i < totalSize; ++i) - data_[i].~T(); + for (; index < totalSize; ++index) + data_[index].~T(); size_ = 0; - freeIndices_.clear(); + freeList_ = 0; } void Reset() { Clear(); - capacity_ = 0; - delete[] data_; + data_.reset(); + freeIndices_.reset(); - freeIndices_.shrink_to_fit(); + capacity_ = 0; } - ~IndexMap() - { Reset(); } - private: + T& GetDataAt(size_t index) + { return reinterpret_cast(data_[index]); } + + T& GetDataAt(size_t index) const + { return reinterpret_cast(data_[index]); } + bool IsAtFullCapacity() const { return capacity_ == size_; } bool HasFreeIndices() const - { return !freeIndices_.empty(); } + { return freeSize_ > 0; } size_t CalcNextCapacity() const { return capacity_ == 0? initial_capacity : capacity_ * grow_factor; } @@ -148,20 +142,23 @@ class IndexMap { // Allocate new storage size_t newCapacity = CalcNextCapacity(); - T* newData = new T[newCapacity]; + + std::unique_ptr newData{ new StorageType[newCapacity] }; + std::unique_ptr newFreeList{ new size_t[newCapacity] }; // Move data to new storage - for (size_t i = 0; i < size_; ++i) + for (size_t i = 0; i < capacity_; ++i) { - new (&newData[i]) T(std::move(data_[i])); - data_[i].~T(); + new (reinterpret_cast(&newData[i])) T{ std::move(reinterpret_cast(data_[i])) }; + reinterpret_cast(data_[i]).~T(); } - - delete[] data_; + + // Free list is empty if we are at max capacity anyway // Use new storage - data_ = newData; - capacity_ = newCapacity; + data_ = std::move(newData); + freeIndices_ = std::move(newFreeList); + capacity_ = newCapacity; } size_t InsertAtBack(T&& value) @@ -172,153 +169,19 @@ class IndexMap size_t InsertAtFreeIndex(T&& value) { - size_t nextFreeIndex = freeIndices_.back(); - freeIndices_.pop_back(); - + size_t nextFreeIndex = freeIndices_[--freeSize_]; new (&data_[nextFreeIndex]) T(std::move(value)); ++size_; return nextFreeIndex; } - T* data_ = nullptr; + std::unique_ptr data_; + std::unique_ptr freeIndices_; + size_t size_ = 0; + size_t freeSize_ = 0; size_t capacity_ = 0; - - std::vector freeIndices_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// NodeVector -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class NodeVector -{ -private: - typedef std::vector DataT; - -public: - void Add(TNode& node) - { - data_.push_back(&node); - } - - void Remove(const TNode& node) - { - data_.erase(std::find(data_.begin(), data_.end(), &node)); - } - - typedef typename DataT::iterator iterator; - typedef typename DataT::const_iterator const_iterator; - - iterator begin() { return data_.begin(); } - iterator end() { return data_.end(); } - - const_iterator begin() const { return data_.begin(); } - const_iterator end() const { return data_.end(); } - -private: - DataT data_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// NodeBuffer -/////////////////////////////////////////////////////////////////////////////////////////////////// -struct SplitTag {}; - -template -class NodeBuffer -{ -public: - using DataT = std::array; - using iterator = typename DataT::iterator; - using const_iterator = typename DataT::const_iterator; - - static const size_t split_size = N / 2; - - NodeBuffer() : - size_( 0 ), - front_( nodes_.begin() ), - back_( nodes_.begin() ) - {} - - NodeBuffer(T* node) : - size_( 1 ), - front_( nodes_.begin() ), - back_( nodes_.begin() + 1 ) - { - nodes_[0] = node; - } - - template - NodeBuffer(TInput srcBegin, TInput srcEnd) : - size_( std::distance(srcBegin, srcEnd) ), // parentheses to allow narrowing conversion - front_( nodes_.begin() ), - back_( size_ != N ? nodes_.begin() + size_ : nodes_.begin() ) - { - std::copy(srcBegin, srcEnd, front_); - } - - // Other must be full - NodeBuffer(NodeBuffer& other, SplitTag) : - size_( split_size ), - front_( nodes_.begin() ), - back_( nodes_.begin() ) - { - for (auto i=0; i nodeData_; + TopoQueue scheduledNodes_; - IndexMap nodeData_; std::vector changedInputs_; LinkOutputMap scheduledLinkOutputs_; @@ -232,7 +233,7 @@ NodeId ReactiveGraph::RegisterNode(IReactiveNode* nodePtr, NodeCategory category void ReactiveGraph::UnregisterNode(NodeId nodeId) { - nodeData_.Remove(nodeId); + nodeData_.Erase(nodeId); } void ReactiveGraph::OnNodeAttach(NodeId nodeId, NodeId parentId) diff --git a/include/react/engine/PulsecountEngine.h b/include/react/engine/PulsecountEngine.h deleted file mode 100644 index 78999c6f..00000000 --- a/include/react/engine/PulsecountEngine.h +++ /dev/null @@ -1,151 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_ENGINE_PULSECOUNTENGINE_H_INCLUDED -#define REACT_DETAIL_ENGINE_PULSECOUNTENGINE_H_INCLUDED - -#pragma once - -#if 0 - -#include "react/detail/Defs.h" - -#include -#include -#include - -#include "tbb/task_group.h" -#include "tbb/spin_rw_mutex.h" -#include "tbb/task.h" - -#include "react/common/Containers.h" -#include "react/common/Types.h" -#include "react/detail/EngineBase.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ -namespace pulsecount { - -using std::atomic; -using std::vector; - -using tbb::task; -using tbb::empty_task; -using tbb::spin_rw_mutex; -using tbb::task_list; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Turn -/////////////////////////////////////////////////////////////////////////////////////////////////// -class Turn : public TurnBase -{ -public: - Turn(TurnIdT id, TransactionFlagsT flags); -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Node -/////////////////////////////////////////////////////////////////////////////////////////////////// -enum class ENodeMark -{ - unmarked, - visited, - should_update -}; - -enum class ENodeState -{ - unchanged, - changed, - dyn_defer, - dyn_repeat -}; - -class Node : public IReactiveNode -{ -public: - using ShiftMutexT = spin_rw_mutex; - - inline void IncCounter() { counter_.fetch_add(1, std::memory_order_relaxed); } - inline bool DecCounter() { return counter_.fetch_sub(1, std::memory_order_relaxed) > 1; } - inline void SetCounter(int c) { counter_.store(c, std::memory_order_relaxed); } - - inline ENodeMark Mark() const - { - return mark_.load(std::memory_order_relaxed); - } - - inline void SetMark(ENodeMark mark) - { - mark_.store(mark, std::memory_order_relaxed); - } - - inline bool ExchangeMark(ENodeMark mark) - { - return mark_.exchange(mark, std::memory_order_relaxed) != mark; - } - - ShiftMutexT ShiftMutex; - NodeVector Successors; - - ENodeState State = ENodeState::unchanged; - -private: - atomic counter_ { 0 }; - atomic mark_ { ENodeMark::unmarked }; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EngineBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -class EngineBase : public IReactiveEngine -{ -public: - using NodeShiftMutexT = Node::ShiftMutexT; - using NodeVectT = vector; - - void OnNodeAttach(Node& node, Node& parent); - void OnNodeDetach(Node& node, Node& parent); - - void OnInputChange(Node& node, Turn& turn); - void Propagate(Turn& turn); - - void OnNodePulse(Node& node, Turn& turn); - void OnNodeIdlePulse(Node& node, Turn& turn); - - void OnDynamicNodeAttach(Node& node, Node& parent, Turn& turn); - void OnDynamicNodeDetach(Node& node, Node& parent, Turn& turn); - -private: - NodeVectT changedInputs_; - empty_task& rootTask_ = *new(task::allocate_root()) empty_task; - task_list spawnList_; -}; - -} // ~namespace pulsecount -/****************************************/ REACT_IMPL_END /***************************************/ - -/*****************************************/ REACT_BEGIN /*****************************************/ - -template -class PulsecountEngine; - -template <> -class PulsecountEngine : - public REACT_IMPL::pulsecount::EngineBase -{}; - -/******************************************/ REACT_END /******************************************/ - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -template <> -struct NodeUpdateTimerEnabled> : std::true_type {}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_DETAIL_ENGINE_PULSECOUNTENGINE_H_INCLUDED - -#endif \ No newline at end of file diff --git a/include/react/engine/SubtreeEngine.h b/include/react/engine/SubtreeEngine.h deleted file mode 100644 index 447c1184..00000000 --- a/include/react/engine/SubtreeEngine.h +++ /dev/null @@ -1,198 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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) - -#if 0 - -#ifndef REACT_DETAIL_ENGINE_SUBTREEENGINE_H_INCLUDED -#define REACT_DETAIL_ENGINE_SUBTREEENGINE_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include -#include - -#include "tbb/spin_rw_mutex.h" -#include "tbb/task.h" - -#include "react/common/Containers.h" -#include "react/common/TopoQueue.h" -#include "react/common/Types.h" -#include "react/detail/EngineBase.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ -namespace subtree { - -using std::atomic; -using std::vector; - -using tbb::task; -using tbb::empty_task; -using tbb::task_list; -using tbb::spin_rw_mutex; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Turn -/////////////////////////////////////////////////////////////////////////////////////////////////// -class Turn : public TurnBase -{ -public: - Turn(TurnIdT id, TransactionFlagsT flags); -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Node -/////////////////////////////////////////////////////////////////////////////////////////////////// -class Node : public IReactiveNode -{ -public: - using ShiftMutexT = spin_rw_mutex; - - inline bool IsQueued() const { return flags_.Test(); } - inline void SetQueuedFlag() { flags_.Set(); } - inline void ClearQueuedFlag() { flags_.Clear(); } - - inline bool IsMarked() const { return flags_.Test(); } - inline void SetMarkedFlag() { flags_.Set(); } - inline void ClearMarkedFlag() { flags_.Clear(); } - - inline bool IsChanged() const { return flags_.Test(); } - inline void SetChangedFlag() { flags_.Set(); } - inline void ClearChangedFlag() { flags_.Clear(); } - - inline bool IsDeferred() const { return flags_.Test(); } - inline void SetDeferredFlag() { flags_.Set(); } - inline void ClearDeferredFlag() { flags_.Clear(); } - - inline bool IsRepeated() const { return flags_.Test(); } - inline void SetRepeatedFlag() { flags_.Set(); } - inline void ClearRepeatedFlag() { flags_.Clear(); } - - inline bool IsInitial() const { return flags_.Test(); } - inline void SetInitialFlag() { flags_.Set(); } - inline void ClearInitialFlag() { flags_.Clear(); } - - inline bool IsRoot() const { return flags_.Test(); } - inline void SetRootFlag() { flags_.Set(); } - inline void ClearRootFlag() { flags_.Clear(); } - - inline bool ShouldUpdate() const { return shouldUpdate_.load(std::memory_order_relaxed); } - inline void SetShouldUpdate(bool b) { shouldUpdate_.store(b, std::memory_order_relaxed); } - - inline void SetReadyCount(int c) - { - readyCount_.store(c, std::memory_order_relaxed); - } - - inline bool IncReadyCount() - { - auto t = readyCount_.fetch_add(1, std::memory_order_relaxed); - return t < (WaitCount - 1); - } - - inline bool DecReadyCount() - { - return readyCount_.fetch_sub(1, std::memory_order_relaxed) > 1; - } - - NodeVector Successors; - ShiftMutexT ShiftMutex; - uint16_t Level = 0; - uint16_t NewLevel = 0; - uint16_t WaitCount = 0; - -private: - enum EFlags : uint16_t - { - flag_queued = 0, - flag_marked, - flag_changed, - flag_deferred, - flag_repeated, - flag_initial, - flag_root - }; - - EnumFlags flags_; - atomic readyCount_ { 0 }; - atomic shouldUpdate_ { false }; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Functors -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct GetLevelFunctor -{ - int operator()(const T* x) const { return x->Level; } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EngineBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -class EngineBase : public IReactiveEngine -{ -public: - using TopoQueueT = TopoQueue>; - using NodeShiftMutexT = Node::ShiftMutexT; - - void OnNodeAttach(Node& node, Node& parent); - void OnNodeDetach(Node& node, Node& parent); - - void OnInputChange(Node& node, Turn& turn); - void Propagate(Turn& turn); - - void OnNodePulse(Node& node, Turn& turn); - void OnNodeIdlePulse(Node& node, Turn& turn); - - void OnDynamicNodeAttach(Node& node, Node& parent, Turn& turn); - void OnDynamicNodeDetach(Node& node, Node& parent, Turn& turn); - -private: - void applyAsyncDynamicAttach(Node& node, Node& parent, Turn& turn); - void applyAsyncDynamicDetach(Node& node, Node& parent, Turn& turn); - - void invalidateSuccessors(Node& node); - void processChildren(Node& node, Turn& turn); - - void markSubtree(Node& root); - - TopoQueueT scheduledNodes_; - vector subtreeRoots_; - - empty_task& rootTask_ = *new(task::allocate_root()) empty_task; - task_list spawnList_; - - bool isInPhase2_ = false; -}; - -} // ~namespace subtree -/****************************************/ REACT_IMPL_END /***************************************/ - -/*****************************************/ REACT_BEGIN /*****************************************/ - -template -class SubtreeEngine; - -template <> -class SubtreeEngine : - public REACT_IMPL::subtree::EngineBase -{}; - -/******************************************/ REACT_END /******************************************/ - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -template <> -struct NodeUpdateTimerEnabled> : std::true_type {}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_DETAIL_ENGINE_SUBTREEENGINE_H_INCLUDED - -#endif \ No newline at end of file diff --git a/include/react/engine/ToposortEngine.h b/include/react/engine/ToposortEngine.h deleted file mode 100644 index 2575bbd9..00000000 --- a/include/react/engine/ToposortEngine.h +++ /dev/null @@ -1,214 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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) - -#if 0 - -#ifndef REACT_DETAIL_ENGINE_TOPOSORTENGINE_H_INCLUDED -#define REACT_DETAIL_ENGINE_TOPOSORTENGINE_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include -#include -#include - -#include "tbb/concurrent_vector.h" -#include "tbb/spin_mutex.h" - -#include "react/common/Containers.h" -#include "react/common/TopoQueue.h" -#include "react/common/Types.h" -#include "react/detail/EngineBase.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -namespace toposort { - -using std::atomic; -using std::vector; -using tbb::concurrent_vector; -using tbb::spin_mutex; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Parameters -/////////////////////////////////////////////////////////////////////////////////////////////////// -static const uint min_weight = 1; -static const uint grain_size = 100; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SeqNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -class SeqNode : public IReactiveNode -{ -public: - int Level { 0 }; - int NewLevel { 0 }; - bool Queued { false }; - - NodeVector Successors; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ParNode -/////////////////////////////////////////////////////////////////////////////////////////////////// -class ParNode : public IReactiveNode -{ -public: - using InvalidateMutexT = spin_mutex; - - int Level { 0 }; - int NewLevel { 0 }; - atomic Collected { false }; - - NodeVector Successors; - InvalidateMutexT InvalidateMutex; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ShiftRequestData -/////////////////////////////////////////////////////////////////////////////////////////////////// -struct DynRequestData -{ - bool ShouldAttach; - ParNode* Node; - ParNode* Parent; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ExclusiveSeqTurn -/////////////////////////////////////////////////////////////////////////////////////////////////// -class SeqTurn : public TurnBase -{ -public: - SeqTurn(TurnIdT id, TransactionFlagsT flags); -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ExclusiveParTurn -/////////////////////////////////////////////////////////////////////////////////////////////////// -class ParTurn : public TurnBase -{ -public: - ParTurn(TurnIdT id, TransactionFlagsT flags); -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Functors -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct GetLevelFunctor -{ - int operator()(const T* x) const { return x->Level; } -}; - -template -struct GetWeightFunctor -{ - uint operator()(T* x) const { return x->IsHeavyweight() ? grain_size : 1; } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EngineBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class EngineBase : public IReactiveEngine -{ -public: - void OnNodeAttach(TNode& node, TNode& parent); - void OnNodeDetach(TNode& node, TNode& parent); - - void OnInputChange(TNode& node, TTurn& turn); - void OnNodePulse(TNode& node, TTurn& turn); - -protected: - virtual void processChildren(TNode& node, TTurn& turn) = 0; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SeqEngineBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -class SeqEngineBase : public EngineBase -{ -public: - using TopoQueueT = TopoQueue>; - - void Propagate(SeqTurn& turn); - - void OnDynamicNodeAttach(SeqNode& node, SeqNode& parent, SeqTurn& turn); - void OnDynamicNodeDetach(SeqNode& node, SeqNode& parent, SeqTurn& turn); - -private: - void invalidateSuccessors(SeqNode& node); - - virtual void processChildren(SeqNode& node, SeqTurn& turn) override; - - TopoQueueT scheduledNodes_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ParEngineBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -class ParEngineBase : public EngineBase -{ -public: - using DynRequestVectT = concurrent_vector; - using TopoQueueT = ConcurrentTopoQueue - < - ParNode*, - grain_size, - GetLevelFunctor, - GetWeightFunctor - >; - - void Propagate(ParTurn& turn); - - void OnDynamicNodeAttach(ParNode& node, ParNode& parent, ParTurn& turn); - void OnDynamicNodeDetach(ParNode& node, ParNode& parent, ParTurn& turn); - -private: - void applyDynamicAttach(ParNode& node, ParNode& parent, ParTurn& turn); - void applyDynamicDetach(ParNode& node, ParNode& parent, ParTurn& turn); - - void invalidateSuccessors(ParNode& node); - - virtual void processChildren(ParNode& node, ParTurn& turn) override; - - TopoQueueT topoQueue_; - DynRequestVectT dynRequests_; -}; - -} // ~namespace toposort - -/****************************************/ REACT_IMPL_END /***************************************/ - -/*****************************************/ REACT_BEGIN /*****************************************/ - -template -class ToposortEngine; - -template <> class ToposortEngine : - public REACT_IMPL::toposort::SeqEngineBase -{}; - -template <> class ToposortEngine : - public REACT_IMPL::toposort::ParEngineBase -{}; - -/******************************************/ REACT_END /******************************************/ - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -template <> -struct NodeUpdateTimerEnabled> : std::true_type {}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_DETAIL_ENGINE_TOPOSORTENGINE_H_INCLUDED - -#endif \ No newline at end of file diff --git a/include/react/logging/EventLog.h b/include/react/logging/EventLog.h deleted file mode 100644 index 6b94277f..00000000 --- a/include/react/logging/EventLog.h +++ /dev/null @@ -1,100 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_LOGGING_EVENTLOG_H_INCLUDED -#define REACT_DETAIL_LOGGING_EVENTLOG_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include -#include -#include -#include - -#include "tbb/concurrent_vector.h" - -#include "Logging.h" -#include "react/common/Types.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EventLog -/////////////////////////////////////////////////////////////////////////////////////////////////// -class EventLog -{ - using TimestampT = std::chrono::time_point; - - class Entry - { - public: - Entry(); - Entry(const Entry& other); - - explicit Entry(IEventRecord* ptr); - - Entry& operator=(Entry& rhs); - - inline const char* EventId() const - { - return data_->EventId(); - } - - inline const TimestampT& Time() const - { - return time_; - } - - inline bool operator<(const Entry& other) const - { - return time_ < other.time_; - } - - inline void Release() - { - delete data_; - } - - void Serialize(std::ostream& out, const TimestampT& startTime) const; - bool Equals(const Entry& other) const; - - private: - TimestampT time_; - IEventRecord* data_; - }; - -public: - - EventLog(); - ~EventLog(); - - void Print(); - void Write(std::ostream& out); - void Clear(); - - template - < - typename TEventRecord, - typename ... TArgs - > - void Append(TArgs ... args) - { - entries_.push_back(Entry(new TEventRecord(args ...))); - } - -private: - using LogEntriesT = tbb::concurrent_vector; - - LogEntriesT entries_; - TimestampT startTime_; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_DETAIL_LOGGING_EVENTLOG_H_INCLUDED \ No newline at end of file diff --git a/include/react/logging/EventRecords.h b/include/react/logging/EventRecords.h deleted file mode 100644 index ea2359f5..00000000 --- a/include/react/logging/EventRecords.h +++ /dev/null @@ -1,263 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_LOGGING_EVENTRECORDS_H_INCLUDED -#define REACT_DETAIL_LOGGING_EVENTRECORDS_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include -#include - -#include "Logging.h" -#include "react/common/Types.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// NodeCreateEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -class NodeCreateEvent : public IEventRecord -{ -public: - NodeCreateEvent(ObjectId nodeId, const char* type); - - virtual const char* EventId() const { return "NodeCreate"; } - - virtual void Serialize(std::ostream& out) const; - -private: - ObjectId nodeId_; - const char * type_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// NodeDestroyEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -class NodeDestroyEvent : public IEventRecord -{ -public: - NodeDestroyEvent(ObjectId nodeId); - - virtual const char* EventId() const { return "NodeDestroy"; } - - virtual void Serialize(std::ostream& out) const; - -private: - ObjectId nodeId_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// NodeAttachEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -class NodeAttachEvent : public IEventRecord -{ -public: - NodeAttachEvent(ObjectId nodeId, ObjectId parentId); - - virtual const char* EventId() const { return "NodeAttach"; } - - virtual void Serialize(std::ostream& out) const; - -private: - ObjectId nodeId_; - ObjectId parentId_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// NodeDetachEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -class NodeDetachEvent : public IEventRecord -{ -public: - NodeDetachEvent(ObjectId nodeId, ObjectId parentId); - - virtual const char* EventId() const { return "NodeDetach"; } - - virtual void Serialize(std::ostream& out) const; - -private: - ObjectId nodeId_; - ObjectId parentId_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// InputNodeAdmissionEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -class InputNodeAdmissionEvent : public IEventRecord -{ -public: - InputNodeAdmissionEvent(ObjectId nodeId, int transactionId); - - virtual const char* EventId() const { return "InputNodeAdmission"; } - - virtual void Serialize(std::ostream& out) const; - -private: - ObjectId nodeId_; - int transactionId_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// NodePulseEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -class NodePulseEvent : public IEventRecord -{ -public: - NodePulseEvent(ObjectId nodeId, int transactionId); - - virtual const char* EventId() const { return "NodePulse"; } - - virtual void Serialize(std::ostream& out) const; - -private: - ObjectId nodeId_; - int transactionId_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// NodeIdlePulseEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -class NodeIdlePulseEvent : public IEventRecord -{ -public: - NodeIdlePulseEvent(ObjectId nodeId, int transactionId); - - virtual const char* EventId() const { return "NodeIdlePulse"; } - - virtual void Serialize(std::ostream& out) const; - -private: - ObjectId nodeId_; - int transactionId_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DynamicNodeAttachEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -class DynamicNodeAttachEvent : public IEventRecord -{ -public: - DynamicNodeAttachEvent(ObjectId nodeId, ObjectId parentId, int transactionId); - - virtual const char* EventId() const { return "DynamicNodeAttach"; } - - virtual void Serialize(std::ostream& out) const; - -private: - ObjectId nodeId_; - ObjectId parentId_; - int transactionId_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DynamicNodeDetachEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -class DynamicNodeDetachEvent : public IEventRecord -{ -public: - DynamicNodeDetachEvent(ObjectId nodeId, ObjectId parentId, int transactionId); - - virtual const char* EventId() const { return "DynamicNodeDetach"; } - - virtual void Serialize(std::ostream& out) const; - -private: - ObjectId nodeId_; - ObjectId parentId_; - int transactionId_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// NodeEvaluateBeginEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -class NodeEvaluateBeginEvent : public IEventRecord -{ -public: - NodeEvaluateBeginEvent(ObjectId nodeId, int transactionId); - - virtual const char* EventId() const { return "NodeEvaluateBegin"; } - - virtual void Serialize(std::ostream& out) const; - -private: - ObjectId nodeId_; - int transactionId_; - std::thread::id threadId_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// NodeEvaluateEndEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -class NodeEvaluateEndEvent : public IEventRecord -{ -public: - NodeEvaluateEndEvent(ObjectId nodeId, int transactionId); - - virtual const char* EventId() const { return "NodeEvaluateEnd"; } - - virtual void Serialize(std::ostream& out) const; - -private: - ObjectId nodeId_; - int transactionId_; - std::thread::id threadId_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// TransactionBeginEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -class TransactionBeginEvent : public IEventRecord -{ -public: - TransactionBeginEvent(int transactionId); - - virtual const char* EventId() const { return "TransactionBegin"; } - - virtual void Serialize(std::ostream& out) const; - -private: - int transactionId_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// TransactionEndEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -class TransactionEndEvent : public IEventRecord -{ -public: - TransactionEndEvent(int transactionId); - - virtual const char* EventId() const { return "TransactionEnd"; } - - virtual void Serialize(std::ostream& out) const; - -private: - int transactionId_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// UserBreakpointEvent -/////////////////////////////////////////////////////////////////////////////////////////////////// -class UserBreakpointEvent : public IEventRecord -{ -public: - UserBreakpointEvent(const char* name); - - virtual const char* EventId() const { return "UserBreakpoint"; } - - virtual void Serialize(std::ostream& out) const; - -private: - std::string name_; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_DETAIL_LOGGING_EVENTRECORDS_H_INCLUDED \ No newline at end of file diff --git a/include/react/logging/Logging.h b/include/react/logging/Logging.h deleted file mode 100644 index 3f0aacd2..00000000 --- a/include/react/logging/Logging.h +++ /dev/null @@ -1,31 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_LOGGING_LOGGING_H_INCLUDED -#define REACT_DETAIL_LOGGING_LOGGING_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IEventRecord -/////////////////////////////////////////////////////////////////////////////////////////////////// -struct IEventRecord -{ - virtual ~IEventRecord() {} - - virtual const char* EventId() const = 0; - virtual void Serialize(std::ostream& out) const = 0; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_DETAIL_LOGGING_LOGGING_H_INCLUDED \ No newline at end of file diff --git a/project/msvc/CppReact.vcxproj b/project/msvc/CppReact.vcxproj index ead65969..6b961f03 100644 --- a/project/msvc/CppReact.vcxproj +++ b/project/msvc/CppReact.vcxproj @@ -168,9 +168,6 @@ - - - diff --git a/project/msvc/CppReact.vcxproj.filters b/project/msvc/CppReact.vcxproj.filters index 76abe5da..c78bde77 100644 --- a/project/msvc/CppReact.vcxproj.filters +++ b/project/msvc/CppReact.vcxproj.filters @@ -19,9 +19,6 @@ {c7adc39d-d19e-4fe2-b945-332515717bc8} - - {6883dd62-b27e-4b11-9cc8-cbac096f4723} - {11a75126-8bfd-4693-be4b-4e06ab73450c} @@ -48,9 +45,6 @@ Header Files\common - - Header Files\engine - Header Files\detail @@ -72,15 +66,9 @@ Header Files\detail\graph - - Header Files\engine - Header Files\common - - Header Files\engine - Header Files\common From d0e5375b9e70cace84c4b2a9a97e8666a149cb69 Mon Sep 17 00:00:00 2001 From: schlangster Date: Tue, 24 Oct 2017 02:36:31 +0200 Subject: [PATCH 243/266] Progress dump. --- benchmarks/src/BenchmarkBase.h | 2 +- benchmarks/src/BenchmarkFanout.h | 2 +- benchmarks/src/BenchmarkGrid.h | 2 +- benchmarks/src/BenchmarkLifeSim.h | 2 +- benchmarks/src/BenchmarkRandom.h | 2 +- benchmarks/src/BenchmarkSequence.h | 2 +- benchmarks/src/Main.cpp | 52 +- examples/src/BasicAlgorithms.cpp | 2 +- examples/src/BasicComposition.cpp | 2 +- examples/src/BasicEvents.cpp | 2 +- examples/src/BasicObservers.cpp | 2 +- examples/src/BasicSignals.cpp | 2 +- examples/src/BasicSynchronization.cpp | 2 +- examples/src/Main.cpp | 7 +- include/react/API.h | 11 +- include/react/Algorithm.h | 87 +- include/react/Event.h | 172 +-- include/react/Group.h | 75 +- include/react/Observer.h | 79 +- include/react/Signal.h | 126 +-- include/react/common/Concurrency.h | 324 ------ include/react/common/RefCounting.h | 4 +- include/react/common/SourceIdSet.h | 4 +- include/react/common/Timing.h | 4 +- include/react/common/TopoQueue.h | 4 +- include/react/common/Types.h | 4 +- include/react/common/Util.h | 249 ----- include/react/common/expected.h | 218 ++++ include/react/common/optional.h | 70 ++ include/react/common/owned_ptr.h | 18 + include/react/common/ptrcache.h | 65 ++ .../react/common/{Containers.h => slotmap.h} | 40 +- include/react/common/syncpoint.h | 195 ++++ include/react/common/utility.h | 58 ++ include/react/detail/Defs.h | 9 +- include/react/detail/ReactiveInput.h | 983 ------------------ .../AlgorithmNodes.h => algorithm_nodes.h} | 162 +-- .../{graph/EventNodes.h => event_nodes.h} | 280 ++--- include/react/detail/graph/PropagationMT.h | 0 include/react/detail/graph/PropagationST.h | 500 --------- include/react/detail/graph_impl.h | 224 ++++ .../{IReactiveGraph.h => graph_interface.h} | 42 +- .../detail/{graph/GraphBase.h => node_base.h} | 40 +- .../ObserverNodes.h => observer_nodes.h} | 60 +- .../{graph/SignalNodes.h => signal_nodes.h} | 159 ++- project/msvc/CppReact.sln | 33 +- project/msvc/CppReact.vcxproj | 56 +- project/msvc/CppReact.vcxproj.filters | 91 +- project/msvc/CppReactBenchmark.vcxproj | 10 +- project/msvc/CppReactExample.vcxproj | 10 +- project/msvc/CppReactTest.vcxproj | 44 +- project/msvc/CppReactTest.vcxproj.filters | 45 +- project/msvc/Example_BasicAlgorithms.vcxproj | 10 +- project/msvc/Example_BasicComposition.vcxproj | 10 +- project/msvc/Example_BasicEvents.vcxproj | 10 +- project/msvc/Example_BasicObservers.vcxproj | 10 +- project/msvc/Example_BasicSignals.vcxproj | 10 +- .../msvc/Example_BasicSynchronization.vcxproj | 10 +- src/detail/graph_impl.cpp | 248 +++++ src/engine/PulsecountEngine.cpp | 6 +- src/engine/SubtreeEngine.cpp | 10 +- src/engine/ToposortEngine.cpp | 14 +- src/logging/EventLog.cpp | 84 -- src/logging/EventRecords.cpp | 210 ---- tests/src/EventStreamTest.cpp | 29 - tests/src/EventStreamTest.h | 2 +- tests/src/EventStreamTestQ.cpp | 2 +- tests/src/MoveTest.cpp | 11 +- tests/src/MoveTest.h | 2 +- tests/src/ObserverTest.cpp | 19 +- tests/src/ObserverTest.h | 2 +- tests/src/ObserverTestQ.cpp | 2 +- tests/src/OperationsTest.cpp | 19 +- tests/src/OperationsTest.h | 2 +- tests/src/OperationsTestQ.cpp | 2 +- tests/src/ParallelizationTest.cpp | 19 +- tests/src/ParallelizationTest.h | 2 +- tests/src/SignalTest.cpp | 20 +- tests/src/SignalTest.h | 2 +- tests/src/SignalTestQ.cpp | 2 +- tests/src/TestUtil.h | 2 +- tests/src/TransactionTest.cpp | 19 +- tests/src/TransactionTest.h | 2 +- tests/src/common_tests.cpp | 132 +++ tests/src/event_tests.cpp | 219 ++++ 85 files changed, 2205 insertions(+), 3576 deletions(-) delete mode 100644 include/react/common/Concurrency.h delete mode 100644 include/react/common/Util.h create mode 100644 include/react/common/expected.h create mode 100644 include/react/common/optional.h create mode 100644 include/react/common/owned_ptr.h create mode 100644 include/react/common/ptrcache.h rename include/react/common/{Containers.h => slotmap.h} (82%) create mode 100644 include/react/common/syncpoint.h create mode 100644 include/react/common/utility.h delete mode 100644 include/react/detail/ReactiveInput.h rename include/react/detail/{graph/AlgorithmNodes.h => algorithm_nodes.h} (68%) rename include/react/detail/{graph/EventNodes.h => event_nodes.h} (66%) delete mode 100644 include/react/detail/graph/PropagationMT.h delete mode 100644 include/react/detail/graph/PropagationST.h create mode 100644 include/react/detail/graph_impl.h rename include/react/detail/{IReactiveGraph.h => graph_interface.h} (63%) rename include/react/detail/{graph/GraphBase.h => node_base.h} (65%) rename include/react/detail/{graph/ObserverNodes.h => observer_nodes.h} (69%) rename include/react/detail/{graph/SignalNodes.h => signal_nodes.h} (74%) create mode 100644 src/detail/graph_impl.cpp delete mode 100644 src/logging/EventLog.cpp delete mode 100644 src/logging/EventRecords.cpp delete mode 100644 tests/src/EventStreamTest.cpp create mode 100644 tests/src/common_tests.cpp create mode 100644 tests/src/event_tests.cpp diff --git a/benchmarks/src/BenchmarkBase.h b/benchmarks/src/BenchmarkBase.h index 7df36bbe..d27d64b1 100644 --- a/benchmarks/src/BenchmarkBase.h +++ b/benchmarks/src/BenchmarkBase.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/benchmarks/src/BenchmarkFanout.h b/benchmarks/src/BenchmarkFanout.h index 672b5f7c..a4855d0f 100644 --- a/benchmarks/src/BenchmarkFanout.h +++ b/benchmarks/src/BenchmarkFanout.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/benchmarks/src/BenchmarkGrid.h b/benchmarks/src/BenchmarkGrid.h index d7ed0bb8..8f17d6f2 100644 --- a/benchmarks/src/BenchmarkGrid.h +++ b/benchmarks/src/BenchmarkGrid.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/benchmarks/src/BenchmarkLifeSim.h b/benchmarks/src/BenchmarkLifeSim.h index 6521f2d4..2511882d 100644 --- a/benchmarks/src/BenchmarkLifeSim.h +++ b/benchmarks/src/BenchmarkLifeSim.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/benchmarks/src/BenchmarkRandom.h b/benchmarks/src/BenchmarkRandom.h index e3ffe9ef..cec422da 100644 --- a/benchmarks/src/BenchmarkRandom.h +++ b/benchmarks/src/BenchmarkRandom.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/benchmarks/src/BenchmarkSequence.h b/benchmarks/src/BenchmarkSequence.h index bf87bc41..705e8f58 100644 --- a/benchmarks/src/BenchmarkSequence.h +++ b/benchmarks/src/BenchmarkSequence.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/benchmarks/src/Main.cpp b/benchmarks/src/Main.cpp index 2af96fdd..bc42b726 100644 --- a/benchmarks/src/Main.cpp +++ b/benchmarks/src/Main.cpp @@ -1,17 +1,17 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) //#define REACT_ENABLE_LOGGING - -#include "tbb/tick_count.h" -#include "tbb/tbbmalloc_proxy.h" +#if 0 +//#include "tbb/tick_count.h" +//#include "tbb/tbbmalloc_proxy.h" //#include "ittnotify.h" -#include "BenchmarkGrid.h" +/*#include "BenchmarkGrid.h" #include "BenchmarkRandom.h" #include "BenchmarkFanout.h" #include "BenchmarkSequence.h" @@ -20,12 +20,12 @@ #include "react/Group.h" #include "react/Signal.h" #include "react/Algorithm.h" -#include "react/common/Util.h" +#include "react/common/Util.h"*/ /////////////////////////////////////////////////////////////////////////////////////////////////// namespace { -using namespace react; +//using namespace react; /*void runBenchmarkGrid(std::ostream& out) { @@ -198,7 +198,7 @@ void debugBenchmarks() void profileBenchmark() { - RUN_BENCHMARK(std::cout, 3, Benchmark_Grid, BenchmarkParams_Grid(100, 10000)); + //RUN_BENCHMARK(std::cout, 3, Benchmark_Grid, BenchmarkParams_Grid(100, 10000)); //RUN_BENCHMARK(std::cout, 1, Benchmark_Grid, BenchmarkParams_Grid(30, 10000), //SourceSetDomain); @@ -217,5 +217,39 @@ int main() { //runBenchmarks(); //debugBenchmarks(); - profileBenchmark(); + //profileBenchmark(); + + +} + +#endif + +#include "react/common/expected.h" +#include "react/signal.h" +#include "react/event.h" +#include "react/algorithm.h" +#include + +#define unwind_on_error(x) if (!x) return UnwindExpected(std::move(x)); + +using namespace react; + + +int main() +{ + Group g; + + VarSignal t1(g); + VarSignal t2(g); + + EventSource e1(g); + EventSource e2(g); + + auto h1 = Hold(1, e1); + auto h2 = Hold(2, e2); + + auto m1 = Monitor(t1); + auto it1 = Iterate(10, [] (EventRange evnts, int b) { return b; }, e1); + + return 0; } \ No newline at end of file diff --git a/examples/src/BasicAlgorithms.cpp b/examples/src/BasicAlgorithms.cpp index c2c5d123..93235700 100644 --- a/examples/src/BasicAlgorithms.cpp +++ b/examples/src/BasicAlgorithms.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/examples/src/BasicComposition.cpp b/examples/src/BasicComposition.cpp index d5354bae..e2fbf3b1 100644 --- a/examples/src/BasicComposition.cpp +++ b/examples/src/BasicComposition.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/examples/src/BasicEvents.cpp b/examples/src/BasicEvents.cpp index da3fa4f2..53e97984 100644 --- a/examples/src/BasicEvents.cpp +++ b/examples/src/BasicEvents.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/examples/src/BasicObservers.cpp b/examples/src/BasicObservers.cpp index fede6fd0..8eb03b81 100644 --- a/examples/src/BasicObservers.cpp +++ b/examples/src/BasicObservers.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/examples/src/BasicSignals.cpp b/examples/src/BasicSignals.cpp index a8b6a60a..749116c8 100644 --- a/examples/src/BasicSignals.cpp +++ b/examples/src/BasicSignals.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/examples/src/BasicSynchronization.cpp b/examples/src/BasicSynchronization.cpp index 8eacfa6e..701b5001 100644 --- a/examples/src/BasicSynchronization.cpp +++ b/examples/src/BasicSynchronization.cpp @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) diff --git a/examples/src/Main.cpp b/examples/src/Main.cpp index 5720dcf1..3d3bb8e4 100644 --- a/examples/src/Main.cpp +++ b/examples/src/Main.cpp @@ -15,6 +15,8 @@ #include "react/Algorithm.h" #include "react/Observer.h" +#include "react/common/expected.h" + using namespace react; template @@ -38,9 +40,6 @@ class GridGraphGenerator void Generate(const Group& group) { - assert(inputSignals.size() >= 1); - assert(widths.size() >= 1); - SignalVectType buf1 = std::move(inputSignals); SignalVectType buf2; @@ -366,6 +365,8 @@ int main() std::this_thread::sleep_for(std::chrono::seconds(5)); + Expected x; + return 0; } diff --git a/include/react/API.h b/include/react/API.h index da892da8..3e20d5bb 100644 --- a/include/react/API.h +++ b/include/react/API.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) @@ -9,8 +9,8 @@ #pragma once -#include "react/detail/Defs.h" -#include "react/common/Util.h" +#include "react/detail/defs.h" +#include "react/common/utility.h" /*****************************************/ REACT_BEGIN /*****************************************/ @@ -26,8 +26,9 @@ enum class WeightHint enum class TransactionFlags { - none = 0, - allow_merging = 1 << 1 + none = 0, + allow_merging = 1 << 1, + sync_linked = 1 << 2 }; REACT_DEFINE_BITMASK_OPERATORS(TransactionFlags) diff --git a/include/react/Algorithm.h b/include/react/Algorithm.h index babc4c8b..e149b491 100644 --- a/include/react/Algorithm.h +++ b/include/react/Algorithm.h @@ -9,28 +9,32 @@ #pragma once -#include "react/detail/Defs.h" +#include "react/detail/defs.h" #include #include #include #include "react/API.h" -#include "react/detail/graph/AlgorithmNodes.h" +#include "react/detail/algorithm_nodes.h" -#include "Event.h" -#include "Signal.h" +#include "react/event.h" +#include "react/signal.h" /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Hold - Hold the most recent event in a signal +/// Hold the most recent event in a signal /////////////////////////////////////////////////////////////////////////////////////////////////// template auto Hold(const Group& group, T&& initialValue, const Event& evnt) -> Signal { using REACT_IMPL::HoldNode; - return Signal::CreateWithNode>(group, std::forward(initialValue), SameGroupOrLink(group, evnt)); + using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; + + return CreateWrappedNode, HoldNode>( + group, std::forward(initialValue), SameGroupOrLink(group, evnt)); } template @@ -38,13 +42,17 @@ auto Hold(T&& initialValue, const Event& evnt) -> Signal { return Hold(evnt.GetGroup(), std::forward(initialValue), evnt); } /////////////////////////////////////////////////////////////////////////////////////////////////// -/// Monitor - Emits value changes of target signal +/// Emits value changes of target signal. /////////////////////////////////////////////////////////////////////////////////////////////////// template auto Monitor(const Group& group, const Signal& signal) -> Event { using REACT_IMPL::MonitorNode; - return Event::CreateWithNode>(group, SameGroupOrLink(group, signal)); + using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; + + return CreateWrappedNode, MonitorNode>( + group, SameGroupOrLink(group, signal)); } template @@ -58,22 +66,38 @@ template auto Iterate(const Group& group, T&& initialValue, F&& func, const Event& evnt) -> Signal { using REACT_IMPL::IterateNode; + using REACT_IMPL::IsCallableWith; + using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; + + using FuncType = typename std::decay::type; + + return CreateWrappedNode, IterateNode>( + group, std::forward(initialValue), std::forward(func), SameGroupOrLink(group, evnt)); +} + +template +auto IterateByRef(const Group& group, T&& initialValue, F&& func, const Event& evnt) -> Signal +{ using REACT_IMPL::IterateByRefNode; using REACT_IMPL::IsCallableWith; + using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; using FuncType = typename std::decay::type; - using IterNodeType = typename std::conditional< - IsCallableWith,S>::value, - IterateNode, - IterateByRefNode>::type; - return Signal::CreateWithNode(group, std::forward(initialValue), std::forward(func), SameGroupOrLink(group, evnt)); + return CreateWrappedNode, IterateByRefNode>( + group, std::forward(initialValue), std::forward(func), SameGroupOrLink(group, evnt)); } template auto Iterate(T&& initialValue, F&& func, const Event& evnt) -> Signal { return Iterate(evnt.GetGroup(), std::forward(initialValue), std::forward(func), evnt); } +template +auto IterateByRef(T&& initialValue, F&& func, const Event& evnt) -> Signal + { return IterateByRef(evnt.GetGroup(), std::forward(initialValue), std::forward(func), evnt); } + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Iterate - Synced /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -81,16 +105,27 @@ template auto Iterate(const Group& group, T&& initialValue, F&& func, const Event& evnt, const Signal& ... signals) -> Signal { using REACT_IMPL::SyncedIterateNode; + using REACT_IMPL::IsCallableWith; + using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; + + using FuncType = typename std::decay::type; + + return CreateWrappedNode, SyncedIterateNode>( + group, std::forward(initialValue), std::forward(func), SameGroupOrLink(group, evnt), SameGroupOrLink(group, signals) ...); +} + +template +auto IterateByRef(const Group& group, T&& initialValue, F&& func, const Event& evnt, const Signal& ... signals) -> Signal +{ using REACT_IMPL::SyncedIterateByRefNode; using REACT_IMPL::IsCallableWith; + using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; using FuncType = typename std::decay::type; - using IterNodeType = typename std::conditional< - IsCallableWith, S, Us ...>::value, - SyncedIterateNode, - SyncedIterateByRefNode>::type; - return Signal::CreateWithNode( + return CreateWrappedNode, SyncedIterateByRefNode>( group, std::forward(initialValue), std::forward(func), SameGroupOrLink(group, evnt), SameGroupOrLink(group, signals) ...); } @@ -98,6 +133,10 @@ template auto Iterate(T&& initialValue, F&& func, const Event& evnt, const Signal& ... signals) -> Signal { return Iterate(evnt.GetGroup(), std::forward(initialValue), std::forward(func), evnt, signals ...); } +template +auto IterateByRef(T&& initialValue, F&& func, const Event& evnt, const Signal& ... signals) -> Signal + { return IterateByRef(evnt.GetGroup(), std::forward(initialValue), std::forward(func), evnt, signals ...); } + /////////////////////////////////////////////////////////////////////////////////////////////////// /// Snapshot - Sets signal value to value of other signal when event is received /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -105,7 +144,11 @@ template auto Snapshot(const Group& group, const Signal& signal, const Event& evnt) -> Signal { using REACT_IMPL::SnapshotNode; - return Signal::CreateWithNode>(group, SameGroupOrLink(group, signal), SameGroupOrLink(group, evnt)); + using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; + + return CreateWrappedNode, SnapshotNode>( + group, SameGroupOrLink(group, signal), SameGroupOrLink(group, evnt)); } template @@ -119,7 +162,11 @@ template auto Pulse(const Group& group, const Signal& signal, const Event& evnt) -> Event { using REACT_IMPL::PulseNode; - return Event::CreateWithNode>(group, SameGroupOrLink(group, signal), SameGroupOrLink(group, evnt)); + using REACT_IMPL::SameGroupOrLink; + using REACT_IMPL::CreateWrappedNode; + + return CreateWrappedNode, PulseNode>( + group, SameGroupOrLink(group, signal), SameGroupOrLink(group, evnt)); } template diff --git a/include/react/Event.h b/include/react/Event.h index 5d97dfff..ff4a0a17 100644 --- a/include/react/Event.h +++ b/include/react/Event.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) @@ -9,62 +9,18 @@ #pragma once -#include "react/detail/Defs.h" -#include "react/API.h" -#include "react/Group.h" +#include "react/detail/defs.h" +#include "react/api.h" +#include "react/group.h" +#include "react/common/ptrcache.h" #include #include #include -#include "react/detail/graph/EventNodes.h" +#include "react/detail/event_nodes.h" -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -template -class EventInternals -{ -protected: - using NodeType = EventStreamNode; - using StorageType = typename NodeType::StorageType; - -public: - EventInternals(const EventInternals&) = default; - EventInternals& operator=(const EventInternals&) = default; - - EventInternals(EventInternals&&) = default; - EventInternals& operator=(EventInternals&&) = default; - - EventInternals(std::shared_ptr&& nodePtr) : - nodePtr_( std::move(nodePtr) ) - { } - - auto GetNodePtr() -> std::shared_ptr& - { return nodePtr_; } - - auto GetNodePtr() const -> const std::shared_ptr& - { return nodePtr_; } - - NodeId GetNodeId() const - { return nodePtr_->GetNodeId(); } - StorageType& Events() - { return nodePtr_->Events(); } - - const StorageType& Events() const - { return nodePtr_->Events(); } - - void SetPendingSuccessorCount(size_t count) - { nodePtr_->SetPendingSuccessorCount(count); } - - void DecrementPendingSuccessorCount() - { nodePtr_->DecrementPendingSuccessorCount(); } - -private: - std::shared_ptr nodePtr_; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ /*****************************************/ REACT_BEGIN /*****************************************/ @@ -81,41 +37,33 @@ class Event : protected REACT_IMPL::EventInternals Event(Event&&) = default; Event& operator=(Event&&) = default; + // Construct with explicit group template - Event(F&& func, const Event& dep) : - Event::Event( REACT_IMPL::CtorTag{ }, CreateProcessingNode(dep.GetGroup(), std::forward(func), dep) ) - { } + Event(const Group& group, F&& func, const Event& dep) : + Event::Event( CreateProcessingNode(group, std::forward(func), dep) ) + { } + // Construct with implicit group template - Event(const Group& group, F&& func, const Event& dep) : - Event::Event( REACT_IMPL::CtorTag{ }, CreateProcessingNode(group, std::forward(func), dep) ) - { } + Event(F&& func, const Event& dep) : + Event::Event( CreateProcessingNode(dep.GetGroup(), std::forward(func), dep) ) + { } + // Construct with explicit group template - Event(F&& func, const Event& dep, const Signal& ... signals) : - Event::Event( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(dep.GetGroup(), std::forward(func), dep, signals ...) ) - { } + Event(const Group& group, F&& func, const Event& dep, const Signal& ... signals) : + Event::Event( CreateSyncedProcessingNode(group, std::forward(func), dep, signals ...) ) + { } + // Construct with implicit group template - Event(const Group& group, F&& func, const Event& dep, const Signal& ... signals) : - Event::Event( REACT_IMPL::CtorTag{ }, CreateSyncedProcessingNode(group, std::forward(func), dep, signals ...) ) - { } + Event(F&& func, const Event& dep, const Signal& ... signals) : + Event::Event( CreateSyncedProcessingNode(dep.GetGroup(), std::forward(func), dep, signals ...) ) + { } auto Tokenize() const -> decltype(auto) { return REACT::Tokenize(*this); } - /*template - auto Merge(Us&& ... deps) const -> decltype(auto) - { return REACT::Merge(*this, std::forward(deps) ...); } - - template - auto Filter(F&& pred) const -> decltype(auto) - { return REACT::Filter(std::forward(pred), *this); } - - template - auto Transform(F&& pred) const -> decltype(auto) - { return REACT::Transform(*this, std::forward(f)); }*/ - auto GetGroup() const -> const Group& { return GetNodePtr()->GetGroup(); } @@ -134,18 +82,11 @@ class Event : protected REACT_IMPL::EventInternals friend auto GetInternals(const Event& e) -> const REACT_IMPL::EventInternals& { return e; } -public: // Internal - template - static Event CreateWithNode(TArgs&& ... args) - { - return Event( REACT_IMPL::CtorTag{ }, std::make_shared(std::forward(args) ...) ); - } - protected: // Private node ctor - Event(REACT_IMPL::CtorTag, std::shared_ptr>&& nodePtr) : + explicit Event(std::shared_ptr>&& nodePtr) : Event::EventInternals( std::move(nodePtr) ) - { } + { } template auto CreateProcessingNode(const Group& group, F&& func, const Event& dep) -> decltype(auto) @@ -166,6 +107,9 @@ class Event : protected REACT_IMPL::EventInternals return std::make_shared::type, Us ...>>( group, std::forward(func), SameGroupOrLink(group, dep), SameGroupOrLink(group, syncs) ...); } + + template + friend static RET impl::CreateWrappedNode(ARGS&& ... args); }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -183,8 +127,8 @@ class EventSource : public Event // Construct event source explicit EventSource(const Group& group) : - EventSource::Event( REACT_IMPL::CtorTag{ }, CreateSourceNode(group) ) - { } + EventSource::Event( CreateSourceNode(group) ) + { } void Emit(const E& value) { EmitValue(value); } @@ -192,7 +136,7 @@ class EventSource : public Event void Emit(E&& value) { EmitValue(std::move(value)); } - template ::value>::type> + template >::type> void Emit() { EmitValue(Token::value); } @@ -214,10 +158,10 @@ class EventSource : public Event void EmitValue(T&& value) { using REACT_IMPL::NodeId; - using REACT_IMPL::ReactiveGraph; - using SrcNodeType = REACT_IMPL::EventSourceNode; + using REACT_IMPL::ReactGraph; + using REACT_IMPL::EventSourceNode; - SrcNodeType* castedPtr = static_cast(this->GetNodePtr().get()); + auto* castedPtr = static_cast*>(this->GetNodePtr().get()); NodeId nodeId = castedPtr->GetNodeId(); auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); @@ -241,8 +185,8 @@ class EventSlot : public Event // Construct emtpy slot EventSlot(const Group& group) : - EventSlot::Event( REACT_IMPL::CtorTag{ }, CreateSlotNode(group) ) - { } + EventSlot::Event( CreateSlotNode(group) ) + { } void Add(const Event& input) { AddInput(input); } @@ -316,50 +260,41 @@ class EventLink : public Event // Construct with group EventLink(const Group& group, const Event& input) : - EventLink::Event( REACT_IMPL::CtorTag{ }, GetOrCreateLinkNode(group, input) ) - { } + EventLink::Event( GetOrCreateLinkNode(group, input) ) + { } protected: static auto GetOrCreateLinkNode(const Group& group, const Event& input) -> decltype(auto) { using REACT_IMPL::EventLinkNode; - - auto& targetGraphPtr = GetInternals(group).GetGraphPtr(); - auto& linkCache = targetGraphPtr->GetLinkCache(); + using REACT_IMPL::IReactNode; + using REACT_IMPL::ReactGraph; - void* k1 = GetInternals(input.GetGroup()).GetGraphPtr().get(); - void* k2 = GetInternals(input).GetNodePtr().get(); + IReactNode* k = GetInternals(input).GetNodePtr().get(); + + ReactGraph::LinkCache& linkCache = GetInternals(group).GetGraphPtr()->GetLinkCache(); - auto nodePtr = linkCache.LookupOrCreate>( - { k1, k2 }, + std::shared_ptr nodePtr = linkCache.LookupOrCreate( + k, [&] { auto nodePtr = std::make_shared>(group, input); nodePtr->SetWeakSelfPtr(std::weak_ptr>{ nodePtr }); - return nodePtr; + return std::static_pointer_cast(nodePtr); }); - return nodePtr; + return std::static_pointer_cast>(nodePtr); } }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Merge /////////////////////////////////////////////////////////////////////////////////////////////////// -template -auto Merge(const Group& group, const Event& dep1, const Event& ... deps) -> decltype(auto) +template +auto Merge(const Group& group, const Event& dep1, const Event& ... deps) -> decltype(auto) { using REACT_IMPL::EventMergeNode; using REACT_IMPL::SameGroupOrLink; - using REACT_IMPL::CtorTag; - - static_assert(sizeof...(Us) > 0, "Merge requires at least 2 inputs."); - - // If supplied, use merge type, otherwise default to common type. - using E = typename std::conditional< - std::is_same::value, - typename std::common_type::type, - T>::type; return Event::CreateWithNode>(group, SameGroupOrLink(group, dep1), SameGroupOrLink(group, deps) ...); } @@ -433,17 +368,6 @@ template auto Transform(F&& op, const Event& dep, const Signal& ... signals) -> Event { return Transform(dep.GetGroup(), std::forward(op), dep, signals ...); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Flatten -/////////////////////////////////////////////////////////////////////////////////////////////////// -/*template -auto Flatten(const Signal>& outer) -> Events -{ - return Events( - std::make_shared, TInnerValue>>( - GetNodePtr(outer), GetNodePtr(outer.Value()))); -}*/ - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Join /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/react/Group.h b/include/react/Group.h index dd62b012..45ec476a 100644 --- a/include/react/Group.h +++ b/include/react/Group.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) @@ -9,15 +9,16 @@ #pragma once -#include "react/detail/Defs.h" +#include "react/detail/defs.h" #include #include #include "react/API.h" +#include "react/common/syncpoint.h" -#include "react/detail/IReactiveGraph.h" -#include "react/detail/graph/PropagationST.h" +#include "react/detail/graph_interface.h" +#include "react/detail/graph_impl.h" /***************************************/ REACT_IMPL_BEGIN /**************************************/ @@ -26,7 +27,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// class GroupInternals { - using GraphType = REACT_IMPL::ReactiveGraph; + using GraphType = REACT_IMPL::ReactGraph; public: GroupInternals() : @@ -53,62 +54,6 @@ class GroupInternals /*****************************************/ REACT_BEGIN /*****************************************/ -#if 0 - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// TransactionStatus -/////////////////////////////////////////////////////////////////////////////////////////////////// -class TransactionStatus -{ - using StateT = REACT_IMPL::SharedWaitingState; - using PtrT = REACT_IMPL::WaitingStatePtrT; - -public: - // Default ctor - TransactionStatus() : - statePtr_( StateT::Create() ) - { } - - // Move ctor - TransactionStatus(TransactionStatus&& other) : - statePtr_( std::move(other.statePtr_) ) - { - other.statePtr_ = StateT::Create(); - } - - // Move assignment - TransactionStatus& operator=(TransactionStatus&& other) - { - if (this != &other) - { - statePtr_ = std::move(other.statePtr_); - other.statePtr_ = StateT::Create(); - } - return *this; - } - - // Deleted copy ctor & assignment - TransactionStatus(const TransactionStatus&) = delete; - TransactionStatus& operator=(const TransactionStatus&) = delete; - - void Wait() - { - assert(statePtr_.Get() != nullptr); - statePtr_->Wait(); - } - -private: - PtrT statePtr_; - - template - friend void AsyncTransaction(TransactionStatus& status, F&& func); - - template - friend void AsyncTransaction(TransactionFlagsT flags, TransactionStatus& status, F&& func); -}; - -#endif - /////////////////////////////////////////////////////////////////////////////////////////////////// /// Group /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -128,12 +73,12 @@ class Group : protected REACT_IMPL::GroupInternals { GetGraphPtr()->DoTransaction(std::forward(func)); } template - void EnqueueTransaction(F&& func) - { EnqueueTransaction(TransactionFlags::none, std::forward(func)); } + void EnqueueTransaction(F&& func, TransactionFlags flags = TransactionFlags::none) + { GetGraphPtr()->EnqueueTransaction(std::forward(func), SyncPoint::Dependency{ }, flags); } template - void EnqueueTransaction(TransactionFlags flags, F&& func) - { GetGraphPtr()->EnqueueTransaction(flags, std::forward(func)); } + void EnqueueTransaction(F&& func, const SyncPoint& syncPoint, TransactionFlags flags = TransactionFlags::none) + { GetGraphPtr()->EnqueueTransaction(std::forward(func), SyncPoint::Dependency{ syncPoint }, flags); } friend bool operator==(const Group& a, const Group& b) { return a.GetGraphPtr() == b.GetGraphPtr(); } diff --git a/include/react/Observer.h b/include/react/Observer.h index a2ff497a..1f28136d 100644 --- a/include/react/Observer.h +++ b/include/react/Observer.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) @@ -9,21 +9,54 @@ #pragma once -#include "react/detail/Defs.h" -#include "react/API.h" -#include "react/Group.h" +#include "react/detail/defs.h" +#include "react/api.h" +#include "react/group.h" #include #include -#include "react/detail/graph/ObserverNodes.h" +#include "react/detail/observer_nodes.h" + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +class ObserverInternals +{ +public: + ObserverInternals(const ObserverInternals&) = default; + ObserverInternals& operator=(const ObserverInternals&) = default; + + ObserverInternals(ObserverInternals&&) = default; + ObserverInternals& operator=(ObserverInternals&&) = default; + + explicit ObserverInternals(std::shared_ptr&& nodePtr) : + nodePtr_( std::move(nodePtr) ) + { } + + auto GetNodePtr() -> std::shared_ptr& + { return nodePtr_; } + + auto GetNodePtr() const -> const std::shared_ptr& + { return nodePtr_; } + + NodeId GetNodeId() const + { return nodePtr_->GetNodeId(); } + +protected: + ObserverInternals() = default; + +private: + std::shared_ptr nodePtr_; +}; + +/****************************************/ REACT_IMPL_END /***************************************/ /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// /// ObserverBase /////////////////////////////////////////////////////////////////////////////////////////////////// -class Observer +class Observer : protected REACT_IMPL::ObserverInternals { private: using NodeType = REACT_IMPL::ObserverNode; @@ -38,50 +71,44 @@ class Observer // Construct signal observer with explicit group template Observer(const Group& group, F&& func, const Signal& subject1, const Signal& ... subjects) : - Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSignalObserverNode(group, std::forward(func), subject1, subjects ...)) - { } + Observer::Observer( CreateSignalObserverNode(group, std::forward(func), subject1, subjects ...)) + { } // Construct signal observer with implicit group template Observer(F&& func, const Signal& subject1, const Signal& ... subjects) : - Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSignalObserverNode(subject1.GetGroup(), std::forward(func), subject1, subjects ...)) - { } + Observer::Observer( CreateSignalObserverNode(subject1.GetGroup(), std::forward(func), subject1, subjects ...)) + { } // Construct event observer with explicit group template Observer(const Group& group, F&& func, const Event& subject) : - Observer::Observer(REACT_IMPL::CtorTag{ }, CreateEventObserverNode(group, std::forward(func), subject)) - { } + Observer::Observer( CreateEventObserverNode(group, std::forward(func), subject)) + { } // Construct event observer with implicit group template Observer(F&& func, const Event& subject) : - Observer::Observer(REACT_IMPL::CtorTag{ }, CreateEventObserverNode(subject.GetGroup(), std::forward(func), subject)) - { } + Observer::Observer( CreateEventObserverNode(subject.GetGroup(), std::forward(func), subject)) + { } // Constructed synced event observer with explicit group template Observer(const Group& group, F&& func, const Event& subject, const Signal& ... signals) : - Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSyncedEventObserverNode(group, std::forward(func), subject, signals ...)) - { } + Observer::Observer( CreateSyncedEventObserverNode(group, std::forward(func), subject, signals ...)) + { } // Constructed synced event observer with implicit group template Observer(F&& func, const Event& subject, const Signal& ... signals) : - Observer::Observer(REACT_IMPL::CtorTag{ }, CreateSyncedEventObserverNode(subject.GetGroup(), std::forward(func), subject, signals ...)) - { } + Observer::Observer( CreateSyncedEventObserverNode(subject.GetGroup(), std::forward(func), subject, signals ...)) + { } public: //Internal // Private node ctor - Observer(REACT_IMPL::CtorTag, std::shared_ptr&& nodePtr) : + explicit Observer(std::shared_ptr&& nodePtr) : nodePtr_(std::move(nodePtr)) - { } - - auto NodePtr() -> std::shared_ptr& - { return nodePtr_; } - - auto NodePtr() const -> const std::shared_ptr& - { return nodePtr_; } + { } protected: template diff --git a/include/react/Signal.h b/include/react/Signal.h index a429f34b..c7e11e8f 100644 --- a/include/react/Signal.h +++ b/include/react/Signal.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) @@ -9,53 +9,18 @@ #pragma once -#include "react/detail/Defs.h" -#include "react/API.h" -#include "react/Group.h" +#include "react/detail/defs.h" +#include "react/api.h" +#include "react/group.h" +#include "react/common/ptrcache.h" #include #include #include #include -#include "react/detail/graph/SignalNodes.h" +#include "react/detail/signal_nodes.h" -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -template -class SignalInternals -{ -public: - SignalInternals(const SignalInternals&) = default; - SignalInternals& operator=(const SignalInternals&) = default; - - SignalInternals(SignalInternals&&) = default; - SignalInternals& operator=(SignalInternals&&) = default; - - SignalInternals(std::shared_ptr>&& nodePtr) : - nodePtr_( std::move(nodePtr) ) - { } - - auto GetNodePtr() -> std::shared_ptr>& - { return nodePtr_; } - - auto GetNodePtr() const -> const std::shared_ptr>& - { return nodePtr_; } - - NodeId GetNodeId() const - { return nodePtr_->GetNodeId(); } - - S& Value() - { return nodePtr_->Value(); } - - const S& Value() const - { return nodePtr_->Value(); } - -private: - std::shared_ptr> nodePtr_; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ /*****************************************/ REACT_BEGIN /*****************************************/ @@ -76,13 +41,13 @@ class Signal : protected REACT_IMPL::SignalInternals template explicit Signal(const Group& group, F&& func, const Signal& dep1, const Signal& ... deps) : Signal::SignalInternals( CreateFuncNode(group, std::forward(func), dep1, deps ...) ) - { } + { } // Construct with implicit group template explicit Signal(F&& func, const Signal& dep1, const Signal& ... deps) : Signal::SignalInternals( CreateFuncNode(dep1.GetGroup(), std::forward(func), dep1, deps ...) ) - { } + { } auto GetGroup() const -> const Group& { return this->GetNodePtr()->GetGroup(); } @@ -102,23 +67,24 @@ class Signal : protected REACT_IMPL::SignalInternals friend auto GetInternals(const Signal& s) -> const REACT_IMPL::SignalInternals& { return s; } - template - static Signal CreateWithNode(TArgs&& ... args) - { return Signal( REACT_IMPL::CtorTag{ }, std::make_shared(std::forward(args) ...) ); } - protected: - Signal(REACT_IMPL::CtorTag, std::shared_ptr>&& nodePtr) : + explicit Signal(std::shared_ptr>&& nodePtr) : Signal::SignalInternals( std::move(nodePtr) ) - { } + { } +private: template auto CreateFuncNode(const Group& group, F&& func, const Signal& dep1, const Signal& ... deps) -> decltype(auto) { using REACT_IMPL::SignalFuncNode; + using REACT_IMPL::SameGroupOrLink; return std::make_shared::type, T1, Ts ...>>( group, std::forward(func), SameGroupOrLink(group, dep1), SameGroupOrLink(group, deps) ...); } + + template + friend RET impl::CreateWrappedNode(ARGS&& ... args); }; /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -136,14 +102,14 @@ class VarSignal : public Signal // Construct with group + default explicit VarSignal(const Group& group) : - VarSignal::Signal( REACT_IMPL::CtorTag{ }, CreateVarNode(group) ) - { } + VarSignal::Signal( CreateVarNode(group) ) + { } // Construct with group + value template VarSignal(const Group& group, T&& value) : - VarSignal::Signal( REACT_IMPL::CtorTag{ }, CreateVarNode(group, std::forward(value)) ) - { } + VarSignal::Signal( CreateVarNode(group, std::forward(value)) ) + { } void Set(const S& newValue) { SetValue(newValue); } @@ -151,12 +117,6 @@ class VarSignal : public Signal void Set(S&& newValue) { SetValue(std::move(newValue)); } - void operator<<=(const S& newValue) - { SetValue(newValue); } - - void operator<<=(S&& newValue) - { SetValue(std::move(newValue)); } - template void Modify(const F& func) { ModifyValue(func); } @@ -168,6 +128,11 @@ class VarSignal : public Signal { return !(a == b); } protected: + explicit VarSignal(std::shared_ptr>&& nodePtr) : + VarSignal::Signal( std::move(nodePtr) ) + { } + +private: static auto CreateVarNode(const Group& group) -> decltype(auto) { using REACT_IMPL::VarSignalNode; @@ -181,7 +146,6 @@ class VarSignal : public Signal return std::make_shared>(group, std::forward(value)); } -private: template void SetValue(T&& newValue) { @@ -226,13 +190,13 @@ class SignalSlot : public Signal // Construct with explicit group SignalSlot(const Group& group, const Signal& input) : - SignalSlot::Signal( REACT_IMPL::CtorTag{ }, CreateSlotNode(group, input) ) - { } + SignalSlot::Signal( CreateSlotNode(group, input) ) + { } // Construct with implicit group explicit SignalSlot(const Signal& input) : - SignalSlot::Signal( REACT_IMPL::CtorTag{ }, CreateSlotNode(input.GetGroup(), input) ) - { } + SignalSlot::Signal( CreateSlotNode(input.GetGroup(), input) ) + { } void Set(const Signal& newInput) { SetInput(newInput); } @@ -241,19 +205,26 @@ class SignalSlot : public Signal { SetInput(newInput); } protected: + explicit SignalSlot(std::shared_ptr>&& nodePtr) : + SignalSlot::Signal( std::move(nodePtr) ) + { } + +private: static auto CreateSlotNode(const Group& group, const Signal& input) -> decltype(auto) { using REACT_IMPL::SignalSlotNode; + using REACT_IMPL::SameGroupOrLink; + return std::make_shared>(group, SameGroupOrLink(group, input)); } -private: void SetInput(const Signal& newInput) { using REACT_IMPL::NodeId; - using SlotNodeType = REACT_IMPL::SignalSlotNode; + using REACT_IMPL::SignalSlotNode; + using REACT_IMPL::SameGroupOrLink; - SlotNodeType* castedPtr = static_cast(this->GetNodePtr().get()); + auto* castedPtr = static_cast*>(this->GetNodePtr().get()); NodeId nodeId = castedPtr->GetInputNodeId(); auto& graphPtr = GetInternals(this->GetGroup()).GetGraphPtr(); @@ -277,31 +248,30 @@ class SignalLink : public Signal // Construct with group SignalLink(const Group& group, const Signal& input) : - SignalLink::Signal( REACT_IMPL::CtorTag{ }, GetOrCreateLinkNode(group, input) ) - { } + SignalLink::Signal( GetOrCreateLinkNode(group, input) ) + { } protected: static auto GetOrCreateLinkNode(const Group& group, const Signal& input) -> decltype(auto) { using REACT_IMPL::SignalLinkNode; - - auto targetGraphPtr = GetInternals(group).GetGraphPtr(); + using REACT_IMPL::IReactNode; + using REACT_IMPL::ReactGraph; - void* k1 = GetInternals(input.GetGroup()).GetGraphPtr().get(); - void* k2 = GetInternals(input).GetNodePtr().get(); + IReactNode* k = GetInternals(input).GetNodePtr().get(); - auto& linkCache = targetGraphPtr->GetLinkCache(); + ReactGraph::LinkCache& linkCache = GetInternals(group).GetGraphPtr()->GetLinkCache(); - auto nodePtr = linkCache.LookupOrCreate>( - { k1, k2 }, + std::shared_ptr nodePtr = linkCache.LookupOrCreate( + k, [&] { auto nodePtr = std::make_shared>(group, input); nodePtr->SetWeakSelfPtr(std::weak_ptr>{ nodePtr }); - return nodePtr; + return std::static_pointer_cast(nodePtr); }); - return nodePtr; + return std::static_pointer_cast>(nodePtr); } }; diff --git a/include/react/common/Concurrency.h b/include/react/common/Concurrency.h deleted file mode 100644 index 0a5c350c..00000000 --- a/include/react/common/Concurrency.h +++ /dev/null @@ -1,324 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_COMMON_CONCURRENCY_H_INCLUDED -#define REACT_COMMON_CONCURRENCY_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include - -#include "react/common/RefCounting.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IWaitingState -/////////////////////////////////////////////////////////////////////////////////////////////////// -class IWaitingState -{ -public: - virtual inline ~IWaitingState() {} - - virtual void Wait() = 0; - - virtual void IncWaitCount() = 0; - virtual void DecWaitCount() = 0; - -protected: - virtual bool IsRefCounted() const = 0; - virtual void IncRefCount() = 0; - virtual void DecRefCount() = 0; - - friend class IntrusiveRefCountingPtr; -}; - -using WaitingStatePtrT = IntrusiveRefCountingPtr; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// WaitingStateBase -/////////////////////////////////////////////////////////////////////////////////////////////////// -class WaitingStateBase : public IWaitingState -{ -public: - virtual inline void Wait() override - { - std::unique_lock lock(mutex_); - condition_.wait(lock, [this] { return !isWaiting_; }); - } - - virtual inline void IncWaitCount() override - { - auto oldVal = waitCount_.fetch_add(1, std::memory_order_relaxed); - - if (oldVal == 0) - {// mutex_ - std::lock_guard scopedLock(mutex_); - isWaiting_ = true; - }// ~mutex_ - } - - virtual inline void DecWaitCount() override - { - auto oldVal = waitCount_.fetch_sub(1, std::memory_order_relaxed); - - if (oldVal == 1) - {// mutex_ - std::lock_guard scopedLock(mutex_); - isWaiting_ = false; - condition_.notify_all(); - }// ~mutex_ - } - - inline bool IsWaiting() - {// mutex_ - std::lock_guard scopedLock(mutex_); - return isWaiting_; - }// ~mutex_ - - template - auto Run(F&& func) -> decltype(func(false)) - {// mutex_ - std::lock_guard scopedLock(mutex_); - return func(isWaiting_); - }// ~mutex_ - - template - bool RunIfWaiting(F&& func) - {// mutex_ - std::lock_guard scopedLock(mutex_); - - if (!isWaiting_) - return false; - - func(); - return true; - }// ~mutex_ - - template - bool RunIfNotWaiting(F&& func) - {// mutex_ - std::lock_guard scopedLock(mutex_); - - if (isWaiting_) - return false; - - func(); - return true; - }// ~mutex_ - -private: - std::atomic waitCount_{ 0 }; - std::condition_variable condition_; - std::mutex mutex_; - bool isWaiting_ = false; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// UniqueWaitingState -/////////////////////////////////////////////////////////////////////////////////////////////////// -class UniqueWaitingState : public WaitingStateBase -{ -protected: - // Do nothing - virtual inline bool IsRefCounted() const override { return false; } - virtual inline void IncRefCount() override {} - virtual inline void DecRefCount() override {} -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SharedWaitingState -/////////////////////////////////////////////////////////////////////////////////////////////////// -class SharedWaitingState : public WaitingStateBase -{ -private: - SharedWaitingState() = default; - -public: - static inline WaitingStatePtrT Create() - { - return WaitingStatePtrT(new SharedWaitingState()); - } - - -protected: - virtual inline bool IsRefCounted() const override { return true; } - - virtual inline void IncRefCount() override - { - refCount_.fetch_add(1, std::memory_order_relaxed); - } - - virtual inline void DecRefCount() override - { - auto oldVal = refCount_.fetch_sub(1, std::memory_order_relaxed); - - if (oldVal == 1) - delete this; - } - -private: - std::atomic refCount_{ 0 }; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// SharedWaitingStateCollection -/////////////////////////////////////////////////////////////////////////////////////////////////// -class SharedWaitingStateCollection : public IWaitingState -{ -private: - SharedWaitingStateCollection(std::vector&& others) : - others_( std::move(others) ) - {} - -public: - static inline WaitingStatePtrT Create(std::vector&& others) - { - return WaitingStatePtrT(new SharedWaitingStateCollection(std::move(others))); - } - - virtual inline bool IsRefCounted() const override { return true; } - - virtual inline void Wait() override - { - for (const auto& e : others_) - e->Wait(); - } - - virtual inline void IncWaitCount() override - { - for (const auto& e : others_) - e->IncWaitCount(); - } - - virtual inline void DecWaitCount() override - { - for (const auto& e : others_) - e->DecWaitCount(); - } - - virtual inline void IncRefCount() override - { - refCount_.fetch_add(1, std::memory_order_relaxed); - } - - virtual inline void DecRefCount() override - { - auto oldVal = refCount_.fetch_sub(1, std::memory_order_relaxed); - - if (oldVal == 1) - delete this; - } - -private: - std::atomic refCount_{ 0 }; - std::vector others_; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// BlockingCondition -/////////////////////////////////////////////////////////////////////////////////////////////////// -class BlockingCondition -{ -public: - inline void Block() - {// mutex_ - std::lock_guard scopedLock(mutex_); - blocked_ = true; - }// ~mutex_ - - inline void Unblock() - {// mutex_ - std::lock_guard scopedLock(mutex_); - blocked_ = false; - condition_.notify_all(); - }// ~mutex_ - - inline void WaitForUnblock() - { - std::unique_lock lock(mutex_); - condition_.wait(lock, [this] { return !blocked_; }); - } - - inline bool IsBlocked() - {// mutex_ - std::lock_guard scopedLock(mutex_); - return blocked_; - }// ~mutex_ - - template - auto Run(F&& func) -> decltype(func(false)) - {// mutex_ - std::lock_guard scopedLock(mutex_); - return func(blocked_); - }// ~mutex_ - - template - bool RunIfBlocked(F&& func) - {// mutex_ - std::lock_guard scopedLock(mutex_); - - if (!blocked_) - return false; - - func(); - return true; - }// ~mutex_ - - template - bool RunIfUnblocked(F&& func) - {// mutex_ - std::lock_guard scopedLock(mutex_); - - if (blocked_) - return false; - - func(); - return true; - }// ~mutex_ - -private: - std::mutex mutex_; - std::condition_variable condition_; - - bool blocked_ = false; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ConditionalCriticalSection -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class ConditionalCriticalSection; - -template -class ConditionalCriticalSection -{ -public: - template - void Access(const F& f) { f(); } -}; - -template -class ConditionalCriticalSection -{ -public: - template - void Access(const F& f) - {// mutex_ - std::lock_guard lock(mutex_); - - f(); - }// ~mutex_ -private: - TMutex mutex_; -}; - -/****************************************/ REACT_IMPL_END /***************************************/ - -#endif // REACT_COMMON_CONCURRENCY_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/RefCounting.h b/include/react/common/RefCounting.h index f2cd518a..37bb5e68 100644 --- a/include/react/common/RefCounting.h +++ b/include/react/common/RefCounting.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) @@ -9,7 +9,7 @@ #pragma once -#include "react/detail/Defs.h" +#include "react/detail/defs.h" #include #include diff --git a/include/react/common/SourceIdSet.h b/include/react/common/SourceIdSet.h index b1a3eeb0..902a39aa 100644 --- a/include/react/common/SourceIdSet.h +++ b/include/react/common/SourceIdSet.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) @@ -9,7 +9,7 @@ #pragma once -#include "react/detail/Defs.h" +#include "react/detail/defs.h" #include #include diff --git a/include/react/common/Timing.h b/include/react/common/Timing.h index 84c58006..7d6c81ac 100644 --- a/include/react/common/Timing.h +++ b/include/react/common/Timing.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) @@ -9,7 +9,7 @@ #pragma once -#include "react/detail/Defs.h" +#include "react/detail/defs.h" #include diff --git a/include/react/common/TopoQueue.h b/include/react/common/TopoQueue.h index 091d116f..2c3c4613 100644 --- a/include/react/common/TopoQueue.h +++ b/include/react/common/TopoQueue.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) @@ -9,7 +9,7 @@ #pragma once -#include "react/detail/Defs.h" +#include "react/detail/defs.h" #include #include diff --git a/include/react/common/Types.h b/include/react/common/Types.h index 0545f858..30a377fa 100644 --- a/include/react/common/Types.h +++ b/include/react/common/Types.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) @@ -9,7 +9,7 @@ #pragma once -#include "react/detail/Defs.h" +#include "react/detail/defs.h" #include #include diff --git a/include/react/common/Util.h b/include/react/common/Util.h deleted file mode 100644 index 4721723d..00000000 --- a/include/react/common/Util.h +++ /dev/null @@ -1,249 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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_COMMON_UTIL_H_INCLUDED -#define REACT_COMMON_UTIL_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include -#include - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -template -struct MakeVoid { using type = void;}; - -template -using VoidType = typename MakeVoid::type; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Unpack tuple - see -/// http://stackoverflow.com/questions/687490/how-do-i-expand-a-tuple-into-variadic-template-functions-arguments -/////////////////////////////////////////////////////////////////////////////////////////////////// - -template -struct Apply { - template - static auto apply(F&& f, T&& t, A&&... a) -> decltype(auto) - { - return Apply::apply(std::forward(f), std::forward(t), std::get(std::forward(t)), std::forward(a)...); - } -}; - -template<> -struct Apply<0> -{ - template - static auto apply(F&& f, T&&, A&&... a) -> decltype(auto) - { - return std::forward(f)(std::forward(a)...); - } -}; - -template -inline auto apply(F&& f, T&& t) -> decltype(auto) -{ - return Apply::type>::value>::apply(std::forward(f), std::forward(t)); -} - -template -struct ApplyMemberFn { - template - static inline auto apply(O obj, F f, T && t, A &&... a) -> decltype(auto) - { - return ApplyMemberFn::apply(obj, f, std::forward(t), std::get(std::forward(t)), std::forward(a)...); - } -}; - -template<> -struct ApplyMemberFn<0> -{ - template - static inline auto apply(O obj, F f, T &&, A &&... a) - -> decltype((obj->*f)(std::forward(a)...)) - { - return (obj->*f)(std::forward(a)...); - } -}; - -template -inline auto applyMemberFn(O obj, F f, T&& t) - -> decltype(ApplyMemberFn::type>::value>::apply(obj, f, std::forward(t))) -{ - return ApplyMemberFn::type>::value> - ::apply(obj, f, std::forward(t)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Helper to enable calling a function on each element of an argument pack. -/// We can't do f(args) ...; because ... expands with a comma. -/// But we can do nop_func(f(args) ...); -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -inline void pass(TArgs&& ...) {} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Identity (workaround to enable enable decltype()::X) -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct Identity -{ - using Type = T; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DontMove! -/////////////////////////////////////////////////////////////////////////////////////////////////// -struct DontMove {}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// DisableIfSame -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct DisableIfSame : - std::enable_if::type, - typename std::decay::type>::value> {}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// AddDummyArgWrapper -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct AddDummyArgWrapper -{ - AddDummyArgWrapper(const AddDummyArgWrapper& other) = default; - - AddDummyArgWrapper(AddDummyArgWrapper&& other) : - MyFunc( std::move(other.MyFunc) ) - {} - - template ::type> - explicit AddDummyArgWrapper(FIn&& func) : MyFunc( std::forward(func) ) {} - - TRet operator()(TArg, TDepValues& ... args) - { - return MyFunc(args ...); - } - - F MyFunc; -}; - -template -struct AddDummyArgWrapper -{ -public: - AddDummyArgWrapper(const AddDummyArgWrapper& other) = default; - - AddDummyArgWrapper(AddDummyArgWrapper&& other) : - MyFunc( std::move(other.MyFunc) ) - {} - - template ::type> - explicit AddDummyArgWrapper(FIn&& func) : MyFunc( std::forward(func) ) {} - - void operator()(TArg, TDepValues& ... args) - { - MyFunc(args ...); - } - - F MyFunc; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// AddDefaultReturnValueWrapper -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -< - typename F, - typename TRet, - TRet return_value -> -struct AddDefaultReturnValueWrapper -{ - AddDefaultReturnValueWrapper(const AddDefaultReturnValueWrapper&) = default; - - AddDefaultReturnValueWrapper(AddDefaultReturnValueWrapper&& other) : - MyFunc( std::move(other.MyFunc) ) - {} - - template - < - typename FIn, - class = typename DisableIfSame::type - > - explicit AddDefaultReturnValueWrapper(FIn&& func) : - MyFunc( std::forward(func) ) - {} - - template - TRet operator()(TArgs&& ... args) - { - MyFunc(std::forward(args) ...); - return return_value; - } - - F MyFunc; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IsCallableWith -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -class IsCallableWith -{ -private: - using NoT = char[1]; - using YesT = char[2]; - - template - < - typename U, - typename = decltype(static_cast((std::declval())(std::declval() ...))) - > - static YesT& check(void*); - - template - static NoT& check(...); - -public: - static const bool value = sizeof(check(nullptr)) == sizeof(YesT); -}; - -template -bool IsBitmaskSet(T flags, T mask) -{ - return (flags & mask) != (T)0; -} - -/****************************************/ REACT_IMPL_END /***************************************/ - -// Expand args by wrapping them in a dummy function -// Use comma operator to replace potential void return value with 0 -#define REACT_EXPAND_PACK(...) REACT_IMPL::pass((__VA_ARGS__ , 0) ...) - -#define REACT_DEFINE_BITMASK_OPERATORS(t) \ - inline t operator|(t lhs, t rhs) \ - { return static_cast(static_cast::type>(lhs) | static_cast::type>(rhs)); } \ - inline t operator&(t lhs, t rhs) \ - { return static_cast(static_cast::type>(lhs) & static_cast::type>(rhs)); } \ - inline t operator^(t lhs, t rhs) \ - { return static_cast(static_cast::type>(lhs) ^ static_cast::type>(rhs)); } \ - inline t operator~(t rhs) \ - { return static_cast(~static_cast::type>(rhs)); } \ - inline t& operator|=(t& lhs, t rhs) \ - { lhs = static_cast(static_cast::type>(lhs) | static_cast::type>(rhs)); return lhs; } \ - inline t& operator&=(t& lhs, t rhs) \ - { lhs = static_cast(static_cast::type>(lhs) & static_cast::type>(rhs)); return lhs; } \ - inline t& operator^=(t& lhs, t rhs) \ - { lhs = static_cast(static_cast::type>(lhs) ^ static_cast::type>(rhs)); return lhs; } - -// Bitmask helper - -#endif // REACT_COMMON_UTIL_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/expected.h b/include/react/common/expected.h new file mode 100644 index 00000000..5bdff539 --- /dev/null +++ b/include/react/common/expected.h @@ -0,0 +1,218 @@ + +// Copyright Sebastian Jeckel 2016. +// 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_COMMON_EXPECTED_H_INCLUDED +#define REACT_COMMON_EXPECTED_H_INCLUDED + +#pragma once + +#include "react/detail/Defs.h" + +#include +#include +#include +#include + +/*****************************************/ REACT_BEGIN /*****************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// IError +/////////////////////////////////////////////////////////////////////////////////////////////////// +class IErrorCause +{ +public: + virtual ~IErrorCause() = default; + + virtual std::string GetMessage() const = 0; + + virtual bool IsOfType(const char* typeId) const = 0; +}; + + +class AllocationError : public IErrorCause +{ +public: + static constexpr const char* type_id = "react::AllocationError"; + + virtual std::string GetMessage() const + { return "Allocation error."; } + + virtual bool IsOfType(const char* typeId) const + { return typeId == type_id; } +}; + + +class PreconditionError : public IErrorCause +{ +public: + static constexpr const char* type_id = "react::PreconditionError"; + + virtual std::string GetMessage() const + { return "Precondition error."; } + + virtual bool IsOfType(const char* typeId) const + { return typeId == type_id; } +}; + + +class PostconditionError : public IErrorCause +{ +public: + static constexpr const char* type_id = "react::PostconditionError"; + + virtual std::string GetMessage() const + { return "Postcondition error."; } + + virtual bool IsOfType(const char* typeId) const + { return typeId == type_id; } +}; + + +class MissingValueError : public IErrorCause +{ +public: + static constexpr const char* type_id = "react::MissingValueError"; + + virtual std::string GetMessage() const + { return "Missing value error."; } + + virtual bool IsOfType(const char* typeId) const + { return typeId == type_id; } +}; + + +class Error +{ +public: + template + < + typename T, + typename = std::enable_if_t> + > + Error(T cause) : + cause_(std::make_shared(std::move(cause))) + { } + + template + < + typename T, + typename = std::enable_if_t> + > + bool IsCause() const + { return cause_->IsOfType(T::type_id); } + + template + < + typename T, + typename = std::enable_if_t> + > + T* GetCause() const + { return static_cast(cause_.get()); } + + std::string GetMessage() const + { return cause_->GetMessage(); } + +private: + std::shared_ptr cause_; +}; + +static Error* GetErrorSentinel() + { return reinterpret_cast(0x1u); } + +struct ErrorDeleter +{ + void operator()(Error* p) const + { + if (p != GetErrorSentinel()) + delete p; + } +}; + +using UniqueErrorPtrType = std::unique_ptr; + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Expected +/// +/// TODO: Optimize for different storage types to avoid branch in destructor. +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class Expected +{ +public: + Expected() = delete; + + Expected(const Expected&) = delete; + + Expected& operator=(const Expected&) = delete; + + Expected(Expected&&) = default; + + Expected& operator=(Expected&&) = default; + + Expected(const T& value) : + error_( GetErrorSentinel() ) + { + ValueRef() = value; + } + + Expected(T&& value) : + error_( GetErrorSentinel() ) + { + ValueRef() = std::move(value); + } + + Expected(Error err) : + error_(new Error(std::move(err))) + { } + + Expected(UniqueErrorPtrType&& err) : + error_(std::move(err)) + { } + + ~Expected() + { + if (error_.get() == GetErrorSentinel()) + { + Destruct(); + } + } + + explicit operator bool() const + { + return error_.get() == GetErrorSentinel(); + } + + Error GetError() + { return *error_; } + +private: + const T& ValueRef() const + { return *reinterpret_cast(&valueStorage_); } + + T& ValueRef() + { return *reinterpret_cast(&valueStorage_); } + + void Destruct() + { reinterpret_cast(&valueStorage_)->~T(); } + + std::aligned_storage_t valueStorage_; + + std::unique_ptr error_; + + template + friend typename UniqueErrorPtrType UnwindExpected(Expected&& ex); +}; + +template +typename UniqueErrorPtrType UnwindExpected(Expected&& ex) +{ + return std::move(std::move(ex).error_); +} + +/******************************************/ REACT_END /******************************************/ + +#endif // REACT_COMMON_INDEXED_STORAGE_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/optional.h b/include/react/common/optional.h new file mode 100644 index 00000000..3208c33b --- /dev/null +++ b/include/react/common/optional.h @@ -0,0 +1,70 @@ + +// Copyright Sebastian Jeckel 2017. +// 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_COMMON_OPTIONAL_H_INCLUDED +#define REACT_COMMON_OPTIONAL_H_INCLUDED + +#pragma once + +#include "react/detail/defs.h" + +#include +#include +#include + +struct NoValueType +{ + struct Tag {}; + + explicit constexpr NoValueType(Tag) + { } +}; + +constexpr NoValueType no_value{ NoValueType::Tag{ } }; + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +template +class OptScalarStorage +{ +public: + OptScalarStorage() : hasValue_( false ) + { } + + OptScalarStorage(NoValueType) : hasValue_( false ) + { } + + OptScalarStorage& operator=(NoValueType) + { + hasValue_ = false; + return *this; + } + + OptScalarStorage(const OptScalarStorage&) = default; + + OptScalarStorage& operator=(const OptScalarStorage&) = default; + + bool HasValue() const + { return hasValue_; } + + const T& Value() const + { return value_; } + + T& Value() + { return value_; } + +private: + T value_; + bool hasValue_; +}; + +/****************************************/ REACT_IMPL_END /***************************************/ + +/*****************************************/ REACT_BEGIN /*****************************************/ + +/******************************************/ REACT_END /******************************************/ + +#endif // REACT_COMMON_OPTIONAL_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/owned_ptr.h b/include/react/common/owned_ptr.h new file mode 100644 index 00000000..f0d06bcb --- /dev/null +++ b/include/react/common/owned_ptr.h @@ -0,0 +1,18 @@ + +// Copyright Sebastian Jeckel 2017. +// 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_COMMON_OWNED_PTR_H_INCLUDED +#define REACT_COMMON_OWNED_PTR_H_INCLUDED + +#pragma once + +#include "react/detail/Defs.h" + +/*****************************************/ REACT_BEGIN /*****************************************/ + +/******************************************/ REACT_END /******************************************/ + +#endif // REACT_COMMON_OWNED_PTR_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/ptrcache.h b/include/react/common/ptrcache.h new file mode 100644 index 00000000..50d71584 --- /dev/null +++ b/include/react/common/ptrcache.h @@ -0,0 +1,65 @@ + +// Copyright Sebastian Jeckel 2017. +// 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_COMMON_PTR_CACHE_H_INCLUDED +#define REACT_COMMON_PTR_CACHE_H_INCLUDED + +#pragma once + +#include "react/detail/Defs.h" + +#include +#include +#include + +/*****************************************/ REACT_BEGIN /*****************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Stores weak pointers to V indexed by K. +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +class WeakPtrCache +{ +public: + template + std::shared_ptr LookupOrCreate(const K& key, F&& createFunc) + { + std::lock_guard scopedLock(mutex_); + + auto it = map_.find(key); + if (it != map_.end()) + { + if (auto ptr = it->second.lock()) + { + return ptr; + } + } + + std::shared_ptr v = createFunc(); + auto res = map_.emplace(key, v); + return v; + } + + void Erase(const K& key) + { + std::lock_guard scopedLock(mutex_); + + auto it = map_.find(key); + if (it != map_.end()) + { + map_.erase(it); + } + } + +private: + std::mutex mutex_; + std::unordered_map> map_; +}; + + +/******************************************/ REACT_END /******************************************/ + +#endif // REACT_COMMON_INDEXED_STORAGE_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/Containers.h b/include/react/common/slotmap.h similarity index 82% rename from include/react/common/Containers.h rename to include/react/common/slotmap.h index 79980acf..c0282ec0 100644 --- a/include/react/common/Containers.h +++ b/include/react/common/slotmap.h @@ -4,8 +4,8 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#ifndef REACT_COMMON_CONTAINERS_H_INCLUDED -#define REACT_COMMON_CONTAINERS_H_INCLUDED +#ifndef REACT_COMMON_INDEXED_STORAGE_H_INCLUDED +#define REACT_COMMON_INDEXED_STORAGE_H_INCLUDED #pragma once @@ -17,13 +17,13 @@ #include #include -/***************************************/ REACT_IMPL_BEGIN /**************************************/ +/*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// -/// IndexMap +/// SlotMap /////////////////////////////////////////////////////////////////////////////////////////////////// template -class IndexedStorage +class SlotMap { static const size_t initial_capacity = 8; static const size_t grow_factor = 2; @@ -33,15 +33,15 @@ class IndexedStorage public: using ValueType = T; - IndexedStorage() = default; + SlotMap() = default; - IndexedStorage(IndexedStorage&&) = default; - IndexedStorage& operator=(IndexedStorage&&) = default; + SlotMap(SlotMap&&) = default; + SlotMap& operator=(SlotMap&&) = default; - IndexedStorage(const IndexedStorage&) = delete; - IndexedStorage& operator=(const IndexedStorage&) = delete; + SlotMap(const SlotMap&) = delete; + SlotMap& operator=(const SlotMap&) = delete; - ~IndexedStorage() + ~SlotMap() { Reset(); } T& operator[](size_t index) @@ -90,26 +90,28 @@ class IndexedStorage // Skip over free indices. for (size_t j = 0; j < freeSize_; ++j) { + size_t freeIndex = freeIndices_[j]; + for (; index < totalSize; ++index) { - if (j == freeIndex_) + if (index == freeIndex) { ++index; break; } else { - data_[index].~T(); + reinterpret_cast(data_[index]).~T(); } } } // Rest for (; index < totalSize; ++index) - data_[index].~T(); + reinterpret_cast(data_[index]).~T(); size_ = 0; - freeList_ = 0; + freeSize_ = 0; } void Reset() @@ -144,7 +146,7 @@ class IndexedStorage size_t newCapacity = CalcNextCapacity(); std::unique_ptr newData{ new StorageType[newCapacity] }; - std::unique_ptr newFreeList{ new size_t[newCapacity] }; + std::unique_ptr newFreeIndices{ new size_t[newCapacity] }; // Move data to new storage for (size_t i = 0; i < capacity_; ++i) @@ -157,7 +159,7 @@ class IndexedStorage // Use new storage data_ = std::move(newData); - freeIndices_ = std::move(newFreeList); + freeIndices_ = std::move(newFreeIndices); capacity_ = newCapacity; } @@ -184,6 +186,6 @@ class IndexedStorage size_t capacity_ = 0; }; -/****************************************/ REACT_IMPL_END /***************************************/ +/******************************************/ REACT_END /******************************************/ -#endif // REACT_COMMON_CONTAINERS_H_INCLUDED \ No newline at end of file +#endif // REACT_COMMON_INDEXED_STORAGE_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/syncpoint.h b/include/react/common/syncpoint.h new file mode 100644 index 00000000..7ca5586b --- /dev/null +++ b/include/react/common/syncpoint.h @@ -0,0 +1,195 @@ + +// Copyright Sebastian Jeckel 2017. +// 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_COMMON_SYNCPOINT_H_INCLUDED +#define REACT_COMMON_SYNCPOINT_H_INCLUDED + +#pragma once + +#include "react/detail/defs.h" + +#include +#include +#include + + +/*****************************************/ REACT_BEGIN /*****************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// SyncPoint +/////////////////////////////////////////////////////////////////////////////////////////////////// +class SyncPoint +{ +private: + class Dependency; + + class ISyncPointState + { + public: + virtual ~ISyncPointState() = default; + + virtual void IncrementWaitCount() = 0; + + virtual void DecrementWaitCount() = 0; + }; + + class SyncPointState : public ISyncPointState + { + public: + virtual void IncrementWaitCount() override + {// mutex_ + std::lock_guard scopedLock(mtx_); + ++waitCount_; + }// ~mutex_ + + virtual void DecrementWaitCount() override + {// mutex_ + std::lock_guard scopedLock(mtx_); + --waitCount_; + + if (waitCount_ == 0) + cv_.notify_all(); + }// ~mutex_ + + void Wait() + { + std::unique_lock lock(mtx_); + cv_.wait(lock, [this] { return waitCount_ == 0; }); + } + + template + bool WaitFor(const std::chrono::duration& relTime) + { + std::unique_lock lock(mtx_); + return cv_.wait_for(lock, relTime, [this] { return waitCount_ == 0; }); + } + + template + bool WaitUntil(const std::chrono::duration& relTime) + { + std::unique_lock lock(mtx_); + return cv_.wait_until(lock, relTime, [this] { return waitCount_ == 0; }); + } + + private: + std::mutex mtx_; + std::condition_variable cv_; + + int waitCount_ = 0; + }; + + class SyncPointStateCollection : public ISyncPointState + { + private: + std::vector + }; + +public: + SyncPoint() : state_( std::make_shared() ) + { } + + SyncPoint(const SyncPoint&) = default; + + SyncPoint& operator=(const SyncPoint&) = default; + + SyncPoint(SyncPoint&&) = default; + + SyncPoint& operator=(SyncPoint&&) = default; + + void Wait() + { + state_->Wait(); + } + + template + bool WaitFor(const std::chrono::duration& relTime) + { + return state_->WaitFor(relTime); + } + + template + bool WaitUntil(const std::chrono::duration& relTime) + { + return state_->WaitUntil(relTime); + } + + class Dependency + { + public: + Dependency() = default; + + // Construct from single sync point. + explicit Dependency(const SyncPoint& sp) : + state_( sp.state_ ) + { + state_->IncrementWaitCount(); + } + + // Construct from vector of other dependencies. + explicit Dependency(std::vector&& others) : + state_( sp.state_ ) + { + state_->IncrementWaitCount(); + } + + Dependency(const Dependency& other) : + state_( other.state_ ) + { + state_->IncrementWaitCount(); + } + + Dependency& operator=(const Dependency& other) + { + if (other.state_) + state_->IncrementWaitCount(); + + if (state_) + state_->DecrementWaitCount(); + + state_ = other.state_; + } + + Dependency(Dependency&& other) : + state_( std::move(other.state_) ) + { } + + Dependency& operator=(Dependency&& other) + { + if (state_) + state_->DecrementWaitCount(); + + state_ = std::move(other.state_); + } + + ~Dependency() + { + if (state_) + state_->DecrementWaitCount(); + } + + void Release() + { + if (state_) + { + state_->DecrementWaitCount(); + state_.reset(); + } + } + + bool IsReleased() const + { return state_ == nullptr; } + + private: + std::shared_ptr state_; + }; + +private: + std::shared_ptr state_; +}; + +/******************************************/ REACT_END /******************************************/ + +#endif // REACT_COMMON_SYNCPOINT_H_INCLUDED \ No newline at end of file diff --git a/include/react/common/utility.h b/include/react/common/utility.h new file mode 100644 index 00000000..d1ce975d --- /dev/null +++ b/include/react/common/utility.h @@ -0,0 +1,58 @@ + +// Copyright Sebastian Jeckel 2017. +// 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_COMMON_UTIL_H_INCLUDED +#define REACT_COMMON_UTIL_H_INCLUDED + +#pragma once + +#include "react/detail/defs.h" + +#include +#include +#include + +/***************************************/ REACT_IMPL_BEGIN /**************************************/ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Helper to enable calling a function on each element of an argument pack. +/// We can't do f(args) ...; because ... expands with a comma. +/// But we can do nop_func(f(args) ...); +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +inline void pass(TArgs&& ...) {} + +template +bool IsBitmaskSet(T flags, T mask) +{ + return (flags & mask) != (T)0; +} + +/****************************************/ REACT_IMPL_END /***************************************/ + +// Expand args by wrapping them in a dummy function +// Use comma operator to replace potential void return value with 0 +#define REACT_EXPAND_PACK(...) REACT_IMPL::pass((__VA_ARGS__ , 0) ...) + +#define REACT_DEFINE_BITMASK_OPERATORS(t) \ + inline t operator|(t lhs, t rhs) \ + { return static_cast(static_cast::type>(lhs) | static_cast::type>(rhs)); } \ + inline t operator&(t lhs, t rhs) \ + { return static_cast(static_cast::type>(lhs) & static_cast::type>(rhs)); } \ + inline t operator^(t lhs, t rhs) \ + { return static_cast(static_cast::type>(lhs) ^ static_cast::type>(rhs)); } \ + inline t operator~(t rhs) \ + { return static_cast(~static_cast::type>(rhs)); } \ + inline t& operator|=(t& lhs, t rhs) \ + { lhs = static_cast(static_cast::type>(lhs) | static_cast::type>(rhs)); return lhs; } \ + inline t& operator&=(t& lhs, t rhs) \ + { lhs = static_cast(static_cast::type>(lhs) & static_cast::type>(rhs)); return lhs; } \ + inline t& operator^=(t& lhs, t rhs) \ + { lhs = static_cast(static_cast::type>(lhs) ^ static_cast::type>(rhs)); return lhs; } + +// Bitmask helper + +#endif // REACT_COMMON_UTIL_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/Defs.h b/include/react/detail/Defs.h index 6bdda2e0..d1509c8c 100644 --- a/include/react/detail/Defs.h +++ b/include/react/detail/Defs.h @@ -1,5 +1,5 @@ -// Copyright Sebastian Jeckel 2016. +// Copyright Sebastian Jeckel 2017. // 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) @@ -9,7 +9,6 @@ #pragma once -#include #include /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -21,15 +20,13 @@ #define REACT_IMPL_END REACT_END } #define REACT_IMPL REACT ::impl -/***************************************/ REACT_IMPL_BEGIN /**************************************/ +/*****************************************/ REACT_BEGIN /*****************************************/ // Type aliases using uint = unsigned int; using uchar = unsigned char; using std::size_t; -struct CtorTag { }; - -/****************************************/ REACT_IMPL_END /***************************************/ +/******************************************/ REACT_END /******************************************/ #endif // REACT_DETAIL_DEFS_H_INCLUDED \ No newline at end of file diff --git a/include/react/detail/ReactiveInput.h b/include/react/detail/ReactiveInput.h deleted file mode 100644 index d156b9fb..00000000 --- a/include/react/detail/ReactiveInput.h +++ /dev/null @@ -1,983 +0,0 @@ - -// Copyright Sebastian Jeckel 2016. -// 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) - -#if 0 - -#ifndef REACT_DETAIL_REACTIVEINPUT_H_INCLUDED -#define REACT_DETAIL_REACTIVEINPUT_H_INCLUDED - -#pragma once - -#include "react/detail/Defs.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tbb/task.h" -#include "tbb/concurrent_queue.h" -#include "tbb/enumerable_thread_specific.h" -#include "tbb/queuing_mutex.h" - -#include "IReactiveEngine.h" -#include "ObserverBase.h" -#include "react/common/Concurrency.h" - -/***************************************/ REACT_IMPL_BEGIN /**************************************/ - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Forward declarations -/////////////////////////////////////////////////////////////////////////////////////////////////// -class IObserver; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// Common types & constants -/////////////////////////////////////////////////////////////////////////////////////////////////// - -enum ETransactionFlags -{ - allow_merging = 1 << 0 -}; - -using TransactionFlagsT = std::underlying_type::type; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// EInputMode -/////////////////////////////////////////////////////////////////////////////////////////////////// -enum EInputMode -{ - consecutive_input, - concurrent_input -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// IContinuationTarget -/////////////////////////////////////////////////////////////////////////////////////////////////// -struct IContinuationTarget -{ - virtual void AsyncContinuation(TransactionFlagsT, const WaitingStatePtrT&, - TransactionFuncT&&) = 0; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ThreadLocalInputState -/////////////////////////////////////////////////////////////////////////////////////////////////// -template -struct ThreadLocalInputState -{ - static REACT_TLS bool IsTransactionActive; -}; - -template -REACT_TLS bool ThreadLocalInputState::IsTransactionActive(false); - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// ContinuationManager -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface -template -class ContinuationManager -{ -public: - void StoreContinuation(IContinuationTarget& target, TransactionFlagsT flags, - TransactionFuncT&& cont); - - bool HasContinuations() const; - void StartContinuations(const WaitingStatePtrT& waitingStatePtr); - - void QueueObserverForDetach(IObserver& obs); - - template - void DetachQueuedObservers(); -}; - -// Non thread-safe implementation -template <> -class ContinuationManager -{ - struct Data_ - { - Data_(IContinuationTarget* target, TransactionFlagsT flags, TransactionFuncT&& func) : - Target( target ), - Flags( flags ), - Func( func ) - {} - - IContinuationTarget* Target; - TransactionFlagsT Flags; - TransactionFuncT Func; - }; - - using DataVectT = std::vector; - using ObsVectT = std::vector; - -public: - void StoreContinuation(IContinuationTarget& target, TransactionFlagsT flags, - TransactionFuncT&& cont) - { - storedContinuations_.emplace_back(&target, flags, std::move(cont)); - } - - bool HasContinuations() const - { - return !storedContinuations_.empty(); - } - - void StartContinuations(const WaitingStatePtrT& waitingStatePtr) - { - for (auto& t : storedContinuations_) - t.Target->AsyncContinuation(t.Flags, waitingStatePtr, std::move(t.Func)); - - storedContinuations_.clear(); - } - - // Todo: Move this somewhere else - void QueueObserverForDetach(IObserver& obs) - { - detachedObservers_.push_back(&obs); - } - - template - void DetachQueuedObservers() - { - for (auto* o : detachedObservers_) - o->UnregisterSelf(); - detachedObservers_.clear(); - } - -private: - DataVectT storedContinuations_; - ObsVectT detachedObservers_; -}; - -// Thread-safe implementation -template <> -class ContinuationManager -{ - struct Data_ - { - Data_(IContinuationTarget* target, TransactionFlagsT flags, TransactionFuncT&& func) : - Target( target ), - Flags( flags ), - Func( func ) - {} - - IContinuationTarget* Target; - TransactionFlagsT Flags; - TransactionFuncT Func; - }; - - using DataVecT = std::vector; - using ObsVectT = std::vector; - -public: - void StoreContinuation(IContinuationTarget& target, TransactionFlagsT flags, - TransactionFuncT&& cont) - { - storedContinuations_.local().emplace_back(&target, flags, std::move(cont)); - ++contCount_; - } - - bool HasContinuations() const - { - return contCount_ != 0; - } - - void StartContinuations(const WaitingStatePtrT& waitingStatePtr) - { - for (auto& v : storedContinuations_) - for (auto& t : v) - t.Target->AsyncContinuation(t.Flags, waitingStatePtr, std::move(t.Func)); - - storedContinuations_.clear(); - contCount_ = 0; - } - - void QueueObserverForDetach(IObserver& obs) - { - detachedObservers_.local().push_back(&obs); - } - - template - void DetachQueuedObservers() - { - for (auto& v : detachedObservers_) - { - for (auto* o : v) - o->UnregisterSelf(); - v.clear(); - } - } - -private: - tbb::enumerable_thread_specific storedContinuations_; - tbb::enumerable_thread_specific detachedObservers_; - - size_t contCount_ = 0; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/// TransactionQueue -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface -template -class TransactionQueue -{ -public: - class QueueEntry - { - public: - explicit QueueEntry(TransactionFlagsT flags); - - void RunMergedInputs(); - - size_t GetWaitingStatePtrCount() const; - void MoveWaitingStatePtrs(std::vector& out); - - void Release(); - - bool IsAsync() const; - }; - - template - bool TryMergeSync(F&& inputFunc); - - template - bool TryMergeAsync(F&& inputFunc, WaitingStatePtrT&& waitingStatePtr); - - void EnterQueue(QueueEntry& turn); - void ExitQueue(QueueEntry& turn); -}; - -// Non thread-safe implementation -template <> -class TransactionQueue -{ -public: - class QueueEntry - { - public: - explicit QueueEntry(TransactionFlagsT flags) {} - - void RunMergedInputs() {} - - size_t GetWaitingStatePtrCount() const { return 0; } - void MoveWaitingStatePtrs(std::vector& out) {} - - void Release() {} - - bool IsAsync() const { return false; } - }; - - template - bool TryMergeSync(F&& inputFunc) { return false; } - - template - bool TryMergeAsync(F&& inputFunc, WaitingStatePtrT&& waitingStatePtr) { return false; } - - void EnterQueue(QueueEntry& turn) {} - void ExitQueue(QueueEntry& turn) {} -}; - -// Thread-safe implementation -template <> -class TransactionQueue -{ -public: - class QueueEntry - { - public: - explicit QueueEntry(TransactionFlagsT flags) : - isMergeable_( (flags & allow_merging) != 0 ) - {} - - void Append(QueueEntry& tr) - { - successor_ = &tr; - tr.waitingState_.IncWaitCount(); - ++waitingStatePtrCount_; - } - - void WaitForUnblock() - { - waitingState_.Wait(); - } - - void RunMergedInputs() const - { - for (const auto& e : merged_) - e.InputFunc(); - } - - size_t GetWaitingStatePtrCount() const - { - return waitingStatePtrCount_; - } - - void MoveWaitingStatePtrs(std::vector& out) - { - if (waitingStatePtrCount_ == 0) - return; - - for (const auto& e : merged_) - if (e.WaitingStatePtr != nullptr) - out.push_back(std::move(e.WaitingStatePtr)); - - if (successor_) - { - out.push_back(WaitingStatePtrT( &successor_->waitingState_ )); - - // Ownership of successors waiting state has been transfered, - // we no longer need it - successor_ = nullptr; - } - - waitingStatePtrCount_ = 0; - } - - void Release() - { - if (waitingStatePtrCount_ == 0) - return; - - // Release merged - for (const auto& e : merged_) - if (e.WaitingStatePtr != nullptr) - e.WaitingStatePtr->DecWaitCount(); - - // Waiting state ptrs may point to stack of waiting threads. - // After decwaitcount, they may start running and terminate. - // Thus, clear merged ASAP because it now contains invalid ptrs. - merged_.clear(); - - // Release next thread in queue - if (successor_) - successor_->waitingState_.DecWaitCount(); - } - - template - bool TryMerge(F&& inputFunc, P&& waitingStatePtr) - { - if (!isMergeable_) - return false; - - // Only merge if target is still waiting - bool merged = waitingState_.RunIfWaiting([&] { - if (waitingStatePtr != nullptr) - { - waitingStatePtr->IncWaitCount(); - ++waitingStatePtrCount_; - } - - merged_.emplace_back(std::forward(inputFunc), std::forward