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