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