JeVois  1.23
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Loading...
Searching...
No Matches
Parameter.H
Go to the documentation of this file.
1// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2//
3// JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2016 by Laurent Itti, the University of Southern
4// California (USC), and iLab at USC. See http://iLab.usc.edu and http://jevois.org for information about this project.
5//
6// This file is part of the JeVois Smart Embedded Machine Vision Toolkit. This program is free software; you can
7// redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
8// Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
10// License for more details. You should have received a copy of the GNU General Public License along with this program;
11// if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12//
13// Contact information: Laurent Itti - 3641 Watt Way, HNB-07A - Los Angeles, CA 90089-2520 - USA.
14// Tel: +1 213 740 3527 - itti@pollux.usc.edu - http://iLab.usc.edu - http://jevois.org
15// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16/*! \file */
17
18// This code is inspired by the Neuromorphic Robotics Toolkit (http://nrtkit.org)
19
20#pragma once
21
22#include <boost/thread.hpp>
23
24// Get our helpers
25#include <jevois/Component/details/ParameterHelpers.H>
26
27/*! \defgroup parameter Parameter-related classes and functions
28 \ingroup component
29
30 The concept of parameter in the JeVois framework embodies wrappers around a single value of any type, with
31 associated documentation (description), default values, possible specification of valid values, accessor functions
32 to obtain or change the value, and optional callback functions that are triggered when the value is
33 changed. Parameters are intended to be used in objects that inherit from Component. The goal of parameters is to
34 expose parameters of a given vision algorithm in such a way that any piece of code that is using that algorithm will
35 automatically inherit and expose these parameters.
36
37 How to explore this documentation module:
38
39 - Start with a bit of general philosophy about components and parameters: \ref Component
40 - Then understand how one may specify valid values for parameters: \ref validvalues
41 - Then have a look at how one may define the name, type, description, default value, category, and optionally
42 valid values for a parameter: \ref ParameterDef<T>
43 - Then you are ready for [Parameter](classjevois_1_1Parameter_3_01Param_00_01Tail_01_8_8_8_01_4.html)
44
45 The other classes in this documentation module are mainly for support of the above ones.
46
47 Convenience macro to define a Parameter type
48 --------------------------------------------
49
50 \code
51 JEVOIS_DECLARE_PARAMETER(ParamName, ParamType, ...)
52 \endcode
53
54 ParamName is the name chosen for the parameter. A new class type will be created with that name, so it must be
55 syntactically correct as a class name. ParamType is the type of the parameter value. The remaining arguments are
56 passed to the constructor of jevois::ParameterDef<T> with T=ParamType.
57
58 Convenience macro to define a Parameter type, with callback
59 -----------------------------------------------------------
60
61 \code
62 JEVOIS_DECLARE_PARAMETER_WITH_CALLBACK(ParamName, ParamType, ...)
63 \endcode
64
65 ParamName is the name chosen for the parameter. A new class type will be created with that name, so it must be
66 syntactically correct as a class name. ParamType is the type of the parameter value. The remaining arguments are
67 passed to the constructor of jevois::ParameterDef<T> with T=ParamType.
68
69 In this version with callback, a pure virtual method is included in the new class that is defined, with the
70 following signature:
71
72 \code
73 virtual void onParamChange(ParamName const & param, ParamType const & newval) = 0;
74 \endcode
75
76 The host class (typically, a Component) that inherits from Parameter<ParamName> must implement an override of this
77 function. A compile-time error will be issued if that override has not been implemented. */
78
79namespace jevois
80{
81 // ######################################################################
82 //! ParameterSummary provides a summary about a parameter
83 /*! This is used mainly for graphical interfaces. \ingroup parameter */
85 {
86 public:
87 //! Descriptor. This is the name of the parameter, qualified by a chain of component names
88 std::string descriptor;
89
90 //! Plain name of the parameter
91 std::string name;
92
93 //! Description of the parameter
94 std::string description;
95
96 //! Parameter value type, as a string
97 std::string valuetype;
98
99 //! Default value of the parameter, as a string
100 std::string defaultvalue;
101
102 //! Current value of the parameter, as a string
103 std::string value;
104
105 //! Description of the parameter's valid values specification, as a string
106 std::string validvalues;
107
108 //! Category of the parameter, as a string
109 std::string category;
110
111 //! Category description
113
114 //! Flag that indicates whether parameter is frozen
115 bool frozen;
116
117 //! Flag that indicates whether parameter is hidden
118 bool hidden;
119 };
120
121 // ######################################################################
122 //! Base class for Parameter
123 /*! This exposes the string interface to the Parameter while derived template classes will expose the
124 value-based interface. \ingroup parameter */
126 {
127 public:
128 //! Constructor
130
131 //! Destructor, will remove the parameter from its owner component
132 virtual ~ParameterBase();
133
134 //! Get the parameter name
135 virtual std::string const & name() const = 0;
136
137 //! Get the parameter fully-qualified name, aka descriptor, including names of owning Component and all parents
138 virtual std::string descriptor() const = 0;
139
140 //! Set the value from a string representation of it
141 /*! @throws std::range_error if the given string cannot be converted to a Parameter value, or the value is invalid
142 according to our valid values spec or rejected by the Parameter's callback (if any). */
143 virtual void strset(std::string const & valstring) = 0;
144
145 //! Get the value as a string
146 virtual std::string const strget() const = 0;
147
148 //! Get summary info about this parameter
149 virtual ParameterSummary const summary() const = 0;
150
151 //! Freeze or un-freeze a parameter; frozen parameters cannot be set(), but get() is still allowed
152 void freeze(bool doit);
153
154 //! Returns whether parameter is frozen
155 bool frozen() const;
156
157 //! Hide or un-hide a parameter; hidden params will not show up in GUI or help message, but still work normally
158 void hide(bool doit);
159
160 //! Returns whether parameter is hidden
161 bool hidden() const;
162
163 //! Reset this parameter to its default value
164 virtual void reset() = 0;
165
166 protected:
167 mutable boost::shared_mutex itsMutex; //!< Mutex to protect the parameter value
168 volatile bool itsFrozen; //!< When true, parameter is frozen (read-only)
169 volatile bool itsHidden; //!< When true, parameter is hidden (not visible in GUI or help message)
170 bool itsVirgin; //!< Param has not yet been explicitly set, need to call the callback (if any) at init time
171
172 private:
173 friend class ParameterRegistry; // allow the registry to call our callbacks with defaut val
174
175 // Call our callback with our current value, used at init() time
176 /* We cannot call the callback during parameter construction because the host Component object is not fully
177 constructed yet (since it derives from its parameters). Thus, for all parameters that have a callback, we will
178 call that callback once during init(), unless it is already called during command-line parsing. */
179 virtual void callbackInitCall() = 0;
180 };
181
182 // Closing and then re-opening the namespace somehow makes doxygen ignore all classes defined below...
183#ifndef JEVOIS_DOXYGEN
184
185} // namespace jevois
186
187// Include ParameterDef now (it needs to know about ParameterBase):
189
190namespace jevois
191{
192 class Component;
193
194#endif // JEVOIS_DOXYGEN
195
196 // ######################################################################
197 //! A changeable parameter for a Component, core class
198 /*! Parameters are used to expose user-configurable settings for a Component. They can be specified from the command
199 line and will be set by the time Component::postInit() is called on the Component which owns the
200 Parameter. A Parameter may have a callback function which is invoked each time an attempt is made to change the
201 Parameter value. \ingroup parameter */
202 template <typename T>
204 {
205 public:
206 //! Constructor
207 /*! \param def A pointer to the definition for this parameter (given by a ParameterDef). */
209
210 //! Destructor
211 virtual ~ParameterCore();
212
213 //! Get the parameter name
214 virtual std::string const & name() const override;
215
216 //! Get the parameter fully-qualified name, aka descriptor
217 virtual std::string descriptor() const override;
218
219 //! Get the value of this Parameter
220 T get() const;
221
222 //! Set the value of this Parameter
223 /*! Will throw jevois::exception::ParameterException if the new value is not accepted, in which case the old value
224 will remain in the Parameter. */
225 void set(T const & newVal);
226
227 //! Set the value from a string representation of it
228 /*! @throws std::range_error if the given string cannot be converted to a valid Parameter value. */
229 virtual void strset(std::string const & valstring) override;
230
231 //! Get the value as a string representation of it
232 virtual std::string const strget() const override;
233
234 //! Get summary info about this parameter
235 virtual ParameterSummary const summary() const override;
236
237 //! Reset this parameter to its default value
238 virtual void reset() override;
239
240 //! Access to our parameter def
241 ParameterDef<T> const & def() const;
242
243 //! Change the ParameterDef of this parameter
244 /*! Use with caution, only people who know what they are doing should use this function. Its thread safety and
245 possible side effects are dubious. */
247
248 //! Set the parameter's callback
249 /*! The callback function is called each time one tries to change the value of the parameter. Try to avoid using
250 setCallback() so you won't confuse users of your class. In most cases, just use the convenience
251 JEVOIS_DECLARE_PARAMETER_WITH_CALLBACK() macro.
252
253 The callback should examine the candidate value newval and (1) if it does not like it, throw an
254 std::range_error with a descriptive message of why the value is rejected, (2) otherwise, it is assumed that
255 the value is accepted and the callback can then allocate resources or do other work with that value (the
256 actual modification of the Parameter object is handled upstream and the callback does not need to worry about
257 it: if it returns without throwing, the proposed value will become the new value of the Parameter). The
258 Parameter is locked-up for writing as long as the callback is running, to avoid destruction of the parameter
259 and/or concurrent parameter value changes by several different threads. Thus, callbacks should try to execute
260 quickly, and should not call set(), etc on the parameter as this will always deadlock (get() is allowed if
261 your callback needs to know the current value of the parameter). */
262 void setCallback(std::function<void(T const &)> cb);
263
264 protected:
265 //! Get the Component to which this Parameter is attached, or nullptr (individual parameters must override)
266 virtual Component const * owner() const = 0;
267
268 private:
269 void callbackInitCall() override; // Call our callback with the default value in our def
270
271 std::function<void(T const &)> itsCallback; // optional callback function
272 T itsVal; // The actual value of the parameter
273 ParameterDef<T> const itsDef; // The parameter's definition
274 };
275
276 // ######################################################################
277 //! A set of Parameters attached to a component
278 /*! This variadic template class is just for the convenience of adding several parameters to a Component in one
279 statement.
280
281 The way in which we have implemented Parameter in the JeVois framework may seem unorthodox at first, but is the
282 best way we have found so far in terms of minimizing burden when writing new components with lots of
283 parameters. In our earlier framework, the iLab Neuromorphic Vision Toolkit (iNVT) started in 1995, parameters were
284 included in components as member variables. The burden to programmers was so high that often they just did not
285 include parameters and hardwired values instead, just to avoid that burden. The burden comes from the
286 requirements:
287
288 - we want to be able to support parameters of any type
289 - we want each parameter to have a name, description, default value, specification of valid values
290 - we want parameters to appear in related groups in the help message
291 - we want to support callbacks, i.e., functions that are called when one tries to change the parameter value
292 - we typically want the callback to be a member function of the Component that owns a given parameter,
293 since changing that parameter value will typically trigger some re-organization in that Component (otherwise
294 the callback might not be needed).
295
296 Possible implementation using class data members for parameters (similar to what we used in iNVT), here shown for
297 a sample int parameter to specify the size of a queue held in a class MyComp that derives from Component:
298
299 \code
300 ParamDef<int> sizeparamdef("size", "Queue size", 5, Range<int>(1, 100), categ);
301 class MyComp : public jevois::Component
302 {
303 public:
304 Param<int> sizeparam; // ouch
305
306 void sizeParamCallback(int newval) { myqueue.resize(newval); }
307
308 MyComp(std::string const & instance) :
309 jevois::Component(instance),
310 sizeparam(sizeparamdef) // ouch
311 {
312 sizeparam.setCallback(&MyComp::sizeParamCallback); // ouch
313 }
314 };
315 \endcode
316
317 So we basically end up with 3 names that people have no idea what to do with and will just use confusing names for
318 (sizeparamdef, sizeparam, sizeParamCallback), and we have to 1) specify the definition of name, description, etc
319 somewhere using some arbitrary name (here sizeparamdef), then add the member variable for the param to the
320 component using some other name (here sizeparam), then construct the param which would typically require linking
321 it to its definition so we can get the default value and such, and finally hook the callback up (note how MyComp
322 is not fully constructed yet when we construct sizeparam hence referencing sizeParamCallback() at that time is
323 dubious at best). In reality, things are even worse since typically the paramdef, component class declaration, and
324 component implementation, should be in 3 different files.
325
326 \note <em>``There are only two hard things in Computer Science: cache invalidation and naming things.'' <b>-- Phil
327 Karlton</b></em>
328
329 The approach we developed for the Neuromorphic Robotics Toolkit (NRT) and refined for JeVois works as follows:
330
331 - each parameter is a unique new class type. We create that type once with one name, and it holds the parameter
332 value and the definition data. This is further facilitated by the
333 JEVOIS_DECLARE_PARAMETER(ParamName, ParamType, ...) variadic macro.
334
335 - for parameters with callbacks, their class type includes a pure virtual onParamChange(param, value) function
336 that will need to be implemented by the host component. This is facilitated by the
337 JEVOIS_DECLARE_PARAMETER_WITH_CALLBACK(ParamName, ParamType, ...) variadic macro. The first argument of
338 onParamChange() is the parameter class type, so that a host component with many parameters will have many
339 different onParamChange() functions, one per parameter that has a callback.
340
341 - components inherit from their parameters using variadic templates to make inheriting from multiple parameters
342 short and easy.
343
344 - each parameter exposes simple functions get(), set(), etc (see ParameterCore and ParameterBase). In a component
345 that has many parameters, accessing parameters is achieved by disambiguating on which base class (i.e., which
346 parameter) one wants to access the get(), set(), etc function, which is achieved by calling paramx::get() vs
347 paramy::get(), etc
348
349 - No need to declare parameter member variables (we inherit from them instead).
350 - No need to do anything at construction of the component.
351 - No need to manually hook the callback function in the component host class to the parameter.
352 - When implementing the callback, all members of the host class are available (since the host class inherits
353 from the Parameter). This is critical since usually callbacks are implemented so that the host component
354 will take some action when a parameter value is changed, e.g., reconfigure itself in some way.
355 - Strong compile-time checking that the programmer did not forget to write the callback function for each
356 parameter that was declared as having a callback.
357 - Only one name used throughout for that parameter and all its associated machinery (definition, callback).
358 - It is easy to write scripts that search the source tree for information about all the parameters of a component,
359 since those are always all specified in the Parameter< ... > inheritance statement.
360
361 Remaining caveat: it is often desirable to use short names for parameters, such as "size", "length", "dims",
362 "threshold", etc and those names may clash between several components as the .H files for these components are
363 included when building a more complex component or system that uses those. This is not an issue for Module, which
364 is a terminal entity and is typically written as a single .C file with no .H file. For components intended for
365 broad use, we currently recommend putting all the parameters in a namespace that is the lowercase version of the
366 component class name.
367
368 Below is the resulting implementation in Manager.H. We start with declaring the parameters, and we inherit from
369 them when declaring the Manager class. We declare the parameters in a new namespace \a manager to avoid name
370 clashes with other parameters of other components:
371
372 \include snip/manager1.C
373
374 For the parameters that we declared as having a callback, we further include in our definition of the Manager
375 class overrides for the pure virtual onParamChange() functions that they added to our manager class. Note the
376 signatures of these functions: The first argument is a const reference to the parameter for which this callback
377 is, and its main role is to disambiguate between the different onParamChange() functions a component may have. The
378 second argument is the proposed new parameter value. The onParamChange() function should examine the candidate new
379 value and (1) if it does not like it, throw and std::range_error with a descriptive message of why the value is
380 rejected, (2) otherwise, it is assumed that the value is accepted and the callback can then allocate resources or
381 do other work with that value (the actual modification of the Parameter object is handled upstream and the
382 callback does not need to worry about it: if it returns without throwing, the proposed value will become the new
383 value of the Parameter). The Parameter is locked-up for writing as long as the callback is running, to avoid
384 destruction of the parameter and/or concurrent parameter value changes by several different threads. Thus,
385 callbacks should try to execute quickly, and should not call set(), etc on the parameter as this will always
386 deadlock (get() is allowed if your callback needs to know the current value of the parameter).
387
388 \include snip/manager2.C
389
390 There is nothing to do in the constructor, destructor, etc of Manager. The only thing that remains to be done is
391 to implement the onParamChange() functions in Manager.C. Note how we do not give a name to the first argument
392 (which is a ref to the parameter itself; that first argument is necessary to disambiguate among the various
393 onParamChange() functions for different parameters, but usually we only care about the new value and do not need
394 the handle to the parameter). This is to avoid compiler warnings about our callback not using that first argument:
395
396 \include snip/manager3.C
397
398 The host component can use a parameter's member functions by calling them with the parameter name as a prefix
399 (this prefix is basically selecting on which base class we want to run the given function). For example, in
400 Manager, we take some action if the \p help parameter has been set at the command line, and then we freeze it. All
401 member functions defined in ParameterBase and in ParameterCore<T> are available on every Parameter (get(), set(),
402 strget(), strset(), name(), descriptor(), summary(), freeze(), etc):
403
404 \include snip/manager4.C
405
406 For completeness, if you wonder what JEVOIS_DECLARE_PARAMETER(ParamName, ParamType, ...) and
407 JEVOIS_DECLARE_PARAMETER_WITH_CALLBACK(ParamName, ParamType, ...) exactly do, here they are in
408 ParameterHelpers.H and reproduced here:
409
410 \include snip/parametermacros.C
411
412 Also see Engine.H or the many components in the jevoisbase library. \ingroup parameter */
413 template <class Param, class ... Tail>
414 class Parameter<Param, Tail ...> : public Param, public Parameter<Tail ...>
415 {
416 static_assert(std::is_base_of<jevois::ParameterBase, Param>::value,
417 "jevois::Parameter<...> template arguments must all be parameters "
418 "(derive from jevois::ParameterBase");
419 };
420
421 // ######################################################################
422 //! Dynamic parameter added to a component at runtime
423 /*! Dynamic parameters can only be accessed by descriptor at the Component level (using getParamVal(), setParamVal(),
424 etc), since there is no unique class type for them and the owning Component does not inherit from them. Typically
425 for use via Component::addDynamicParameter(). Use with caution. Mainly developed to enable endowing python modules
426 with JeVois parameters. \ingroup parameter */
427 template <typename T>
429 {
430 public:
431
432 //! Our type
434
435 //! Constructor
437
438 //! Destructor
440
441 //! Handle to owning component
442 virtual Component const * owner() const override;
443
444 private:
445 Component * itsComp;
446 };
447
448} // namespace jevois
449
A component of a model hierarchy.
Definition Component.H:182
Dynamic parameter added to a component at runtime.
Definition Parameter.H:429
DynamicParameter(Component *comp, ParameterDef< T > const &pdef)
Constructor.
DynamicParameter< T > type
Our type.
Definition Parameter.H:433
virtual Component const * owner() const override
Handle to owning component.
virtual ~DynamicParameter()
Destructor.
Base class for Parameter.
Definition Parameter.H:126
virtual std::string const & name() const =0
Get the parameter name.
volatile bool itsFrozen
When true, parameter is frozen (read-only)
Definition Parameter.H:168
virtual void reset()=0
Reset this parameter to its default value.
virtual std::string descriptor() const =0
Get the parameter fully-qualified name, aka descriptor, including names of owning Component and all p...
virtual void strset(std::string const &valstring)=0
Set the value from a string representation of it.
volatile bool itsHidden
When true, parameter is hidden (not visible in GUI or help message)
Definition Parameter.H:169
bool itsVirgin
Param has not yet been explicitly set, need to call the callback (if any) at init time.
Definition Parameter.H:170
boost::shared_mutex itsMutex
Mutex to protect the parameter value.
Definition Parameter.H:167
void hide(bool doit)
Hide or un-hide a parameter; hidden params will not show up in GUI or help message,...
virtual ~ParameterBase()
Destructor, will remove the parameter from its owner component.
bool hidden() const
Returns whether parameter is hidden.
virtual ParameterSummary const summary() const =0
Get summary info about this parameter.
virtual std::string const strget() const =0
Get the value as a string.
bool frozen() const
Returns whether parameter is frozen.
void freeze(bool doit)
Freeze or un-freeze a parameter; frozen parameters cannot be set(), but get() is still allowed.
ParameterBase()
Constructor.
A changeable parameter for a Component, core class.
Definition Parameter.H:204
virtual std::string const strget() const override
Get the value as a string representation of it.
virtual void strset(std::string const &valstring) override
Set the value from a string representation of it.
ParameterCore(ParameterDef< T > const &def)
Constructor.
ParameterDef< T > const & def() const
Access to our parameter def.
void set(T const &newVal)
Set the value of this Parameter.
virtual ~ParameterCore()
Destructor.
void changeParameterDef(ParameterDef< T > const &def)
Change the ParameterDef of this parameter.
virtual std::string const & name() const override
Get the parameter name.
virtual std::string descriptor() const override
Get the parameter fully-qualified name, aka descriptor.
virtual void reset() override
Reset this parameter to its default value.
T get() const
Get the value of this Parameter.
virtual ParameterSummary const summary() const override
Get summary info about this parameter.
virtual Component const * owner() const =0
Get the Component to which this Parameter is attached, or nullptr (individual parameters must overrid...
void setCallback(std::function< void(T const &)> cb)
Set the parameter's callback.
A Parameter Definition.
A simple registry of all parameters associated with a Component.
ParameterSummary provides a summary about a parameter.
Definition Parameter.H:85
std::string name
Plain name of the parameter.
Definition Parameter.H:91
std::string category
Category of the parameter, as a string.
Definition Parameter.H:109
std::string categorydescription
Category description.
Definition Parameter.H:112
std::string descriptor
Descriptor. This is the name of the parameter, qualified by a chain of component names.
Definition Parameter.H:88
std::string valuetype
Parameter value type, as a string.
Definition Parameter.H:97
std::string value
Current value of the parameter, as a string.
Definition Parameter.H:103
std::string validvalues
Description of the parameter's valid values specification, as a string.
Definition Parameter.H:106
std::string defaultvalue
Default value of the parameter, as a string.
Definition Parameter.H:100
bool frozen
Flag that indicates whether parameter is frozen.
Definition Parameter.H:115
bool hidden
Flag that indicates whether parameter is hidden.
Definition Parameter.H:118
std::string description
Description of the parameter.
Definition Parameter.H:94
Main namespace for all JeVois classes and functions.
Definition Concepts.dox:2