Thrill  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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  s += "--", s += longkey_;
82  if (!keytype_.empty()) {
83  s += ' ' + keytype_;
84  }
85  return s;
86  }
87 };
88 
89 //! specialization of argument for boolean flags (can only be set to true).
90 struct CmdlineParser::ArgumentBool final : public Argument {
91  //! reference to boolean to set to true
92  bool& dest_;
93 
94  //! contructor filling most attributes
95  ArgumentBool(char key, const std::string& longkey,
96  const std::string& keytype, const std::string& desc,
97  bool required, bool& dest) // NOLINT
98  : Argument(key, longkey, keytype, desc, required), dest_(dest) { }
99 
100  const char * type_name() const final { return "bool"; }
101 
102  //! "process" argument: just set to true, no argument is used.
103  bool process(int& argc, const char* const*& argv) final { // NOLINT
104  unused(argc), unused(argv);
105  dest_ = true;
106  return true;
107  }
108 
109  void print_value(std::ostream& os) const final {
110  os << (dest_ ? "true" : "false");
111  }
112 };
113 
114 //! specialization of argument for integer options or parameters
115 struct CmdlineParser::ArgumentInt final : public Argument {
116  int& dest_;
117 
118  //! contructor filling most attributes
119  ArgumentInt(char key, const std::string& longkey,
120  const std::string& keytype, const std::string& desc,
121  bool required, int& dest) // NOLINT
122  : Argument(key, longkey, keytype, desc, required), dest_(dest) { }
123 
124  const char * type_name() const final { return "integer"; }
125 
126  //! parse signed integer using sscanf.
127  bool process(int& argc, const char* const*& argv) final { // NOLINT
128  if (argc == 0)
129  return false;
130  char* endptr;
131  long x = strtol(argv[0], &endptr, 10);
132  if (endptr != nullptr && *endptr == 0 &&
134  --argc, ++argv;
135  dest_ = static_cast<int>(x);
136  return true;
137  }
138  else {
139  return false;
140  }
141  }
142 
143  void print_value(std::ostream& os) const final { os << dest_; }
144 };
145 
146 //! specialization of argument for unsigned integer options or parameters
147 struct CmdlineParser::ArgumentUnsigned final : public Argument {
148  unsigned int& dest_;
149 
150  //! contructor filling most attributes
151  ArgumentUnsigned(char key, const std::string& longkey,
152  const std::string& keytype, const std::string& desc,
153  bool required, unsigned int& dest) // NOLINT
154  : Argument(key, longkey, keytype, desc, required), dest_(dest) { }
155 
156  const char * type_name() const final { return "unsigned"; }
157 
158  //! parse unsigned integer using sscanf.
159  bool process(int& argc, const char* const*& argv) final { // NOLINT
160  if (argc == 0)
161  return false;
162  char* endptr;
163  unsigned long x = strtoul(argv[0], &endptr, 10);
164  if (endptr != nullptr && *endptr == 0 &&
166  --argc, ++argv;
167  dest_ = static_cast<unsigned int>(x);
168  return true;
169  }
170  else {
171  return false;
172  }
173  }
174 
175  void print_value(std::ostream& os) const final { os << dest_; }
176 };
177 
178 //! specialization of argument for size_t options or parameters
179 struct CmdlineParser::ArgumentSizeT final : public Argument {
180  size_t& dest_;
181 
182  //! contructor filling most attributes
183  ArgumentSizeT(char key, const std::string& longkey,
184  const std::string& keytype, const std::string& desc,
185  bool required, size_t& dest) // NOLINT
186  : Argument(key, longkey, keytype, desc, required), dest_(dest) { }
187 
188  const char * type_name() const final { return "size_t"; }
189 
190  //! parse size_t using sscanf.
191  bool process(int& argc, const char* const*& argv) final { // NOLINT
192  if (argc == 0)
193  return false;
194  char* endptr;
195  unsigned long long x = strtoull(argv[0], &endptr, 10);
196  if (endptr != nullptr && *endptr == 0 &&
198  --argc, ++argv;
199  dest_ = x;
200  return true;
201  }
202  else {
203  return false;
204  }
205  }
206 
207  void print_value(std::ostream& os) const final { os << dest_; }
208 };
209 
210 //! specialization of argument for float options or parameters
211 struct CmdlineParser::ArgumentFloat final : public Argument {
212  float& dest_;
213 
214  //! contructor filling most attributes
215  ArgumentFloat(char key, const std::string& longkey,
216  const std::string& keytype, const std::string& desc,
217  bool required, float& dest) // NOLINT
218  : Argument(key, longkey, keytype, desc, required), dest_(dest) { }
219 
220  const char * type_name() const final { return "float"; }
221 
222  //! parse unsigned integer using sscanf.
223  bool process(int& argc, const char* const*& argv) final { // NOLINT
224  if (argc == 0)
225  return false;
226  char* endptr;
227  dest_ = strtof(argv[0], &endptr);
228  if (endptr != nullptr && *endptr == 0) {
229  --argc, ++argv;
230  return true;
231  }
232  else {
233  return false;
234  }
235  }
236 
237  void print_value(std::ostream& os) const final { os << dest_; }
238 };
239 
240 //! specialization of argument for double options or parameters
241 struct CmdlineParser::ArgumentDouble final : public Argument {
242  double& dest_;
243 
244  //! contructor filling most attributes
245  ArgumentDouble(char key, const std::string& longkey,
246  const std::string& keytype, const std::string& desc,
247  bool required, double& dest) // NOLINT
248  : Argument(key, longkey, keytype, desc, required), dest_(dest) { }
249 
250  const char * type_name() const final { return "double"; }
251 
252  //! parse unsigned integer using sscanf.
253  bool process(int& argc, const char* const*& argv) final { // NOLINT
254  if (argc == 0)
255  return false;
256  char* endptr;
257  dest_ = strtod(argv[0], &endptr);
258  if (endptr != nullptr && *endptr == 0) {
259  --argc, ++argv;
260  return true;
261  }
262  else {
263  return false;
264  }
265  }
266 
267  void print_value(std::ostream& os) const final { os << dest_; }
268 };
269 
270 //! specialization of argument for SI/IEC suffixes byte size options or
271 //! parameters
272 struct CmdlineParser::ArgumentBytes32 final : public Argument {
273  uint32_t& dest_;
274 
275  //! contructor filling most attributes
276  ArgumentBytes32(char key, const std::string& longkey,
277  const std::string& keytype, const std::string& desc,
278  bool required, uint32_t& dest) // NOLINT
279  : Argument(key, longkey, keytype, desc, required), dest_(dest) { }
280 
281  const char * type_name() const final { return "bytes"; }
282 
283  //! parse byte size using SI/IEC parser.
284  bool process(int& argc, const char* const*& argv) final { // NOLINT
285  if (argc == 0)
286  return false;
287  uint64_t dest;
288  if (parse_si_iec_units(argv[0], &dest) &&
289  static_cast<uint64_t>(
290  dest_ = static_cast<uint32_t>(dest)) == dest) {
291  --argc, ++argv;
292  return true;
293  }
294  else {
295  return false;
296  }
297  }
298 
299  void print_value(std::ostream& os) const final { os << dest_; }
300 };
301 
302 //! specialization of argument for SI/IEC suffixes byte size options or
303 //! parameters
304 struct CmdlineParser::ArgumentBytes64 final : public Argument {
305  uint64_t& dest_;
306 
307  //! contructor filling most attributes
308  ArgumentBytes64(char key, const std::string& longkey,
309  const std::string& keytype, const std::string& desc,
310  bool required, uint64_t& dest) // NOLINT
311  : Argument(key, longkey, keytype, desc, required), dest_(dest) { }
312 
313  const char * type_name() const final { return "bytes"; }
314 
315  //! parse byte size using SI/IEC parser.
316  bool process(int& argc, const char* const*& argv) final { // NOLINT
317  if (argc == 0)
318  return false;
319  if (parse_si_iec_units(argv[0], &dest_)) {
320  --argc, ++argv;
321  return true;
322  }
323  else {
324  return false;
325  }
326  }
327 
328  void print_value(std::ostream& os) const final { os << dest_; }
329 };
330 
331 //! specialization of argument for string options or parameters
332 struct CmdlineParser::ArgumentString final : public Argument {
333  std::string& dest_;
334 
335  //! contructor filling most attributes
336  ArgumentString(char key, const std::string& longkey,
337  const std::string& keytype, const std::string& desc,
338  bool required, std::string& dest) // NOLINT
339  : Argument(key, longkey, keytype, desc, required), dest_(dest) { }
340 
341  const char * type_name() const final { return "string"; }
342 
343  //! "process" string argument just by storing it.
344  bool process(int& argc, const char* const*& argv) final { // NOLINT
345  if (argc == 0)
346  return false;
347  dest_ = argv[0];
348  --argc, ++argv;
349  return true;
350  }
351 
352  void print_value(std::ostream& os) const final {
353  os << '"' << dest_ << '"';
354  }
355 };
356 
357 //! specialization of argument for multiple string options or parameters
358 struct CmdlineParser::ArgumentStringlist final : public Argument {
359  std::vector<std::string>& dest_;
360 
361  //! contructor filling most attributes
362  ArgumentStringlist(char key, const std::string& longkey,
363  const std::string& keytype, const std::string& desc,
364  bool required, std::vector<std::string>& dest) // NOLINT
365  : Argument(key, longkey, keytype, desc, required), dest_(dest) {
366  repeated_ = true;
367  }
368 
369  const char * type_name() const final { return "string list"; }
370 
371  //! "process" string argument just by storing it in vector.
372  bool process(int& argc, const char* const*& argv) final { // NOLINT
373  if (argc == 0)
374  return false;
375  dest_.emplace_back(argv[0]);
376  --argc, ++argv;
377  return true;
378  }
379 
380  void print_value(std::ostream& os) const final {
381  os << '[';
382  for (size_t i = 0; i < dest_.size(); ++i) {
383  if (i != 0)
384  os << ',';
385  os << '"' << dest_[i] << '"';
386  }
387  os << ']';
388  }
389 };
390 
391 /******************************************************************************/
392 
393 void CmdlineParser::calc_option_max(const Argument* arg) {
395  arg->option_text().size() + 2, option_max_width_);
396 }
397 
398 void CmdlineParser::calc_param_max(const Argument* arg) {
400  arg->param_text().size() + 2, param_max_width_);
401 }
402 
403 /******************************************************************************/
404 
406  std::ostream& os, const std::string& text,
407  size_t wraplen, size_t indent_first, size_t indent_rest, size_t current,
408  size_t indent_newline) {
409 
410  std::string::size_type t = 0;
411  size_t indent = indent_first;
412 
413  while (t != text.size()) {
414  std::string::size_type to = t, lspace = t;
415 
416  // scan forward in text until we hit a newline or wrap point
417  while (to != text.size() && to + current + indent < t + wraplen &&
418  text[to] != '\n') {
419  if (text[to] == ' ')
420  lspace = to;
421  ++to;
422  }
423 
424  // go back to last space
425  if (to != text.size() && text[to] != '\n' && lspace != t)
426  to = lspace + 1;
427 
428  // output line
429  os << std::string(indent, ' ') << text.substr(t, to - t) << std::endl;
430 
431  current = 0;
432  indent = indent_rest;
433 
434  // skip over last newline
435  if (to != text.size() && text[to] == '\n') {
436  indent = indent_newline;
437  ++to;
438  }
439 
440  t = to;
441  }
442 }
443 
444 /******************************************************************************/
445 
447 
449  for (size_t i = 0; i < option_list_.size(); ++i)
450  delete option_list_[i];
451  option_list_.clear();
452 
453  for (size_t i = 0; i < param_list_.size(); ++i)
454  delete param_list_[i];
455  param_list_.clear();
456 }
457 
458 void CmdlineParser::set_description(const std::string& description) {
459  description_ = description;
460 }
461 
463  author_ = author;
464 }
465 
466 void CmdlineParser::set_verbose_process(bool verbose_process) {
467  verbose_process_ = verbose_process;
468 }
469 
470 /******************************************************************************/
471 
472 void CmdlineParser::add_bool(char key, const std::string& longkey,
473  const std::string& keytype, bool& dest,
474  const std::string& desc) {
475  option_list_.emplace_back(
476  new ArgumentBool(key, longkey, keytype, desc, false, dest));
478 }
479 
480 void CmdlineParser::add_flag(char key, const std::string& longkey,
481  const std::string& keytype, bool& dest,
482  const std::string& desc) {
483  return add_bool(key, longkey, keytype, dest, desc);
484 }
485 
486 void CmdlineParser::add_int(char key, const std::string& longkey,
487  const std::string& keytype, int& dest,
488  const std::string& desc) {
489  option_list_.emplace_back(
490  new ArgumentInt(key, longkey, keytype, desc, false, dest));
492 }
493 
494 void CmdlineParser::add_unsigned(char key, const std::string& longkey,
495  const std::string& keytype, unsigned int& dest,
496  const std::string& desc) {
497  option_list_.emplace_back(
498  new ArgumentUnsigned(key, longkey, keytype, desc, false, dest));
500 }
501 
502 void CmdlineParser::add_uint(char key, const std::string& longkey,
503  const std::string& keytype, unsigned int& dest,
504  const std::string& desc) {
505  return add_unsigned(key, longkey, keytype, dest, desc);
506 }
507 
508 void CmdlineParser::add_size_t(char key, const std::string& longkey,
509  const std::string& keytype, size_t& dest,
510  const std::string& desc) {
511  option_list_.emplace_back(
512  new ArgumentSizeT(key, longkey, keytype, desc, false, dest));
514 }
515 
516 void CmdlineParser::add_float(char key, const std::string& longkey,
517  const std::string& keytype, float& dest,
518  const std::string& desc) {
519  option_list_.emplace_back(
520  new ArgumentFloat(key, longkey, keytype, desc, false, dest));
522 }
523 
524 void CmdlineParser::add_double(char key, const std::string& longkey,
525  const std::string& keytype, double& dest,
526  const std::string& desc) {
527  option_list_.emplace_back(
528  new ArgumentDouble(key, longkey, keytype, desc, false, dest));
530 }
531 
532 void CmdlineParser::add_bytes(char key, const std::string& longkey,
533  const std::string& keytype, uint32_t& dest,
534  const std::string& desc) {
535  option_list_.emplace_back(
536  new ArgumentBytes32(key, longkey, keytype, desc, false, dest));
538 }
539 
540 void CmdlineParser::add_bytes(char key, const std::string& longkey,
541  const std::string& keytype, uint64_t& dest,
542  const std::string& desc) {
543  option_list_.emplace_back(
544  new ArgumentBytes64(key, longkey, keytype, desc, false, dest));
546 }
547 
548 void CmdlineParser::add_string(char key, const std::string& longkey,
549  const std::string& keytype, std::string& dest,
550  const std::string& desc) {
551  option_list_.emplace_back(
552  new ArgumentString(key, longkey, keytype, desc, false, dest));
554 }
555 
557  char key, const std::string& longkey,
558  const std::string& keytype, std::vector<std::string>& dest,
559  const std::string& desc) {
560 
561  option_list_.emplace_back(
562  new ArgumentStringlist(key, longkey, keytype, desc, false, dest));
564 }
565 
566 /******************************************************************************/
567 
569  char key, const std::string& longkey, bool& dest, const std::string& desc) {
570  return add_bool(key, longkey, "", dest, desc);
571 }
572 
574  char key, const std::string& longkey, bool& dest, const std::string& desc) {
575  return add_bool(key, longkey, dest, desc);
576 }
577 
579  char key, const std::string& longkey, int& dest, const std::string& desc) {
580  return add_int(key, longkey, "", dest, desc);
581 }
582 
583 void CmdlineParser::add_unsigned(char key, const std::string& longkey,
584  unsigned int& dest, const std::string& desc) {
585  return add_unsigned(key, longkey, "", dest, desc);
586 }
587 
588 void CmdlineParser::add_uint(char key, const std::string& longkey,
589  unsigned int& dest, const std::string& desc) {
590  return add_unsigned(key, longkey, dest, desc);
591 }
592 
593 void CmdlineParser::add_size_t(char key, const std::string& longkey,
594  size_t& dest, const std::string& desc) {
595  return add_size_t(key, longkey, "", dest, desc);
596 }
597 
598 void CmdlineParser::add_float(char key, const std::string& longkey,
599  float& dest, const std::string& desc) {
600  return add_float(key, longkey, "", dest, desc);
601 }
602 
603 void CmdlineParser::add_double(char key, const std::string& longkey,
604  double& dest, const std::string& desc) {
605  return add_double(key, longkey, "", dest, desc);
606 }
607 
608 void CmdlineParser::add_bytes(char key, const std::string& longkey,
609  uint32_t& dest, const std::string& desc) {
610  return add_bytes(key, longkey, "", dest, desc);
611 }
612 
613 void CmdlineParser::add_bytes(char key, const std::string& longkey,
614  uint64_t& dest, const std::string& desc) {
615  return add_bytes(key, longkey, "", dest, desc);
616 }
617 
618 void CmdlineParser::add_string(char key, const std::string& longkey,
619  std::string& dest, const std::string& desc) {
620  return add_string(key, longkey, "", dest, desc);
621 }
622 
624  char key, const std::string& longkey,
625  std::vector<std::string>& dest, const std::string& desc) {
626  return add_stringlist(key, longkey, "", dest, desc);
627 }
628 
629 /******************************************************************************/
630 
632  const std::string& longkey, bool& dest, const std::string& desc) {
633  return add_bool(0, longkey, "", dest, desc);
634 }
635 
637  const std::string& longkey, bool& dest, const std::string& desc) {
638  return add_bool(0, longkey, dest, desc);
639 }
640 
642  const std::string& longkey, int& dest, const std::string& desc) {
643  return add_int(0, longkey, "", dest, desc);
644 }
645 
647  unsigned int& dest, const std::string& desc) {
648  return add_unsigned(0, longkey, "", dest, desc);
649 }
650 
652  unsigned int& dest, const std::string& desc) {
653  return add_unsigned(0, longkey, dest, desc);
654 }
655 
657  size_t& dest, const std::string& desc) {
658  return add_size_t(0, longkey, "", dest, desc);
659 }
660 
662  float& dest, const std::string& desc) {
663  return add_float(0, longkey, "", dest, desc);
664 }
665 
667  double& dest, const std::string& desc) {
668  return add_double(0, longkey, "", dest, desc);
669 }
670 
672  uint32_t& dest, const std::string& desc) {
673  return add_bytes(0, longkey, "", dest, desc);
674 }
675 
677  uint64_t& dest, const std::string& desc) {
678  return add_bytes(0, longkey, "", dest, desc);
679 }
680 
682  std::string& dest, const std::string& desc) {
683  return add_string(0, longkey, "", dest, desc);
684 }
685 
687  const std::string& longkey,
688  std::vector<std::string>& dest, const std::string& desc) {
689  return add_stringlist(0, longkey, "", dest, desc);
690 }
691 
692 /******************************************************************************/
693 
695  const std::string& name, int& dest, const std::string& desc) {
696  param_list_.emplace_back(new ArgumentInt(0, name, "", desc, true, dest));
697  calc_param_max(param_list_.back());
698 }
699 
701  const std::string& name, unsigned int& dest, const std::string& desc) {
702  param_list_.emplace_back(
703  new ArgumentUnsigned(0, name, "", desc, true, dest));
704  calc_param_max(param_list_.back());
705 }
706 
708  const std::string& name, unsigned int& dest, const std::string& desc) {
709  add_param_unsigned(name, dest, desc);
710 }
711 
713  const std::string& name, size_t& dest, const std::string& desc) {
714  param_list_.emplace_back(new ArgumentSizeT(0, name, "", desc, true, dest));
715  calc_param_max(param_list_.back());
716 }
717 
719  const std::string& name, float& dest, const std::string& desc) {
720  param_list_.emplace_back(new ArgumentFloat(0, name, "", desc, true, dest));
721  calc_param_max(param_list_.back());
722 }
723 
725  const std::string& name, double& dest, const std::string& desc) {
726  param_list_.emplace_back(new ArgumentDouble(0, name, "", desc, true, dest));
727  calc_param_max(param_list_.back());
728 }
729 
731  const std::string& name, uint32_t& dest, const std::string& desc) {
732  param_list_.emplace_back(
733  new ArgumentBytes32(0, name, "", desc, true, dest));
734  calc_param_max(param_list_.back());
735 }
736 
738  const std::string& name, uint64_t& dest, const std::string& desc) {
739  param_list_.emplace_back(
740  new ArgumentBytes64(0, name, "", desc, true, dest));
741  calc_param_max(param_list_.back());
742 }
743 
745  const std::string& name, std::string& dest, const std::string& desc) {
746  param_list_.emplace_back(new ArgumentString(0, name, "", desc, true, dest));
747  calc_param_max(param_list_.back());
748 }
749 
751  const std::string& name, std::vector<std::string>& dest,
752  const std::string& desc) {
753  param_list_.emplace_back(
754  new ArgumentStringlist(0, name, "", desc, true, dest));
755  calc_param_max(param_list_.back());
756 }
757 
758 /******************************************************************************/
759 
761  const std::string& name, int& dest, const std::string& desc) {
762  param_list_.emplace_back(new ArgumentInt(0, name, "", desc, false, dest));
763  calc_param_max(param_list_.back());
764 }
765 
767  const std::string& name, unsigned int& dest, const std::string& desc) {
768  param_list_.emplace_back(
769  new ArgumentUnsigned(0, name, "", desc, false, dest));
770  calc_param_max(param_list_.back());
771 }
772 
774  const std::string& name, unsigned int& dest, const std::string& desc) {
775  return add_opt_param_unsigned(name, dest, desc);
776 }
777 
779  const std::string& name, size_t& dest, const std::string& desc) {
780  param_list_.emplace_back(new ArgumentSizeT(0, name, "", desc, false, dest));
781  calc_param_max(param_list_.back());
782 }
783 
785  const std::string& name, float& dest, const std::string& desc) {
786  param_list_.emplace_back(new ArgumentFloat(0, name, "", desc, false, dest));
787  calc_param_max(param_list_.back());
788 }
789 
791  const std::string& name, double& dest, const std::string& desc) {
792  param_list_.emplace_back(
793  new ArgumentDouble(0, name, "", desc, false, dest));
794  calc_param_max(param_list_.back());
795 }
796 
798  const std::string& name, uint32_t& dest, const std::string& desc) {
799  param_list_.emplace_back(
800  new ArgumentBytes32(0, name, "", desc, false, dest));
801  calc_param_max(param_list_.back());
802 }
803 
805  const std::string& name, uint64_t& dest, const std::string& desc) {
806  param_list_.emplace_back(
807  new ArgumentBytes64(0, name, "", desc, false, dest));
808  calc_param_max(param_list_.back());
809 }
810 
812  const std::string& name, std::string& dest, const std::string& desc) {
813  param_list_.emplace_back(
814  new ArgumentString(0, name, "", desc, false, dest));
815  calc_param_max(param_list_.back());
816 }
817 
819  const std::string& name, std::vector<std::string>& dest,
820  const std::string& desc) {
821  param_list_.emplace_back(
822  new ArgumentStringlist(0, name, "", desc, false, dest));
823  calc_param_max(param_list_.back());
824 }
825 
826 /******************************************************************************/
827 
828 void CmdlineParser::print_usage(std::ostream& os) {
829  std::ios::fmtflags flags(os.flags());
830 
831  os << "Usage: " << program_name_
832  << (!option_list_.empty() ? " [options]" : "");
833 
834  for (ArgumentList::const_iterator it = param_list_.begin();
835  it != param_list_.end(); ++it) {
836  const Argument* arg = *it;
837 
838  os << (arg->required_ ? " <" : " [") << arg->longkey_
839  << (arg->repeated_ ? " ..." : "") << (arg->required_ ? '>' : ']');
840  }
841 
842  os << std::endl;
843 
844  if (!description_.empty()) {
845  os << std::endl;
847  }
848  if (!author_.empty()) {
849  os << "Author: " << author_ << std::endl;
850  }
851 
852  if (!description_.empty() || !author_.empty())
853  os << std::endl;
854 
855  if (!param_list_.empty()) {
856  os << "Parameters:" << std::endl;
857 
858  for (ArgumentList::const_iterator it = param_list_.begin();
859  it != param_list_.end(); ++it) {
860  const Argument* arg = *it;
861 
862  os << " " << std::setw(static_cast<int>(param_max_width_))
863  << std::left << arg->param_text();
864  output_wrap(os, arg->desc_, line_wrap_, 0, param_max_width_ + 2,
865  param_max_width_ + 2, 8);
866  }
867  }
868 
869  if (!option_list_.empty()) {
870  os << "Options:" << std::endl;
871 
872  for (ArgumentList::const_iterator it = option_list_.begin();
873  it != option_list_.end(); ++it) {
874  const Argument* arg = *it;
875 
876  os << " " << std::setw(static_cast<int>(option_max_width_))
877  << std::left << arg->option_text();
878  output_wrap(os, arg->desc_, line_wrap_, 0, option_max_width_ + 2,
879  option_max_width_ + 2, 8);
880  }
881  }
882 
883  os.flags(flags);
884 }
885 
887  return print_usage(std::cout);
888 }
889 
891  int argc, const char* const* argv, const Argument* arg, std::ostream& os) {
892  os << "Error: argument ";
893  if (argc != 0)
894  os << '"' << argv[0] << '"';
895 
896  os << " for " << arg->type_name() << " option " << arg->option_text()
897  << (argc == 0 ? " is missing!" : " is invalid!") << std::endl
898  << std::endl;
899 
900  print_usage(os);
901 }
902 
904  int argc, const char* const* argv, const Argument* arg, std::ostream& os) {
905  os << "Error: argument ";
906  if (argc != 0)
907  os << '"' << argv[0] << '"';
908 
909  os << " for " << arg->type_name() << " parameter " << arg->param_text()
910  << (argc == 0 ? " is missing!" : " is invalid!") << std::endl
911  << std::endl;
912 
913  print_usage(os);
914 }
915 
917  int argc, const char* const* argv, std::ostream& os) {
918  program_name_ = argv[0];
919  --argc, ++argv;
920 
921  // search for help string and output help
922  for (int i = 0; i < argc; ++i) {
923  if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
924  print_usage(os);
925  return false;
926  }
927  }
928 
929  // current argument in param_list_
930  ArgumentList::iterator argi = param_list_.begin();
931  bool end_optlist = false;
932 
933  while (argc != 0) {
934  const char* arg = argv[0];
935 
936  if (arg[0] == '-' && !end_optlist) {
937  // option, advance to argument
938  --argc, ++argv;
939  if (arg[1] == '-') {
940  if (arg[2] == '-') {
941  end_optlist = true;
942  }
943  else {
944  // long option
945  ArgumentList::const_iterator oi = option_list_.begin();
946  for ( ; oi != option_list_.end(); ++oi) {
947  if ((arg + 2) == (*oi)->longkey_) {
948  if (!(*oi)->process(argc, argv)) {
949  print_option_error(argc, argv, *oi, os);
950  return false;
951  }
952  else if (verbose_process_) {
953  os << "Option " << (*oi)->option_text()
954  << " set to ";
955  (*oi)->print_value(os);
956  os << '.' << std::endl;
957  }
958  break;
959  }
960  }
961  if (oi == option_list_.end()) {
962  os << "Error: unknown option \"" << arg << "\"."
963  << std::endl << std::endl;
964  print_usage(os);
965  return false;
966  }
967  }
968  }
969  else {
970  // short option
971  if (arg[1] == 0) {
972  os << "Invalid option \"" << arg << "\"." << std::endl;
973  }
974  else {
975  size_t offset = 1, arg_length = strlen(arg);
976  int old_argc = argc;
977  // Arguments will increase argc, so abort if it increases,
978  // while flags won't, so increase offset and parse next
979  while (offset < arg_length && argc == old_argc) {
980  ArgumentList::const_iterator oi = option_list_.begin();
981  for ( ; oi != option_list_.end(); ++oi) {
982  if (arg[offset] == (*oi)->key_) {
983  ++offset;
984  if (!(*oi)->process(argc, argv)) {
985  print_option_error(argc, argv, *oi, os);
986  return false;
987  }
988  else if (verbose_process_) {
989  os << "Option "
990  << (*oi)->option_text()
991  << " set to ";
992  (*oi)->print_value(os);
993  os << '.' << std::endl;
994  }
995  break;
996  }
997  }
998  if (oi == option_list_.end()) {
999  os << "Error: unknown option \"";
1000  if (arg_length > 2) {
1001  // multiple short options combined
1002  os << "-" << arg[offset]
1003  << "\" at position " << offset
1004  << " in option sequence \"";
1005  }
1006  os << arg << "\"." << std::endl << std::endl;
1007  print_usage(os);
1008  return false;
1009  }
1010  }
1011  }
1012  }
1013  }
1014  else {
1015  if (argi != param_list_.end()) {
1016  if (!(*argi)->process(argc, argv)) {
1017  print_param_error(argc, argv, *argi, os);
1018  return false;
1019  }
1020  else if (verbose_process_) {
1021  os << "Parameter " << (*argi)->param_text() << " set to ";
1022  (*argi)->print_value(os);
1023  os << '.' << std::endl;
1024  }
1025  (*argi)->found_ = true;
1026  if (!(*argi)->repeated_)
1027  ++argi;
1028  }
1029  else {
1030  os << "Error: unexpected extra argument "
1031  << "\"" << argv[0] << "\"." << std::endl << std::endl;
1032  --argc, ++argv;
1033  print_usage(os);
1034  return false;
1035  }
1036  }
1037  }
1038 
1039  bool good = true;
1040 
1041  for (ArgumentList::const_iterator it = param_list_.begin();
1042  it != param_list_.end(); ++it) {
1043  if ((*it)->required_ && !(*it)->found_) {
1044  os << "Error: argument for parameter " << (*it)->longkey_
1045  << " is required!" << std::endl;
1046  good = false;
1047  }
1048  }
1049 
1050  if (!good) {
1051  os << std::endl;
1052  print_usage(os);
1053  }
1054 
1055  return good;
1056 }
1057 
1058 bool CmdlineParser::process(int argc, const char* const* argv) {
1059  return process(argc, argv, std::cout);
1060 }
1061 
1062 void CmdlineParser::print_result(std::ostream& os) {
1063  std::ios::fmtflags flags(os.flags());
1064 
1065  size_t maxlong = std::max(param_max_width_, option_max_width_);
1066 
1067  if (!param_list_.empty()) {
1068  os << "Parameters:" << std::endl;
1069 
1070  for (ArgumentList::const_iterator it = param_list_.begin();
1071  it != param_list_.end(); ++it) {
1072  const Argument* arg = *it;
1073 
1074  os << " " << std::setw(static_cast<int>(maxlong))
1075  << std::left << arg->param_text();
1076 
1077  std::string typestr = "(" + std::string(arg->type_name()) + ")";
1078  os << std::setw(max_type_name_ + 4) << typestr;
1079 
1080  arg->print_value(os);
1081 
1082  os << std::endl;
1083  }
1084  }
1085 
1086  if (!option_list_.empty()) {
1087  os << "Options:" << std::endl;
1088 
1089  for (ArgumentList::const_iterator it = option_list_.begin();
1090  it != option_list_.end(); ++it) {
1091  const Argument* arg = *it;
1092 
1093  os << " " << std::setw(static_cast<int>(maxlong))
1094  << std::left << arg->option_text();
1095 
1096  std::string typestr = "(" + std::string(arg->type_name()) + ")";
1097  os << std::setw(max_type_name_ + 4) << std::left << typestr;
1098 
1099  arg->print_value(os);
1100 
1101  os << std::endl;
1102  }
1103  }
1104 
1105  os.flags(flags);
1106 }
1107 
1109  return print_result(std::cout);
1110 }
1111 
1112 } // namespace tlx
1113 
1114 /******************************************************************************/
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
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
size_t param_max_width_
formatting width for parameters, 'param <#>'
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, '-s, –switch <#>'
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.