KDBindings API Documentation  1.0.95
property.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 
15 #include <kdbindings/signal.h>
16 
17 #include <iostream>
18 #include <memory>
19 #include <type_traits>
20 
21 namespace KDBindings {
22 
30 namespace Private {
31 
32 template<typename X, typename Y, typename = void>
33 struct are_equality_comparable : std::false_type {
34 };
35 
36 template<typename X, typename Y>
37 struct are_equality_comparable<X, Y,
38  std::enable_if_t<
39  std::is_same<
40  std::decay_t<
41  decltype(std::equal_to<>{}(std::declval<X>(), std::declval<Y>()))>,
42  bool>::value>> : std::true_type {
43 };
44 
45 template<typename X, typename Y>
46 constexpr bool are_equality_comparable_v = are_equality_comparable<X, Y>::value;
47 
48 } // namespace Private
49 
56 struct ReadOnlyProperty : std::runtime_error {
57  ReadOnlyProperty() = delete;
58 
59  using std::runtime_error::runtime_error;
60 };
61 
77 template<typename T>
78 struct equal_to {
86  auto operator()(const T &x, const T &y) const noexcept
87  -> std::enable_if_t<Private::are_equality_comparable_v<T, T>, bool>
88  {
89  return std::equal_to<>{}(x, y);
90  }
91 
99  template<typename X, typename Y>
100  auto operator()(const X &, const Y &) const noexcept
101  -> std::enable_if_t<!Private::are_equality_comparable_v<X, Y>, bool>
102  {
103  return false;
104  }
105 };
106 
107 // This forwrad declaration is required so that
108 // Property can declare PropertyNode as a friend
109 // class.
110 namespace Private {
111 template<typename PropertyType>
112 class PropertyNode;
113 }
114 
136 template<typename T>
137 class Property
138 {
139 public:
140  typedef T valuetype;
141 
147  Property() = default;
148 
153  {
154  m_destroyed.emit();
155  }
156 
160  explicit Property(T value) noexcept(std::is_nothrow_move_constructible<T>::value)
161  : m_value{ std::move(value) }
162  {
163  }
164 
168  Property(Property<T> const &other) = delete;
169  Property &operator=(Property<T> const &other) = delete;
170 
180  Property(Property<T> &&other) noexcept(std::is_nothrow_move_constructible<T>::value)
181  : m_value(std::move(other.m_value))
182  , m_valueAboutToChange(std::move(other.m_valueAboutToChange))
183  , m_valueChanged(std::move(other.m_valueChanged))
184  , m_destroyed(std::move(other.m_destroyed))
185  , m_updater(std::move(other.m_updater))
186  {
187  // We do not move the m_moved signal yet so that objects interested in the moved-into
188  // property can recreate any connections they need.
189 
190  // If we have an updater, let it know how to update our internal value
191  if (m_updater) {
192  using namespace std::placeholders;
193  m_updater->setUpdateFunction(
194  std::bind(&Property<T>::setHelper, this, _1));
195  }
196 
197  // Emit the moved signals for the moved from and moved to properties
198  m_moved.emit(*this);
199  other.m_moved.emit(*this);
200  m_moved = std::move(other.m_moved);
201  }
202 
206  Property &operator=(Property<T> &&other) noexcept(std::is_nothrow_move_assignable<T>::value)
207  {
208  // We do not move the m_moved signal yet so that objects interested in the moved-into
209  // property can recreate any connections they need.
210  m_value = std::move(other.m_value);
211  m_valueAboutToChange = std::move(other.m_valueAboutToChange);
212  m_valueChanged = std::move(other.m_valueChanged);
213  m_destroyed = std::move(other.m_destroyed);
214  m_updater = std::move(other.m_updater);
215 
216  // If we have an updater, let it know how to update our internal value
217  if (m_updater) {
218  using namespace std::placeholders;
219  m_updater->setUpdateFunction(
220  std::bind(&Property<T>::setHelper, this, _1));
221  }
222 
223  // Emit the moved signals for the moved from and moved to properties
224  m_moved.emit(*this);
225  other.m_moved.emit(*this);
226  m_moved = std::move(other.m_moved);
227 
228  return *this;
229  }
230 
237  template<typename UpdaterT>
238  explicit Property(std::unique_ptr<UpdaterT> &&updater)
239  {
240  *this = std::move(updater);
241  }
242 
254  template<typename UpdaterT>
255  Property &operator=(std::unique_ptr<UpdaterT> &&updater)
256  {
257  m_updater = std::move(updater);
258 
259  // Let the updater know how to update our internal value
260  using namespace std::placeholders;
261  m_updater->setUpdateFunction(
262  std::bind(&Property<T>::setHelper, this, _1));
263 
264  // Now synchronise our value with whatever the updator has right now.
265  setHelper(m_updater->get());
266 
267  return *this;
268  }
269 
278  void reset()
279  {
280  m_updater.reset();
281  }
282 
289  Signal<const T &, const T &> &valueAboutToChange() const { return m_valueAboutToChange; }
290 
296  Signal<const T &> &valueChanged() const { return m_valueChanged; }
297 
301  Signal<> &destroyed() const { return m_destroyed; }
302 
306  bool hasBinding() const noexcept { return m_updater.get() != nullptr; }
307 
322  void set(T value)
323  {
324  if (m_updater) {
325  throw ReadOnlyProperty{
326  "Cannot set value on a read-only property. This property likely holds the result of a binding expression."
327  };
328  }
329  setHelper(std::move(value));
330  }
331 
335  T const &get() const
336  {
337  return m_value;
338  }
339 
345  Property<T> &operator=(T const &rhs)
346  {
347  set(std::move(rhs));
348  return *this;
349  }
350 
356  T const &operator()() const
357  {
358  return Property<T>::get();
359  }
360 
361 private:
362  void setHelper(T value)
363  {
364  if (equal_to<T>{}(value, m_value))
365  return;
366 
367  m_valueAboutToChange.emit(m_value, value);
368  m_value = std::move(value);
369  m_valueChanged.emit(m_value);
370  }
371 
372  T m_value;
373  // the signals in a property are mutable, as a property
374  // being "const" should mean that it's value or binding does
375  // not change, not that nobody can listen to it anymore.
376  mutable Signal<const T &, const T &> m_valueAboutToChange;
377  mutable Signal<const T &> m_valueChanged; // By const ref so we can emit the signal for move-only types of T e.g. std::unique_ptr<int>
378 
379  // The PropertyNode needs to be a friend class of the Property, as it needs
380  // access to the m_moved Signal.
381  // The decision to make this Signal private was made after the suggestion by
382  // @jm4R who reported issues with the move constructors noexcept guarantee.
383  // (https://github.com/KDAB/KDBindings/issues/24)
384  // Ideally we would like to figure out a way to remove the moved signal entirely
385  // at some point. However currently it is still needed for Property bindings to
386  // keep track of moved Properties.
387  template<typename PropertyType>
388  friend class Private::PropertyNode;
389  mutable Signal<Property<T> &> m_moved;
390 
391  mutable Signal<> m_destroyed;
392  std::unique_ptr<PropertyUpdater<T>> m_updater;
393 };
394 
398 template<typename T>
399 std::ostream &operator<<(std::ostream &stream, Property<T> const &property)
400 {
401  stream << property.get();
402  return stream;
403 }
404 
409 template<typename T>
410 std::istream &operator>>(std::istream &stream, Property<T> &prop)
411 {
412  T temp;
413  stream >> temp;
414  prop.set(std::move(temp));
415  return stream;
416 }
417 
418 namespace Private {
419 
420 template<typename T>
421 struct is_property_helper : std::false_type {
422 };
423 
424 template<typename T>
425 struct is_property_helper<Property<T>> : std::true_type {
426 };
427 
428 template<typename T>
429 struct is_property : is_property_helper<std::decay_t<T>> {
430 };
431 
432 } // namespace Private
433 
460 } // namespace KDBindings
A property represents a value that can be part of or the result of data binding.
Definition: property.h:138
bool hasBinding() const noexcept
Definition: property.h:306
T const & get() const
Definition: property.h:335
Property(Property< T > const &other)=delete
void reset()
Disconnects the binding from this Property.
Definition: property.h:278
Signal< const T &, const T & > & valueAboutToChange() const
Definition: property.h:289
Property< T > & operator=(T const &rhs)
Definition: property.h:345
void set(T value)
Definition: property.h:322
Property & operator=(std::unique_ptr< UpdaterT > &&updater)
Definition: property.h:255
Property & operator=(Property< T > const &other)=delete
Signal< const T & > & valueChanged() const
Definition: property.h:296
Property(Property< T > &&other) noexcept(std::is_nothrow_move_constructible< T >::value)
Properties are movable.
Definition: property.h:180
Property(std::unique_ptr< UpdaterT > &&updater)
Definition: property.h:238
T const & operator()() const
Definition: property.h:356
Property(T value) noexcept(std::is_nothrow_move_constructible< T >::value)
Definition: property.h:160
Signal & destroyed() const
Definition: property.h:301
Property & operator=(Property< T > &&other) noexcept(std::is_nothrow_move_assignable< T >::value)
Definition: property.h:206
void emit(Args... p) const
Definition: signal.h:568
The main namespace of the KDBindings library.
Definition: binding.h:21
std::istream & operator>>(std::istream &stream, Property< T > &prop)
Definition: property.h:410
std::ostream & operator<<(std::ostream &stream, Property< T > const &property)
Definition: property.h:399
Definition: utils.h:161
An instance of the KDBindings::equal_to struct is used to decide whether two values of type T are equ...
Definition: property.h:78
auto operator()(const T &x, const T &y) const noexcept -> std::enable_if_t< Private::are_equality_comparable_v< T, T >, bool >
Definition: property.h:86
auto operator()(const X &, const Y &) const noexcept -> std::enable_if_t<!Private::are_equality_comparable_v< X, Y >, bool >
Definition: property.h:100

© 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