JeVois  1.0
JeVois Smart Embedded Machine Vision Toolkit
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
26 
27 namespace jevois
28 {
29  /*! \defgroup parameter Parameter-related classes and functions
30  \ingroup component
31 
32  The concept of parameter in the JeVois framework embodies wrappers around a single value of any type, with
33  associated documentation (description), default values, possible specification of valid values, accessor functions
34  to obtain or change the value, and optional callback functions that are triggered when the value is
35  changed. Parameters are intended to be used in objects that inherit from Component. The goal of parameters is to
36  expose parameters of a given vision algorithm in such a way that any piece of code that is using that algorithm
37  will automatically inherit and expose these parameters.
38 
39  How to explore this documentation module:
40 
41  - Start with a bit of general philosophy about components and parameters: Component
42  - Then understand how one may specify valid values for parameters: \ref validvalues
43  - Then have a look at how one may define the name, type, description, default value, category, and optionally
44  valid values for a parameter: ParameterDef
45  - Then you are ready for Parameter
46 
47  The other classes in this documentation modules are mainly for support of the above ones. */
48 
49  /*! @{ */ // **********************************************************************
50 
51  // ######################################################################
52  //! ParameterSummary provides a summary about a parameter
54  {
55  public:
56  //! Descriptor. This is the name of the parameter, qualified by a chain of component names
57  std::string descriptor;
58 
59  //! Plain name of the parameter
60  std::string name;
61 
62  //! Description of the parameter
63  std::string description;
64 
65  //! Parameter value type, as a string
66  std::string valuetype;
67 
68  //! Default value of the parameter, as a string
69  std::string defaultvalue;
70 
71  //! Current value of the parameter, as a string
72  std::string value;
73 
74  //! Description of the parameter's valid values specification, as a string
75  std::string validvalues;
76 
77  //! Category of the parameter, as a string
78  std::string category;
79 
80  //! Category description
81  std::string categorydescription;
82 
83  //! Flag that indicates whether parameter is frozen
84  bool frozen;
85  };
86 
87  // ######################################################################
88  //! Base class for Parameter
89  /*! This exposes the string interface to the Parameter while derived template classes will expose the
90  value-based interface. */
92  {
93  public:
94  //! Constructor
95  ParameterBase();
96 
97  //! Destructor, will remove the parameter from its owner component
98  virtual ~ParameterBase();
99 
100  //! Get the parameter name
101  virtual std::string const & name() const = 0;
102 
103  //! Get the parameter fully-qualified name, aka descriptor, including names of owning Component and all parents
104  virtual std::string descriptor() const = 0;
105 
106  //! Set the value from a string representation of it
107  /*! @throws std::range_error if the given string cannot be converted to a Parameter value, or the value is invalid
108  according to our valid values spec or rejected by the Parameter's callback (if any). */
109  virtual void strset(std::string const & valstring) = 0;
110 
111  //! Get the value as a string
112  virtual std::string const strget() const = 0;
113 
114  //! Get summary info about this parameter
115  virtual ParameterSummary const summary() const = 0;
116 
117  //! Freeze this parameter, it becomes read-only and will not show up in the help message
118  void freeze();
119 
120  //! Unfreeze this parameter, it becomes read-write and will show up in the help message
121  void unFreeze();
122 
123  protected:
124  mutable boost::shared_mutex itsMutex; //!< Mutex to protect the parameter value
125  volatile bool itsFrozen; //!< When true, parameter is frozen (read-only, does not show up in help message)
126  bool itsVirgin; //!< Param has not yet been explicitly set, need to call the callback (if any) at init time
127 
128  private:
129  friend class ParameterRegistry; // allow the registry to call our callbacks with defaut val
130 
131  // Call our callback with our current value, used at init() time
132  /* We cannot call the callback during parameter construction because the host Component object is not fully
133  constructed yet (since it derives from its parameters). Thus, for all parameters that have a callback, we will
134  call that callback once during init(), unless it is already called during command-line parsing. */
135  virtual void callbackInitCall() = 0;
136  };
137 } // namespace jevois
138 
139 // Include ParameterDef now (it needs to know about ParameterBase):
141 
142 namespace jevois
143 {
144  class Component;
145 
146  // ######################################################################
147  //! A changeable parameter for a Component, core class
148  /*! Parameters are used to expose user-configurable settings for a Component. They can be specified from the command
149  line and will be set by the time Component::postInit() is called on the Component which owns the
150  Parameter. A Parameter may have a callback function which is invoked each time an attempt is made to change the
151  Parameter value. */
152  template <typename T>
154  {
155  public:
156  //! Constructor
157  /*! \param def A pointer to the definition for this parameter (given by a ParameterDef). */
158  ParameterCore(ParameterDef<T> const & def);
159 
160  //! Destructor
161  virtual ~ParameterCore();
162 
163  //! Get the parameter name
164  virtual std::string const & name() const override;
165 
166  //! Get the parameter fully-qualified name, aka descriptor
167  virtual std::string descriptor() const override;
168 
169  //! Get the value of this Parameter
170  T get() const;
171 
172  //! Set the value of this Parameter
173  /*! Will throw jevois::exception::ParameterException if the new value is not accepted, in which case the old value
174  will remain in the Parameter. */
175  void set(T const & newVal);
176 
177  //! Set the value from a string representation of it
178  /*! @throws std::range_error if the given string cannot be converted to a valid Parameter value. */
179  virtual void strset(std::string const & valstring) override;
180 
181  //! Get the value as a string representation of it
182  virtual std::string const strget() const override;
183 
184  //! Get summary info about this parameter
185  virtual ParameterSummary const summary() const override;
186 
187  //! Change the ParameterDef of this parameter
188  /*! Use with caution, only people who know what they are doing should use this function. Its thread safety and
189  possible side effects are dubious. */
190  void changeParameterDef(ParameterDef<T> const & def);
191 
192  //! Set the parameter's callback
193  /*! The callback function is called each time one tries to change the value of the parameter. Try to avoid using
194  setCallback() so you won't confuse users of your class. In most cases, just use the convenience
195  JEVOIS_DECLARE_PARAMETER_WITH_CALLBACK() macro.
196 
197  The callback should examine the candidate value newval and (1) if it does not like it, throw and
198  std::range_error with a descriptive message of why the value is rejected, (2) otherwise, it is assumed that
199  the value is accepted and the callback can then allocate resources or do other work with that value (the
200  actual modification of the Parameter object is handled upstream and the callback does not need to worry about
201  it: if it returns without throwing, the proposed value will become the new value of the Parameter). The
202  Parameter is locked-up for writing as long as the callback is running, to avoid destruction of the parameter
203  and/or concurrent parameter value changes by several different threads. Thus, callbacks should try to execute
204  quickly, and should not call set(), etc on the parameter as this will always deadlock (get() is allowed if
205  your callback needs to know the current value of the parameter). */
206  void setCallback(std::function<void(T const &)> cb);
207 
208  protected:
209  //! Get the Component to which this Parameter is attached, or nullptr (individual parameters must override)
210  virtual Component const * owner() const = 0;
211 
212  private:
213  void callbackInitCall() override; // Call our callback with the default value in our def
214 
215  std::function<void(T const &)> itsCallback; // optional callback function
216  T itsVal; // The actual value of the parameter
217  ParameterDef<T> const itsDef; // The parameter's definition
218  };
219 
220  // ######################################################################
221  //! A set of Parameters attached to a component
222  /*! This variadic template class is just for the convenience of adding several parameters to a Component in one
223  statement.
224 
225  The way in which we have implemented Parameter in the JeVois framework may seem unorthodox at first, but is the
226  best way we have found so far in terms of minimizing burden when writing new components with lots of
227  parameters. In our earlier framework, the iLab Neuromorphic Vision Toolkit (iNVT) started in 1995, parameters were
228  included in components as member variables. The burden to programmers was so high that often they just did not
229  include parameters and hardwired values instead, just to avoid that burden. The burden comes from the
230  requirements:
231 
232  - we want to be able to support parameters of any type
233  - we want each parameter to have a name, description, default value, specification of valid values
234  - we want parameters to appear in related groups in the help message
235  - we want to support callbacks, i.e., functions that are called when one tries to change the parameter value
236  - we typically want the callback to be a member function of the Component that owns a given parameter,
237  since changing that parameter value will typically trigger some re-organization in that Component (otherwise
238  the callback might not be needed).
239 
240  Possible implementation using class data members for parameters (similar to what we used in iNVT), here shown for
241  a sample int parameter to specify the size of a queue held in a class MyComp that derives from Component:
242 
243  \code
244  ParamDef<int> sizeparamdef("size", "Queue size", 5, Range<int>(1, 100), categ);
245  class MyComp : public jevois::Component
246  {
247  public:
248  Param<int> sizeparam; // ouch
249 
250  void sizeParamCallback(int newval) { myqueue.resize(newval); }
251 
252  MyComp(std::string const & instance) :
253  jevois::Component(instance),
254  sizeparam(sizeparamdef) // ouch
255  {
256  sizeparam.setCallback(&MyComp::sizeParamCallback); // ouch
257  }
258  };
259  \endcode
260 
261  So we basically end up with 3 names that people have no idea what to do with and will just use confusing names for
262  (sizeparamdef, sizeparam, sizeParamCallback), and we have to 1) specify the definition of name, description, etc
263  somewhere using some arbitrary name (here sizeparamdef), then add the member variable for the param to the
264  component using some other name (here sizeparam), then construct the param which would typically require linking
265  it to its definition so we can get the default value and such, and finally hook the callback up (note how MyComp
266  is not fully constructed yet when we construct sizeparam hence referencing sizeParamCallback() at that time is
267  dubious at best). In reality, things are even worse since typically the paramdef, component class declaration, and
268  component implementation, should be in 3 different files.
269 
270  The approach we developed for the Neuromorphic Robotics Toolkit (NRT) and refined for JeVois works as follows:
271 
272  - each parameter is a unique new class type. We create that type once with one name, and it holds the parameter
273  value and the definition data. This is further facilitated by the
274  JEVOIS_DECLARE_PARAMETER(ParamName, ParamType, ...) variadic macro.
275 
276  - for parameters with callbacks, their class type includes a pure virtual onParamChange(param, value) function
277  that will need to be implemented by the host component. This is facilitated by the
278  JEVOIS_DECLARE_PARAMETER_WITH_CALLBACK(ParamName, ParamType, ...) variadic macro. The first argument of
279  onParamChange() is the parameter class type, so that a host component with many parameters will have many
280  different onParamChange() functions, one per parameter that has a callback.
281 
282  - components inherit from their parameters using variadic templates to make inheriting from multiple parameters
283  short and easy.
284 
285  - each parameter exposes simple functions get(), set(), etc (see ParameterCore and ParameterBase). In a component
286  that has many parameters, accessing parameters is achieved by disambiguating on which base class (i.e., which
287  parameter) one wants to access the get(), set(), etc function, which is achieved by calling paramx::get() vs
288  paramy::get(), etc
289 
290  - No need to declare parameter member variables (we inherit from them instead).
291  - No need to do anything at construction of the component.
292  - No need to manually hook the callback function in the component host class to the parameter.
293  - Strong compile-time checking that the programmer did not forget to write the callback function for each
294  parameter that was declared as having a callback.
295  - Only one name used throughout for that parameter and all its associated machinery (definition, callback).
296  - It is easy to write scripts that search the source tree for information about all the parameters of a component,
297  since those are always all specified in the Parameter< ... > inheritance statement.
298 
299  Remaining caveat: it is often desirable to use short names for parameters, such as "size", "length", "dims", etc
300  and those names may clash between several components as the .H files for these components are included when
301  building a more complex component that uses those. This is not an issue for Module, which is a terminal entity and
302  is typically written as a single .C file with no .H file. For components intended for broad use, we currently
303  recommend putting all the parameters in a namespace that is the lowercase version of the component class name.
304 
305  Below is the resulting implementation in Manager.H. We start with declaring the parameters, and we inherit from
306  them when declaring the Manager class:
307 
308  \include snip/manager1.C
309 
310  For the parameters that we declared as having a callback, we further include in our definition of the Manager
311  class overrides for the pure virtual onParamChange() functions that they added to our manager class. Note the
312  signatures of these functions: The first argument is a const reference to the parameter for which this callback
313  is, and its main role is to disambiguate between the different onParamChange() functions a component may have. The
314  second argument is the proposed new parameter value. The onParamChange() function should examine the candidate new
315  value and (1) if it does not like it, throw and std::range_error with a descriptive message of why the value is
316  rejected, (2) otherwise, it is assumed that the value is accepted and the callback can then allocate resources or
317  do other work with that value (the actual modification of the Parameter object is handled upstream and the
318  callback does not need to worry about it: if it returns without throwing, the proposed value will become the new
319  value of the Parameter). The Parameter is locked-up for writing as long as the callback is running, to avoid
320  destruction of the parameter and/or concurrent parameter value changes by several different threads. Thus,
321  callbacks should try to execute quickly, and should not call set(), etc on the parameter as this will always
322  deadlock (get() is allowed if your callback needs to know the current value of the parameter).
323 
324  \include snip/manager2.C
325 
326  There is nothing to do in the constructor, destructor, etc of Manager. The only thing that remains to be done is
327  to implement the onParamChange() functions in Manager.C. Note how we use the JEVOIS_UNUSED_PARAM(x) macro to avoid
328  compiler warnings about our callbacks not using the "param" function parameter:
329 
330  \include snip/manager3.C
331 
332  For completeness, if you wonder what JEVOIS_DECLARE_PARAMETER(ParamName, ParamType, ...) and
333  JEVOIS_DECLARE_PARAMETER_WITH_CALLBACK(ParamName, ParamType, ...) exactly do, here they are in
334  ParameterHelpers.H and reproduced here:
335 
336  \include snip/parametermacros.C
337 
338  Also see Engine.H or the many components in the jevoisbase library. */
339  template <class Param, class ... Tail>
340  class Parameter<Param, Tail ...> : public Param, public Parameter<Tail ...>
341  {
342  static_assert(std::is_base_of<jevois::ParameterBase, Param>::value,
343  "jevois::Parameter<...> template arguments must all be parameters "
344  "(derive from jevois::ParameterBase");
345  };
346 
347  //! Convenience macro to define a Parameter type
348  /*! \def JEVOIS_DECLARE_PARAMETER(ParamName, ParamType, ...)
349  \hideinitializer
350 
351  ParamName is the name chosen for the parameter. A new class type will be created with that name, so it must be
352  syntactically correct as a class name. ParamType is the type of the parameter value. The remaining arguments are
353  passed to the constructor of ParameterDef */
354 
355  //! Convenience macro to define a Parameter type
356  /*! \def JEVOIS_DECLARE_PARAMETER_WITH_CALLBACK(ParamName, ParamType, ...)
357  \hideinitializer
358 
359  ParamName is the name chosen for the parameter. A new class type will be created with that name, so it must be
360  syntactically correct as a class name. ParamType is the type of the parameter value. The remaining arguments are
361  passed to the constructor of ParameterDef<T> with T=ParamType.
362 
363  In this version with callback, a pure virtual method is included in the new class that is defined, with the
364  following signature:
365 
366  \code
367  virtual void onParamChange(ParamName const & param, ParamType const & newval) = 0;
368  \endcode
369 
370  The host class (typically, a Component) that inherits from Parameter<ParamName> must implement an override of this
371  function. */
372 
373  /*! @} */ // **********************************************************************
374 
375 } // namespace jevois
376 
std::string name
Plain name of the parameter.
Definition: Parameter.H:60
std::string validvalues
Description of the parameter's valid values specification, as a string.
Definition: Parameter.H:75
std::string description
Description of the parameter.
Definition: Parameter.H:63
A changeable parameter for a Component, core class.
Definition: Parameter.H:153
void unFreeze()
Unfreeze this parameter, it becomes read-write and will show up in the help message.
Definition: ParameterImpl.H:41
virtual std::string const strget() const override
Get the value as a string representation of it.
std::string descriptor
Descriptor. This is the name of the parameter, qualified by a chain of component names.
Definition: Parameter.H:57
Generic variadic template class template definition for Component Parameters.
ParameterBase()
Constructor.
Definition: ParameterImpl.H:25
ParameterSummary provides a summary about a parameter.
Definition: Parameter.H:53
std::string value
Current value of the parameter, as a string.
Definition: Parameter.H:72
std::string defaultvalue
Default value of the parameter, as a string.
Definition: Parameter.H:69
virtual std::string const & name() const override
Get the parameter name.
Definition: ParameterImpl.H:60
A Parameter Definition.
Definition: ParameterDef.H:88
bool itsVirgin
Param has not yet been explicitly set, need to call the callback (if any) at init time...
Definition: Parameter.H:126
A component of a model hierarchy.
Definition: Component.H:176
virtual void strset(std::string const &valstring) override
Set the value from a string representation of it.
virtual std::string descriptor() const =0
Get the parameter fully-qualified name, aka descriptor, including names of owning Component and all p...
virtual Component const * owner() const =0
Get the Component to which this Parameter is attached, or nullptr (individual parameters must overrid...
void changeParameterDef(ParameterDef< T > const &def)
Change the ParameterDef of this parameter.
std::string valuetype
Parameter value type, as a string.
Definition: Parameter.H:66
virtual ParameterSummary const summary() const =0
Get summary info about this parameter.
virtual ParameterSummary const summary() const override
Get summary info about this parameter.
virtual std::string descriptor() const override
Get the parameter fully-qualified name, aka descriptor.
Definition: ParameterImpl.H:65
bool frozen
Flag that indicates whether parameter is frozen.
Definition: Parameter.H:84
void freeze()
Freeze this parameter, it becomes read-only and will not show up in the help message.
Definition: ParameterImpl.H:37
Base class for Parameter.
Definition: Parameter.H:91
void setCallback(std::function< void(T const &)> cb)
Set the parameter's callback.
Definition: ParameterImpl.H:81
virtual void strset(std::string const &valstring)=0
Set the value from a string representation of it.
void set(T const &newVal)
Set the value of this Parameter.
Definition: ParameterImpl.H:89
virtual ~ParameterCore()
Destructor.
Definition: ParameterImpl.H:52
virtual std::string const strget() const =0
Get the value as a string.
std::string category
Category of the parameter, as a string.
Definition: Parameter.H:78
boost::shared_mutex itsMutex
Mutex to protect the parameter value.
Definition: Parameter.H:124
A simple registry of all parameters associated with a Component.
std::string categorydescription
Category description.
Definition: Parameter.H:81
volatile bool itsFrozen
When true, parameter is frozen (read-only, does not show up in help message)
Definition: Parameter.H:125
ParameterCore(ParameterDef< T > const &def)
Constructor.
Definition: ParameterImpl.H:46
virtual std::string const & name() const =0
Get the parameter name.
virtual ~ParameterBase()
Destructor, will remove the parameter from its owner component.
Definition: ParameterImpl.H:30