Alamo
ParmParse.H
Go to the documentation of this file.
1 //
2 // This is a thin wrapper to the amrex::ParmParse class
3 // This class exists to add some additional parsing capability,
4 // e.g. parsing Set::Matrix and Set::Vector data types.
5 //
6 // :ref:`IO::ParmParse` uses static :code:`Parse()` functions to
7 // perform cascading class-based input parsing.
8 // See the :ref:`autodoc` section for instructions on adding documentation.
9 //
10 // :bdg-warning-line:`This is standard infrastructure code; make sure you know what you ard doing before you change it.`
11 //
12 // .. _query-directives:
13 //
14 // **Query directives**
15 //
16 // Alamo uses different query directives to standardize reading inputs and automatic documentation.
17 // The type of query that is used (query vs query_required, etc) triggers different handlers and different
18 // automatic documentation procedures.
19 // For instance, the use of a :code:`query_default` causes a default value to be set and added to the metadata
20 // file, and it also serves as a flag for the autodoc system to document that a default value is available.
21 // The following table is a reference for the different kinds of query directives.
22 //
23 //
24 // .. table::
25 // :widths: 1 99
26 //
27 // +---------------------------------+-----------------------------------------------------------------+
28 // | BC Type | Description |
29 // +=================================+=================================================================+
30 // | :bdg-warning:`query` | Standard IO for bool, string, Set::Scalarthat does not enforce |
31 // | | defaults or required values. Not recommended for general use. |
32 // +---------------------------------+-----------------------------------------------------------------+
33 // | :bdg-success:`query_required` | Similar to query, but will abort if no value is specified. |
34 // | | Required values are indicated by :bdg-danger-line:`required`. |
35 // +---------------------------------+-----------------------------------------------------------------+
36 // | :bdg-success:`query_default` | Similar to query, but will fill with default value if no value |
37 // | | is provided. Will also add default value to metadata. |
38 // | | Default values are indicated by green badge values, e.g. |
39 // | | :bdg-success:`0.0`. |
40 // +---------------------------------+-----------------------------------------------------------------+
41 // | :bdg-success:`query_validate` | For strings, read in a value and enforce that the value is one |
42 // | | of a supplied number of values. Optionally make required, or |
43 // | | set the default value to the first supplied value. |
44 // | | Acceptable options are indicated by blue badge values, e.g. |
45 // | | :bdg-primary:`1.0`, :bdg-primary:`2.0`. If a default value is |
46 // | | available, it is indicated by a green badge, e.g. |
47 // | | :bdg-success:`1.0`. |
48 // +---------------------------------+-----------------------------------------------------------------+
49 // | :bdg-success:`query_file` | Read in a string that defines a file name. |
50 // | | Check to make sure that the file exists and is a regular file, |
51 // | | and print an informative error message if not (this can be |
52 // | | disabled). |
53 // | | Also, copy the file to the output directory, with the full path |
54 // | | preserved by replacing / with _. |
55 // | | (This can also be disabled, but doing so is discouraged.) |
56 // | | Default values are not allowed. |
57 // | | File paths are indicated by :bdg-secondary-line:`file path`. |
58 // +---------------------------------+-----------------------------------------------------------------+
59 // | :bdg-primary:`queryarr` | Read in an array of numbers into either a standard vector or |
60 // | | into a :code:`Set::Vector` or :code:`Set::Scalar`. |
61 // | | No defaults or existence checking is performed. |
62 // +---------------------------------+-----------------------------------------------------------------+
63 // | :bdg-primary:`queryclass` | Read a class object with a specified prefix. |
64 // | | How that class is read in is determined by its :code:`Parse` |
65 // | | function. |
66 // +---------------------------------+-----------------------------------------------------------------+
67 //
68 // .. _query_locator_macros:
69 //
70 // **Query macros**
71 //
72 // A set of preprocessor macros are defined so that you can call a query function using :code:`pp_` instead
73 // of :code:`pp.`.
74 // For instance, the following two can be used interchangeably:
75 //
76 // .. code-block:: cpp
77 //
78 // pp.query_required("myvar",myvar); /* function version */
79 // pp_query_required("myvar",myvar); /* preprocessor macro version - preferred*/
80 //
81 // Using the preprocessor macros enables code location information to be passed to the parser, so that
82 // more informative error messages will be printed out.
83 // Note that **the ParmParse object must always be called** :code:`pp` **for this to work**.
84 //
85 //
86 
87 #ifndef IO_PARMPARSE
88 #define IO_PARMPARSE
89 
90 #include <filesystem>
91 #include <exception>
92 #include <list>
93 
94 #include "Util/Util.H"
95 #include "Set/Set.H"
96 #include "AMReX_ParmParse.H"
97 
98 
99 #define pp_query_required(...) pp.query_required(__VA_ARGS__,INFO)
100 #define pp_query_default(...) pp.query_default(__VA_ARGS__,INFO)
101 #define pp_query_validate(...) pp.query_validate(__VA_ARGS__,INFO)
102 #define pp_query_file(...) pp.query_file(__VA_ARGS__,INFO)
103 #define pp_queryarr(...) pp.queryarr(__VA_ARGS__,INFO)
104 #define pp_query(...) pp.query(__VA_ARGS__)
105 #define pp_queryclass(...) pp.queryclass(__VA_ARGS__,INFO)
106 #define pp_forbid(...) pp.forbid(__VA_ARGS__,INFO)
107 
108 namespace IO
109 {
110 class ParmParse : public amrex::ParmParse
111 {
112 private:
113  void Define()
114  {
115  if (checked_for_input_files) return;
116  int k = 0;
117  std::string inputfile = "";
118  while (this->querykth("input",k,inputfile))
119  {
120  Util::Message(INFO,"Including inputs from "+inputfile);
121  this->addfile(inputfile);
122  k++;
123  }
125  }
127 
128 public:
129  ParmParse(std::string arg) : amrex::ParmParse::ParmParse(arg) {Define();} ;
130  ParmParse() : amrex::ParmParse::ParmParse() {Define();} ;
131  std::string getPrefix() const {return m_prefix;};
132  void ignore(std::string name)
133  {
134  (void)amrex::ParmParse::contains(name.c_str());
135  }
136 
137  void forbid(std::string name, std::string explanation,
138  std::string file = "", std::string func = "", int line = -1)
139  {
140  if (amrex::ParmParse::contains(name.c_str()))
141  {
142  Util::ParmParseException(INFO,file,func,line,full(name),full(name)," forbidden: ", explanation);
143  }
144  std::set<std::string> subs = amrex::ParmParse::getEntries(name);
145  if (subs.size())
146  {
147  Util::ParmParseException(INFO,file,func,line,full(name),full(name)," forbidden: ", explanation);
148  }
149  }
150 
151  bool contains(std::string name)
152  {
153  if (amrex::ParmParse::contains(name.c_str()))
154  return true;
155  if (amrex::ParmParse::contains(full(name).c_str()))
156  return true;
157  {
158  std::set<std::string> subs = amrex::ParmParse::getEntries(name.c_str());
159  if (subs.size())
160  return true;
161  }
162  {
163  std::set<std::string> subs = amrex::ParmParse::getEntries(full(name).c_str());
164  if (subs.size())
165  return true;
166  }
167  return false;
168  }
169 
170  template<typename T>
171  int query_required( std::string name, T & value,
172  std::string file = "", std::string func = "", int line = -1)
173  {
174  if (!contains(name.c_str()))
175  {
176  Util::ParmParseException(INFO,file,func,line,full(name),"required value for ",full(name)," missing");
177  }
178  return query(name.c_str(),value);
179  }
180 
181  template<typename T>
182  int query_default( std::string name, T & value, T defaultvalue,
183  std::string = "", std::string = "", int = -1)
184  {
185  if (!contains(name.c_str()))
186  {
187  add(name.c_str(),defaultvalue);
188  }
189  return query(name.c_str(),value);
190  }
191 
192  int query_validate( std::string name, int & value, std::vector<int> possibleintvals,
193  std::string file = "", std::string func = "", int line = -1)
194  {
195  // First value is accepted by default...
196 
197  // set default value
198  value = possibleintvals[0];
199 
200  // get the read value (if it exists)
201  int retval = query(name.c_str(),value);
202 
203  // check to make sure the read value matches one of the inpus
204  bool ok = false;
205  for (unsigned int i = 0; i < possibleintvals.size(); i++)
206  {
207  if (value == possibleintvals[i]) ok = true;
208  }
209 
210  if (ok) return retval;
211 
212  std::stringstream ss;
213  ss << possibleintvals[0];
214  for (unsigned int i = 1; i < possibleintvals.size(); i++)
215  ss << "," << possibleintvals[i];
216 
217  Util::ParmParseException(INFO,file,func,line,full(name),"' expected [", ss.str(), "] but got ", value);
218 
219  return -1;
220  }
221 
222 
223  int query_validate( std::string name, std::string & value, std::vector<const char *> possiblecharvals, bool firstbydefault,
224  std::string file = "", std::string func = "", int line = -1)
225  {
226  // if not using default values, then the input must be specified
227  if (!firstbydefault)
228  {
229  if (!contains(name.c_str()))
230  {
231  Util::ParmParseException(INFO,file,func,line,full(name),"required value for ",full(name)," missing");
232  }
233  }
234 
235  // set default value
236  value = std::string(possiblecharvals[0]);
237 
238  // get the read value (if it exists)
239  int retval = query(name.c_str(),value);
240 
241  // check to make sure the read value matches one of the inpus
242  bool ok = false;
243  for (unsigned int i = 0; i < possiblecharvals.size(); i++)
244  {
245  if (value == std::string(possiblecharvals[i])) ok = true;
246  }
247 
248  if (ok) return retval;
249 
250  std::stringstream ss;
251  ss << possiblecharvals[0];
252  for (unsigned int i = 1; i < possiblecharvals.size(); i++)
253  ss << "," << possiblecharvals[i];
254 
255  Util::ParmParseException(INFO,file,func,line,full(name),"' expected [", ss.str(), "] but got ", value);
256 
257  return -1;
258  }
259 
260  int query_validate( std::string name, std::string & value, std::vector<const char *> possiblecharvals,
261  std::string file = "", std::string func = "", int line = -1)
262  {
263  return query_validate(name,value,possiblecharvals,true,file,func,line);
264  }
265 
266 
267  // special case for strings
268  int query_default( std::string name, std::string & value, const char *defaultvalue,
269  std::string file = "", std::string func = "", int line = -1)
270  {
271  return query_default(name, value, std::string(defaultvalue), file, func, line);
272  }
273  // special case for bools
274  int query_default( std::string name, int & value, bool defaultvalue,
275  std::string file = "", std::string func = "query_default", int line = -1)
276  {
277  int defaultint = 0;
278  if (defaultvalue) defaultint = 1;
279  return query_default(name, value, defaultint, file, func, line);
280  }
281 
282 
283 
284  // validate filenames
285  int query_file( std::string name, std::string & value, bool copyfile, bool checkfile,
286  std::string file = "", std::string func = "query_file", int line = -1)
287  {
288  try
289  {
290  if (!contains(name.c_str()))
291  {
292  Util::ParmParseException(INFO,file,func,line,full(name),full(name)," must be specified");
293  }
294 
295  int retval = query(name.c_str(),value);
296 
297  if (amrex::ParallelDescriptor::IOProcessor())
298  {
299  if ( checkfile && ! std::filesystem::exists(value))
300  {
301  Util::ParmParseException(INFO,file,func,line,full(name),full(name)," does not exist");
302  }
303  if ( checkfile && !std::filesystem::is_regular_file(value))
304  {
305  Util::ParmParseException(INFO,file,func,line,full(name),full(name)," is not a regular file");
306  }
307  if ( copyfile )
308  {
309  Util::CopyFileToOutputDir(value, true, full(name));
310  }
311  }
312  return retval;
313  }
314  catch (...)
315  {
316  Util::ParmParseException(INFO,file,func,line,full(name));
317  }
318  return -1;
319  }
320  int query_file( std::string name, std::string & value, bool copyfile,
321  std::string file = "", std::string func = "query_file", int line = -1)
322  {
323  return query_file(name,value,copyfile,true,file,func,line);
324  }
325  int query_file( std::string name, std::string & value,
326  std::string file = "", std::string func = "query_file", int line = -1)
327  {
328  return query_file(name,value,true,true,file,func,line);
329  }
330 
331 
332  template<typename T>
333  int queryarr( std::string name, std::vector<T> & value,
334  std::string /*file*/, std::string /*func*/, int /*line*/)
335  {
336  return amrex::ParmParse::queryarr(name.c_str(),value);
337  }
338  int queryarr( std::string name, Set::Vector & value,
339  std::string file = "", std::string func = "queryarr", int line = -1)
340  {
341  std::vector<Set::Scalar> vals;
342  amrex::ParmParse::queryarr(name.c_str(), vals);
343  if (vals.size() < AMREX_SPACEDIM)
344  {
345  Util::ParmParseException(INFO,file,func,line,full(name),full(name)," requires at least ", AMREX_SPACEDIM, " arguments, got ",vals.size());
346  }
347  for (int i = 0; i < AMREX_SPACEDIM; i++) value(i) = vals[i];
348  return 0;
349  }
350  int queryarr( std::string name, Set::Matrix & value,
351  std::string file = "", std::string func = "queryarr", int line = -1)
352  {
353  std::vector<Set::Scalar> vals;
354  amrex::ParmParse::queryarr(name.c_str(), vals);
355  if (vals.size() == 9)
356  {
357 #if AMREX_SPACEDIM==2
358  Util::Warning(file,func,line,"Reading a 3D matrix (",full(name),")into a 2D code - some values will be ignored.");
359  value(0,0) = vals[0]; value(0,1)= vals[1];
360  value(1,0) = vals[3]; value(1,1)= vals[4];
361 #endif
362 #if AMREX_SPACEDIM==3
363  value(0,0) = vals[0]; value(0,1)= vals[1]; value(0,2)= vals[2];
364  value(1,0) = vals[3]; value(1,1)= vals[4]; value(1,2)= vals[5];
365  value(2,0) = vals[6]; value(2,1)= vals[7]; value(2,2)= vals[8];
366 #endif
367  }
368  else if (vals.size() == 4)
369  {
370 #if AMREX_SPACEDIM==2
371  value(0,0) = vals[0]; value(0,1)= vals[1];
372  value(1,0) = vals[2]; value(1,1)= vals[3];
373 #endif
374 #if AMREX_SPACEDIM==3
375  Util::Warning(file,func,line,"Reading a 2D matrix (",full(name),")into a 3D code - remaining values will be set to zero.");
376  value(0,0) = vals[0]; value(0,1)= vals[1]; value(0,2)= 0.0;
377  value(1,0) = vals[2]; value(1,1)= vals[3]; value(1,2)= 0.0;
378  value(2,0) = 0.0; value(2,1)= 0.0; value(2,2)= 0.0;
379 #endif
380  }
381  else
382  {
383  Util::ParmParseException(INFO,file,func,line,full(name),full(name)," needs either 4 or 9 components, but got ",vals.size());
384  }
385  return 0;
386  }
387 
389  {
390  int cnt = 0;
391  for (auto li = m_table->begin(), End = m_table->end(); li != End; ++li)
392  {
393  if (!li->second.m_count && li->first.rfind(getPrefix()+".",0) != std::string::npos)
394  {
395  Util::Warning(INFO,li->first);
396  cnt++;
397  }
398  }
399  return cnt;
400  }
401 
402  std::vector<std::string> GetUnusedInputs()
403  {
404  std::vector<std::string> ret;
405  for (auto li = m_table->begin(), End = m_table->end(); li != End; ++li)
406  {
407  if (!li->second.m_count && li->first.rfind(getPrefix()+".",0) != std::string::npos)
408  {
409  ret.push_back(li->first);
410  }
411  }
412  return ret;
413  }
414 
415  static int AllUnusedInputs()
416  {
417  ParmParse pp;
418  int cnt = 0;
419  for (auto li = pp.m_table->begin(), End = pp.m_table->end(); li != End; ++li)
420  {
421  if (!li->second.m_count)
422  {
423  Util::Warning(INFO,li->first);
424  cnt++;
425  }
426  }
427  return cnt;
428  }
429  std::string prefix ()
430  {
431  return getPrefix();
432  }
433  std::string full (std::string name)
434  {
435  std::string prefix = getPrefix();
436  if (prefix != "") return getPrefix() + "." + name;
437  else return name;
438  }
439 
440 
441  using amrex::ParmParse::queryarr;
442  template<class T>
443  void queryclass(std::string name, T * value,
444  std::string file = "", std::string func = "", int line = -1)
445  {
446  auto old_prefix = m_prefix;
447  try
448  {
449  if (old_prefix.empty()) m_prefix = name;
450  else m_prefix.append(".").append(name);
451  T::Parse(*value, *this);
452  std::vector<std::string> unused_inputs = GetUnusedInputs();
453  if (unused_inputs.size())
454  {
455  std::stringstream ss;
456  for (unsigned int i=0; i < unused_inputs.size(); i++)
457  ss << "\n\t" << unused_inputs[i];
458  Util::ParmParseException(INFO,file,func,line,name,"The following inputs were specified but not used",ss.str());
459  }
460  }
461  catch (...)
462  {
463  m_prefix = old_prefix;
464  Util::ParmParseException(INFO,file,func,line,full(name));
465  }
466  m_prefix = old_prefix;
467  }
468  template<class T>
469  void queryclass(std::string name, T & value,
470  std::string file = "", std::string func = "", int line = __LINE__)
471  {
472  auto old_prefix = m_prefix;
473  try
474  {
475  if (old_prefix.empty()) m_prefix = name;
476  else m_prefix.append(".").append(name);
477  T::Parse(value, *this);
478  std::vector<std::string> unused_inputs = GetUnusedInputs();
479  if (unused_inputs.size())
480  {
481  std::stringstream ss;
482  for (unsigned int i=0; i < unused_inputs.size(); i++)
483  ss << "\n\t" << unused_inputs[i];
484  Util::ParmParseException(INFO,file,func,line,name,"The following inputs were specified but not used",ss.str());
485  }
486  }
487  catch (...)
488  {
489  m_prefix = old_prefix;
490  Util::ParmParseException(INFO,file,func,line,full(name));
491  }
492  m_prefix = old_prefix;
493  }
494  template<class T>
495  void queryclass(T * value,
496  std::string file = "", std::string func = "", int line = __LINE__)
497  {
498  try
499  {
500  T::Parse(*value, *this);
501  std::vector<std::string> unused_inputs = GetUnusedInputs();
502  if (unused_inputs.size())
503  {
504  std::stringstream ss;
505  for (unsigned int i=0; i < unused_inputs.size(); i++)
506  ss << "\n\t" << unused_inputs[i];
507  Util::ParmParseException(INFO,file,func,line,getPrefix(),"The following inputs were specified but not used",ss.str());
508  }
509  }
510  catch (...)
511  {
512  Util::ParmParseException(INFO,file,func,line,getPrefix());
513  }
514  }
515  template<class T>
516  void queryclass(T & value,
517  std::string file = "", std::string func = "", int line = __LINE__)
518  {
519  try
520  {
521  T::Parse(value, *this);
522  std::vector<std::string> unused_inputs = GetUnusedInputs();
523  if (unused_inputs.size())
524  {
525  std::stringstream ss;
526  for (unsigned int i=0; i < unused_inputs.size(); i++)
527  ss << "\n\t" << unused_inputs[i];
528  Util::ParmParseException(INFO,file,func,line,getPrefix(),"The following inputs were specified but not used",ss.str());
529  }
530  }
531  catch (...)
532  {
533  Util::ParmParseException(INFO,file,func,line,getPrefix());
534  }
535  }
536 };
537 }
538 #endif
IO::ParmParse::query_default
int query_default(std::string name, T &value, T defaultvalue, std::string="", std::string="", int=-1)
Definition: ParmParse.H:182
IO::ParmParse::ParmParse
ParmParse(std::string arg)
Definition: ParmParse.H:129
Util::String::Parse
std::complex< int > Parse(std::string input)
Definition: Util.cpp:308
IO::ParmParse::query_file
int query_file(std::string name, std::string &value, bool copyfile, std::string file="", std::string func="query_file", int line=-1)
Definition: ParmParse.H:320
Util.H
IO::ParmParse::full
std::string full(std::string name)
Definition: ParmParse.H:433
IO::ParmParse::prefix
std::string prefix()
Definition: ParmParse.H:429
IO::ParmParse::query_validate
int query_validate(std::string name, std::string &value, std::vector< const char * > possiblecharvals, std::string file="", std::string func="", int line=-1)
Definition: ParmParse.H:260
Util::ParmParseException
void ParmParseException(std::string file, std::string func, int line, std::string file2, std::string, int line2, std::string fullname, Args const &... args)
Definition: Util.H:250
IO::ParmParse::Define
void Define()
Definition: ParmParse.H:113
IO::ParmParse::AllUnusedInputs
static int AllUnusedInputs()
Definition: ParmParse.H:415
IO::ParmParse::query_file
int query_file(std::string name, std::string &value, std::string file="", std::string func="query_file", int line=-1)
Definition: ParmParse.H:325
Set::Vector
Eigen::Matrix< amrex::Real, AMREX_SPACEDIM, 1 > Vector
Definition: Base.H:20
IO::ParmParse::queryclass
void queryclass(T &value, std::string file="", std::string func="", int line=__LINE__)
Definition: ParmParse.H:516
IO::ParmParse::queryarr
int queryarr(std::string name, Set::Matrix &value, std::string file="", std::string func="queryarr", int line=-1)
Definition: ParmParse.H:350
IO::ParmParse::queryclass
void queryclass(std::string name, T &value, std::string file="", std::string func="", int line=__LINE__)
Definition: ParmParse.H:469
IO::ParmParse::query_required
int query_required(std::string name, T &value, std::string file="", std::string func="", int line=-1)
Definition: ParmParse.H:171
IO
Definition: FileNameParse.H:7
Set::Matrix
Eigen::Matrix< amrex::Real, AMREX_SPACEDIM, AMREX_SPACEDIM > Matrix
Definition: Base.H:23
IO::ParmParse::GetUnusedInputs
std::vector< std::string > GetUnusedInputs()
Definition: ParmParse.H:402
IO::ParmParse::forbid
void forbid(std::string name, std::string explanation, std::string file="", std::string func="", int line=-1)
Definition: ParmParse.H:137
IO::ParmParse::query_default
int query_default(std::string name, std::string &value, const char *defaultvalue, std::string file="", std::string func="", int line=-1)
Definition: ParmParse.H:268
IO::ParmParse::queryclass
void queryclass(T *value, std::string file="", std::string func="", int line=__LINE__)
Definition: ParmParse.H:495
IO::ParmParse::checked_for_input_files
static bool checked_for_input_files
Definition: ParmParse.H:126
IO::ParmParse::contains
bool contains(std::string name)
Definition: ParmParse.H:151
IO::ParmParse::ParmParse
ParmParse()
Definition: ParmParse.H:130
IO::ParmParse::query_default
int query_default(std::string name, int &value, bool defaultvalue, std::string file="", std::string func="query_default", int line=-1)
Definition: ParmParse.H:274
Set.H
IO::ParmParse::queryarr
int queryarr(std::string name, Set::Vector &value, std::string file="", std::string func="queryarr", int line=-1)
Definition: ParmParse.H:338
Util::Warning
void Warning(std::string file, std::string func, int line, Args const &... args)
Definition: Util.H:171
IO::ParmParse::getPrefix
std::string getPrefix() const
Definition: ParmParse.H:131
IO::ParmParse::query_validate
int query_validate(std::string name, std::string &value, std::vector< const char * > possiblecharvals, bool firstbydefault, std::string file="", std::string func="", int line=-1)
Definition: ParmParse.H:223
IO::ParmParse
Definition: ParmParse.H:110
IO::ParmParse::ignore
void ignore(std::string name)
Definition: ParmParse.H:132
IO::ParmParse::query_file
int query_file(std::string name, std::string &value, bool copyfile, bool checkfile, std::string file="", std::string func="query_file", int line=-1)
Definition: ParmParse.H:285
Util::CopyFileToOutputDir
void CopyFileToOutputDir(std::string a_path, bool fullpath, std::string prefix)
Definition: Util.cpp:49
IO::ParmParse::queryclass
void queryclass(std::string name, T *value, std::string file="", std::string func="", int line=-1)
Definition: ParmParse.H:443
INFO
#define INFO
Definition: Util.H:20
IO::ParmParse::queryarr
int queryarr(std::string name, std::vector< T > &value, std::string, std::string, int)
Definition: ParmParse.H:333
IO::ParmParse::AnyUnusedInputs
int AnyUnusedInputs()
Definition: ParmParse.H:388
IO::ParmParse::query_validate
int query_validate(std::string name, int &value, std::vector< int > possibleintvals, std::string file="", std::string func="", int line=-1)
Definition: ParmParse.H:192
Util::Message
void Message(std::string file, std::string func, int line, Args const &... args)
Definition: Util.H:133