vSMC  v3.0.0
Scalable Monte Carlo
program_option.hpp
Go to the documentation of this file.
1 //============================================================================
2 // vSMC/include/vsmc/utility/program_option.hpp
3 //----------------------------------------------------------------------------
4 // vSMC: Scalable Monte Carlo
5 //----------------------------------------------------------------------------
6 // Copyright (c) 2013-2016, Yan Zhou
7 // All rights reserved.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions are met:
11 //
12 // Redistributions of source code must retain the above copyright notice,
13 // this list of conditions and the following disclaimer.
14 //
15 // Redistributions in binary form must reproduce the above copyright notice,
16 // this list of conditions and the following disclaimer in the documentation
17 // and/or other materials provided with the distribution.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
20 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 // POSSIBILITY OF SUCH DAMAGE.
30 //============================================================================
31 
32 #ifndef VSMC_UTILITY_PROGRAM_OPTION_HPP
33 #define VSMC_UTILITY_PROGRAM_OPTION_HPP
34 
35 #include <vsmc/internal/common.hpp>
36 
37 #ifdef VSMC_CLANG
38 #pragma clang diagnostic push
39 #pragma clang diagnostic ignored "-Wweak-vtables"
40 #endif
41 
42 #define VSMC_RUNTIME_ASSERT_UTILITY_PROGRAM_OPTION_NULLPTR(ptr, func) \
43  VSMC_RUNTIME_ASSERT((ptr != nullptr), \
44  "**ProgramOption::" #func \
45  "** ATTEMPT TO SET OPTION WITH A NULL POINTER")
46 
47 namespace vsmc
48 {
49 
50 namespace internal
51 {
52 
53 inline void program_option_warning(const std::string &name,
54  const std::string &msg, bool silent, std::ostream &os)
55 {
56  if (silent)
57  return;
58 
59  os << "vSMC Program Option Warning\n";
60  os << "Option: --" << name << '\n';
61  os << "Message : " << msg << std::endl;
62 }
63 
64 } // namespace vsmc::internal
65 
69 {
70  public:
71  ProgramOption() = default;
72  ProgramOption(const ProgramOption &) = default;
73  ProgramOption &operator=(const ProgramOption &) = default;
74  virtual ~ProgramOption() {}
75 
76  virtual bool is_bool() const = 0;
77  virtual bool is_vector() const = 0;
78  virtual bool set(
79  const std::string &, const std::string &, bool, std::ostream &) = 0;
80  virtual bool set_default() = 0;
81  virtual std::string description() const = 0;
82  virtual std::string default_str() const = 0;
83 
84  protected:
85  bool set_value(const std::string &name, const std::string &sval,
86  bool *dest, bool silent, std::ostream &os)
87  {
88  const char *const sptr = sval.c_str();
89  const std::size_t size = sval.size();
90 
91  bool is_numb = true;
92  bool is_zero = true;
93  for (std::size_t i = 0; i != size; ++i) {
94  char c = sptr[i];
95  is_numb = is_numb && c >= '0' && c <= '9';
96  is_zero = is_zero && (c == '0');
97  }
98  if (is_zero) {
99  *dest = false;
100  return true;
101  } else if (is_numb) {
102  *dest = true;
103  return true;
104  }
105 
106  bool is_true = false;
107  is_true = is_true || std::strcmp(sptr, "y") == 0;
108  is_true = is_true || std::strcmp(sptr, "Y") == 0;
109  is_true = is_true || std::strcmp(sptr, "yes") == 0;
110  is_true = is_true || std::strcmp(sptr, "Yes") == 0;
111  is_true = is_true || std::strcmp(sptr, "YES") == 0;
112  is_true = is_true || std::strcmp(sptr, "t") == 0;
113  is_true = is_true || std::strcmp(sptr, "T") == 0;
114  is_true = is_true || std::strcmp(sptr, "true") == 0;
115  is_true = is_true || std::strcmp(sptr, "True") == 0;
116  is_true = is_true || std::strcmp(sptr, "TRUE") == 0;
117  if (is_true) {
118  *dest = true;
119  return true;
120  }
121 
122  bool is_false = false;
123  is_false = is_false || std::strcmp(sptr, "n") == 0;
124  is_false = is_false || std::strcmp(sptr, "N") == 0;
125  is_false = is_false || std::strcmp(sptr, "no") == 0;
126  is_false = is_false || std::strcmp(sptr, "No") == 0;
127  is_false = is_false || std::strcmp(sptr, "NO") == 0;
128  is_false = is_false || std::strcmp(sptr, "f") == 0;
129  is_false = is_false || std::strcmp(sptr, "F") == 0;
130  is_false = is_false || std::strcmp(sptr, "false") == 0;
131  is_false = is_false || std::strcmp(sptr, "False") == 0;
132  is_false = is_false || std::strcmp(sptr, "FALSE") == 0;
133  if (is_false) {
134  *dest = false;
135  return true;
136  }
137 
139  name, "Failed to set value: " + sval, silent, os);
140  return false;
141  }
142 
143  bool set_value(const std::string &, const std::string &sval,
144  std::string *dest, bool, std::ostream &)
145  {
146  *dest = sval;
147 
148  return true;
149  }
150 
151  template <typename T>
152  bool set_value(const std::string &name, const std::string &sval, T *dest,
153  bool silent, std::ostream &os)
154  {
155  std::stringstream ss;
156  ss.str(sval);
157  T tval;
158  ss >> tval;
159  if (ss.fail()) {
161  name, "Failed to set value: " + sval, silent, os);
162  ss.clear();
163  return false;
164  }
165  *dest = std::move(tval);
166 
167  return true;
168  }
169 }; // class ProgramOption
170 
174 {
175  public:
176  ProgramOptionHelp() : help_(false) {}
177 
178  bool is_bool() const { return true; }
179 
180  bool is_vector() const { return false; }
181 
182  bool set(const std::string &name, const std::string &sval, bool silent,
183  std::ostream &os)
184  {
185  return set_value(name, sval, &help_, silent, os);
186  }
187 
188  bool set_default() { return false; }
189 
190  std::string description() const
191  {
192  return std::string("Print this help information");
193  }
194 
195  std::string default_str() const { return std::string("(false)"); }
196 
197  bool help() const { return help_; }
198 
199  private:
200  bool help_;
201 }; // ProgramOptionHelp
202 
205 template <typename T>
207 {
208  public:
209  ProgramOptionDefault(const std::string &desc)
210  : desc_(desc), default_(T()), has_default_(false)
211  {
212  }
213 
214  template <typename V>
215  ProgramOptionDefault(const std::string &desc, V val)
216  : desc_(desc), default_(static_cast<T>(val)), has_default_(true)
217  {
218  }
219 
220  bool is_bool() const { return std::is_same<T, bool>::value; }
221 
222  std::string description() const { return desc_; }
223 
224  std::string default_str() const
225  {
226  return has_default_ ? default_val2str(default_) : std::string();
227  }
228 
229  protected:
230  bool set_value_default(T *dest)
231  {
232  if (has_default_)
233  *dest = default_;
234 
235  return has_default_;
236  }
237 
238  private:
239  std::string desc_;
240  T default_;
241  bool has_default_;
242 
243  template <typename U>
244  std::string default_val2str(const U &val) const
245  {
246  std::stringstream ss;
247  ss << '(' << val << ')';
248 
249  return ss.str();
250  }
251 
252  std::string default_val2str(bool val) const
253  {
254  return val ? std::string("(true)") : std::string("(false)");
255  }
256 }; // ProgramOptionDefault
257 
260 template <typename T>
262 {
263  public:
264  ProgramOptionScalar(const std::string &desc, T *ptr)
265  : ProgramOptionDefault<T>(desc), ptr_(ptr)
266  {
267  }
268 
269  template <typename V>
270  ProgramOptionScalar(const std::string &desc, T *ptr, V val)
271  : ProgramOptionDefault<T>(desc, val), ptr_(ptr)
272  {
273  }
274 
275  bool is_vector() const { return false; }
276 
277  bool set(const std::string &name, const std::string &sval, bool silent,
278  std::ostream &os)
279  {
280  return this->set_value(name, sval, ptr_, silent, os);
281  }
282 
283  bool set_default() { return this->set_value_default(ptr_); }
284 
285  private:
286  T *const ptr_;
287 }; // class ProgramOptionScalar
288 
291 template <typename T, typename Alloc>
293 {
294  public:
295  ProgramOptionVector(const std::string &desc, std::vector<T, Alloc> *ptr)
296  : ProgramOptionDefault<T>(desc), ptr_(ptr)
297  {
298  }
299 
300  template <typename V>
302  const std::string &desc, std::vector<T, Alloc> *ptr, V val)
303  : ProgramOptionDefault<T>(desc, val), ptr_(ptr)
304  {
305  }
306 
307  bool is_vector() const { return true; }
308 
309  bool set(const std::string &name, const std::string &sval, bool silent,
310  std::ostream &os)
311  {
312  T val;
313  bool success = this->set_value(name, sval, &val, silent, os);
314  if (success)
315  ptr_->push_back(val);
316 
317  return success;
318  }
319 
320  bool set_default()
321  {
322  T val;
323  bool success = this->set_value_default(&val);
324  if (success)
325  ptr_->push_back(val);
326 
327  return success;
328  }
329 
330  private:
331  std::vector<T, Alloc> *const ptr_;
332 }; // class ProgramOptionVector
333 
337 {
338  public:
339  explicit ProgramOptionMap(bool silent = false)
340  : silent_(silent), help_ptr_(std::make_shared<ProgramOptionHelp>())
341  {
342  add_option("help", help_ptr_);
343  }
344 
351  template <typename T>
353  const std::string &name, const std::string &desc, T *ptr)
354  {
356 
357  return add_option(
358  name, std::make_shared<ProgramOptionScalar<T>>(desc, ptr));
359  }
360 
362  template <typename T, typename V>
364  const std::string &name, const std::string &desc, T *ptr, V val)
365  {
367 
368  return add_option(
369  name, std::make_shared<ProgramOptionScalar<T>>(desc, ptr, val));
370  }
371 
373  template <typename T, typename Alloc>
374  ProgramOptionMap &add(const std::string &name, const std::string &desc,
375  std::vector<T, Alloc> *ptr)
376  {
378 
379  return add_option(
380  name, std::make_shared<ProgramOptionVector<T, Alloc>>(desc, ptr));
381  }
382 
384  template <typename T, typename Alloc, typename V>
385  ProgramOptionMap &add(const std::string &name, const std::string &desc,
386  std::vector<T, Alloc> *ptr, V val)
387  {
389 
390  return add_option(name,
391  std::make_shared<ProgramOptionVector<T, Alloc>>(desc, ptr, val));
392  }
393 
394  ProgramOptionMap &remove(const std::string &name)
395  {
396  auto iter = option_find(name);
397  if (iter != option_vec_.end())
398  option_vec_.erase(iter);
399 
400  return *this;
401  }
402 
413  void process(
414  int argc, const char *const *argv, std::ostream &os = std::cout)
415  {
416  std::string arg;
417  Vector<std::string> arg_vector;
418  arg_vector.reserve(static_cast<std::size_t>(argc));
419  for (int i = 0; i != argc; ++i) {
420  arg = process_arg(argv[i]);
421  if (!arg.empty())
422  arg_vector.push_back(arg);
423  }
424  process_arg_vector(arg_vector, os);
425  }
426 
428  void process(int argc, char *const *argv, std::ostream &os = std::cout)
429  {
430  std::string arg;
431  Vector<std::string> arg_vector;
432  arg_vector.reserve(static_cast<std::size_t>(argc));
433  for (int i = 0; i != argc; ++i) {
434  arg = process_arg(argv[i]);
435  if (!arg.empty())
436  arg_vector.push_back(arg);
437  }
438  process_arg_vector(arg_vector, os);
439  }
440 
442  void print_help(std::ostream &os = std::cout) const
443  {
444  std::size_t len[2] = {0, 0};
445  Vector<std::string> str[3];
446  for (const auto &option : option_vec_) {
447  str[0].push_back("--" + option.name);
448  str[1].push_back(option.ptr->description());
449  str[2].push_back(option.ptr->default_str());
450  len[0] = std::max(len[0], str[0].back().size());
451  len[1] = std::max(len[1], str[1].back().size());
452  }
453  len[0] += 4;
454  len[1] += 4;
455  for (std::size_t i = 0; i != str[0].size(); ++i) {
456  os << str[0][i] << std::string(len[0] - str[0][i].size(), ' ');
457  os << str[1][i] << std::string(len[1] - str[1][i].size(), ' ');
458  os << str[2][i] << std::endl;
459  }
460  }
461 
463  std::size_t count(const std::string &name) const
464  {
465  auto iter = option_find(name);
466  if (iter != option_vec_.end())
467  return iter->count;
468  return 0;
469  }
470 
472  bool help() { return help_ptr_->help(); }
473 
475  std::shared_ptr<ProgramOption> option(const std::string &name)
476  {
477  auto iter = option_find(name);
478  if (iter != option_vec_.end())
479  return iter->ptr;
480  return std::shared_ptr<ProgramOption>(
481  static_cast<ProgramOption *>(nullptr));
482  }
483 
485  std::shared_ptr<const ProgramOption> option(const std::string &name) const
486  {
487  auto iter = option_find(name);
488  if (iter != option_vec_.end())
489  return iter->ptr;
490  return std::shared_ptr<const ProgramOption>(
491  static_cast<const ProgramOption *>(nullptr));
492  }
493 
496  void silent(bool flag) { silent_ = flag; }
497 
498  private:
499  struct option_type {
500  std::string name;
501  std::shared_ptr<ProgramOption> ptr;
502  std::size_t count;
503  };
504 
505  using option_vec_type = Vector<option_type>;
506 
507  bool silent_;
508  std::shared_ptr<ProgramOptionHelp> help_ptr_;
509  option_vec_type option_vec_;
510 
511  option_vec_type::iterator option_find(const std::string &name)
512  {
513  auto iter = option_vec_.begin();
514  for (; iter != option_vec_.end(); ++iter)
515  if (iter->name == name)
516  break;
517 
518  return iter;
519  }
520 
521  option_vec_type::const_iterator option_find(const std::string &name) const
522  {
523  auto iter = option_vec_.begin();
524  for (; iter != option_vec_.end(); ++iter)
525  if (iter->name == name)
526  break;
527 
528  return iter;
529  }
530 
531  ProgramOptionMap &add_option(
532  const std::string &name, std::shared_ptr<ProgramOption> optr)
533  {
534  option_type option = {name, optr, 0};
535  auto iter = option_find(name);
536  if (iter != option_vec_.end())
537  *iter = option;
538  else
539  option_vec_.push_back(option);
540 
541  return *this;
542  }
543 
544  void process_arg_vector(Vector<std::string> &arg_vector, std::ostream &os)
545  {
547  Vector<std::string> svals;
548  auto aiter = arg_vector.begin();
549  while (aiter != arg_vector.end() && !is_option(*aiter))
550  ++aiter;
551  while (aiter != arg_vector.end()) {
552  std::string name(aiter->begin() + 2, aiter->end());
553  ++aiter;
554  svals.clear();
555  while (aiter != arg_vector.end() && !is_option(*aiter)) {
556  svals.push_back(*aiter);
557  ++aiter;
558  }
559  name_svals.push_back(std::make_pair(name, svals));
560  }
561 
562  const std::string sval_true("1");
563  for (auto &nsv : name_svals) {
564  auto iter = option_find(nsv.first);
565  if (iter == option_vec_.end()) {
567  nsv.first, "Unknown option", silent_, os);
568  continue;
569  }
570 
571  bool proc = false;
572  if (nsv.second.size() == 0 && iter->ptr->is_bool()) {
573  proc = process_option(iter, sval_true, os);
574  } else if (nsv.second.size() == 0) {
576  nsv.first, "No value found", silent_, os);
577  } else if (iter->ptr->is_vector()) {
578  for (const auto &sval : nsv.second)
579  proc = process_option(iter, sval, os);
580  } else {
581  proc = process_option(iter, nsv.second.back(), os) || proc;
582  }
583  if (proc)
584  ++iter->count;
585  }
586 
587  for (auto &option : option_vec_)
588  if (option.count == 0)
589  if (option.ptr->set_default())
590  option.count = 1;
591 
592  if (help())
593  print_help(os);
594  }
595 
596  std::string process_arg(const char *arg) const
597  {
598  std::size_t s = std::strlen(arg);
599  std::size_t e = s;
600  while (e != 0 && (arg[e - 1] == ' ' || arg[e - 1] == ','))
601  --e;
602 
603  return std::string(arg, arg + e);
604  }
605 
606  bool process_option(option_vec_type::iterator iter,
607  const std::string &sval, std::ostream &os)
608  {
609  if (sval.empty()) {
611  iter->name, "No value found", silent_, os);
612  return false;
613  }
614  return iter->ptr->set(iter->name, sval, silent_, os);
615  }
616 
617  bool is_option(const std::string &str) const
618  {
619  if (str.size() < 3)
620  return false;
621 
622  if (str[0] != '-')
623  return false;
624 
625  if (str[1] != '-')
626  return false;
627 
628  return true;
629  }
630 }; // class ProgramOptionMap
631 
632 } // namespace vsmc
633 
634 #ifdef VSMC_CLANG
635 #pragma clang diagnostic pop
636 #endif
637 
638 #endif // VSMC_UTILITY_PROGRAM_OPTION_HPP
std::vector< T, Alloc > Vector
std::vector with Allocator as default allocator
Definition: monitor.hpp:48
ProgramOptionMap & add(const std::string &name, const std::string &desc, std::vector< T, Alloc > *ptr)
Add an option with multiple value.
std::string default_str() const
ProgramOptionVector(const std::string &desc, std::vector< T, Alloc > *ptr, V val)
std::basic_ostream< CharT, Traits > & ostream(std::basic_ostream< CharT, Traits > &os, const std::array< T, N > &ary)
Definition: basic.hpp:215
ProgramOptionMap & add(const std::string &name, const std::string &desc, std::vector< T, Alloc > *ptr, V val)
Add an option with multiple value, with a default value.
ProgramOptionMap & add(const std::string &name, const std::string &desc, T *ptr)
Add an option with a single value.
bool set_value(const std::string &, const std::string &sval, std::string *dest, bool, std::ostream &)
ProgramOptionVector(const std::string &desc, std::vector< T, Alloc > *ptr)
ProgramOptionDefault(const std::string &desc)
STL namespace.
#define VSMC_RUNTIME_ASSERT_UTILITY_PROGRAM_OPTION_NULLPTR(ptr, func)
std::string description() const
std::shared_ptr< ProgramOption > option(const std::string &name)
Get the underlying option object.
void silent(bool flag)
Set the silent flag, if true, no warning messages will be printed for unknown options etc...
ProgramOptionScalar(const std::string &desc, T *ptr)
ProgramOptionScalar(const std::string &desc, T *ptr, V val)
bool set_value(const std::string &name, const std::string &sval, bool *dest, bool silent, std::ostream &os)
void program_option_warning(const std::string &name, const std::string &msg, bool silent, std::ostream &os)
std::shared_ptr< const ProgramOption > option(const std::string &name) const
Get the underlying option object.
ProgramOptionDefault(const std::string &desc, V val)
bool help()
If the "help" option is processed and set to true.
Option with multiple values.
void process(int argc, const char *const *argv, std::ostream &os=std::cout)
Process the options.
Option base class.
Option with a single value.
std::string default_str() const
void print_help(std::ostream &os=std::cout) const
Print help information for each option.
bool is_zero(const T &a)
Definition: basic.hpp:101
Option with a default value.
std::size_t count(const std::string &name) const
Count the number of successful processing of an option.
void add(std::size_t n, const float *a, const float *b, float *y)
Definition: vmath.hpp:74
ProgramOptionMap & add(const std::string &name, const std::string &desc, T *ptr, V val)
Add an option with a single value, with a default value.
bool set_value(const std::string &name, const std::string &sval, T *dest, bool silent, std::ostream &os)
void process(int argc, char *const *argv, std::ostream &os=std::cout)
Process the options.
ProgramOptionMap(bool silent=false)
std::string description() const