TeiaCareSDK  v0.1.0
TeiaCareSDK is a collection of reusable C++ components
observable.hpp
1 // Copyright 2024 TeiaCare
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #pragma once
16 
17 #include <teiacare/sdk/non_copyable.hpp>
18 #include <teiacare/sdk/non_moveable.hpp>
19 
20 #include <atomic>
21 #include <concepts>
22 #include <functional>
23 #include <mutex>
24 #include <type_traits>
25 
26 namespace tc::sdk
27 {
28 /*!
29  * \class observable
30  * \brief Utility class to observe a value with a user provided callback
31  *
32  * \tparam T Observed item type
33  * \tparam CallbackT User defined callback type
34  *
35  * The callback is invoked every time the observed value is updated.
36  * The observable callback receives as a parameter the updated observed value.
37  * Thread safe.
38  *
39  * \note T must be Equality Comparable, i.e. it must implement the equality comparison operator:
40  * \code
41  * bool operator==(T const&) const { }
42  * \endcode
43  */
44 template <std::equality_comparable T>
45 class observable // : private non_copyable, private non_moveable
46 {
47 public:
48  /*!
49  * \brief Constructor
50  * \tparam T initial value for the observed value
51  * \tparam CallbackT callable object that is invoked every time the value is changed
52  *
53  * Creates a tc::sdk::observable instance.
54  */
55  explicit observable(const T& t, const std::function<void(T)>& callback)
56  : _value{t}
57  , _callback{callback}
58  , _is_callback_enabled{true}
59  {
60  // static_assert(std::is_invocable_v<CallbackT, T>, "CallbackT must be an invocable object that accepts T");
61  // static_assert(std::is_same_v<void, std::invoke_result_t<CallbackT, T>>, "CallbackT must return void");
62  }
63 
64  /*!
65  * \brief Constructor
66  * \tparam T initial value for the observed value
67  * \tparam CallbackT callable object that is invoked every time the value is changed
68  *
69  * Creates a tc::sdk::observable instance.
70  */
71  explicit observable(T&& t, const std::function<void(T)>& callback)
72  : _value{std::forward<T>(t)}
73  , _callback{callback}
74  , _is_callback_enabled{true}
75  {
76  // static_assert(std::is_invocable_v<CallbackT, T>, "CallbackT must be an invocable object that accepts T");
77  // static_assert(std::is_same_v<void, std::invoke_result_t<CallbackT, T>>, "CallbackT must return void");
78  }
79 
80  /*!
81  * \brief Destructor
82  *
83  * Destructs this.
84  */
85  ~observable() = default;
86 
87  /*!
88  * \brief Check if the callback is currently enabled
89  * \return true if the callback is currently enabled
90  */
91  bool callback_enabled() const
92  {
93  std::lock_guard lock(_mutex);
94  return _is_callback_enabled.load();
95  }
96 
97  /*!
98  * \brief Enable or disable the callback temporarily
99  * \param is_enabled enable or disable callback
100  */
101  void callback_enabled(bool is_enabled)
102  {
103  std::lock_guard lock(_mutex);
104  _is_callback_enabled.store(is_enabled);
105  }
106 
107  /*!
108  * \brief Return the observed value
109  * \return const observed value
110  */
111  operator T const() const
112  {
113  return value();
114  }
115 
116  /*!
117  * \brief Return the observed value
118  * \return observed value
119  */
120  operator T()
121  {
122  return value();
123  }
124 
125  /*!
126  * \brief Return the observed value
127  * \return the observed value
128  */
129  T value() const
130  {
131  std::lock_guard lock(_mutex);
132  return _value;
133  }
134 
135  /*!
136  * \brief Update the observed value
137  * \tparam t the updated value
138  * \return a reference to self
139  *
140  * If the parameter value is different from the observed one the callback is invoked.
141  */
143  {
144  set(std::forward<T>(t));
145  return *this;
146  }
147 
148  /*!
149  * \brief Update the observed value
150  * \tparam t the updated value
151  * \return a reference to self
152  *
153  * If the parameter value is different from the observed one the callback is invoked.
154  */
155  observable& operator=(const T& t)
156  {
157  set(t);
158  return *this;
159  }
160 
161 protected:
162  void set(const T& value)
163  {
164  std::lock_guard lock(_mutex);
165 
166  if (value == _value)
167  return;
168 
169  _value = value;
170 
171  if (_is_callback_enabled)
172  _callback(_value);
173  }
174 
175  void set(T&& value)
176  {
177  std::lock_guard lock(_mutex);
178 
179  if (value == _value)
180  return;
181 
182  _value = value;
183 
184  if (_is_callback_enabled)
185  _callback(_value);
186  }
187 
188 private:
189  mutable std::mutex _mutex;
190  T _value;
191  const std::function<void(T)> _callback;
192  std::atomic_bool _is_callback_enabled;
193 };
194 
195 }
Utility class to observe a value with a user provided callback.
Definition: observable.hpp:46
observable & operator=(T &&t)
Update the observed value.
Definition: observable.hpp:142
observable(T &&t, const std::function< void(T)> &callback)
Constructor.
Definition: observable.hpp:71
~observable()=default
Destructor.
void callback_enabled(bool is_enabled)
Enable or disable the callback temporarily.
Definition: observable.hpp:101
observable(const T &t, const std::function< void(T)> &callback)
Constructor.
Definition: observable.hpp:55
observable & operator=(const T &t)
Update the observed value.
Definition: observable.hpp:155
T value() const
Return the observed value.
Definition: observable.hpp:129
bool callback_enabled() const
Check if the callback is currently enabled.
Definition: observable.hpp:91