Skip to content

Commit 76be2a9

Browse files
committed
Added Ref<T> 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<T> -> State<Ref<T>> binding. Semantically, it's the same as using State<T*>, but avoids the null check. The State<Ref<T>> changes when the referenced state changes.
1 parent 5467003 commit 76be2a9

3 files changed

Lines changed: 71 additions & 13 deletions

File tree

include/react/api.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#pragma once
1111

12+
#include <type_traits>
1213
#include <vector>
1314

1415
#include "react/detail/defs.h"
@@ -93,6 +94,26 @@ using EventValueSink = std::back_insert_iterator<std::vector<E>>;
9394
// Observer
9495
class Observer;
9596

97+
// Ref
98+
template <typename T>
99+
using Ref = std::reference_wrapper<const T>;
100+
101+
template <typename T>
102+
bool HasChanged(const T& a, const T& b)
103+
{ return !(a == b); }
104+
105+
template <typename T>
106+
bool HasChanged(const Ref<T>& a, const Ref<T>& b)
107+
{ return true; }
108+
109+
template <typename T, typename V>
110+
void ListInsert(T& list, V&& value)
111+
{ list.push_back(std::forward<V>(value)); }
112+
113+
template <typename T, typename V>
114+
void MapInsert(T& map, V&& value)
115+
{ map.insert(std::forward<V>(value)); }
116+
96117
/******************************************/ REACT_END /******************************************/
97118

98119
#endif // REACT_TYPETRAITS_H_INCLUDED

include/react/detail/state_nodes.h

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -171,22 +171,19 @@ class StateFuncNode : public StateNode<S>
171171
}
172172

173173
virtual UpdateResult Update(TurnId turnId) noexcept override
174-
{
175-
bool changed = false;
176-
174+
{
177175
S newValue = apply([this] (const auto& ... deps)
178176
{ return this->func_(GetInternals(deps).Value() ...); }, depHolder_);
179177

180-
if (! (this->Value() == newValue))
178+
if (HasChanged(this->Value(), newValue))
181179
{
182180
this->Value() = std::move(newValue);
183-
changed = true;
184-
}
185-
186-
if (changed)
187181
return UpdateResult::changed;
182+
}
188183
else
184+
{
189185
return UpdateResult::unchanged;
186+
}
190187
}
191188

192189
private:
@@ -439,6 +436,37 @@ class StateInternals
439436
std::shared_ptr<StateNode<S>> nodePtr_;
440437
};
441438

439+
///////////////////////////////////////////////////////////////////////////////////////////////////
440+
/// StateRefNode
441+
///////////////////////////////////////////////////////////////////////////////////////////////////
442+
template <typename S>
443+
class StateRefNode : public StateNode<Ref<S>>
444+
{
445+
public:
446+
StateRefNode(const Group& group, const State<S>& input) :
447+
StateRefNode::StateNode( group, std::cref(GetInternals(input).Value()) ),
448+
input_( input )
449+
{
450+
this->RegisterMe();
451+
this->AttachToMe(GetInternals(input).GetNodeId());
452+
}
453+
454+
~StateRefNode()
455+
{
456+
this->DetachFromMe(GetInternals(input_).GetNodeId());
457+
this->UnregisterMe();
458+
}
459+
460+
virtual UpdateResult Update(TurnId turnId) noexcept override
461+
{
462+
this->Value() = std::cref(GetInternals(input_).Value());
463+
return UpdateResult::changed;
464+
}
465+
466+
private:
467+
State<S> input_;
468+
};
469+
442470
///////////////////////////////////////////////////////////////////////////////////////////////////
443471
/// SameGroupOrLink
444472
///////////////////////////////////////////////////////////////////////////////////////////////////

include/react/state.h

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,7 @@ class StateSlot : public State<S>
202202
StateSlot& operator=(StateSlot&&) = default;
203203

204204
void Set(const State<S>& newInput)
205-
{ SetInput(newInput); }
206-
207-
void operator<<=(const State<S>& newInput)
208-
{ SetInput(newInput); }
205+
{ SetSlotInput(newInput); }
209206

210207
protected:
211208
StateSlot(std::shared_ptr<REACT_IMPL::StateNode<S>>&& nodePtr) :
@@ -221,7 +218,7 @@ class StateSlot : public State<S>
221218
return std::make_shared<StateSlotNode<S>>(group, SameGroupOrLink(group, input));
222219
}
223220

224-
void SetInput(const State<S>& newInput)
221+
void SetSlotInput(const State<S>& newInput)
225222
{
226223
using REACT_IMPL::NodeId;
227224
using REACT_IMPL::StateSlotNode;
@@ -282,6 +279,18 @@ class StateLink : public State<S>
282279
}
283280
};
284281

282+
///////////////////////////////////////////////////////////////////////////////////////////////////
283+
/// CreateRef
284+
///////////////////////////////////////////////////////////////////////////////////////////////////
285+
template <typename S>
286+
auto CreateRef(const State<S>& state) -> State<Ref<S>>
287+
{
288+
using REACT_IMPL::StateRefNode;
289+
using REACT_IMPL::CreateWrappedNode;
290+
291+
return CreateWrappedNode<State<Ref<S>>, StateRefNode<S>>(state.GetGroup(), state);
292+
}
293+
285294
///////////////////////////////////////////////////////////////////////////////////////////////////
286295
/// ObjectContext
287296
///////////////////////////////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)