1 //
2 // Copyright 2011,2014-2016 Ettus Research
3 // Copyright 2018 Ettus Research, a National Instruments Company
4 // Copyright 2019 Ettus Research, a National Instruments Brand
5 //
6 // SPDX-License-Identifier: GPL-3.0-or-later
7 //
8
9 #pragma once
10
11 #include <uhd/config.hpp>
12 #include <uhd/utils/noncopyable.hpp>
13 #include <functional>
14 #include <memory>
15 #include <string>
16 #include <typeindex>
17 #include <vector>
18
19 namespace uhd {
20
21 /*!
22 * A templated property interface for holding the state
23 * associated with a property in a uhd::property_tree
24 * and registering callbacks when that value changes.
25 *
26 * A property is defined to have two separate vales:
27 * - Desired value: Value requested by the user
28 * - Coerced value: Value that was actually possible
29 * given HW and other requirements
30 *
31 * By default, the desired and coerced values are
32 * identical as long as the property is not coerced.
33 * A property can be coerced in two way:
34 * 1. Using a coercer: A callback function that takes
35 * in a desired value and produces a coerced value.
36 * A property must have *exactly one* coercer.
37 * 2. Manual coercion: Manually calling the set_coerced
38 * API function to coerce the value of the property. In
39 * order to use manual coercion, the property must be
40 * created with the MANUAL_COERCE mode.
41 * If the coerce mode for a property is AUTO_COERCE then
42 * it always has a coercer. If the set_coercer API is
43 * never used, then the default coercer is used which
44 * simply set the coerced value to the desired value.
45 *
46 * It is possible to get notified every time the desired
47 * or coerced values of a property potentially change
48 * using subscriber callbacks. Every property can have
49 * zero or more desired and coerced subscribers.
50 *
51 * If storing the property readback state in software is
52 * not appropriate (for example if it needs to be queried
53 * from hardware) then it is possible to use a publisher
54 * callback to get the value of the property. Calling
55 * get on the property will always call the publisher and
56 * the cached desired and coerced values are updated only
57 * using set* calls. A property must have *at most one*
58 * publisher. It is legal to have both a coercer
59 * and publisher for a property but the only way to access
60 * the desired and coerced values in that case would be by
61 * notification using the desired and coerced subscribers.
62 * Publishers are useful for creating read-only properties.
63 *
64 * Requirements for the template type T:
65 * - T must have a copy constructor
66 * - T must have an assignment operator
67 */
68 template <typename T>
69 class property : uhd::noncopyable
70 {
71 public:
72 typedef std::function<void(const T&)> subscriber_type;
73 typedef std::function<T(void)> publisher_type;
74 typedef std::function<T(const T&)> coercer_type;
75
76 virtual ~property<T>(void) = 0;
77
78 /*!
79 * Register a coercer into the property.
80 * A coercer is a callback function that updates the
81 * coerced value of a property.
82 *
83 * Only one coercer may be registered per property.
84 * \param coercer the coercer callback function
85 * \return a reference to this property for chaining
86 * \throws uhd::assertion_error if called more than once
87 */
88 virtual property<T>& set_coercer(const coercer_type& coercer) = 0;
89
90 /*!
91 * Register a publisher into the property.
92 * A publisher is a callback function the provides the value
93 * for a property.
94 *
95 * Only one publisher may be registered per property.
96 * \param publisher the publisher callback function
97 * \return a reference to this property for chaining
98 * \throws uhd::assertion_error if called more than once
99 */
100 virtual property<T>& set_publisher(const publisher_type& publisher) = 0;
101
102 /*!
103 * Register a subscriber into the property.
104 * All desired subscribers are called when the desired value
105 * potentially changes.
106 *
107 * Once a subscriber is registered, it cannot be unregistered.
108 * \param subscriber the subscriber callback function
109 * \return a reference to this property for chaining
110 */
111 virtual property<T>& add_desired_subscriber(const subscriber_type& subscriber) = 0;
112
113 /*!
114 * Register a subscriber into the property.
115 * All coerced subscribers are called when the coerced value
116 * potentially changes.
117 *
118 * Once a subscriber is registered, it cannot be unregistered.
119 * \param subscriber the subscriber callback function
120 * \return a reference to this property for chaining
121 */
122 virtual property<T>& add_coerced_subscriber(const subscriber_type& subscriber) = 0;
123
124 /*!
125 * Update calls all subscribers w/ the current value.
126 *
127 * \return a reference to this property for chaining
128 * \throws uhd::assertion_error
129 */
130 virtual property<T>& update(void) = 0;
131
132 /*!
133 * Set the new value and call all the necessary subscribers.
134 * Order of operations:
135 * - The desired value of the property is updated
136 * - All desired subscribers are called
137 * - If coerce mode is AUTO then the coercer is called
138 * - If coerce mode is AUTO then all coerced subscribers are called
139 *
140 * \param value the new value to set on this property
141 * \return a reference to this property for chaining
142 * \throws uhd::assertion_error
143 */
144 virtual property<T>& set(const T& value) = 0;
145
146 /*!
147 * Set a coerced value and call all subscribers.
148 * The coercer is bypassed, and the specified value is
149 * used as the coerced value. All coerced subscribers
150 * are called. This function can only be used when the
151 * coerce mode is set to MANUAL_COERCE.
152 *
153 * \param value the new value to set on this property
154 * \return a reference to this property for chaining
155 * \throws uhd::assertion_error
156 */
157 virtual property<T>& set_coerced(const T& value) = 0;
158
159 /*!
160 * Get the current value of this property.
161 * The publisher (when provided) yields the value,
162 * otherwise an internal coerced value is returned.
163 *
164 * \return the current value in the property
165 * \throws uhd::assertion_error
166 */
167 virtual const T get(void) const = 0;
168
169 /*!
170 * Get the current desired value of this property.
171 *
172 * \return the current desired value in the property
173 * \throws uhd::assertion_error
174 */
175 virtual const T get_desired(void) const = 0;
176
177 /*!
178 * A property is empty if it has never been set.
179 * A property with a publisher is never empty.
180 *
181 * \return true if the property is empty
182 */
183 virtual bool empty(void) const = 0;
184 };
185
186 template <typename T>
~property(void)187 property<T>::~property(void)
188 {
189 /* NOP */
190 }
191
192 /*!
193 * FS Path: A glorified string with path manipulations.
194 * Inspired by boost filesystem path, but without the dependency.
195 *
196 * Notice: we do not declare UHD_API on the whole structure
197 * because MSVC will do weird things with std::string and linking.
198 */
199 struct fs_path : std::string
200 {
201 UHD_API fs_path(void);
202 UHD_API fs_path(const char*);
203 UHD_API fs_path(const std::string&);
204 UHD_API std::string leaf(void) const;
205 UHD_API fs_path branch_path(void) const;
206 };
207
208 UHD_API fs_path operator/(const fs_path&, const fs_path&);
209 UHD_API fs_path operator/(const fs_path&, size_t);
210
211 /*!
212 * The property tree provides a file system structure for accessing properties.
213 */
214 class UHD_API property_tree : uhd::noncopyable
215 {
216 public:
217 typedef std::shared_ptr<property_tree> sptr;
218
219 enum coerce_mode_t { AUTO_COERCE, MANUAL_COERCE };
220
221 virtual ~property_tree(void) = 0;
222
223 //! Create a new + empty property tree
224 static sptr make(void);
225
226 //! Get a subtree with a new root starting at path
227 virtual sptr subtree(const fs_path& path) const = 0;
228
229 //! Remove a property or directory (recursive)
230 virtual void remove(const fs_path& path) = 0;
231
232 //! True if the path exists in the tree
233 virtual bool exists(const fs_path& path) const = 0;
234
235 //! Get an iterable to all things in the given path
236 virtual std::vector<std::string> list(const fs_path& path) const = 0;
237
238 //! Create a new property entry in the tree
239 template <typename T>
240 property<T>& create(const fs_path& path, coerce_mode_t coerce_mode = AUTO_COERCE);
241
242 //! Get access to a property in the tree
243 template <typename T>
244 property<T>& access(const fs_path& path);
245
246 //! Pop a property off the tree, and returns the property
247 template <typename T>
248 std::shared_ptr<property<T>> pop(const fs_path& path);
249
250 private:
251 //! Internal pop function
252 virtual std::shared_ptr<void> _pop(const fs_path& path) = 0;
253
254 //! Internal create property with wild-card type
255 virtual void _create(const fs_path& path,
256 const std::shared_ptr<void>& prop,
257 std::type_index prop_type) = 0;
258
259 //! Internal access property with wild-card type
260 virtual std::shared_ptr<void>& _access(const fs_path& path) const = 0;
261
262 //! Internal access property with wild-card type but with type verification
263 virtual std::shared_ptr<void>& _access_with_type_check(
264 const fs_path& path, std::type_index expected_prop_type) const = 0;
265 };
266
267 } // namespace uhd
268
269 #include <uhd/property_tree.ipp>
270