JeVois  1.20
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). */
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  //! Access to our parameter def
227  ParameterDef<T> const & def() const;
228 
229  //! Change the ParameterDef of this parameter
230  /*! Use with caution, only people who know what they are doing should use this function. Its thread safety and
231  possible side effects are dubious. */
232  void changeParameterDef(ParameterDef<T> const & def);
233 
234  //! Set the parameter's callback
235  /*! The callback function is called each time one tries to change the value of the parameter. Try to avoid using
236  setCallback() so you won't confuse users of your class. In most cases, just use the convenience
237  JEVOIS_DECLARE_PARAMETER_WITH_CALLBACK() macro.
238 
239  The callback should examine the candidate value newval and (1) if it does not like it, throw an
240  std::range_error with a descriptive message of why the value is rejected, (2) otherwise, it is assumed that
241  the value is accepted and the callback can then allocate resources or do other work with that value (the
242  actual modification of the Parameter object is handled upstream and the callback does not need to worry about
243  it: if it returns without throwing, the proposed value will become the new value of the Parameter). The
244  Parameter is locked-up for writing as long as the callback is running, to avoid destruction of the parameter
245  and/or concurrent parameter value changes by several different threads. Thus, callbacks should try to execute
246  quickly, and should not call set(), etc on the parameter as this will always deadlock (get() is allowed if
247  your callback needs to know the current value of the parameter). */
248  void setCallback(std::function<void(T const &)> cb);
249 
250  protected:
251  //! Get the Component to which this Parameter is attached, or nullptr (individual parameters must override)
252  virtual Component const * owner() const = 0;
253 
254  private:
255  void callbackInitCall() override; // Call our callback with the default value in our def
256 
257  std::function<void(T const &)> itsCallback; // optional callback function
258  T itsVal; // The actual value of the parameter
259  ParameterDef<T> const itsDef; // The parameter's definition
260  };
261 
262  // ######################################################################
263  //! A set of Parameters attached to a component
264  /*! This variadic template class is just for the convenience of adding several parameters to a Component in one
265  statement.
266 
267  The way in which we have implemented Parameter in the JeVois framework may seem unorthodox at first, but is the
268  best way we have found so far in terms of minimizing burden when writing new components with lots of
269  parameters. In our earlier framework, the iLab Neuromorphic Vision Toolkit (iNVT) started in 1995, parameters were
270  included in components as member variables. The burden to programmers was so high that often they just did not
271  include parameters and hardwired values instead, just to avoid that burden. The burden comes from the
272  requirements:
273 
274  - we want to be able to support parameters of any type
275  - we want each parameter to have a name, description, default value, specification of valid values
276  - we want parameters to appear in related groups in the help message
277  - we want to support callbacks, i.e., functions that are called when one tries to change the parameter value
278  - we typically want the callback to be a member function of the Component that owns a given parameter,
279  since changing that parameter value will typically trigger some re-organization in that Component (otherwise
280  the callback might not be needed).
281 
282  Possible implementation using class data members for parameters (similar to what we used in iNVT), here shown for
283  a sample int parameter to specify the size of a queue held in a class MyComp that derives from Component:
284 
285  \code
286  ParamDef<int> sizeparamdef("size", "Queue size", 5, Range<int>(1, 100), categ);
287  class MyComp : public jevois::Component
288  {
289  public:
290  Param<int> sizeparam; // ouch
291 
292  void sizeParamCallback(int newval) { myqueue.resize(newval); }
293 
294  MyComp(std::string const & instance) :
295  jevois::Component(instance),
296  sizeparam(sizeparamdef) // ouch
297  {
298  sizeparam.setCallback(&MyComp::sizeParamCallback); // ouch
299  }
300  };
301  \endcode
302 
303  So we basically end up with 3 names that people have no idea what to do with and will just use confusing names for
304  (sizeparamdef, sizeparam, sizeParamCallback), and we have to 1) specify the definition of name, description, etc
305  somewhere using some arbitrary name (here sizeparamdef), then add the member variable for the param to the
306  component using some other name (here sizeparam), then construct the param which would typically require linking
307  it to its definition so we can get the default value and such, and finally hook the callback up (note how MyComp
308  is not fully constructed yet when we construct sizeparam hence referencing sizeParamCallback() at that time is
309  dubious at best). In reality, things are even worse since typically the paramdef, component class declaration, and
310  component implementation, should be in 3 different files.
311 
312  The approach we developed for the Neuromorphic Robotics Toolkit (NRT) and refined for JeVois works as follows:
313 
314  - each parameter is a unique new class type. We create that type once with one name, and it holds the parameter
315  value and the definition data. This is further facilitated by the
316  JEVOIS_DECLARE_PARAMETER(ParamName, ParamType, ...) variadic macro.
317 
318  - for parameters with callbacks, their class type includes a pure virtual onParamChange(param, value) function
319  that will need to be implemented by the host component. This is facilitated by the
320  JEVOIS_DECLARE_PARAMETER_WITH_CALLBACK(ParamName, ParamType, ...) variadic macro. The first argument of
321  onParamChange() is the parameter class type, so that a host component with many parameters will have many
322  different onParamChange() functions, one per parameter that has a callback.
323 
324  - components inherit from their parameters using variadic templates to make inheriting from multiple parameters
325  short and easy.
326 
327  - each parameter exposes simple functions get(), set(), etc (see ParameterCore and ParameterBase). In a component
328  that has many parameters, accessing parameters is achieved by disambiguating on which base class (i.e., which
329  parameter) one wants to access the get(), set(), etc function, which is achieved by calling paramx::get() vs
330  paramy::get(), etc
331 
332  - No need to declare parameter member variables (we inherit from them instead).
333  - No need to do anything at construction of the component.
334  - No need to manually hook the callback function in the component host class to the parameter.
335  - Strong compile-time checking that the programmer did not forget to write the callback function for each
336  parameter that was declared as having a callback.
337  - Only one name used throughout for that parameter and all its associated machinery (definition, callback).
338  - It is easy to write scripts that search the source tree for information about all the parameters of a component,
339  since those are always all specified in the Parameter< ... > inheritance statement.
340 
341  Remaining caveat: it is often desirable to use short names for parameters, such as "size", "length", "dims", etc
342  and those names may clash between several components as the .H files for these components are included when
343  building a more complex component that uses those. This is not an issue for Module, which is a terminal entity and
344  is typically written as a single .C file with no .H file. For components intended for broad use, we currently
345  recommend putting all the parameters in a namespace that is the lowercase version of the component class name.
346 
347  Below is the resulting implementation in Manager.H. We start with declaring the parameters, and we inherit from
348  them when declaring the Manager class:
349 
350  \include snip/manager1.C
351 
352  For the parameters that we declared as having a callback, we further include in our definition of the Manager
353  class overrides for the pure virtual onParamChange() functions that they added to our manager class. Note the
354  signatures of these functions: The first argument is a const reference to the parameter for which this callback
355  is, and its main role is to disambiguate between the different onParamChange() functions a component may have. The
356  second argument is the proposed new parameter value. The onParamChange() function should examine the candidate new
357  value and (1) if it does not like it, throw and std::range_error with a descriptive message of why the value is
358  rejected, (2) otherwise, it is assumed that the value is accepted and the callback can then allocate resources or
359  do other work with that value (the actual modification of the Parameter object is handled upstream and the
360  callback does not need to worry about it: if it returns without throwing, the proposed value will become the new
361  value of the Parameter). The Parameter is locked-up for writing as long as the callback is running, to avoid
362  destruction of the parameter and/or concurrent parameter value changes by several different threads. Thus,
363  callbacks should try to execute quickly, and should not call set(), etc on the parameter as this will always
364  deadlock (get() is allowed if your callback needs to know the current value of the parameter).
365 
366  \include snip/manager2.C
367 
368  There is nothing to do in the constructor, destructor, etc of Manager. The only thing that remains to be done is
369  to implement the onParamChange() functions in Manager.C. Note how we use the JEVOIS_UNUSED_PARAM(x) macro to avoid
370  compiler warnings about our callbacks not using the "param" function parameter:
371 
372  \include snip/manager3.C
373 
374  For completeness, if you wonder what JEVOIS_DECLARE_PARAMETER(ParamName, ParamType, ...) and
375  JEVOIS_DECLARE_PARAMETER_WITH_CALLBACK(ParamName, ParamType, ...) exactly do, here they are in
376  ParameterHelpers.H and reproduced here:
377 
378  \include snip/parametermacros.C
379 
380  Also see Engine.H or the many components in the jevoisbase library. */
381  template <class Param, class ... Tail>
382  class Parameter<Param, Tail ...> : public Param, public Parameter<Tail ...>
383  {
384  static_assert(std::is_base_of<jevois::ParameterBase, Param>::value,
385  "jevois::Parameter<...> template arguments must all be parameters "
386  "(derive from jevois::ParameterBase");
387  };
388 
389  // ######################################################################
390  //! Dynamic parameter added to a component at runtime
391  /*! Dynamic parameters can only be accessed by descriptor at the Component level (using getParamVal(), setParamVal(),
392  etc), since there is no unique class type for them and the owning Component does not inherit from them. Typically
393  for use via Component::addDynamicParameter(). Use with caution. Mainly developed to enable endowing python modules
394  with JeVois parameters. */
395  template <typename T>
397  {
398  public:
399 
400  //! Our type
402 
403  //! Constructor
404  DynamicParameter(Component * comp, ParameterDef<T> const & pdef);
405 
406  //! Destructor
407  virtual ~DynamicParameter();
408 
409  //! Handle to owning component
410  virtual Component const * owner() const override;
411 
412  private:
413  Component * itsComp;
414  };
415 
416  /*! @} */ // **********************************************************************
417 
418 } // namespace jevois
419 
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:181
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::DynamicParameter::owner
virtual const Component * owner() const override
Handle to owning component.
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::DynamicParameter::~DynamicParameter
virtual ~DynamicParameter()
Destructor.
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::DynamicParameter
Dynamic parameter added to a component at runtime.
Definition: Parameter.H:396
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::ParameterCore::def
const ParameterDef< T > & def() const
Access to our parameter def.
jevois::ParameterBase::freeze
void freeze()
Freeze this parameter, it becomes read-only and will not show up in the help message.
jevois::DynamicParameter::type
DynamicParameter< T > type
Our type.
Definition: Parameter.H:401
jevois::DynamicParameter::DynamicParameter
DynamicParameter(Component *comp, ParameterDef< T > const &pdef)
Constructor.