TeiaCareSDK  v0.1.0
TeiaCareSDK is a collection of reusable C++ components
Loading...
Searching...
No Matches
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
26namespace 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 */
44template <std::equality_comparable T>
45class observable // : private non_copyable, private non_moveable
46{
47public:
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 */
156 {
157 set(t);
158 return *this;
159 }
160
161protected:
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
188private:
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}
Thread safe, blocking queue.
Utility class to observe a value with a user provided callback.
observable(T &&t, const std::function< void(T)> &callback)
Constructor.
~observable()=default
Destructor.
void callback_enabled(bool is_enabled)
Enable or disable the callback temporarily.
observable(const T &t, const std::function< void(T)> &callback)
Constructor.
T value() const
Return the observed value.
bool callback_enabled() const
Check if the callback is currently enabled.
observable & operator=(T &&t)
Update the observed value.
observable & operator=(const T &t)
Update the observed value.