JeVois  1.21
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
118 // ######################################################################
119 //! Base class for Parameter
120 /*! This exposes the string interface to the Parameter while derived template classes will expose the
121 value-based interface. \ingroup parameter */
123 {
124 public:
125 //! Constructor
127
128 //! Destructor, will remove the parameter from its owner component
129 virtual ~ParameterBase();
130
131 //! Get the parameter name
132 virtual std::string const & name() const = 0;
133
134 //! Get the parameter fully-qualified name, aka descriptor, including names of owning Component and all parents
135 virtual std::string descriptor() const = 0;
136
137 //! Set the value from a string representation of it
138 /*! @throws std::range_error if the given string cannot be converted to a Parameter value, or the value is invalid
139 according to our valid values spec or rejected by the Parameter's callback (if any). */
140 virtual void strset(std::string const & valstring) = 0;
141
142 //! Get the value as a string
143 virtual std::string const strget() const = 0;
144
145 //! Get summary info about this parameter
146 virtual ParameterSummary const summary() const = 0;
147
148 //! Freeze or un-freeze a parameter; frozen parameters cannot be set(), but get() is still allowed
149 void freeze(bool doit);
150
151 //! Returns whether parameter is frozen
152 bool frozen() const;
153
154 //! Reset this parameter to its default value
155 virtual void reset() = 0;
156
157 protected:
158 mutable boost::shared_mutex itsMutex; //!< Mutex to protect the parameter value
159 volatile bool itsFrozen; //!< When true, parameter is frozen (read-only, does not show up in help message)
160 bool itsVirgin; //!< Param has not yet been explicitly set, need to call the callback (if any) at init time
161
162 private:
163 friend class ParameterRegistry; // allow the registry to call our callbacks with defaut val
164
165 // Call our callback with our current value, used at init() time
166 /* We cannot call the callback during parameter construction because the host Component object is not fully
167 constructed yet (since it derives from its parameters). Thus, for all parameters that have a callback, we will
168 call that callback once during init(), unless it is already called during command-line parsing. */
169 virtual void callbackInitCall() = 0;
170 };
171
172 // Closing and then re-opening the namespace somehow makes doxygen ignore all classes defined below...
173#ifndef JEVOIS_DOXYGEN
174
175} // namespace jevois
176
177// Include ParameterDef now (it needs to know about ParameterBase):
179
180namespace jevois
181{
182 class Component;
183
184#endif // JEVOIS_DOXYGEN
185
186 // ######################################################################
187 //! A changeable parameter for a Component, core class
188 /*! Parameters are used to expose user-configurable settings for a Component. They can be specified from the command
189 line and will be set by the time Component::postInit() is called on the Component which owns the
190 Parameter. A Parameter may have a callback function which is invoked each time an attempt is made to change the
191 Parameter value. \ingroup parameter */
192 template <typename T>
194 {
195 public:
196 //! Constructor
197 /*! \param def A pointer to the definition for this parameter (given by a ParameterDef). */
199
200 //! Destructor
201 virtual ~ParameterCore();
202
203 //! Get the parameter name
204 virtual std::string const & name() const override;
205
206 //! Get the parameter fully-qualified name, aka descriptor
207 virtual std::string descriptor() const override;
208
209 //! Get the value of this Parameter
210 T get() const;
211
212 //! Set the value of this Parameter
213 /*! Will throw jevois::exception::ParameterException if the new value is not accepted, in which case the old value
214 will remain in the Parameter. */
215 void set(T const & newVal);
216
217 //! Set the value from a string representation of it
218 /*! @throws std::range_error if the given string cannot be converted to a valid Parameter value. */
219 virtual void strset(std::string const & valstring) override;
220
221 //! Get the value as a string representation of it
222 virtual std::string const strget() const override;
223
224 //! Get summary info about this parameter
225 virtual ParameterSummary const summary() const override;
226
227 //! Reset this parameter to its default value
228 virtual void reset() override;
229
230 //! Access to our parameter def
231 ParameterDef<T> const & def() const;
232
233 //! Change the ParameterDef of this parameter
234 /*! Use with caution, only people who know what they are doing should use this function. Its thread safety and
235 possible side effects are dubious. */
237
238 //! Set the parameter's callback
239 /*! The callback function is called each time one tries to change the value of the parameter. Try to avoid using
240 setCallback() so you won't confuse users of your class. In most cases, just use the convenience
241 JEVOIS_DECLARE_PARAMETER_WITH_CALLBACK() macro.
242
243 The callback should examine the candidate value newval and (1) if it does not like it, throw an
244 std::range_error with a descriptive message of why the value is rejected, (2) otherwise, it is assumed that
245 the value is accepted and the callback can then allocate resources or do other work with that value (the
246 actual modification of the Parameter object is handled upstream and the callback does not need to worry about
247 it: if it returns without throwing, the proposed value will become the new value of the Parameter). The
248 Parameter is locked-up for writing as long as the callback is running, to avoid destruction of the parameter
249 and/or concurrent parameter value changes by several different threads. Thus, callbacks should try to execute
250 quickly, and should not call set(), etc on the parameter as this will always deadlock (get() is allowed if
251 your callback needs to know the current value of the parameter). */
252 void setCallback(std::function<void(T const &)> cb);
253
254 protected:
255 //! Get the Component to which this Parameter is attached, or nullptr (individual parameters must override)
256 virtual Component const * owner() const = 0;
257
258 private:
259 void callbackInitCall() override; // Call our callback with the default value in our def
260
261 std::function<void(T const &)> itsCallback; // optional callback function
262 T itsVal; // The actual value of the parameter
263 ParameterDef<T> const itsDef; // The parameter's definition
264 };
265
266 // ######################################################################
267 //! A set of Parameters attached to a component
268 /*! This variadic template class is just for the convenience of adding several parameters to a Component in one
269 statement.
270
271 The way in which we have implemented Parameter in the JeVois framework may seem unorthodox at first, but is the
272 best way we have found so far in terms of minimizing burden when writing new components with lots of
273 parameters. In our earlier framework, the iLab Neuromorphic Vision Toolkit (iNVT) started in 1995, parameters were
274 included in components as member variables. The burden to programmers was so high that often they just did not
275 include parameters and hardwired values instead, just to avoid that burden. The burden comes from the
276 requirements:
277
278 - we want to be able to support parameters of any type
279 - we want each parameter to have a name, description, default value, specification of valid values
280 - we want parameters to appear in related groups in the help message
281 - we want to support callbacks, i.e., functions that are called when one tries to change the parameter value
282 - we typically want the callback to be a member function of the Component that owns a given parameter,
283 since changing that parameter value will typically trigger some re-organization in that Component (otherwise
284 the callback might not be needed).
285
286 Possible implementation using class data members for parameters (similar to what we used in iNVT), here shown for
287 a sample int parameter to specify the size of a queue held in a class MyComp that derives from Component:
288
289 \code
290 ParamDef<int> sizeparamdef("size", "Queue size", 5, Range<int>(1, 100), categ);
291 class MyComp : public jevois::Component
292 {
293 public:
294 Param<int> sizeparam; // ouch
295
296 void sizeParamCallback(int newval) { myqueue.resize(newval); }
297
298 MyComp(std::string const & instance) :
299 jevois::Component(instance),
300 sizeparam(sizeparamdef) // ouch
301 {
302 sizeparam.setCallback(&MyComp::sizeParamCallback); // ouch
303 }
304 };
305 \endcode
306
307 So we basically end up with 3 names that people have no idea what to do with and will just use confusing names for
308 (sizeparamdef, sizeparam, sizeParamCallback), and we have to 1) specify the definition of name, description, etc
309 somewhere using some arbitrary name (here sizeparamdef), then add the member variable for the param to the
310 component using some other name (here sizeparam), then construct the param which would typically require linking
311 it to its definition so we can get the default value and such, and finally hook the callback up (note how MyComp
312 is not fully constructed yet when we construct sizeparam hence referencing sizeParamCallback() at that time is
313 dubious at best). In reality, things are even worse since typically the paramdef, component class declaration, and
314 component implementation, should be in 3 different files.
315
316 \note <em>``There are only two hard things in Computer Science: cache invalidation and naming things.'' <b>-- Phil
317 Karlton</b></em>
318
319 The approach we developed for the Neuromorphic Robotics Toolkit (NRT) and refined for JeVois works as follows:
320
321 - each parameter is a unique new class type. We create that type once with one name, and it holds the parameter
322 value and the definition data. This is further facilitated by the
323 JEVOIS_DECLARE_PARAMETER(ParamName, ParamType, ...) variadic macro.
324
325 - for parameters with callbacks, their class type includes a pure virtual onParamChange(param, value) function
326 that will need to be implemented by the host component. This is facilitated by the
327 JEVOIS_DECLARE_PARAMETER_WITH_CALLBACK(ParamName, ParamType, ...) variadic macro. The first argument of
328 onParamChange() is the parameter class type, so that a host component with many parameters will have many
329 different onParamChange() functions, one per parameter that has a callback.
330
331 - components inherit from their parameters using variadic templates to make inheriting from multiple parameters
332 short and easy.
333
334 - each parameter exposes simple functions get(), set(), etc (see ParameterCore and ParameterBase). In a component
335 that has many parameters, accessing parameters is achieved by disambiguating on which base class (i.e., which
336 parameter) one wants to access the get(), set(), etc function, which is achieved by calling paramx::get() vs
337 paramy::get(), etc
338
339 - No need to declare parameter member variables (we inherit from them instead).
340 - No need to do anything at construction of the component.
341 - No need to manually hook the callback function in the component host class to the parameter.
342 - When implementing the callback, all members of the host class are available (since the host class inherits
343 from the Parameter). This is critical since usually callbacks are implemented so that the host component
344 will take some action when a parameter value is changed, e.g., reconfigure itself in some way.
345 - Strong compile-time checking that the programmer did not forget to write the callback function for each
346 parameter that was declared as having a callback.
347 - Only one name used throughout for that parameter and all its associated machinery (definition, callback).
348 - It is easy to write scripts that search the source tree for information about all the parameters of a component,
349 since those are always all specified in the Parameter< ... > inheritance statement.
350
351 Remaining caveat: it is often desirable to use short names for parameters, such as "size", "length", "dims",
352 "threshold", etc and those names may clash between several components as the .H files for these components are
353 included when building a more complex component or system that uses those. This is not an issue for Module, which
354 is a terminal entity and is typically written as a single .C file with no .H file. For components intended for
355 broad use, we currently recommend putting all the parameters in a namespace that is the lowercase version of the
356 component class name.
357
358 Below is the resulting implementation in Manager.H. We start with declaring the parameters, and we inherit from
359 them when declaring the Manager class. We declare the parameters in a new namespace \a manager to avoid name
360 clashes with other parameters of other components:
361
362 \include snip/manager1.C
363
364 For the parameters that we declared as having a callback, we further include in our definition of the Manager
365 class overrides for the pure virtual onParamChange() functions that they added to our manager class. Note the
366 signatures of these functions: The first argument is a const reference to the parameter for which this callback
367 is, and its main role is to disambiguate between the different onParamChange() functions a component may have. The
368 second argument is the proposed new parameter value. The onParamChange() function should examine the candidate new
369 value and (1) if it does not like it, throw and std::range_error with a descriptive message of why the value is
370 rejected, (2) otherwise, it is assumed that the value is accepted and the callback can then allocate resources or
371 do other work with that value (the actual modification of the Parameter object is handled upstream and the
372 callback does not need to worry about it: if it returns without throwing, the proposed value will become the new
373 value of the Parameter). The Parameter is locked-up for writing as long as the callback is running, to avoid
374 destruction of the parameter and/or concurrent parameter value changes by several different threads. Thus,
375 callbacks should try to execute quickly, and should not call set(), etc on the parameter as this will always
376 deadlock (get() is allowed if your callback needs to know the current value of the parameter).
377
378 \include snip/manager2.C
379
380 There is nothing to do in the constructor, destructor, etc of Manager. The only thing that remains to be done is
381 to implement the onParamChange() functions in Manager.C. Note how we do not give a name to the first argument
382 (which is a ref to the parameter itself; that first argument is necessary to disambiguate among the various
383 onParamChange() functions for different parameters, but usually we only care about the new value and do not need
384 the handle to the parameter). This is to avoid compiler warnings about our callback not using that first argument:
385
386 \include snip/manager3.C
387
388 The host component can use a parameter's member functions by calling them with the parameter name as a prefix
389 (this prefix is basically selecting on which base class we want to run the given function). For example, in
390 Manager, we take some action if the \p help parameter has been set at the command line, and then we freeze it. All
391 member functions defined in ParameterBase and in ParameterCore<T> are available on every Parameter (get(), set(),
392 strget(), strset(), name(), descriptor(), summary(), freeze(), etc):
393
394 \include snip/manager4.C
395
396 For completeness, if you wonder what JEVOIS_DECLARE_PARAMETER(ParamName, ParamType, ...) and
397 JEVOIS_DECLARE_PARAMETER_WITH_CALLBACK(ParamName, ParamType, ...) exactly do, here they are in
398 ParameterHelpers.H and reproduced here:
399
400 \include snip/parametermacros.C
401
402 Also see Engine.H or the many components in the jevoisbase library. \ingroup parameter */
403 template <class Param, class ... Tail>
404 class Parameter<Param, Tail ...> : public Param, public Parameter<Tail ...>
405 {
406 static_assert(std::is_base_of<jevois::ParameterBase, Param>::value,
407 "jevois::Parameter<...> template arguments must all be parameters "
408 "(derive from jevois::ParameterBase");
409 };
410
411 // ######################################################################
412 //! Dynamic parameter added to a component at runtime
413 /*! Dynamic parameters can only be accessed by descriptor at the Component level (using getParamVal(), setParamVal(),
414 etc), since there is no unique class type for them and the owning Component does not inherit from them. Typically
415 for use via Component::addDynamicParameter(). Use with caution. Mainly developed to enable endowing python modules
416 with JeVois parameters. \ingroup parameter */
417 template <typename T>
419 {
420 public:
421
422 //! Our type
424
425 //! Constructor
427
428 //! Destructor
430
431 //! Handle to owning component
432 virtual Component const * owner() const override;
433
434 private:
435 Component * itsComp;
436 };
437
438} // namespace jevois
439
A component of a model hierarchy.
Definition Component.H:182
Dynamic parameter added to a component at runtime.
Definition Parameter.H:419
DynamicParameter(Component *comp, ParameterDef< T > const &pdef)
Constructor.
DynamicParameter< T > type
Our type.
Definition Parameter.H:423
virtual Component const * owner() const override
Handle to owning component.
virtual ~DynamicParameter()
Destructor.
Base class for Parameter.
Definition Parameter.H:123
virtual std::string const & name() const =0
Get the parameter name.
volatile bool itsFrozen
When true, parameter is frozen (read-only, does not show up in help message)
Definition Parameter.H:159
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.
bool itsVirgin
Param has not yet been explicitly set, need to call the callback (if any) at init time.
Definition Parameter.H:160
boost::shared_mutex itsMutex
Mutex to protect the parameter value.
Definition Parameter.H:158
virtual ~ParameterBase()
Destructor, will remove the parameter from its owner component.
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:194
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
std::string description
Description of the parameter.
Definition Parameter.H:94
Main namespace for all JeVois classes and functions.
Definition Concepts.dox:2