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