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
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
26namespace tlx {
27
28/******************************************************************************/
29// Argument and Struct Hierarchy below it.
30
31//! base class of all options and parameters
33{
34public:
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.
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
50public:
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).
96class TLX_VISIBILITY_HIDDEN CmdlineParser::ArgumentBool final
97 : public Argument
98{
99protected:
100 //! reference to boolean to set to true
101 bool& dest_;
102
103public:
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
125class TLX_VISIBILITY_HIDDEN CmdlineParser::ArgumentInt final
126 : public Argument
127{
128protected:
129 int& dest_;
130
131public:
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
161class TLX_VISIBILITY_HIDDEN CmdlineParser::ArgumentUnsigned final
162 : public Argument
163{
164protected:
165 unsigned int& dest_;
166
167public:
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
197class TLX_VISIBILITY_HIDDEN CmdlineParser::ArgumentSizeT final
198 : public Argument
199{
200protected:
201 size_t& dest_;
202
203public:
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
233class TLX_VISIBILITY_HIDDEN CmdlineParser::ArgumentFloat final
234 : public Argument
235{
236protected:
237 float& dest_;
238
239public:
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
267class TLX_VISIBILITY_HIDDEN CmdlineParser::ArgumentDouble final : public Argument
268{
269protected:
270 double& dest_;
271
272public:
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
301class TLX_VISIBILITY_HIDDEN CmdlineParser::ArgumentBytes32 final
302 : public Argument
303{
304protected:
305 uint32_t& dest_;
306
307public:
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
337class TLX_VISIBILITY_HIDDEN CmdlineParser::ArgumentBytes64 final : public Argument
338{
339protected:
340 uint64_t& dest_;
341
342public:
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
368class TLX_VISIBILITY_HIDDEN CmdlineParser::ArgumentString final
369 : public Argument
370{
371protected:
372 std::string& dest_;
373
374public:
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
398class TLX_VISIBILITY_HIDDEN CmdlineParser::ArgumentStringlist final
399 : public Argument
400{
401protected:
402 std::vector<std::string>& dest_;
403
404public:
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
437void CmdlineParser::calc_option_max(const Argument* arg) {
438 option_max_width_ = std::max(
439 arg->option_text().size() + 2, option_max_width_);
440}
441
442void 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
502void CmdlineParser::set_description(const std::string& description) {
503 description_ = description;
504}
505
506void CmdlineParser::set_author(const std::string& author) {
507 author_ = author;
508}
509
510void CmdlineParser::set_verbose_process(bool verbose_process) {
511 verbose_process_ = verbose_process;
512}
513
514/******************************************************************************/
515
516void 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));
522}
523
524void 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
530void 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));
536}
537
538void 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));
544}
545
546void 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
552void 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));
558}
559
560void 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));
566}
567
568void 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));
574}
575
576void 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));
582}
583
584void 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));
590}
591
592void 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));
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));
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
627void 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
632void 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
637void 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
642void 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
647void 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
652void 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
657void 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
662void 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
690void 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
695void 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
700void 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
705void CmdlineParser::add_float(const std::string& longkey,
706 float& dest, const std::string& desc) {
707 return add_float(0, longkey, "", dest, desc);
708}
709
710void CmdlineParser::add_double(const std::string& longkey,
711 double& dest, const std::string& desc) {
712 return add_double(0, longkey, "", dest, desc);
713}
714
715void 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
720void 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
725void 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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
868}
869
870/******************************************************************************/
871
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
880void 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;
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
1110bool CmdlineParser::process(int argc, const char* const* argv) {
1111 return process(argc, argv, std::cout);
1112}
1113
1114void 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/******************************************************************************/
base class of all options and parameters
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_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.
std::string keytype_
option type description, e.g. "<#>" to indicate numbers
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.
bool required_
required, process() fails if the option/parameter is not found.
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.
virtual void print_value(std::ostream &os) const =0
format value to ostream
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 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
ArgumentList option_list_
list of options available
void set_author(const std::string &author)
Set author of program, will be wrapped.
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.
size_t option_max_width_
formatting width for options, '-s, –switch <#>'
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
CmdlineParser & sort()
sort options by key (but not the positional parameters)
std::string param_text() const
return 'longkey [keytype]'
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 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_param_uint(const std::string &name, unsigned int &dest, const std::string &desc)
add 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
const char * program_name_
argv[0] for usage.
CmdlineParser()
Constructor.
void set_verbose_process(bool verbose_process)
Set verbose processing of command line arguments.
virtual const char * type_name() const =0
return formatted type name to user
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
virtual bool process(int &argc, const char *const *&argv)=0
process one item from command line for this argument
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
virtual ~Argument()=default
empty virtual destructor
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
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 print_result()
print nicely formatted result of processing to std::cout
std::string desc_
longer description, which will be wrapped
bool & dest_
reference to boolean to set to true
bool verbose_process_
verbose processing of arguments
const char * type_name() const final
ArgumentBytes64(char key, const std::string &longkey, const std::string &keytype, const std::string &desc, bool required, uint64_t &dest)
contructor filling most attributes
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
ArgumentList param_list_
list of parameters, both required and optional
void calc_param_max(const Argument *arg)
update maximum formatting width for new parameter
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_param_double(const std::string &name, double &dest, const std::string &desc)
add double parameter [name] with description and store to dest
std::string author_
user set author of program, will be wrapped
void set_description(const std::string &description)
Set description of program, text will be wrapped.
ArgumentBool(char key, const std::string &longkey, const std::string &keytype, const std::string &desc, bool required, bool &dest)
contructor filling most attributes
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
~CmdlineParser()
Delete all added arguments.
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 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
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
std::string option_text() const
return '-s, –longkey [keytype]'
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_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
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 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_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
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 print_option_error(int argc, const char *const *argv, const Argument *arg, std::ostream &os)
print error about option.
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
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
Argument(char key, const std::string &longkey, const std::string &keytype, const std::string &desc, bool required)
contructor filling most attributes
std::vector< std::string > & 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
ArgumentFloat(char key, const std::string &longkey, const std::string &keytype, const std::string &desc, bool required, float &dest)
contructor filling most attributes
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
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
unsigned int & dest_
static constexpr int max_type_name_
maximum length of a type_name() result
void print_usage()
output to std::cout nicely formatted usage information including description of all parameters and op...
std::string description_
user set description of program, will be wrapped
void print_value(std::ostream &os) const final
bool process(int &argc, const char *const *&argv) final
"process" argument: just set to true, no argument is used.
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.
size_t param_max_width_
formatting width for parameters, 'param <#>'
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 calc_option_max(const Argument *arg)
update maximum formatting width for new option
std::string longkey_
long option key or name for parameters
char key_
single letter short option, or 0 is none
#define TLX_VISIBILITY_HIDDEN
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.
static void sort(Iterator begin, Iterator end, Comparator cmp=Comparator())
Call best known sorting network for up to sixteen elements with given comparison method.
Definition: best.hpp:523
void unused(Types &&...)
Definition: unused.hpp:20