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& name, int& dest, const std::string& desc) {
633  param_list_.emplace_back(new ArgumentInt(0, name, "", desc, true, dest));
634  calc_param_max(param_list_.back());
635 }
636 
638  const std::string& name, unsigned int& dest, const std::string& desc) {
639  param_list_.emplace_back(
640  new ArgumentUnsigned(0, name, "", desc, true, dest));
641  calc_param_max(param_list_.back());
642 }
643 
645  const std::string& name, unsigned int& dest, const std::string& desc) {
646  add_param_unsigned(name, dest, desc);
647 }
648 
650  const std::string& name, size_t& dest, const std::string& desc) {
651  param_list_.emplace_back(new ArgumentSizeT(0, name, "", desc, true, dest));
652  calc_param_max(param_list_.back());
653 }
654 
656  const std::string& name, float& dest, const std::string& desc) {
657  param_list_.emplace_back(new ArgumentFloat(0, name, "", desc, true, dest));
658  calc_param_max(param_list_.back());
659 }
660 
662  const std::string& name, double& dest, const std::string& desc) {
663  param_list_.emplace_back(new ArgumentDouble(0, name, "", desc, true, dest));
664  calc_param_max(param_list_.back());
665 }
666 
668  const std::string& name, uint32_t& dest, const std::string& desc) {
669  param_list_.emplace_back(
670  new ArgumentBytes32(0, name, "", desc, true, dest));
671  calc_param_max(param_list_.back());
672 }
673 
675  const std::string& name, uint64_t& dest, const std::string& desc) {
676  param_list_.emplace_back(
677  new ArgumentBytes64(0, name, "", desc, true, dest));
678  calc_param_max(param_list_.back());
679 }
680 
682  const std::string& name, std::string& dest, const std::string& desc) {
683  param_list_.emplace_back(new ArgumentString(0, name, "", desc, true, dest));
684  calc_param_max(param_list_.back());
685 }
686 
688  const std::string& name, std::vector<std::string>& dest,
689  const std::string& desc) {
690  param_list_.emplace_back(
691  new ArgumentStringlist(0, name, "", desc, true, dest));
692  calc_param_max(param_list_.back());
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, false, 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, false, dest));
707  calc_param_max(param_list_.back());
708 }
709 
711  const std::string& name, unsigned int& dest, const std::string& desc) {
712  return add_opt_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, false, 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, false, 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(
730  new ArgumentDouble(0, name, "", desc, false, dest));
731  calc_param_max(param_list_.back());
732 }
733 
735  const std::string& name, uint32_t& dest, const std::string& desc) {
736  param_list_.emplace_back(
737  new ArgumentBytes32(0, name, "", desc, false, dest));
738  calc_param_max(param_list_.back());
739 }
740 
742  const std::string& name, uint64_t& dest, const std::string& desc) {
743  param_list_.emplace_back(
744  new ArgumentBytes64(0, name, "", desc, false, dest));
745  calc_param_max(param_list_.back());
746 }
747 
749  const std::string& name, std::string& dest, const std::string& desc) {
750  param_list_.emplace_back(
751  new ArgumentString(0, name, "", desc, false, dest));
752  calc_param_max(param_list_.back());
753 }
754 
756  const std::string& name, std::vector<std::string>& dest,
757  const std::string& desc) {
758  param_list_.emplace_back(
759  new ArgumentStringlist(0, name, "", desc, false, dest));
760  calc_param_max(param_list_.back());
761 }
762 
763 /******************************************************************************/
764 
765 void CmdlineParser::print_usage(std::ostream& os) {
766  std::ios::fmtflags flags(os.flags());
767 
768  os << "Usage: " << program_name_
769  << (!option_list_.empty() ? " [options]" : "");
770 
771  for (ArgumentList::const_iterator it = param_list_.begin();
772  it != param_list_.end(); ++it) {
773  const Argument* arg = *it;
774 
775  os << (arg->required_ ? " <" : " [") << arg->longkey_
776  << (arg->repeated_ ? " ..." : "") << (arg->required_ ? '>' : ']');
777  }
778 
779  os << std::endl;
780 
781  if (!description_.empty()) {
782  os << std::endl;
784  }
785  if (!author_.empty()) {
786  os << "Author: " << author_ << std::endl;
787  }
788 
789  if (!description_.empty() || !author_.empty())
790  os << std::endl;
791 
792  if (!param_list_.empty()) {
793  os << "Parameters:" << std::endl;
794 
795  for (ArgumentList::const_iterator it = param_list_.begin();
796  it != param_list_.end(); ++it) {
797  const Argument* arg = *it;
798 
799  os << " " << std::setw(static_cast<int>(param_max_width_))
800  << std::left << arg->param_text();
801  output_wrap(os, arg->desc_, line_wrap_, 0, param_max_width_ + 2,
802  param_max_width_ + 2, 8);
803  }
804  }
805 
806  if (!option_list_.empty()) {
807  os << "Options:" << std::endl;
808 
809  for (ArgumentList::const_iterator it = option_list_.begin();
810  it != option_list_.end(); ++it) {
811  const Argument* arg = *it;
812 
813  os << " " << std::setw(static_cast<int>(option_max_width_))
814  << std::left << arg->option_text();
815  output_wrap(os, arg->desc_, line_wrap_, 0, option_max_width_ + 2,
816  option_max_width_ + 2, 8);
817  }
818  }
819 
820  os.flags(flags);
821 }
822 
824  return print_usage(std::cout);
825 }
826 
828  int argc, const char* const* argv, const Argument* arg, std::ostream& os) {
829  os << "Error: argument ";
830  if (argc != 0)
831  os << '"' << argv[0] << '"';
832 
833  os << " for " << arg->type_name() << " option " << arg->option_text()
834  << (argc == 0 ? " is missing!" : " is invalid!") << std::endl
835  << std::endl;
836 
837  print_usage(os);
838 }
839 
841  int argc, const char* const* argv, const Argument* arg, std::ostream& os) {
842  os << "Error: argument ";
843  if (argc != 0)
844  os << '"' << argv[0] << '"';
845 
846  os << " for " << arg->type_name() << " parameter " << arg->param_text()
847  << (argc == 0 ? " is missing!" : " is invalid!") << std::endl
848  << std::endl;
849 
850  print_usage(os);
851 }
852 
854  int argc, const char* const* argv, std::ostream& os) {
855  program_name_ = argv[0];
856  --argc, ++argv;
857 
858  // search for help string and output help
859  for (int i = 0; i < argc; ++i) {
860  if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
861  print_usage(os);
862  return false;
863  }
864  }
865 
866  // current argument in param_list_
867  ArgumentList::iterator argi = param_list_.begin();
868  bool end_optlist = false;
869 
870  while (argc != 0) {
871  const char* arg = argv[0];
872 
873  if (arg[0] == '-' && !end_optlist) {
874  // option, advance to argument
875  --argc, ++argv;
876  if (arg[1] == '-') {
877  if (arg[2] == '-') {
878  end_optlist = true;
879  }
880  else {
881  // long option
882  ArgumentList::const_iterator oi = option_list_.begin();
883  for ( ; oi != option_list_.end(); ++oi) {
884  if ((arg + 2) == (*oi)->longkey_) {
885  if (!(*oi)->process(argc, argv)) {
886  print_option_error(argc, argv, *oi, os);
887  return false;
888  }
889  else if (verbose_process_) {
890  os << "Option " << (*oi)->option_text()
891  << " set to ";
892  (*oi)->print_value(os);
893  os << '.' << std::endl;
894  }
895  break;
896  }
897  }
898  if (oi == option_list_.end()) {
899  os << "Error: unknown option \"" << arg << "\"."
900  << std::endl << std::endl;
901  print_usage(os);
902  return false;
903  }
904  }
905  }
906  else {
907  // short option
908  if (arg[1] == 0) {
909  os << "Invalid option \"" << arg << "\"." << std::endl;
910  }
911  else {
912  size_t offset = 1, arg_length = strlen(arg);
913  int old_argc = argc;
914  // Arguments will increase argc, so abort if it increases,
915  // while flags won't, so increase offset and parse next
916  while (offset < arg_length && argc == old_argc) {
917  ArgumentList::const_iterator oi = option_list_.begin();
918  for ( ; oi != option_list_.end(); ++oi) {
919  if (arg[offset] == (*oi)->key_) {
920  ++offset;
921  if (!(*oi)->process(argc, argv)) {
922  print_option_error(argc, argv, *oi, os);
923  return false;
924  }
925  else if (verbose_process_) {
926  os << "Option "
927  << (*oi)->option_text()
928  << " set to ";
929  (*oi)->print_value(os);
930  os << '.' << std::endl;
931  }
932  break;
933  }
934  }
935  if (oi == option_list_.end()) {
936  os << "Error: unknown option \"";
937  if (arg_length > 2) {
938  // multiple short options combined
939  os << "-" << arg[offset]
940  << "\" at position " << offset
941  << " in option sequence \"";
942  }
943  os << arg << "\"." << std::endl << std::endl;
944  print_usage(os);
945  return false;
946  }
947  }
948  }
949  }
950  }
951  else {
952  if (argi != param_list_.end()) {
953  if (!(*argi)->process(argc, argv)) {
954  print_param_error(argc, argv, *argi, os);
955  return false;
956  }
957  else if (verbose_process_) {
958  os << "Parameter " << (*argi)->param_text() << " set to ";
959  (*argi)->print_value(os);
960  os << '.' << std::endl;
961  }
962  (*argi)->found_ = true;
963  if (!(*argi)->repeated_)
964  ++argi;
965  }
966  else {
967  os << "Error: unexpected extra argument "
968  << "\"" << argv[0] << "\"." << std::endl << std::endl;
969  --argc, ++argv;
970  print_usage(os);
971  return false;
972  }
973  }
974  }
975 
976  bool good = true;
977 
978  for (ArgumentList::const_iterator it = param_list_.begin();
979  it != param_list_.end(); ++it) {
980  if ((*it)->required_ && !(*it)->found_) {
981  os << "Error: argument for parameter " << (*it)->longkey_
982  << " is required!" << std::endl;
983  good = false;
984  }
985  }
986 
987  if (!good) {
988  os << std::endl;
989  print_usage(os);
990  }
991 
992  return good;
993 }
994 
995 bool CmdlineParser::process(int argc, const char* const* argv) {
996  return process(argc, argv, std::cout);
997 }
998 
999 void CmdlineParser::print_result(std::ostream& os) {
1000  std::ios::fmtflags flags(os.flags());
1001 
1002  size_t maxlong = std::max(param_max_width_, option_max_width_);
1003 
1004  if (!param_list_.empty()) {
1005  os << "Parameters:" << std::endl;
1006 
1007  for (ArgumentList::const_iterator it = param_list_.begin();
1008  it != param_list_.end(); ++it) {
1009  const Argument* arg = *it;
1010 
1011  os << " " << std::setw(static_cast<int>(maxlong))
1012  << std::left << arg->param_text();
1013 
1014  std::string typestr = "(" + std::string(arg->type_name()) + ")";
1015  os << std::setw(max_type_name_ + 4) << typestr;
1016 
1017  arg->print_value(os);
1018 
1019  os << std::endl;
1020  }
1021  }
1022 
1023  if (!option_list_.empty()) {
1024  os << "Options:" << std::endl;
1025 
1026  for (ArgumentList::const_iterator it = option_list_.begin();
1027  it != option_list_.end(); ++it) {
1028  const Argument* arg = *it;
1029 
1030  os << " " << std::setw(static_cast<int>(maxlong))
1031  << std::left << arg->option_text();
1032 
1033  std::string typestr = "(" + std::string(arg->type_name()) + ")";
1034  os << std::setw(max_type_name_ + 4) << std::left << typestr;
1035 
1036  arg->print_value(os);
1037 
1038  os << std::endl;
1039  }
1040  }
1041 
1042  os.flags(flags);
1043 }
1044 
1046  return print_result(std::cout);
1047 }
1048 
1049 } // namespace tlx
1050 
1051 /******************************************************************************/
void add_opt_param_bytes(const std::string &name, uint32_t &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_float(char key, const std::string &longkey, const std::string &keytype, float &dest, const std::string &desc)
void add_size_t(char key, const std::string &longkey, const std::string &keytype, size_t &dest, const std::string &desc)
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.
void add_uint(char key, const std::string &longkey, const std::string &keytype, unsigned int &dest, const std::string &desc)
~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 add_double(char key, const std::string &longkey, const std::string &keytype, double &dest, const std::string &desc)
void add_int(char key, const std::string &longkey, const std::string &keytype, int &dest, const std::string &desc)
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 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 add_string(char key, const std::string &longkey, const std::string &keytype, std::string &dest, const std::string &desc)
add string option -key, –longkey [keytype] and store to dest
void print_result()
print nicely formatted result of processing to std::cout
ArgumentList param_list_
list of parameters, both required and optional
void add_bytes(char key, const std::string &longkey, const std::string &keytype, uint32_t &dest, const std::string &desc)
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_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
void add_flag(char key, const std::string &longkey, const std::string &keytype, bool &dest, const std::string &desc)
static constexpr int max_type_name_
maximum length of a type_name() result
void add_stringlist(char key, const std::string &longkey, const std::string &keytype, std::vector< std::string > &dest, const std::string &desc)
add string list option -key, –longkey [keytype] 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
std::string author_
user set author of program, will be wrapped
unsigned int line_wrap_
set line wrap length
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_unsigned(char key, const std::string &longkey, const std::string &keytype, unsigned int &dest, const std::string &desc)
size_t option_max_width_
formatting width for options, '-s, –switch <#>'
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_bool(char key, const std::string &longkey, const std::string &keytype, bool &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)
static constexpr const T & max(const T &a, const T &b)
template for constexpr max, because std::max is not good enough.
Definition: functional.hpp:65
void print_param_error(int argc, const char *const *argv, const Argument *arg, std::ostream &os)
print error about parameter.