Thrill  0.1
cmdline_parser.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  * tlx/cmdline_parser.cpp
3  *
4  * Part of tlx - http://panthema.net/tlx
5  *
6  * Copyright (C) 2013-2015 Timo Bingmann <[email protected]>
7  *
8  * All rights reserved. Published under the Boost Software License, Version 1.0
9  ******************************************************************************/
10 
11 #include <tlx/cmdline_parser.hpp>
12 
13 #include <algorithm>
14 #include <cstdlib>
15 #include <cstring>
16 #include <iomanip>
17 #include <iostream>
18 #include <limits>
19 #include <string>
20 #include <vector>
21 
23 #include <tlx/unused.hpp>
24 
25 namespace tlx {
26 
27 /******************************************************************************/
28 // Argument and Struct Hierarchy below it.
29 
30 //! base class of all options and parameters
31 struct CmdlineParser::Argument {
32 
33  //! single letter short option, or 0 is none
34  char key_;
35  //! long option key or name for parameters
36  std::string longkey_;
37  //! option type description, e.g. "<#>" to indicate numbers
38  std::string keytype_;
39  //! longer description, which will be wrapped
40  std::string desc_;
41  //! required, process() fails if the option/parameter is not found.
42  bool required_;
43  //! found during processing of command line
44  bool found_ = false;
45  //! repeated argument, i.e. std::vector<std::string>
46  bool repeated_ = false;
47 
48  //! contructor filling most attributes
49  Argument(char key, const std::string& longkey, const std::string& keytype,
50  const std::string& desc, bool required)
51  : key_(key), longkey_(longkey), keytype_(keytype), desc_(desc),
52  required_(required) { }
53 
54  //! empty virtual destructor
55  virtual ~Argument() = default;
56 
57  //! return formatted type name to user
58  virtual const char * type_name() const = 0;
59 
60  //! process one item from command line for this argument
61  virtual bool process(int& argc, const char* const*& argv) = 0; // NOLINT
62 
63  //! format value to ostream
64  virtual void print_value(std::ostream& os) const = 0;
65 
66  //! return 'longkey [keytype]'
67  std::string param_text() const {
68  std::string s = longkey_;
69  if (!keytype_.empty()) {
70  s += ' ' + keytype_;
71  }
72  return s;
73  }
74 
75  //! return '-s, --longkey [keytype]'
76  std::string option_text() const {
77  std::string s;
78  if (key_ != 0) {
79  s += '-', s += key_, s += ", ";
80  }
81  else {
82  s += " ";
83  }
84  s += "--", s += longkey_;
85  if (!keytype_.empty()) {
86  s += ' ' + keytype_;
87  }
88  return s;
89  }
90 };
91 
92 //! specialization of argument for boolean flags (can only be set to true).
93 struct CmdlineParser::ArgumentBool final : public Argument {
94  //! reference to boolean to set to true
95  bool& dest_;
96 
97  //! contructor filling most attributes
98  ArgumentBool(char key, const std::string& longkey,
99  const std::string& keytype, const std::string& desc,
100  bool required, bool& dest) // NOLINT
101  : Argument(key, longkey, keytype, desc, required), dest_(dest) { }
102 
103  const char * type_name() const final { return "bool"; }
104 
105  //! "process" argument: just set to true, no argument is used.
106  bool process(int& argc, const char* const*& argv) final { // NOLINT
107  unused(argc), unused(argv);
108  dest_ = true;
109  return true;
110  }
111 
112  void print_value(std::ostream& os) const final {
113  os << (dest_ ? "true" : "false");
114  }
115 };
116 
117 //! specialization of argument for integer options or parameters
118 struct CmdlineParser::ArgumentInt final : public Argument {
119  int& dest_;
120 
121  //! contructor filling most attributes
122  ArgumentInt(char key, const std::string& longkey,
123  const std::string& keytype, const std::string& desc,
124  bool required, int& dest) // NOLINT
125  : Argument(key, longkey, keytype, desc, required), dest_(dest) { }
126 
127  const char * type_name() const final { return "integer"; }
128 
129  //! parse signed integer using sscanf.
130  bool process(int& argc, const char* const*& argv) final { // NOLINT
131  if (argc == 0)
132  return false;
133  char* endptr;
134  long x = strtol(argv[0], &endptr, 10);
135  if (endptr != nullptr && *endptr == 0 &&
137  --argc, ++argv;
138  dest_ = static_cast<int>(x);
139  return true;
140  }
141  else {
142  return false;
143  }
144  }
145 
146  void print_value(std::ostream& os) const final { os << dest_; }
147 };
148 
149 //! specialization of argument for unsigned integer options or parameters
150 struct CmdlineParser::ArgumentUnsigned final : public Argument {
151  unsigned int& dest_;
152 
153  //! contructor filling most attributes
154  ArgumentUnsigned(char key, const std::string& longkey,
155  const std::string& keytype, const std::string& desc,
156  bool required, unsigned int& dest) // NOLINT
157  : Argument(key, longkey, keytype, desc, required), dest_(dest) { }
158 
159  const char * type_name() const final { return "unsigned"; }
160 
161  //! parse unsigned integer using sscanf.
162  bool process(int& argc, const char* const*& argv) final { // NOLINT
163  if (argc == 0)
164  return false;
165  char* endptr;
166  unsigned long x = strtoul(argv[0], &endptr, 10);
167  if (endptr != nullptr && *endptr == 0 &&
169  --argc, ++argv;
170  dest_ = static_cast<unsigned int>(x);
171  return true;
172  }
173  else {
174  return false;
175  }
176  }
177 
178  void print_value(std::ostream& os) const final { os << dest_; }
179 };
180 
181 //! specialization of argument for size_t options or parameters
182 struct CmdlineParser::ArgumentSizeT final : public Argument {
183  size_t& dest_;
184 
185  //! contructor filling most attributes
186  ArgumentSizeT(char key, const std::string& longkey,
187  const std::string& keytype, const std::string& desc,
188  bool required, size_t& dest) // NOLINT
189  : Argument(key, longkey, keytype, desc, required), dest_(dest) { }
190 
191  const char * type_name() const final { return "size_t"; }
192 
193  //! parse size_t using sscanf.
194  bool process(int& argc, const char* const*& argv) final { // NOLINT
195  if (argc == 0)
196  return false;
197  char* endptr;
198  unsigned long long x = strtoull(argv[0], &endptr, 10);
199  if (endptr != nullptr && *endptr == 0 &&
201  --argc, ++argv;
202  dest_ = x;
203  return true;
204  }
205  else {
206  return false;
207  }
208  }
209 
210  void print_value(std::ostream& os) const final { os << dest_; }
211 };
212 
213 //! specialization of argument for float options or parameters
214 struct CmdlineParser::ArgumentFloat final : public Argument {
215  float& dest_;
216 
217  //! contructor filling most attributes
218  ArgumentFloat(char key, const std::string& longkey,
219  const std::string& keytype, const std::string& desc,
220  bool required, float& dest) // NOLINT
221  : Argument(key, longkey, keytype, desc, required), dest_(dest) { }
222 
223  const char * type_name() const final { return "float"; }
224 
225  //! parse unsigned integer using sscanf.
226  bool process(int& argc, const char* const*& argv) final { // NOLINT
227  if (argc == 0)
228  return false;
229  char* endptr;
230  dest_ = strtof(argv[0], &endptr);
231  if (endptr != nullptr && *endptr == 0) {
232  --argc, ++argv;
233  return true;
234  }
235  else {
236  return false;
237  }
238  }
239 
240  void print_value(std::ostream& os) const final { os << dest_; }
241 };
242 
243 //! specialization of argument for double options or parameters
244 struct CmdlineParser::ArgumentDouble final : public Argument {
245  double& dest_;
246 
247  //! contructor filling most attributes
248  ArgumentDouble(char key, const std::string& longkey,
249  const std::string& keytype, const std::string& desc,
250  bool required, double& dest) // NOLINT
251  : Argument(key, longkey, keytype, desc, required), dest_(dest) { }
252 
253  const char * type_name() const final { return "double"; }
254 
255  //! parse unsigned integer using sscanf.
256  bool process(int& argc, const char* const*& argv) final { // NOLINT
257  if (argc == 0)
258  return false;
259  char* endptr;
260  dest_ = strtod(argv[0], &endptr);
261  if (endptr != nullptr && *endptr == 0) {
262  --argc, ++argv;
263  return true;
264  }
265  else {
266  return false;
267  }
268  }
269 
270  void print_value(std::ostream& os) const final { os << dest_; }
271 };
272 
273 //! specialization of argument for SI/IEC suffixes byte size options or
274 //! parameters
275 struct CmdlineParser::ArgumentBytes32 final : public Argument {
276  uint32_t& dest_;
277 
278  //! contructor filling most attributes
279  ArgumentBytes32(char key, const std::string& longkey,
280  const std::string& keytype, const std::string& desc,
281  bool required, uint32_t& dest) // NOLINT
282  : Argument(key, longkey, keytype, desc, required), dest_(dest) { }
283 
284  const char * type_name() const final { return "bytes"; }
285 
286  //! parse byte size using SI/IEC parser.
287  bool process(int& argc, const char* const*& argv) final { // NOLINT
288  if (argc == 0)
289  return false;
290  uint64_t dest;
291  if (parse_si_iec_units(argv[0], &dest) &&
292  static_cast<uint64_t>(
293  dest_ = static_cast<uint32_t>(dest)) == dest) {
294  --argc, ++argv;
295  return true;
296  }
297  else {
298  return false;
299  }
300  }
301 
302  void print_value(std::ostream& os) const final { os << dest_; }
303 };
304 
305 //! specialization of argument for SI/IEC suffixes byte size options or
306 //! parameters
307 struct CmdlineParser::ArgumentBytes64 final : public Argument {
308  uint64_t& dest_;
309 
310  //! contructor filling most attributes
311  ArgumentBytes64(char key, const std::string& longkey,
312  const std::string& keytype, const std::string& desc,
313  bool required, uint64_t& dest) // NOLINT
314  : Argument(key, longkey, keytype, desc, required), dest_(dest) { }
315 
316  const char * type_name() const final { return "bytes"; }
317 
318  //! parse byte size using SI/IEC parser.
319  bool process(int& argc, const char* const*& argv) final { // NOLINT
320  if (argc == 0)
321  return false;
322  if (parse_si_iec_units(argv[0], &dest_)) {
323  --argc, ++argv;
324  return true;
325  }
326  else {
327  return false;
328  }
329  }
330 
331  void print_value(std::ostream& os) const final { os << dest_; }
332 };
333 
334 //! specialization of argument for string options or parameters
335 struct CmdlineParser::ArgumentString final : public Argument {
336  std::string& dest_;
337 
338  //! contructor filling most attributes
339  ArgumentString(char key, const std::string& longkey,
340  const std::string& keytype, const std::string& desc,
341  bool required, std::string& dest) // NOLINT
342  : Argument(key, longkey, keytype, desc, required), dest_(dest) { }
343 
344  const char * type_name() const final { return "string"; }
345 
346  //! "process" string argument just by storing it.
347  bool process(int& argc, const char* const*& argv) final { // NOLINT
348  if (argc == 0)
349  return false;
350  dest_ = argv[0];
351  --argc, ++argv;
352  return true;
353  }
354 
355  void print_value(std::ostream& os) const final {
356  os << '"' << dest_ << '"';
357  }
358 };
359 
360 //! specialization of argument for multiple string options or parameters
361 struct CmdlineParser::ArgumentStringlist final : public Argument {
362  std::vector<std::string>& dest_;
363 
364  //! contructor filling most attributes
365  ArgumentStringlist(char key, const std::string& longkey,
366  const std::string& keytype, const std::string& desc,
367  bool required, std::vector<std::string>& dest) // NOLINT
368  : Argument(key, longkey, keytype, desc, required), dest_(dest) {
369  repeated_ = true;
370  }
371 
372  const char * type_name() const final { return "string list"; }
373 
374  //! "process" string argument just by storing it in vector.
375  bool process(int& argc, const char* const*& argv) final { // NOLINT
376  if (argc == 0)
377  return false;
378  dest_.emplace_back(argv[0]);
379  --argc, ++argv;
380  return true;
381  }
382 
383  void print_value(std::ostream& os) const final {
384  os << '[';
385  for (size_t i = 0; i < dest_.size(); ++i) {
386  if (i != 0)
387  os << ',';
388  os << '"' << dest_[i] << '"';
389  }
390  os << ']';
391  }
392 };
393 
394 /******************************************************************************/
395 
396 void CmdlineParser::calc_option_max(const Argument* arg) {
398  arg->option_text().size() + 2, option_max_width_);
399 }
400 
401 void CmdlineParser::calc_param_max(const Argument* arg) {
403  arg->param_text().size() + 2, param_max_width_);
404 }
405 
406 /******************************************************************************/
407 
409  std::ostream& os, const std::string& text,
410  size_t wraplen, size_t indent_first, size_t indent_rest, size_t current,
411  size_t indent_newline) {
412 
413  std::string::size_type t = 0;
414  size_t indent = indent_first;
415 
416  while (t != text.size()) {
417  std::string::size_type to = t, lspace = t;
418 
419  // scan forward in text until we hit a newline or wrap point
420  while (to != text.size() && to + current + indent < t + wraplen &&
421  text[to] != '\n') {
422  if (text[to] == ' ')
423  lspace = to;
424  ++to;
425  }
426 
427  // go back to last space
428  if (to != text.size() && text[to] != '\n' && lspace != t)
429  to = lspace + 1;
430 
431  // output line
432  os << std::string(indent, ' ') << text.substr(t, to - t) << std::endl;
433 
434  current = 0;
435  indent = indent_rest;
436 
437  // skip over last newline
438  if (to != text.size() && text[to] == '\n') {
439  indent = indent_newline;
440  ++to;
441  }
442 
443  t = to;
444  }
445 }
446 
447 /******************************************************************************/
448 
450 
452  for (size_t i = 0; i < option_list_.size(); ++i)
453  delete option_list_[i];
454  option_list_.clear();
455 
456  for (size_t i = 0; i < param_list_.size(); ++i)
457  delete param_list_[i];
458  param_list_.clear();
459 }
460 
461 void CmdlineParser::set_description(const std::string& description) {
462  description_ = description;
463 }
464 
466  author_ = author;
467 }
468 
469 void CmdlineParser::set_verbose_process(bool verbose_process) {
470  verbose_process_ = verbose_process;
471 }
472 
473 /******************************************************************************/
474 
475 void CmdlineParser::add_bool(char key, const std::string& longkey,
476  const std::string& keytype, bool& dest,
477  const std::string& desc) {
478  option_list_.emplace_back(
479  new ArgumentBool(key, longkey, keytype, desc, false, dest));
481 }
482 
483 void CmdlineParser::add_flag(char key, const std::string& longkey,
484  const std::string& keytype, bool& dest,
485  const std::string& desc) {
486  return add_bool(key, longkey, keytype, dest, desc);
487 }
488 
489 void CmdlineParser::add_int(char key, const std::string& longkey,
490  const std::string& keytype, int& dest,
491  const std::string& desc) {
492  option_list_.emplace_back(
493  new ArgumentInt(key, longkey, keytype, desc, false, dest));
495 }
496 
497 void CmdlineParser::add_unsigned(char key, const std::string& longkey,
498  const std::string& keytype, unsigned int& dest,
499  const std::string& desc) {
500  option_list_.emplace_back(
501  new ArgumentUnsigned(key, longkey, keytype, desc, false, dest));
503 }
504 
505 void CmdlineParser::add_uint(char key, const std::string& longkey,
506  const std::string& keytype, unsigned int& dest,
507  const std::string& desc) {
508  return add_unsigned(key, longkey, keytype, dest, desc);
509 }
510 
511 void CmdlineParser::add_size_t(char key, const std::string& longkey,
512  const std::string& keytype, size_t& dest,
513  const std::string& desc) {
514  option_list_.emplace_back(
515  new ArgumentSizeT(key, longkey, keytype, desc, false, dest));
517 }
518 
519 void CmdlineParser::add_float(char key, const std::string& longkey,
520  const std::string& keytype, float& dest,
521  const std::string& desc) {
522  option_list_.emplace_back(
523  new ArgumentFloat(key, longkey, keytype, desc, false, dest));
525 }
526 
527 void CmdlineParser::add_double(char key, const std::string& longkey,
528  const std::string& keytype, double& dest,
529  const std::string& desc) {
530  option_list_.emplace_back(
531  new ArgumentDouble(key, longkey, keytype, desc, false, dest));
533 }
534 
535 void CmdlineParser::add_bytes(char key, const std::string& longkey,
536  const std::string& keytype, uint32_t& dest,
537  const std::string& desc) {
538  option_list_.emplace_back(
539  new ArgumentBytes32(key, longkey, keytype, desc, false, dest));
541 }
542 
543 void CmdlineParser::add_bytes(char key, const std::string& longkey,
544  const std::string& keytype, uint64_t& dest,
545  const std::string& desc) {
546  option_list_.emplace_back(
547  new ArgumentBytes64(key, longkey, keytype, desc, false, dest));
549 }
550 
551 void CmdlineParser::add_string(char key, const std::string& longkey,
552  const std::string& keytype, std::string& dest,
553  const std::string& desc) {
554  option_list_.emplace_back(
555  new ArgumentString(key, longkey, keytype, desc, false, dest));
557 }
558 
560  char key, const std::string& longkey,
561  const std::string& keytype, std::vector<std::string>& dest,
562  const std::string& desc) {
563 
564  option_list_.emplace_back(
565  new ArgumentStringlist(key, longkey, keytype, desc, false, dest));
567 }
568 
569 /******************************************************************************/
570 
572  char key, const std::string& longkey, bool& dest, const std::string& desc) {
573  return add_bool(key, longkey, "", dest, desc);
574 }
575 
577  char key, const std::string& longkey, bool& dest, const std::string& desc) {
578  return add_bool(key, longkey, dest, desc);
579 }
580 
582  char key, const std::string& longkey, int& dest, const std::string& desc) {
583  return add_int(key, longkey, "", dest, desc);
584 }
585 
586 void CmdlineParser::add_unsigned(char key, const std::string& longkey,
587  unsigned int& dest, const std::string& desc) {
588  return add_unsigned(key, longkey, "", dest, desc);
589 }
590 
591 void CmdlineParser::add_uint(char key, const std::string& longkey,
592  unsigned int& dest, const std::string& desc) {
593  return add_unsigned(key, longkey, dest, desc);
594 }
595 
596 void CmdlineParser::add_size_t(char key, const std::string& longkey,
597  size_t& dest, const std::string& desc) {
598  return add_size_t(key, longkey, "", dest, desc);
599 }
600 
601 void CmdlineParser::add_float(char key, const std::string& longkey,
602  float& dest, const std::string& desc) {
603  return add_float(key, longkey, "", dest, desc);
604 }
605 
606 void CmdlineParser::add_double(char key, const std::string& longkey,
607  double& dest, const std::string& desc) {
608  return add_double(key, longkey, "", dest, desc);
609 }
610 
611 void CmdlineParser::add_bytes(char key, const std::string& longkey,
612  uint32_t& dest, const std::string& desc) {
613  return add_bytes(key, longkey, "", dest, desc);
614 }
615 
616 void CmdlineParser::add_bytes(char key, const std::string& longkey,
617  uint64_t& dest, const std::string& desc) {
618  return add_bytes(key, longkey, "", dest, desc);
619 }
620 
621 void CmdlineParser::add_string(char key, const std::string& longkey,
622  std::string& dest, const std::string& desc) {
623  return add_string(key, longkey, "", dest, desc);
624 }
625 
627  char key, const std::string& longkey,
628  std::vector<std::string>& dest, const std::string& desc) {
629  return add_stringlist(key, longkey, "", dest, desc);
630 }
631 
632 /******************************************************************************/
633 
635  const std::string& longkey, bool& dest, const std::string& desc) {
636  return add_bool(0, longkey, "", dest, desc);
637 }
638 
640  const std::string& longkey, bool& dest, const std::string& desc) {
641  return add_bool(0, longkey, dest, desc);
642 }
643 
645  const std::string& longkey, int& dest, const std::string& desc) {
646  return add_int(0, longkey, "", dest, desc);
647 }
648 
650  unsigned int& dest, const std::string& desc) {
651  return add_unsigned(0, longkey, "", dest, desc);
652 }
653 
655  unsigned int& dest, const std::string& desc) {
656  return add_unsigned(0, longkey, dest, desc);
657 }
658 
660  size_t& dest, const std::string& desc) {
661  return add_size_t(0, longkey, "", dest, desc);
662 }
663 
665  float& dest, const std::string& desc) {
666  return add_float(0, longkey, "", dest, desc);
667 }
668 
670  double& dest, const std::string& desc) {
671  return add_double(0, longkey, "", dest, desc);
672 }
673 
675  uint32_t& dest, const std::string& desc) {
676  return add_bytes(0, longkey, "", dest, desc);
677 }
678 
680  uint64_t& dest, const std::string& desc) {
681  return add_bytes(0, longkey, "", dest, desc);
682 }
683 
685  std::string& dest, const std::string& desc) {
686  return add_string(0, longkey, "", dest, desc);
687 }
688 
690  const std::string& longkey,
691  std::vector<std::string>& dest, const std::string& desc) {
692  return add_stringlist(0, longkey, "", dest, desc);
693 }
694 
695 /******************************************************************************/
696 
698  const std::string& name, int& dest, const std::string& desc) {
699  param_list_.emplace_back(new ArgumentInt(0, name, "", desc, true, dest));
700  calc_param_max(param_list_.back());
701 }
702 
704  const std::string& name, unsigned int& dest, const std::string& desc) {
705  param_list_.emplace_back(
706  new ArgumentUnsigned(0, name, "", desc, true, dest));
707  calc_param_max(param_list_.back());
708 }
709 
711  const std::string& name, unsigned int& dest, const std::string& desc) {
712  add_param_unsigned(name, dest, desc);
713 }
714 
716  const std::string& name, size_t& dest, const std::string& desc) {
717  param_list_.emplace_back(new ArgumentSizeT(0, name, "", desc, true, dest));
718  calc_param_max(param_list_.back());
719 }
720 
722  const std::string& name, float& dest, const std::string& desc) {
723  param_list_.emplace_back(new ArgumentFloat(0, name, "", desc, true, dest));
724  calc_param_max(param_list_.back());
725 }
726 
728  const std::string& name, double& dest, const std::string& desc) {
729  param_list_.emplace_back(new ArgumentDouble(0, name, "", desc, true, dest));
730  calc_param_max(param_list_.back());
731 }
732 
734  const std::string& name, uint32_t& dest, const std::string& desc) {
735  param_list_.emplace_back(
736  new ArgumentBytes32(0, name, "", desc, true, dest));
737  calc_param_max(param_list_.back());
738 }
739 
741  const std::string& name, uint64_t& dest, const std::string& desc) {
742  param_list_.emplace_back(
743  new ArgumentBytes64(0, name, "", desc, true, dest));
744  calc_param_max(param_list_.back());
745 }
746 
748  const std::string& name, std::string& dest, const std::string& desc) {
749  param_list_.emplace_back(new ArgumentString(0, name, "", desc, true, dest));
750  calc_param_max(param_list_.back());
751 }
752 
754  const std::string& name, std::vector<std::string>& dest,
755  const std::string& desc) {
756  param_list_.emplace_back(
757  new ArgumentStringlist(0, name, "", desc, true, dest));
758  calc_param_max(param_list_.back());
759 }
760 
761 /******************************************************************************/
762 
764  const std::string& name, int& dest, const std::string& desc) {
765  param_list_.emplace_back(new ArgumentInt(0, name, "", desc, false, dest));
766  calc_param_max(param_list_.back());
767 }
768 
770  const std::string& name, unsigned int& dest, const std::string& desc) {
771  param_list_.emplace_back(
772  new ArgumentUnsigned(0, name, "", desc, false, dest));
773  calc_param_max(param_list_.back());
774 }
775 
777  const std::string& name, unsigned int& dest, const std::string& desc) {
778  return add_opt_param_unsigned(name, dest, desc);
779 }
780 
782  const std::string& name, size_t& dest, const std::string& desc) {
783  param_list_.emplace_back(new ArgumentSizeT(0, name, "", desc, false, dest));
784  calc_param_max(param_list_.back());
785 }
786 
788  const std::string& name, float& dest, const std::string& desc) {
789  param_list_.emplace_back(new ArgumentFloat(0, name, "", desc, false, dest));
790  calc_param_max(param_list_.back());
791 }
792 
794  const std::string& name, double& dest, const std::string& desc) {
795  param_list_.emplace_back(
796  new ArgumentDouble(0, name, "", desc, false, dest));
797  calc_param_max(param_list_.back());
798 }
799 
801  const std::string& name, uint32_t& dest, const std::string& desc) {
802  param_list_.emplace_back(
803  new ArgumentBytes32(0, name, "", desc, false, dest));
804  calc_param_max(param_list_.back());
805 }
806 
808  const std::string& name, uint64_t& dest, const std::string& desc) {
809  param_list_.emplace_back(
810  new ArgumentBytes64(0, name, "", desc, false, dest));
811  calc_param_max(param_list_.back());
812 }
813 
815  const std::string& name, std::string& dest, const std::string& desc) {
816  param_list_.emplace_back(
817  new ArgumentString(0, name, "", desc, false, dest));
818  calc_param_max(param_list_.back());
819 }
820 
822  const std::string& name, std::vector<std::string>& dest,
823  const std::string& desc) {
824  param_list_.emplace_back(
825  new ArgumentStringlist(0, name, "", desc, false, dest));
826  calc_param_max(param_list_.back());
827 }
828 
829 /******************************************************************************/
830 
832  std::sort(option_list_.begin(), option_list_.end(),
833  [](const Argument* a, Argument* b) {
834  return a->longkey_ < b->longkey_;
835  });
836  return *this;
837 }
838 
839 void CmdlineParser::print_usage(std::ostream& os) {
840  std::ios::fmtflags flags(os.flags());
841 
842  os << "Usage: " << program_name_
843  << (!option_list_.empty() ? " [options]" : "");
844 
845  for (ArgumentList::const_iterator it = param_list_.begin();
846  it != param_list_.end(); ++it) {
847  const Argument* arg = *it;
848 
849  os << (arg->required_ ? " <" : " [") << arg->longkey_
850  << (arg->repeated_ ? " ..." : "") << (arg->required_ ? '>' : ']');
851  }
852 
853  os << std::endl;
854 
855  if (!description_.empty()) {
856  os << std::endl;
858  }
859  if (!author_.empty()) {
860  os << "Author: " << author_ << std::endl;
861  }
862 
863  if (!description_.empty() || !author_.empty())
864  os << std::endl;
865 
866  if (!param_list_.empty()) {
867  os << "Parameters:" << std::endl;
868 
869  for (ArgumentList::const_iterator it = param_list_.begin();
870  it != param_list_.end(); ++it) {
871  const Argument* arg = *it;
872 
873  os << " " << std::setw(static_cast<int>(param_max_width_))
874  << std::left << arg->param_text();
875  output_wrap(os, arg->desc_, line_wrap_, 0, param_max_width_ + 2,
876  param_max_width_ + 2, 8);
877  }
878  }
879 
880  if (!option_list_.empty()) {
881  os << "Options:" << std::endl;
882 
883  for (ArgumentList::const_iterator it = option_list_.begin();
884  it != option_list_.end(); ++it) {
885  const Argument* arg = *it;
886 
887  os << " " << std::setw(static_cast<int>(option_max_width_))
888  << std::left << arg->option_text();
889  output_wrap(os, arg->desc_, line_wrap_, 0, option_max_width_ + 2,
890  option_max_width_ + 2, 8);
891  }
892  }
893 
894  os.flags(flags);
895 }
896 
898  return print_usage(std::cout);
899 }
900 
902  int argc, const char* const* argv, const Argument* arg, std::ostream& os) {
903  os << "Error: argument ";
904  if (argc != 0)
905  os << '"' << argv[0] << '"';
906 
907  os << " for " << arg->type_name() << " option " << arg->option_text()
908  << (argc == 0 ? " is missing!" : " is invalid!") << std::endl
909  << std::endl;
910 
911  print_usage(os);
912 }
913 
915  int argc, const char* const* argv, const Argument* arg, std::ostream& os) {
916  os << "Error: argument ";
917  if (argc != 0)
918  os << '"' << argv[0] << '"';
919 
920  os << " for " << arg->type_name() << " parameter " << arg->param_text()
921  << (argc == 0 ? " is missing!" : " is invalid!") << std::endl
922  << std::endl;
923 
924  print_usage(os);
925 }
926 
928  int argc, const char* const* argv, std::ostream& os) {
929  program_name_ = argv[0];
930  --argc, ++argv;
931 
932  // search for help string and output help
933  for (int i = 0; i < argc; ++i) {
934  if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
935  print_usage(os);
936  return false;
937  }
938  }
939 
940  // current argument in param_list_
941  ArgumentList::iterator argi = param_list_.begin();
942  bool end_optlist = false;
943 
944  while (argc != 0) {
945  const char* arg = argv[0];
946 
947  if (arg[0] == '-' && !end_optlist) {
948  // option, advance to argument
949  --argc, ++argv;
950  if (arg[1] == '-') {
951  if (arg[2] == '-') {
952  end_optlist = true;
953  }
954  else {
955  // long option
956  ArgumentList::const_iterator oi = option_list_.begin();
957  for ( ; oi != option_list_.end(); ++oi) {
958  if ((arg + 2) == (*oi)->longkey_) {
959  if (!(*oi)->process(argc, argv)) {
960  print_option_error(argc, argv, *oi, os);
961  return false;
962  }
963  else if (verbose_process_) {
964  os << "Option " << (*oi)->option_text()
965  << " set to ";
966  (*oi)->print_value(os);
967  os << '.' << std::endl;
968  }
969  break;
970  }
971  }
972  if (oi == option_list_.end()) {
973  os << "Error: unknown option \"" << arg << "\"."
974  << std::endl << std::endl;
975  print_usage(os);
976  return false;
977  }
978  }
979  }
980  else {
981  // short option
982  if (arg[1] == 0) {
983  os << "Invalid option \"" << arg << "\"." << std::endl;
984  }
985  else {
986  size_t offset = 1, arg_length = strlen(arg);
987  int old_argc = argc;
988  // Arguments will increase argc, so abort if it increases,
989  // while flags won't, so increase offset and parse next
990  while (offset < arg_length && argc == old_argc) {
991  ArgumentList::const_iterator oi = option_list_.begin();
992  for ( ; oi != option_list_.end(); ++oi) {
993  if (arg[offset] == (*oi)->key_) {
994  ++offset;
995  if (!(*oi)->process(argc, argv)) {
996  print_option_error(argc, argv, *oi, os);
997  return false;
998  }
999  else if (verbose_process_) {
1000  os << "Option "
1001  << (*oi)->option_text()
1002  << " set to ";
1003  (*oi)->print_value(os);
1004  os << '.' << std::endl;
1005  }
1006  break;
1007  }
1008  }
1009  if (oi == option_list_.end()) {
1010  os << "Error: unknown option \"";
1011  if (arg_length > 2) {
1012  // multiple short options combined
1013  os << "-" << arg[offset]
1014  << "\" at position " << offset
1015  << " in option sequence \"";
1016  }
1017  os << arg << "\"." << std::endl << std::endl;
1018  print_usage(os);
1019  return false;
1020  }
1021  }
1022  }
1023  }
1024  }
1025  else {
1026  if (argi != param_list_.end()) {
1027  if (!(*argi)->process(argc, argv)) {
1028  print_param_error(argc, argv, *argi, os);
1029  return false;
1030  }
1031  else if (verbose_process_) {
1032  os << "Parameter " << (*argi)->param_text() << " set to ";
1033  (*argi)->print_value(os);
1034  os << '.' << std::endl;
1035  }
1036  (*argi)->found_ = true;
1037  if (!(*argi)->repeated_)
1038  ++argi;
1039  }
1040  else {
1041  os << "Error: unexpected extra argument "
1042  << "\"" << argv[0] << "\"." << std::endl << std::endl;
1043  --argc, ++argv;
1044  print_usage(os);
1045  return false;
1046  }
1047  }
1048  }
1049 
1050  bool good = true;
1051 
1052  for (ArgumentList::const_iterator it = param_list_.begin();
1053  it != param_list_.end(); ++it) {
1054  if ((*it)->required_ && !(*it)->found_) {
1055  os << "Error: argument for parameter " << (*it)->longkey_
1056  << " is required!" << std::endl;
1057  good = false;
1058  }
1059  }
1060 
1061  if (!good) {
1062  os << std::endl;
1063  print_usage(os);
1064  }
1065 
1066  return good;
1067 }
1068 
1069 bool CmdlineParser::process(int argc, const char* const* argv) {
1070  return process(argc, argv, std::cout);
1071 }
1072 
1073 void CmdlineParser::print_result(std::ostream& os) {
1074  std::ios::fmtflags flags(os.flags());
1075 
1076  size_t maxlong = std::max(param_max_width_, option_max_width_);
1077 
1078  if (!param_list_.empty()) {
1079  os << "Parameters:" << std::endl;
1080 
1081  for (ArgumentList::const_iterator it = param_list_.begin();
1082  it != param_list_.end(); ++it) {
1083  const Argument* arg = *it;
1084 
1085  os << " " << std::setw(static_cast<int>(maxlong))
1086  << std::left << arg->param_text();
1087 
1088  std::string typestr = "(" + std::string(arg->type_name()) + ")";
1089  os << std::setw(max_type_name_ + 4) << typestr;
1090 
1091  arg->print_value(os);
1092 
1093  os << std::endl;
1094  }
1095  }
1096 
1097  if (!option_list_.empty()) {
1098  os << "Options:" << std::endl;
1099 
1100  for (ArgumentList::const_iterator it = option_list_.begin();
1101  it != option_list_.end(); ++it) {
1102  const Argument* arg = *it;
1103 
1104  os << " " << std::setw(static_cast<int>(maxlong))
1105  << std::left << arg->option_text();
1106 
1107  std::string typestr = "(" + std::string(arg->type_name()) + ")";
1108  os << std::setw(max_type_name_ + 4) << std::left << typestr;
1109 
1110  arg->print_value(os);
1111 
1112  os << std::endl;
1113  }
1114  }
1115 
1116  os.flags(flags);
1117 }
1118 
1120  return print_result(std::cout);
1121 }
1122 
1123 } // namespace tlx
1124 
1125 /******************************************************************************/
void add_opt_param_bytes(const std::string &name, uint32_t &dest, const std::string &desc)
void add_unsigned(char key, const std::string &longkey, unsigned int &dest, const std::string &desc)
static void output_wrap(std::ostream &os, const std::string &text, size_t wraplen, size_t indent_first=0, size_t indent_rest=0, size_t current=0, size_t indent_newline=0)
void add_uint(char key, const std::string &longkey, unsigned int &dest, const std::string &desc)
static uint_pair max()
return an uint_pair instance containing the largest value possible
Definition: uint_types.hpp:226
CmdlineParser & sort()
sort options by key (but not the positional parameters)
void add_param_double(const std::string &name, double &dest, const std::string &desc)
add double parameter [name] with description and store to dest
ArgumentList option_list_
list of options available
void add_param_int(const std::string &name, int &dest, const std::string &desc)
add signed integer parameter [name] with description and store to dest
void set_description(const std::string &description)
Set description of program, text will be wrapped.
~CmdlineParser()
Delete all added arguments.
void add_opt_param_double(const std::string &name, double &dest, const std::string &desc)
add optional double parameter [name] with description and store to dest
void print_option_error(int argc, const char *const *argv, const Argument *arg, std::ostream &os)
print error about option.
void add_opt_param_string(const std::string &name, std::string &dest, const std::string &desc)
add optional string parameter [name] with description and store to dest
bool verbose_process_
verbose processing of arguments
void add_opt_param_uint(const std::string &name, unsigned int &dest, const std::string &desc)
void calc_option_max(const Argument *arg)
update maximum formatting width for new option
void add_param_size_t(const std::string &name, size_t &dest, const std::string &desc)
add size_t parameter [name] with description and store to dest
void add_size_t(char key, const std::string &longkey, size_t &dest, const std::string &desc)
add size_t option -key, –longkey with description and store to dest
void calc_param_max(const Argument *arg)
update maximum formatting width for new parameter
void add_opt_param_unsigned(const std::string &name, unsigned int &dest, const std::string &desc)
void print_result()
print nicely formatted result of processing to std::cout
ArgumentList param_list_
list of parameters, both required and optional
void add_param_bytes(const std::string &name, uint32_t &dest, const std::string &desc)
CmdlineParser()
Constructor.
void add_param_uint(const std::string &name, unsigned int &dest, const std::string &desc)
void add_bytes(char key, const std::string &longkey, uint32_t &dest, const std::string &desc)
void add_opt_param_float(const std::string &name, float &dest, const std::string &desc)
add optional float parameter [name] with description and store to dest
void unused(Types &&...)
Definition: unused.hpp:20
static constexpr int max_type_name_
maximum length of a type_name() result
void add_float(char key, const std::string &longkey, float &dest, const std::string &desc)
add float option -key, –longkey with description and store to dest
const char * program_name_
argv[0] for usage.
bool parse_si_iec_units(const char *str, uint64_t *out_size, char default_unit)
Parse a string like "343KB" or "44 GiB" into the corresponding size in bytes.
list x
Definition: gen_data.py:39
std::basic_string< char, std::char_traits< char >, Allocator< char > > string
string with Manager tracking
Definition: allocator.hpp:220
std::string description_
user set description of program, will be wrapped
Command line parser which automatically fills variables and prints nice usage messages.
size_t param_max_width_
formatting width for parameters, &#39;param <#>&#39;
void add_param_string(const std::string &name, std::string &dest, const std::string &desc)
add string parameter [name] with description and store to dest
void add_string(char key, const std::string &longkey, std::string &dest, const std::string &desc)
add string option -key, –longkey and store to dest
std::string author_
user set author of program, will be wrapped
unsigned int line_wrap_
set line wrap length
void add_double(char key, const std::string &longkey, double &dest, const std::string &desc)
add double option -key, –longkey with description and store to dest
void add_param_float(const std::string &name, float &dest, const std::string &desc)
add float parameter [name] with description and store to dest
void add_param_stringlist(const std::string &name, std::vector< std::string > &dest, const std::string &desc)
void add_opt_param_int(const std::string &name, int &dest, const std::string &desc)
void set_verbose_process(bool verbose_process)
Set verbose processing of command line arguments.
void add_bool(char key, const std::string &longkey, bool &dest, const std::string &desc)
size_t option_max_width_
formatting width for options, &#39;-s, –switch <#>&#39;
void add_stringlist(char key, const std::string &longkey, std::vector< std::string > &dest, const std::string &desc)
add string list option -key, –longkey and store to dest
void add_opt_param_stringlist(const std::string &name, std::vector< std::string > &dest, const std::string &desc)
void add_opt_param_size_t(const std::string &name, size_t &dest, const std::string &desc)
add optional size_t parameter [name] with description and store to dest
void set_author(const std::string &author)
Set author of program, will be wrapped.
void add_flag(char key, const std::string &longkey, bool &dest, const std::string &desc)
void add_int(char key, const std::string &longkey, int &dest, const std::string &desc)
void add_param_unsigned(const std::string &name, unsigned int &dest, const std::string &desc)
add unsigned integer parameter [name] with description and store to dest
bool process(int argc, const char *const *argv, std::ostream &os)
void print_param_error(int argc, const char *const *argv, const Argument *arg, std::ostream &os)
print error about parameter.