MFEM  v4.2.0
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
optparser.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-806117.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability visit https://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11 
12 #include "optparser.hpp"
13 #include "../linalg/vector.hpp"
14 #include <cctype>
15 
16 namespace mfem
17 {
18 
19 using namespace std;
20 
21 int isValidAsInt(char * s)
22 {
23  if ( s == NULL || *s == '\0' )
24  {
25  return 0; // Empty string
26  }
27 
28  if ( *s == '+' || *s == '-' )
29  {
30  ++s;
31  }
32 
33  if ( *s == '\0')
34  {
35  return 0; // sign character only
36  }
37 
38  while (*s)
39  {
40  if ( !isdigit(*s) )
41  {
42  return 0;
43  }
44  ++s;
45  }
46 
47  return 1;
48 }
49 
50 int isValidAsDouble(char * s)
51 {
52  // A valid floating point number for atof using the "C" locale is formed by
53  // - an optional sign character (+ or -),
54  // - followed by a sequence of digits, optionally containing a decimal-point
55  // character (.),
56  // - optionally followed by an exponent part (an e or E character followed by
57  // an optional sign and a sequence of digits).
58 
59  if ( s == NULL || *s == '\0' )
60  {
61  return 0; // Empty string
62  }
63 
64  if ( *s == '+' || *s == '-' )
65  {
66  ++s;
67  }
68 
69  if ( *s == '\0')
70  {
71  return 0; // sign character only
72  }
73 
74  while (*s)
75  {
76  if (!isdigit(*s))
77  {
78  break;
79  }
80  ++s;
81  }
82 
83  if (*s == '\0')
84  {
85  return 1; // s = "123"
86  }
87 
88  if (*s == '.')
89  {
90  ++s;
91  while (*s)
92  {
93  if (!isdigit(*s))
94  {
95  break;
96  }
97  ++s;
98  }
99  if (*s == '\0')
100  {
101  return 1; // this is a fixed point double s = "123." or "123.45"
102  }
103  }
104 
105  if (*s == 'e' || *s == 'E')
106  {
107  ++s;
108  return isValidAsInt(s);
109  }
110  else
111  {
112  return 0; // we have encounter a wrong character
113  }
114 }
115 
116 void parseArray(char * str, Array<int> & var)
117 {
118  var.SetSize(0);
119  std::stringstream input(str);
120  int val;
121  while ( input >> val)
122  {
123  var.Append(val);
124  }
125 }
126 
127 void parseVector(char * str, Vector & var)
128 {
129  int nentries = 0;
130  double val;
131  {
132  std::stringstream input(str);
133  while ( input >> val)
134  {
135  ++nentries;
136  }
137  }
138 
139  var.SetSize(nentries);
140  {
141  nentries = 0;
142  std::stringstream input(str);
143  while ( input >> val)
144  {
145  var(nentries++) = val;
146  }
147  }
148 }
149 
151 {
152  option_check.SetSize(options.Size());
153  option_check = 0;
154  for (int i = 1; i < argc; )
155  {
156  if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0)
157  {
158  // print help message
159  error_type = 1;
160  return;
161  }
162 
163  for (int j = 0; true; j++)
164  {
165  if (j >= options.Size())
166  {
167  // unrecognized option
168  error_type = 2;
169  error_idx = i;
170  return;
171  }
172 
173  if (strcmp(argv[i], options[j].short_name) == 0 ||
174  strcmp(argv[i], options[j].long_name) == 0)
175  {
176  OptionType type = options[j].type;
177 
178  if ( option_check[j] )
179  {
180  error_type = 4;
181  error_idx = j;
182  return;
183  }
184  option_check[j] = 1;
185 
186  i++;
187  if (type != ENABLE && type != DISABLE && i >= argc)
188  {
189  // missing argument
190  error_type = 3;
191  error_idx = j;
192  return;
193  }
194 
195  int isValid = 1;
196  switch (options[j].type)
197  {
198  case INT:
199  isValid = isValidAsInt(argv[i]);
200  *(int *)(options[j].var_ptr) = atoi(argv[i++]);
201  break;
202  case DOUBLE:
203  isValid = isValidAsDouble(argv[i]);
204  *(double *)(options[j].var_ptr) = atof(argv[i++]);
205  break;
206  case STRING:
207  *(const char **)(options[j].var_ptr) = argv[i++];
208  break;
209  case ENABLE:
210  *(bool *)(options[j].var_ptr) = true;
211  option_check[j+1] = 1; // Do not allow the DISABLE Option
212  break;
213  case DISABLE:
214  *(bool *)(options[j].var_ptr) = false;
215  option_check[j-1] = 1; // Do not allow the ENABLE Option
216  break;
217  case ARRAY:
218  parseArray(argv[i++], *(Array<int>*)(options[j].var_ptr) );
219  break;
220  case VECTOR:
221  parseVector(argv[i++], *(Vector*)(options[j].var_ptr) );
222  break;
223  }
224 
225  if (!isValid)
226  {
227  error_type = 5;
228  error_idx = i;
229  return;
230  }
231 
232  break;
233  }
234  }
235  }
236 
237  // check for missing required options
238  for (int i = 0; i < options.Size(); i++)
239  if (options[i].required &&
240  (option_check[i] == 0 ||
241  (options[i].type == ENABLE && option_check[++i] == 0)))
242  {
243  error_type = 6; // required option missing
244  error_idx = i; // for a boolean option i is the index of DISABLE
245  return;
246  }
247 
248  error_type = 0;
249 }
250 
251 void OptionsParser::WriteValue(const Option &opt, std::ostream &out)
252 {
253  switch (opt.type)
254  {
255  case INT:
256  out << *(int *)(opt.var_ptr);
257  break;
258 
259  case DOUBLE:
260  out << *(double *)(opt.var_ptr);
261  break;
262 
263  case STRING:
264  out << *(const char **)(opt.var_ptr);
265  break;
266 
267  case ARRAY:
268  {
269  Array<int> &list = *(Array<int>*)(opt.var_ptr);
270  out << '\'';
271  if (list.Size() > 0)
272  {
273  out << list[0];
274  }
275  for (int i = 1; i < list.Size(); i++)
276  {
277  out << ' ' << list[i];
278  }
279  out << '\'';
280  break;
281  }
282 
283  case VECTOR:
284  {
285  Vector &list = *(Vector*)(opt.var_ptr);
286  out << '\'';
287  if (list.Size() > 0)
288  {
289  out << list(0);
290  }
291  for (int i = 1; i < list.Size(); i++)
292  {
293  out << ' ' << list(i);
294  }
295  out << '\'';
296  break;
297  }
298 
299  default: // provide a default to suppress warning
300  break;
301  }
302 }
303 
304 void OptionsParser::PrintOptions(ostream &out) const
305 {
306  static const char *indent = " ";
307 
308  out << "Options used:\n";
309  for (int j = 0; j < options.Size(); j++)
310  {
311  OptionType type = options[j].type;
312 
313  out << indent;
314  if (type == ENABLE)
315  {
316  if (*(bool *)(options[j].var_ptr) == true)
317  {
318  out << options[j].long_name;
319  }
320  else
321  {
322  out << options[j+1].long_name;
323  }
324  j++;
325  }
326  else
327  {
328  out << options[j].long_name << " ";
329  WriteValue(options[j], out);
330  }
331  out << '\n';
332  }
333 }
334 
335 void OptionsParser::PrintError(ostream &out) const
336 {
337  static const char *line_sep = "";
338 
339  out << line_sep;
340  switch (error_type)
341  {
342  case 2:
343  out << "Unrecognized option: " << argv[error_idx] << '\n' << line_sep;
344  break;
345 
346  case 3:
347  out << "Missing argument for the last option: " << argv[argc-1] << '\n'
348  << line_sep;
349  break;
350 
351  case 4:
352  if (options[error_idx].type == ENABLE )
353  out << "Option " << options[error_idx].long_name << " or "
354  << options[error_idx + 1].long_name
355  << " provided multiple times\n" << line_sep;
356  else if (options[error_idx].type == DISABLE)
357  out << "Option " << options[error_idx - 1].long_name << " or "
358  << options[error_idx].long_name
359  << " provided multiple times\n" << line_sep;
360  else
361  out << "Option " << options[error_idx].long_name
362  << " provided multiple times\n" << line_sep;
363  break;
364 
365  case 5:
366  out << "Wrong option format: " << argv[error_idx - 1] << " "
367  << argv[error_idx] << '\n' << line_sep;
368  break;
369 
370  case 6:
371  out << "Missing required option: " << options[error_idx].long_name
372  << '\n' << line_sep;
373  break;
374  }
375  out << endl;
376 }
377 
378 void OptionsParser::PrintHelp(ostream &out) const
379 {
380  static const char *indent = " ";
381  static const char *seprtr = ", ";
382  static const char *descr_sep = "\n\t";
383  static const char *line_sep = "";
384  static const char *types[] = { " <int>", " <double>", " <string>", "", "",
385  " '<int>...'", " '<double>...'"
386  };
387 
388  out << indent << "-h" << seprtr << "--help" << descr_sep
389  << "Print this help message and exit.\n" << line_sep;
390  for (int j = 0; j < options.Size(); j++)
391  {
392  OptionType type = options[j].type;
393 
394  out << indent << options[j].short_name << types[type]
395  << seprtr << options[j].long_name << types[type]
396  << seprtr;
397  if (options[j].required)
398  {
399  out << "(required)";
400  }
401  else
402  {
403  if (type == ENABLE)
404  {
405  j++;
406  out << options[j].short_name << types[type] << seprtr
407  << options[j].long_name << types[type] << seprtr
408  << "current option: ";
409  if (*(bool *)(options[j].var_ptr) == true)
410  {
411  out << options[j-1].long_name;
412  }
413  else
414  {
415  out << options[j].long_name;
416  }
417  }
418  else
419  {
420  out << "current value: ";
421  WriteValue(options[j], out);
422  }
423  }
424  out << descr_sep;
425 
426  if (options[j].description)
427  {
428  out << options[j].description << '\n';
429  }
430  out << line_sep;
431  }
432 }
433 
434 void OptionsParser::PrintUsage(ostream &out) const
435 {
436  static const char *line_sep = "";
437 
438  PrintError(out);
439  out << "Usage: " << argv[0] << " [options] ...\n" << line_sep
440  << "Options:\n" << line_sep;
441  PrintHelp(out);
442 }
443 
444 }
int Size() const
Return the logical size of the array.
Definition: array.hpp:124
void parseVector(char *str, Vector &var)
Definition: optparser.cpp:127
void SetSize(int s)
Resize the vector to size s.
Definition: vector.hpp:459
void PrintHelp(std::ostream &out) const
Print the help message.
Definition: optparser.cpp:378
void Parse()
Parse the command-line options. Note that this function expects all the options provided through the ...
Definition: optparser.cpp:150
int Append(const T &el)
Append element &#39;el&#39; to array, resize if necessary.
Definition: array.hpp:726
void PrintUsage(std::ostream &out) const
Print the usage message.
Definition: optparser.cpp:434
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition: array.hpp:654
void PrintError(std::ostream &out) const
Print the error message.
Definition: optparser.cpp:335
void PrintOptions(std::ostream &out) const
Print the options.
Definition: optparser.cpp:304
Vector data type.
Definition: vector.hpp:51
int isValidAsInt(char *s)
Definition: optparser.cpp:21
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition: globals.hpp:66
void parseArray(char *str, Array< int > &var)
Definition: optparser.cpp:116
int isValidAsDouble(char *s)
Definition: optparser.cpp:50