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