Bayeux  3.4.1
Core Foundation library for SuperNEMO
reflective_command-inl.h
Go to the documentation of this file.
1 /* Author(s) : Francois Mauger <mauger@lpccaen.in2p3.fr>
3  * Creation date : 2016-07-12
4  * Last modified : 2016-07-12
5  */
6 
7 #ifndef DATATOOLS_UI_REFLECTIVE_COMMAND_INL_H
8 #define DATATOOLS_UI_REFLECTIVE_COMMAND_INL_H
9 
10 // This project:
12 
13 // Third Party:
14 // - Boost:
15 #include <boost/lexical_cast.hpp>
16 #include <boost/program_options/options_description.hpp>
17 // - Camp:
18 #include <camp/classget.hpp>
19 #include <camp/userobject.hpp>
20 #include <camp/value.hpp>
21 #include <camp/args.hpp>
22 #include <camp/class.hpp>
23 #include <camp/errors.hpp>
24 #include <camp/enum.hpp>
25 #include <camp/type.hpp>
26 
27 // This project:
30 #include <datatools/ui/wrappers.h>
32 #include <datatools/units.h>
33 
34 namespace datatools {
35 
36  namespace ui {
37 
38  template <class Type>
40  : target_command<Type>()
41  {
42  return;
43  }
44 
45  template <class Type>
47  const std::string & func_name_,
48  const std::string & name_,
49  const std::string & description_,
50  const version_id & vid_)
51  : target_command<Type>(target_,
52  (name_.empty() ? func_name_ : name_),
53  description_, vid_)
54  {
55  set_func_name(func_name_);
56  return;
57  }
58 
59  template <class Type>
61  {
64  }
65  return;
66  }
67 
68  template <class Type>
69  void reflective_command<Type>::set_func_name(const std::string & func_name_)
70  {
71  _func_name_ = func_name_;
72  return;
73  }
74 
75  template <class Type>
77  {
79  this->base_command::_init(config_);
80  bool forbid_short_ui_options = false;
81  std::string classname(datatools::detail::reflection::guid<Type>());
82  if (datatools::logger::is_debug(logging)) {
83  DT_LOG_DEBUG(logging, "classname = '" << classname << "'");
84  DT_LOG_DEBUG(logging, "List of registered metaclasses:");
85  for (std::size_t i = 0; i < camp::classCount(); i++) {
86  const camp::Class & metaclass = camp::classByIndex(i);
87  DT_LOG_DEBUG(logging, "Class: '" << metaclass.name() << "'");
88  }
89  }
90 
91  try {
92  camp::classByName(classname);
93  } catch (std::exception & error) {
94  DT_THROW(std::logic_error,
95  "No class '" << classname << "' with reflection support: " << error.what());
96  }
97  const camp::Class & target_class = camp::classByName(classname);
98 
99  std::string funcname = _func_name_;
100  if (funcname.empty()) {
101  funcname = this->reflective_command<Type>::get_name();
102  }
103 
104  DT_THROW_IF(!target_class.hasFunction(funcname),
105  std::logic_error,
106  "Class '" << classname << "' has no function '" << funcname << "'!");
107  _func_ = &target_class.function(funcname);
108 
110  DT_LOG_TRACE(logging, "Builder...");
112  builder.logging = logging;
113  builder.set_classname(classname);
114  builder.set_funcname(funcname);
115  builder.set_function(*_func_);
116  builder.build(_method_);
117  DT_LOG_TRACE(logging, "Method is built.");
118  if (datatools::logger::is_trace(logging)) {
119  _method_.tree_dump(std::clog, "Reflective command description : ", "[trace]: ");
120  }
121  // _method_.tree_dump(std::cerr, "Method : ", "DEVEL: ");
123  if (_method_.has_display_name()) {
124  base_command::set_display_name(_method_.get_display_name());
125  }
126  }
128  if (_method_.has_terse_description()) {
129  base_command::set_terse_description(_method_.get_terse_description());
130  }
131  }
134 
135  // Positional arguments description:
138 
139  // Options description:
140  namespace po = boost::program_options;
141  po::options_description_easy_init easy_init
142  = opts.add_options();
143 
144  // Input arguments:
145  std::vector<std::string> input_arg_names;
146  _method_.build_list_of_input_argument_names(input_arg_names);
147 
148  // Output arguments:
149  std::vector<std::string> output_arg_names;
150  _method_.build_list_of_output_argument_names(output_arg_names);
151  DT_THROW_IF(output_arg_names.size() > 1,
152  std::logic_error,
153  "Class '" << classname << "' function '" << funcname << "' has too many output parameters!");
154 
155  for (std::size_t iarg = 0; iarg < input_arg_names.size(); iarg++) {
156  const datatools::introspection::argument & arg = _method_.get_argument_by_rank(iarg);
157  std::string arg_name = arg.get_name();
158  std::string arg_value_name = "value";
159  std::string arg_help = "";
160  if (arg.has_description()) {
161  arg_help = arg.get_description();
162  }
163  if (arg_help.back() != '\n') arg_help += '\n';
166  std::string dd_type_id;
167 
168  if (arg_dd.has_type_id()) {
169  dd_type_id = arg_dd.get_type_id();
170  }
171  std::string type_label;
172  std::string type_details;
174  type_label = "boolean";
175  } else if (datatools::introspection::is_integer(dd_type)) {
176  type_label = "integer";
177  } else if (datatools::introspection::is_string(dd_type)) {
178  type_label = "string";
179  } else if (datatools::introspection::is_real(dd_type)) {
180  type_label = "real";
181  if (arg_dd.is_dimensionless()) {
182  type_details = "dimensionless";
183  } else {
184  std::ostringstream type_details_oss;
185  type_details_oss << "dimension: '" << arg_dd.get_unit_dimension_label() << "'";
186  if (arg_dd.has_preferred_unit()) {
187  type_details_oss << ", preferred unit: '" << arg_dd.get_preferred_unit_symbol() << "'";
188  }
189  type_details = type_details_oss.str();
190  }
191  } else if (datatools::introspection::is_enum(dd_type)) {
192  type_label = "enumeration";
193  if (!dd_type_id.empty()) {
194  type_details = dd_type_id;
195  }
196  }
197 
198  // Argument description :
199  std::ostringstream rang_desc;
200  rang_desc << "Rank: " << iarg << "\n";
201  arg_help += rang_desc.str();
202 
203  std::ostringstream type_desc;
204  type_desc << "Type: '" << type_label << "'";
205  if (!type_details.empty()) {
206  type_desc << " (" << type_details << ")";
207  }
208  type_desc << "\n";
209  if (type_desc.str().length()) {
210  arg_help += type_desc.str();
211  }
213  easy_init(arg_name.c_str(),
214  po::value<datatools::ui::wrapped_boolean>()
215  ->value_name(arg_value_name),
216  arg_help.c_str()
217  );
218  } else if (datatools::introspection::is_integer(dd_type)) {
219  easy_init(arg_name.c_str(),
220  po::value<datatools::ui::wrapped_integer>()
221  ->value_name(arg_value_name),
222  arg_help.c_str()
223  );
224  forbid_short_ui_options = true;
225  } else if (datatools::introspection::is_string(dd_type)) {
226  easy_init(arg_name.c_str(),
227  po::value<std::string>()
228  ->value_name(arg_value_name),
229  arg_help.c_str()
230  );
231  } else if (datatools::introspection::is_real(dd_type)) {
232  if (arg_dd.is_dimensionless()) {
233  easy_init(arg_name.c_str(),
234  po::value<datatools::ui::wrapped_real_without_unit>()
235  ->value_name(arg_value_name),
236  arg_help.c_str()
237  );
238  } else {
239  easy_init(arg_name.c_str(),
240  po::value<datatools::ui::wrapped_real_with_unit>()
241  ->value_name(arg_value_name),
242  arg_help.c_str()
243  );
244  }
245  forbid_short_ui_options = true;
246  } else if (datatools::introspection::is_enum(dd_type)) {
247  if (!dd_type_id.empty()) {
248  std::ostringstream supported_value_desc;
249  supported_value_desc << "Allowed values are:\n";
250  const camp::Enum & metaenum = camp::enumByName(dd_type_id);
251  DT_LOG_TRACE(logging, "Enum name = '" << metaenum.name());
252  for (std::size_t ivalue = 0; ivalue < metaenum.size(); ivalue++) {
253  const camp::Enum::Pair & value_pair = metaenum.pair(ivalue);
254  supported_value_desc << " - \"" << value_pair.name << "\"\n";
255  }
256  if (supported_value_desc.str().length()) {
257  arg_help += supported_value_desc.str();
258  }
259  }
260  easy_init(arg_name.c_str(),
261  po::value<std::string>()
262  ->value_name(arg_value_name),
263  arg_help.c_str());
264  } else {
265  DT_THROW(std::logic_error, "Class '" << classname << "' function '" << funcname << "': "
266  << "unsupported data type for input argument #" << iarg << "!");
267  }
268 
269  // Positional:
270  args.add(arg_name.c_str(), 1);
271  }
272 
273  if (forbid_short_ui_options) {
275  }
276 
277  return;
278  }
279 
280  template <class Type>
282  {
283  // datatools::logger::priority logging = this->reflective_command<Type>::get_logging_priority();
284  _method_.reset();
285  _func_ = nullptr;
286  _func_name_.clear();
287  this->base_command::_fini();
288  return;
289  }
290 
291  template <class Type>
293  uint32_t /*flags_*/)
294  {
296  DT_LOG_TRACE_ENTERING(logging);
297  cri_.reset();
299  try {
300  DT_LOG_TRACE(logging, "Entering try block...");
301  camp::UserObject targetObj(this->reflective_command<Type>::_grab_target());
302  if (!_func_->callable(targetObj)) {
304  }
305  DT_LOG_TRACE(logging,
306  "Building args from the vmap...");
307  camp::Args args;
310  arg_builder.logging = logging;
311  arg_builder.build(args);
312  DT_LOG_TRACE(logging,
313  "Calling function '" << _func_->name() << "'...");
314  camp::Value returned_value = _func_->call(targetObj, args);
315  if (returned_value.type() != camp::noType) {
316  DT_LOG_TRACE(logging,
317  "Returned value = '" << returned_value << "'...");
318  repr(std::clog, returned_value, _method_.get_unique_returned());
319  std::clog << std::endl;
320  }
321  } catch (std::exception & error) {
324  this->reflective_command<Type>::get_name() + ": " + error.what());
325  }
326 
327  DT_LOG_TRACE_EXITING(logging);
328  return;
329  }
330 
331  // static
332  template <class Type>
333  void reflective_command<Type>::repr(std::ostream & out_,
334  const camp::Value & value_,
336  {
338  if (value_.type() == camp::boolType) {
339  bool value = value_;
340  datatools::io::write_boolean(out_, value, true);
341  } else if (value_.type() == camp::intType) {
342  int value = value_;
343  datatools::io::write_integer(out_, value);
344  } else if (value_.type() == camp::realType) {
345  double value = value_;
346  if (arg_dd.is_dimensionless()) {
348  value,
350  } else {
351  std::string display_unit;
352  if (arg_dd.has_preferred_unit()) {
353  display_unit = arg_dd.get_preferred_unit_symbol();
354  } else if (arg_dd.has_implicit_unit()) {
355  display_unit = arg_dd.get_implicit_unit_symbol();
356  } else if (arg_dd.has_explicit_unit_dimension()) {
357  const std::string & unit_dimension = arg_dd.get_explicit_unit_dimension_label();
358  display_unit = datatools::units::get_default_unit_symbol_from_label(unit_dimension);
359  }
360  if (arg_dd.has_implicit_unit()) {
361  double unit_value = datatools::units::get_unit(arg_dd.get_implicit_unit_symbol());
362  value *= unit_value;
363  }
365  value,
367  display_unit,
368  "",
369  1.0,
371  }
372  } else if (value_.type() == camp::stringType) {
373  std::string value = value_;
374  datatools::io::write_quoted_string(std::clog, value);
375  } else if (value_.type() == camp::enumType) {
376  std::clog << value_;
377  } else {
378  DT_THROW(std::logic_error, "Unsupported CAMP type=[" << value_.type() << "]!");
379  }
380  return;
381  }
382 
383  } // end of namespace ui
384 
385 } // end of namespace datatools
386 
387 #endif // DATATOOLS_UI_REFLECTIVE_COMMAND_INL_H
388 
389 // Local Variables: --
390 // mode: c++ --
391 // c-file-style: "gnu" --
392 // tab-width: 2 --
393 // End: --
bool is_enum(data_type)
Check if a data type is an enumeration.
std::string get_unit_dimension_label() const
Return the unit dimension label.
void set_classname(const std::string &classname_)
Set the class name.
Description of a method argument.
Definition: argument.h:46
#define DT_THROW(ExceptionType, Message)
Definition: exception.h:121
Data description.
Definition: data_description.h:40
priority
Priority levels for logging from most to least critical.
Definition: logger.h:82
static void write_quoted_string(std::ostream &, const std::string &a_str)
Write a quoted string.
#define DT_LOG_TRACE_EXITING(Priority)
Log a fonction exiting message if Priority is greater or equal to PRIO_TRACE.
Definition: logger_macros.h:267
const std::string & get_type_id() const
Return the type identifier (for enumeration and class)
const std::string & get_explicit_unit_dimension_label() const
Return the real data explicit unit dimension label ("length"/"time"/"mass"...)
void set_funcname(const std::string &funcname_)
void build(camp::Args &args_)
Build the list of function arguments for the reflection API.
Definition: ioutils.h:106
void set_function(const camp::Function &func_)
Set the function.
const std::string & get_description() const
Return a non mutable reference of the argument's description.
datatools::logger::priority logging
Definition: builder.h:164
bool has_type_id() const
Check if the data has a type identifier (for enumeration and class)
const std::string & get_name() const
Return the argument's explicit name in the list of arguments it belongs to.
bool is_boolean(data_type)
Check if a data type is a boolean.
static void write_integer(std::ostream &, int)
Write an integer.
static void write_real_number(std::ostream &out_, double val_, int precision_=REAL_PRECISION, const std::string &unit_symbol_="", const std::string &unit_label_="", double unit_value_=1.0, uint32_t flags_=0)
Write a double value in an ASCII stream with unit support.
const std::string & get_implicit_unit_symbol() const
Return the real data implicit unit symbol.
static bool is_trace(priority p_)
static void repr(std::ostream &out_, const camp::Value &value_, const datatools::introspection::argument &arg_)
Definition: reflective_command-inl.h:333
virtual void _init(const datatools::properties &config_)
Definition: reflective_command-inl.h:76
bool is_integer(data_type)
Check if a data type is an integer.
void set_error_code(error_code_type code_)
Set the error code.
const std::string & get_preferred_unit_symbol() const
Return the (real) data preferred unit (ex: "volt", "ns", "mA"...)
bool has_preferred_unit() const
Check if the (real) data has a preferred unit (ex: "volt", "ns", "mA"...)
virtual void _init(const properties &config_)
logger::priority get_logging_priority() const
Return the logging priority threshold.
virtual void _fini()
Definition: reflective_command-inl.h:281
Command returned information.
Definition: command_utils.h:78
#define DT_LOG_DEBUG(Priority, Message)
Log Message if Priority is greater or equal to PRIO_DEBUG.
Definition: logger_macros.h:147
virtual void _run(datatools::command::returned_info &cri_, uint32_t flags_=0)
Definition: reflective_command-inl.h:292
static bool is_debug(priority p_)
bool has_implicit_unit() const
Check if the (real) data is stored using an implicit unit (ex: "volt", "ns", "mA"....
bool is_string(data_type)
Check if a data type is a string.
std::string get_default_unit_symbol_from_label(const std::string &unit_label_)
Return the symbol of the default unit associated to a unit label supported by the datatools::units cl...
virtual ~reflective_command()
Destructor.
Definition: reflective_command-inl.h:60
bool has_explicit_unit_dimension() const
Check if the (real) data is stored using an explicit unit of known dimension (ex: "length"/"time"/"ma...
bool is_dimensionless() const
Check if real data is dimensionless.
#define DT_COMMAND_RETURNED_ERROR(ReturnedInfo, ErrorCode, ErrorMessage)
Definition: command_macros.h:65
This class uses a variable map from the command line to build a list of arguments for calling a funct...
Definition: builder.h:152
double get_unit(const std::string &unit_str_, bool throw_=false)
Get the CLHEP unspecified unit value from a string.
datatools::logger::priority logging
Definition: builder.h:136
boost::program_options::positional_options_description args_type
Definition: base_command.h:55
bool has_terse_description() const
Check if the description is empty.
Generic success.
Definition: command_utils.h:54
Generic failure.
Definition: command_utils.h:56
bool has_description() const
Check if the argument has a description.
Simple type wrappers.
void set_func_name(const std::string &func_name_)
Set the function name.
Definition: reflective_command-inl.h:69
const data_description & get_data_description() const
Return a non mutable reference of the argument's data description.
data_type
Supported data types.
Definition: data_type.h:41
#define DT_THROW_IF(Condition, ExceptionType, Message)
Definition: exception.h:76
bool has_display_name() const
Check if the display name is empty.
Method builder.
void set_terse_description(const std::string &terse_description_)
Set the description.
#define DT_LOG_TRACE(Priority, Message)
Log Message if Priority is greater or equal to PRIO_TRACE.
Definition: logger_macros.h:227
The Bayeux/datatools library top-level namespace.
Definition: algo.h:13
Base command for a target object.
Definition: target_command.h:38
const std::string & get_name() const
Return the name.
reflective_command()
Default constructor.
Definition: reflective_command-inl.h:39
data_type get_type() const
Return the data type.
#define DT_LOG_TRACE_ENTERING(Priority)
Log a fonction entering message if Priority is greater or equal to PRIO_TRACE.
Definition: logger_macros.h:261
void set_display_name(const std::string &display_name_)
Set the display name.
static void write_boolean(std::ostream &a_out, bool a_bool, bool as_alpha=false)
Write a boolean.
Invalid context.
Definition: command_utils.h:58
boost::program_options::options_description opts_type
Definition: base_command.h:54
A class representing a version ID :
Definition: version_id.h:67
void build(method &method_)
Build the description of the method from the reflection function object.
This class uses the reflection API to build the description of a function or a class method....
Definition: builder.h:92
validators for command line
bool is_real(data_type)
Check if a data type is a real.
Description of a method argument.
static const int REAL8_PRECISION
Default precision for double.
Definition: ioutils.h:111
Reflective command for a target object.
Definition: reflective_command.h:41
A dictionary of arbitrary properties.
Definition: properties.h:125