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