KDBindings API Documentation  1.0.95
node.h
Go to the documentation of this file.
1 /*
2  This file is part of KDBindings.
3 
4  SPDX-FileCopyrightText: 2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
5  Author: Sean Harmer <sean.harmer@kdab.com>
6 
7  SPDX-License-Identifier: MIT
8 
9  Contact KDAB at <info@kdab.com> for commercial licensing options.
10 */
11 
12 #pragma once
13 
14 #include <kdbindings/property.h>
15 #include <kdbindings/signal.h>
16 
17 #include <functional>
18 #include <memory>
19 #include <stdexcept>
20 #include <type_traits>
21 #include <utility>
22 
23 namespace KDBindings {
24 
29 class PropertyDestroyedError : public std::runtime_error
30 {
31 public:
33 
34  using std::runtime_error::runtime_error;
35 };
36 
37 namespace Private {
38 
39 class Dirtyable
40 {
41 public:
42  virtual ~Dirtyable() = default;
43 
44  Dirtyable() = default;
45 
46  void setParent(Dirtyable *newParent)
47  {
48  auto **parentVar = parentVariable();
49  if (parentVar) {
50  *parentVar = newParent;
51  }
52  }
53 
54  // overridden by Binding
55  virtual void markDirty()
56  {
57  auto *dirtyVar = dirtyVariable();
58  if (dirtyVar) {
59  if (*dirtyVar) {
60  return;
61  // We are already dirty, don't bother marking the whole tree again.
62  }
63 
64  // we only want to have one override for dirtyVariable,
65  // which is const, so we have to const cast here.
66  *const_cast<bool *>(dirtyVar) = true;
67  }
68 
69  auto **parentVar = parentVariable();
70  if (parentVar && *parentVar) {
71  (*parentVar)->markDirty();
72  }
73  }
74 
75  bool isDirty() const
76  {
77  auto *dirtyVar = dirtyVariable();
78  return dirtyVar && *dirtyVar;
79  }
80 
81 protected:
82  virtual Dirtyable **parentVariable() = 0;
83  virtual const bool *dirtyVariable() const = 0;
84 };
85 
86 template<typename ResultType>
87 class NodeInterface : public Dirtyable
88 {
89 public:
90  // Returns a reference, because we cache each evaluated value.
91  // const, because it shouldn't modify the return value of the AST.
92  // Requires mutable caches
93  virtual const ResultType &evaluate() const = 0;
94 
95 protected:
96  NodeInterface() = default;
97 };
98 
99 template<typename ResultType>
100 class Node
101 {
102 public:
103  Node(std::unique_ptr<NodeInterface<ResultType>> &&nodeInterface)
104  : m_interface(std::move(nodeInterface))
105  {
106  }
107 
108  const ResultType &evaluate() const
109  {
110  return m_interface->evaluate();
111  }
112 
113  void setParent(Dirtyable *newParent)
114  {
115  m_interface->setParent(newParent);
116  }
117 
118  bool isDirty() const
119  {
120  return m_interface->isDirty();
121  }
122 
123 private:
124  std::unique_ptr<NodeInterface<ResultType>> m_interface;
125 };
126 
127 template<typename T>
128 class ConstantNode : public NodeInterface<T>
129 {
130 public:
131  explicit ConstantNode(const T &value)
132  : m_value{ value }
133  {
134  }
135 
136  const T &evaluate() const override
137  {
138  return m_value;
139  }
140 
141 protected:
142  // A constant can never be dirty, so it doesn't need to
143  // know its parent, as it doesn't have to notify it.
144  Dirtyable **parentVariable() override { return nullptr; }
145  const bool *dirtyVariable() const override { return nullptr; }
146 
147 private:
148  T m_value;
149 };
150 
151 template<typename PropertyType>
152 class PropertyNode : public NodeInterface<PropertyType>
153 {
154 public:
155  explicit PropertyNode(const Property<PropertyType> &property)
156  : m_parent(nullptr), m_dirty(false)
157  {
158  setProperty(property);
159  }
160 
161  // PropertyNodes cannot be moved
162  PropertyNode(PropertyNode<PropertyType> &&) = delete;
163 
164  PropertyNode(const PropertyNode<PropertyType> &other)
165  : Dirtyable(other.isDirty())
166  {
167  setProperty(*other.m_property);
168  }
169 
170  virtual ~PropertyNode()
171  {
172  m_valueChangedHandle.disconnect();
173  m_movedHandle.disconnect();
174  m_destroyedHandle.disconnect();
175  }
176 
177  const PropertyType &evaluate() const override
178  {
179  if (!m_property) {
180  throw PropertyDestroyedError("The Property this node refers to no longer exists!");
181  }
182 
183  m_dirty = false;
184  return m_property->get();
185  }
186 
187  void propertyMoved(const Property<PropertyType> &property)
188  {
189  if (&property != m_property) {
190  m_property = &property;
191  } else {
192  // Another property was moved into the property this node refers to.
193  // Therefore it will no longer update this Node.
194  m_property = nullptr;
195  }
196  }
197 
198  void propertyDestroyed()
199  {
200  m_property = nullptr;
201  }
202 
203 protected:
204  Dirtyable **parentVariable() override { return &m_parent; }
205  const bool *dirtyVariable() const override { return &m_dirty; }
206 
207 private:
208  void setProperty(const Property<PropertyType> &property)
209  {
210  m_property = &property;
211 
212  // Connect to all signals, even for const properties
213  m_valueChangedHandle = m_property->valueChanged().connect([this]() { this->markDirty(); });
214  m_movedHandle = m_property->m_moved.connect([this](const Property<PropertyType> &newProp) { this->propertyMoved(newProp); });
215  m_destroyedHandle = m_property->destroyed().connect([this]() { this->propertyDestroyed(); });
216  }
217 
218  const Property<PropertyType> *m_property;
219  ConnectionHandle m_movedHandle;
220  ConnectionHandle m_valueChangedHandle;
221  ConnectionHandle m_destroyedHandle;
222 
223  Dirtyable *m_parent;
224  mutable bool m_dirty;
225 };
226 
227 template<typename ResultType, typename Operator, typename... Ts>
228 class OperatorNode : public NodeInterface<ResultType>
229 {
230 public:
231  // add another typename template for the Operator type, so
232  // it can be a universal reference.
233  template<typename Op>
234  explicit OperatorNode(Op &&op, Node<Ts> &&...arguments)
235  : m_parent{ nullptr }, m_dirty{ true /*dirty until reevaluated*/ }, m_op{ std::move(op) }, m_values{ std::move(arguments)... }, m_result(reevaluate())
236  {
237  static_assert(
238  std::is_convertible_v<decltype(m_op(std::declval<Ts>()...)), ResultType>,
239  "The result of the Operator must be convertible to the ReturnType of the Node");
240 
241  setParents<0>();
242  }
243 
244  template<std::size_t I>
245  auto setParents() -> std::enable_if_t<I == sizeof...(Ts)>
246  {
247  }
248 
249  // The enable_if_t confuses clang-format into thinking the
250  // first "<" is a comparison, and not the second.
251  // clang-format off
252  template<std::size_t I>
253  auto setParents() -> std::enable_if_t<I < sizeof...(Ts)>
254  // clang-format on
255  {
256  std::get<I>(m_values).setParent(this);
257  setParents<I + 1>();
258  }
259 
260  virtual ~OperatorNode() = default;
261 
262  const ResultType &evaluate() const override
263  {
264  if (Dirtyable::isDirty()) {
265  m_result = reevaluate();
266  }
267 
268  return m_result;
269  }
270 
271 protected:
272  Dirtyable **parentVariable() override { return &m_parent; }
273  const bool *dirtyVariable() const override { return &m_dirty; }
274 
275 private:
276  template<std::size_t... Is>
277  ResultType reevaluate_helper(std::index_sequence<Is...>) const
278  {
279  return m_op(std::get<Is>(m_values).evaluate()...);
280  }
281 
282  ResultType reevaluate() const
283  {
284  m_dirty = false;
285 
286  return reevaluate_helper(std::make_index_sequence<sizeof...(Ts)>());
287  }
288 
289  Dirtyable *m_parent;
290  mutable bool m_dirty;
291 
292  Operator m_op;
293  std::tuple<Node<Ts>...> m_values;
294 
295  // Note: it is important that m_result is evaluated last!
296  // Otherwise the call to reevaluate in the constructor will fail.
297  mutable ResultType m_result;
298 };
299 
300 template<typename T>
301 struct is_node_helper : std::false_type {
302 };
303 
304 template<typename T>
305 struct is_node_helper<Node<T>> : std::true_type {
306 };
307 
308 template<typename T>
309 struct is_node : is_node_helper<T> {
310 };
311 
312 } // namespace Private
313 
314 } // namespace KDBindings
A PropertyDestroyedError is thrown whenever a binding is evaluated that references a property that no...
Definition: node.h:30
The main namespace of the KDBindings library.
Definition: binding.h:21
Definition: utils.h:161

© Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
KDBindings
Reactive programming & data binding in C++
https://github.com/KDAB/KDBindings/
Generated by doxygen 1.9.1