Commit 2d32cd77c9aef8ad6172faed905fc7c2c9a0c850

Authored by Henry Fredrick Schreiner
1 parent 87155a5d

Spelling fixes and readme updates

Showing 2 changed files with 97 additions and 74 deletions
README.md
... ... @@ -9,11 +9,13 @@ The following attributes were deemed most important in a CLI parser library:
9 9  
10 10 * Easy to include (i.e., header only, one file if possible, no external requirements): While many programs depend on Boost, that should not be a requirement if all you want is CLI parsing.
11 11 * Short Syntax: This is one of the main points of a CLI parser, it should make variables from the command line nearly as easy to define as any other variables. If most of your program is hidden in CLI parsing, this is a problem for readability.
12   -* Work with GCC 4.8 (CentOS 7) or above, or MacOS Clang (C++11)
13   -* Well tested using Travis
14   -* Good help printing (in progress)
15   -* Standard idioms supported naturally, like flags
  12 +* Work with GCC 4.8 (CentOS 7) or above, or MacOS Clang (C++11).
  13 +* Well tested using Travis.
  14 +* Good help printing (in progress).
  15 +* Standard idioms supported naturally, like flags.
16 16 * Easy to execute, with help, parse errors, etc. providing correct exit and details.
  17 +* Easy to extend as part of a framework that provides "applications".
  18 +* Simple support for subcommands,
17 19  
18 20 The major CLI parsers out there include:
19 21  
... ... @@ -55,7 +57,7 @@ try {
55 57 }
56 58 ```
57 59  
58   -The initialization is just one line, adding options is just two each. The try/catch block ensures that `-h,--help` or a parse error will exit with the correct return code. (The return here should be inside `main`). After the app runs, the filename will be set to the correct value if it was passed, otherwise it will be set to the default. You can check to see if this was passed on the command line with `app.count("file")`.
  60 +The initialization is just one line, adding options is just two each. The try/catch block ensures that `-h,--help` or a parse error will exit with the correct return code. (The return here should be inside `main`). After the app runs, the filename will be set to the correct value if it was passed, otherwise it will be set to the default. You can check to see if this was passed on the command line with `app.count("--file")`.
59 61  
60 62 The supported values are:
61 63  
... ... @@ -74,14 +76,19 @@ app.add_set(option_name,
74 76 set_of_possible_options,
75 77 flags, ...)
76 78  
  79 +App* subcom = app.add_subcommand(name, discription);
  80 +
77 81 ```
78 82  
  83 +
79 84 There are several flags:
80 85  
81   -* `CLI::DEFAULT`: Print the default value in help
82   -* `CLI::POSITIONAL`: Accept this option also as positional (or only as positional, if nameless)
83   -* `CLI::REQUIRED`: The program will quit if this option is not present
84   -* `CLI::OPTS(N)`: Take `N` values instead of as many as possible, only for vector args
  86 +* `CLI::Default`: Print the default value in help
  87 +* `CLI::Required`: The program will quit if this option is not present
  88 +* `CLI::Opts(N)`: Take `N` values instead of as many as possible, only for vector args
  89 +* `CLI::ExistingFile`: Requires that the file exists if given
  90 +* `CLI::ExistingDirectory`: Requires that the directory exists
  91 +* `CLI::NonexistentPath`: Requires that the path does not exist
85 92  
86 93 Options can be given as:
87 94  
... ... @@ -94,15 +101,31 @@ Options can be given as:
94 101 * `--file filename` (space)
95 102 * `--file=filename` (equals)
96 103  
97   -An option must start with a alphabetic character or underscore. For long options, anything but an equals sign is valid after that. Names are given as a comma separated string, with optional dash or dashes (the only way to get a one char long name is to be explicit with the dashes, however). An option or flag can have as many as you want, and afterward, using `count`, you can use any of the names, with optional dashes, to count the options.
  104 +An option must start with a alphabetic character or underscore. For long options, anything but an equals sign or a comma is valid after that. Names are given as a comma separated string, with the dash or dashes. An option or flag can have as many names as you want, and afterward, using `count`, you can use any of the names, with dashes as needed, to count the options. One of the names is allowed to be given without proceeding dash(es); if present the option is a positional option, and that name will be used on help line for its positional form.
98 105  
99   -Extra positional arguments will cause the program to exit, so at least one `CLI::POSITIONAL` option with a vector is recommended if you want to allow extraneous arguments
  106 +Extra positional arguments will cause the program to exit, so at least one positional option with a vector is recommended if you want to allow extraneous arguments
100 107 If `--` is present in the command line,
101 108 everything after that is positional only.
102 109  
103   -## Syntax 2
104 110  
105   -The second syntax looks like this:
  111 +## Subcommands
  112 +
  113 +Subcommands are naturally supported, with an infinite depth. To add a subcommand, call the `add_subcommand` method with a name and an optional description. This gives a pointer to an `App` that behaves just like the main app, and can take options or further subcommands.
  114 +
  115 +All `App`s have a `get_subcommand()` method, which returns a pointer to the subcommand passed on the command line, or `nullptr` if no subcommand was given. A simple compare of this pointer to each subcommand allows choosing based on subcommand. For many cases, however, using an app's callback may be easier. Every app executes a callback function after it parses; just use a lambda function (with capture to get parsed values) to `.add_callback`. If you throw CLI::Success, you can
  116 +even exit the program through the callback. The main `App` has a callback slot, as well, but it is generally not as useful.
  117 +
  118 +
  119 +
  120 +> ## Subclassing
  121 +>
  122 +> The App class was designed allow toolkits to subclass it, to provide default options and setup/teardown code. Subcommands remain `App`'s, since those are not expected to need setup and teardown. The default `App` only adds a help flag, `-h,--help`.
  123 +>
  124 +> Also, in a related note, the `App`s you get a pointer to are stored in the parent `App` and cannot be deleted.
  125 +
  126 +## Make syntax
  127 +
  128 +A second, provisional syntax looks like this:
106 129  
107 130 ```cpp
108 131 CLI::App app{"App description"};
... ... @@ -121,7 +144,7 @@ std::cout << "This will throw an error if int not passed: " << *int_value << std
121 144 ```
122 145  
123 146  
124   -Internally, it uses the same mechanism to work, it just provides a single line definition, but requires a template argument for non-strings, and creates an object that must be dereferenced to be used. This object (CLI::Value<type>) supports conversion to bool, allowing you to easily check if an option was passed without resorting to count. Dereferencing will also throw an error if no value was passed and no default was given.
  147 +Internally, it uses the same mechanism to work, it just provides a single line definition, but requires a template argument for non-strings, and creates an object that must be dereferenced to be used. This object (`CLI::Value<type>`) supports conversion to bool, allowing you to easily check if an option was passed without resorting to count. Dereferencing will also throw an error if no value was passed and no default was given.
125 148  
126 149 The same functions as the first syntax are supported, only with `make` instead of `add`, and with the variable to bind to replaced by the default value (optional). If you want to use something other than a string option and do not want to give a default, you need to give a template parameter with the type.
127 150  
... ...
include/CLI.hpp
... ... @@ -174,13 +174,13 @@ namespace detail {
174 174 return "STRING";
175 175 }
176 176  
177   - void format_help(std::stringstream &out, std::string name, std::string discription, size_t wid) {
  177 + void format_help(std::stringstream &out, std::string name, std::string description, size_t wid) {
178 178 name = " " + name;
179 179 out << std::setw(wid) << std::left << name;
180   - if(discription != "") {
  180 + if(description != "") {
181 181 if(name.length()>=wid)
182 182 out << std::endl << std::setw(wid) << "";
183   - out << discription << std::endl;
  183 + out << description << std::endl;
184 184 }
185 185 }
186 186  
... ... @@ -410,7 +410,7 @@ protected:
410 410 std::string pname;
411 411  
412 412 detail::Combiner opts;
413   - std::string discription;
  413 + std::string description;
414 414 callback_t callback;
415 415  
416 416 // These are for help strings
... ... @@ -422,8 +422,8 @@ protected:
422 422  
423 423  
424 424 public:
425   - Option(std::string name, std::string discription = "", detail::Combiner opts=Nothing, std::function<bool(results_t)> callback=[](results_t){return true;}) :
426   - opts(opts), discription(discription), callback(callback){
  425 + Option(std::string name, std::string description = "", detail::Combiner opts=Nothing, std::function<bool(results_t)> callback=[](results_t){return true;}) :
  426 + opts(opts), description(description), callback(callback){
427 427 std::tie(snames, lnames, pname) = detail::get_names(detail::split_names(name));
428 428 }
429 429  
... ... @@ -457,14 +457,14 @@ public:
457 457 return opts.defaulted;
458 458 }
459 459  
460   - /// True if option has discription
461   - bool has_discription() const {
462   - return discription.length() > 0;
  460 + /// True if option has description
  461 + bool has_description() const {
  462 + return description.length() > 0;
463 463 }
464 464  
465   - /// Get the discription
466   - const std::string& get_discription() const {
467   - return discription;
  465 + /// Get the description
  466 + const std::string& get_description() const {
  467 + return description;
468 468 }
469 469  
470 470 /// The name and any extras needed for positionals
... ... @@ -561,7 +561,7 @@ public:
561 561 /// Diagnostic representation
562 562 std::string string() const {
563 563 std::string val = "Option: " + get_name() + "\n"
564   - + " " + discription + "\n"
  564 + + " " + description + "\n"
565 565 + " [";
566 566 for(const auto& item : results) {
567 567 if(&item!=&results[0])
... ... @@ -638,7 +638,7 @@ class App {
638 638 protected:
639 639  
640 640 std::string name;
641   - std::string prog_discription;
  641 + std::string prog_description;
642 642 std::vector<Option> options;
643 643 std::vector<std::string> missing_options;
644 644 std::deque<std::string> positionals;
... ... @@ -680,15 +680,15 @@ public:
680 680 }
681 681  
682 682 /// Create a new program. Pass in the same arguments as main(), along with a help string.
683   - App(std::string prog_discription="")
684   - : prog_discription(prog_discription) {
  683 + App(std::string prog_description="")
  684 + : prog_description(prog_description) {
685 685  
686 686 add_flag("-h,--help", "Print this help message and exit");
687 687  
688 688 }
689 689  
690   - App* add_subcommand(std::string name, std::string discription="") {
691   - subcommands.emplace_back(new App(discription));
  690 + App* add_subcommand(std::string name, std::string description="") {
  691 + subcommands.emplace_back(new App(description));
692 692 subcommands.back()->name = name;
693 693 return subcommands.back().get();
694 694 }
... ... @@ -707,15 +707,15 @@ public:
707 707 * For example,
708 708 *
709 709 * std::string filename
710   - * program.add_option("filename", filename, "discription of filename");
  710 + * program.add_option("filename", filename, "description of filename");
711 711 */
712 712 Option* add_option(
713 713 std::string name,
714 714 callback_t callback,
715   - std::string discription="",
  715 + std::string description="",
716 716 detail::Combiner opts=Validators
717 717 ) {
718   - Option myopt{name, discription, opts, callback};
  718 + Option myopt{name, description, opts, callback};
719 719 if(std::find(std::begin(options), std::end(options), myopt) == std::end(options))
720 720 options.push_back(myopt);
721 721 else
... ... @@ -729,7 +729,7 @@ public:
729 729 Option* add_option(
730 730 std::string name,
731 731 T &variable, ///< The variable to set
732   - std::string discription="",
  732 + std::string description="",
733 733 detail::Combiner opts=Validators
734 734 ) {
735 735  
... ... @@ -746,7 +746,7 @@ public:
746 746 return detail::lexical_cast(res[0][0], variable);
747 747 };
748 748  
749   - Option* retval = add_option(name, fun, discription, opts);
  749 + Option* retval = add_option(name, fun, description, opts);
750 750 retval->typeval = detail::type_name<T>();
751 751 if(opts.defaulted) {
752 752 std::stringstream out;
... ... @@ -761,7 +761,7 @@ public:
761 761 Option* add_option(
762 762 std::string name,
763 763 std::vector<T> &variable, ///< The variable vector to set
764   - std::string discription="",
  764 + std::string description="",
765 765 detail::Combiner opts=Args
766 766 ) {
767 767  
... ... @@ -778,7 +778,7 @@ public:
778 778 return variable.size() > 0 && retval;
779 779 };
780 780  
781   - Option* retval = add_option(name, fun, discription, opts);
  781 + Option* retval = add_option(name, fun, description, opts);
782 782 retval->typeval = detail::type_name<T>();
783 783 if(opts.defaulted) {
784 784 retval->defaultval = "[" + detail::join(variable) + "]";
... ... @@ -792,23 +792,23 @@ public:
792 792 Option* add_option(
793 793 std::string name,
794 794 T &variable, ///< The variable to set
795   - std::string discription,
  795 + std::string description,
796 796 detail::Combiner opts,
797 797 detail::Combiner opts2,
798 798 Args... args ///< More options
799 799 ) {
800   - return add_option(name, variable, discription, opts|opts2, args...);
  800 + return add_option(name, variable, description, opts|opts2, args...);
801 801 }
802 802 /// Add option for flag
803 803 Option* add_flag(
804 804 std::string name,
805   - std::string discription=""
  805 + std::string description=""
806 806 ) {
807 807 CLI::callback_t fun = [](CLI::results_t){
808 808 return true;
809 809 };
810 810  
811   - Option* opt = add_option(name, fun, discription, Nothing);
  811 + Option* opt = add_option(name, fun, description, Nothing);
812 812 if(opt->positional())
813 813 throw IncorrectConstruction("Flags cannot be positional");
814 814 return opt;
... ... @@ -820,7 +820,7 @@ public:
820 820 Option* add_flag(
821 821 std::string name,
822 822 T &count, ///< A varaible holding the count
823   - std::string discription=""
  823 + std::string description=""
824 824 ) {
825 825  
826 826 count = 0;
... ... @@ -829,7 +829,7 @@ public:
829 829 return true;
830 830 };
831 831  
832   - Option* opt = add_option(name, fun, discription, Nothing);
  832 + Option* opt = add_option(name, fun, description, Nothing);
833 833 if(opt->positional())
834 834 throw IncorrectConstruction("Flags cannot be positional");
835 835 return opt;
... ... @@ -841,7 +841,7 @@ public:
841 841 Option* add_flag(
842 842 std::string name,
843 843 T &count, ///< A varaible holding true if passed
844   - std::string discription=""
  844 + std::string description=""
845 845 ) {
846 846  
847 847 count = false;
... ... @@ -850,7 +850,7 @@ public:
850 850 return res.size() == 1;
851 851 };
852 852  
853   - Option* opt = add_option(name, fun, discription, Nothing);
  853 + Option* opt = add_option(name, fun, description, Nothing);
854 854 if(opt->positional())
855 855 throw IncorrectConstruction("Flags cannot be positional");
856 856 return opt;
... ... @@ -863,7 +863,7 @@ public:
863 863 std::string name,
864 864 T &member, ///< The selected member of the set
865 865 std::set<T> options, ///< The set of posibilities
866   - std::string discription="",
  866 + std::string description="",
867 867 detail::Combiner opts=Validators
868 868 ) {
869 869  
... ... @@ -883,7 +883,7 @@ public:
883 883 return std::find(std::begin(options), std::end(options), member) != std::end(options);
884 884 };
885 885  
886   - Option* retval = add_option(name, fun, discription, opts);
  886 + Option* retval = add_option(name, fun, description, opts);
887 887 retval->typeval = detail::type_name<T>();
888 888 retval->typeval += " in {" + detail::join(options) + "}";
889 889 if(opts.defaulted) {
... ... @@ -900,12 +900,12 @@ public:
900 900 std::string name,
901 901 T &member,
902 902 std::set<T> options, ///< The set of posibilities
903   - std::string discription,
  903 + std::string description,
904 904 detail::Combiner opts,
905 905 detail::Combiner opts2,
906 906 Args... args
907 907 ) {
908   - return add_set(name, member, options, discription, opts|opts2, args...);
  908 + return add_set(name, member, options, description, opts|opts2, args...);
909 909 }
910 910  
911 911  
... ... @@ -916,7 +916,7 @@ public:
916 916 enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>
917 917 Value<T> make_option(
918 918 std::string name,
919   - std::string discription="",
  919 + std::string description="",
920 920 detail::Combiner opts=Validators
921 921 ) {
922 922  
... ... @@ -936,7 +936,7 @@ public:
936 936 ptr->reset(new T()); // resets the internal ptr
937 937 return detail::lexical_cast(res[0][0], **ptr);
938 938 };
939   - Option* retval = add_option(name, fun, discription, opts);
  939 + Option* retval = add_option(name, fun, description, opts);
940 940 retval->typeval = detail::type_name<T>();
941 941 return out;
942 942 }
... ... @@ -944,12 +944,12 @@ public:
944 944 template<typename T = std::string, typename... Args>
945 945 Value<T> make_option(
946 946 std::string name,
947   - std::string discription,
  947 + std::string description,
948 948 detail::Combiner opts,
949 949 detail::Combiner opts2,
950 950 Args... args
951 951 ) {
952   - return make_option(name, discription, opts|opts2, args...);
  952 + return make_option(name, description, opts|opts2, args...);
953 953 }
954 954  
955 955 /// Prototype for new output style with default
... ... @@ -958,7 +958,7 @@ public:
958 958 Value<T> make_option(
959 959 std::string name,
960 960 const T& default_value,
961   - std::string discription="",
  961 + std::string description="",
962 962 detail::Combiner opts=Validators
963 963 ) {
964 964  
... ... @@ -979,7 +979,7 @@ public:
979 979 ptr->reset(new T()); // resets the internal ptr
980 980 return detail::lexical_cast(res[0][0], **ptr);
981 981 };
982   - Option* retval = add_option(name, fun, discription, opts);
  982 + Option* retval = add_option(name, fun, description, opts);
983 983 retval->typeval = detail::type_name<T>();
984 984 std::stringstream ot;
985 985 ot << default_value;
... ... @@ -992,7 +992,7 @@ public:
992 992 enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>
993 993 Value<T> make_option(
994 994 std::string name,
995   - std::string discription="",
  995 + std::string description="",
996 996 detail::Combiner opts=Args
997 997 ) {
998 998  
... ... @@ -1012,7 +1012,7 @@ public:
1012 1012 }
1013 1013 return (*ptr)->size() > 0 && retval;
1014 1014 };
1015   - Option* retval = add_option(name, fun, discription, opts);
  1015 + Option* retval = add_option(name, fun, description, opts);
1016 1016 retval->typeval = detail::type_name<T>();
1017 1017 return out;
1018 1018 }
... ... @@ -1022,18 +1022,18 @@ public:
1022 1022 Value<T> make_option(
1023 1023 std::string name,
1024 1024 const T& default_value,
1025   - std::string discription,
  1025 + std::string description,
1026 1026 detail::Combiner opts,
1027 1027 detail::Combiner opts2,
1028 1028 Args... args
1029 1029 ) {
1030   - return make_option(name, default_value, discription, opts|opts2, args...);
  1030 + return make_option(name, default_value, description, opts|opts2, args...);
1031 1031 }
1032 1032  
1033 1033 /// Prototype for new output style: flag
1034 1034 Value<int> make_flag(
1035 1035 std::string name,
1036   - std::string discription=""
  1036 + std::string description=""
1037 1037 ) {
1038 1038  
1039 1039 Value<int> out(name);
... ... @@ -1046,7 +1046,7 @@ public:
1046 1046 return true;
1047 1047 };
1048 1048  
1049   - Option* opt = add_option(name, fun, discription, Nothing);
  1049 + Option* opt = add_option(name, fun, description, Nothing);
1050 1050 if(opt->positional())
1051 1051 throw IncorrectConstruction("Flags cannot be positional");
1052 1052 return out;
... ... @@ -1057,7 +1057,7 @@ public:
1057 1057 Value<T> make_set(
1058 1058 std::string name,
1059 1059 std::set<T> options, ///< The set of posibilities
1060   - std::string discription="",
  1060 + std::string description="",
1061 1061 detail::Combiner opts=Validators
1062 1062 ) {
1063 1063  
... ... @@ -1081,7 +1081,7 @@ public:
1081 1081 return std::find(std::begin(options), std::end(options), **ptr) != std::end(options);
1082 1082 };
1083 1083  
1084   - Option* retval = add_option(name, fun, discription, opts);
  1084 + Option* retval = add_option(name, fun, description, opts);
1085 1085 retval->typeval = detail::type_name<T>();
1086 1086 retval->typeval += " in {" + detail::join(options) + "}";
1087 1087 return out;
... ... @@ -1092,12 +1092,12 @@ public:
1092 1092 Value<T> make_set(
1093 1093 std::string name,
1094 1094 std::set<T> options,
1095   - std::string discription,
  1095 + std::string description,
1096 1096 detail::Combiner opts,
1097 1097 detail::Combiner opts2,
1098 1098 Args... args
1099 1099 ) {
1100   - return make_set(name, options, discription, opts|opts2, args...);
  1100 + return make_set(name, options, description, opts|opts2, args...);
1101 1101 }
1102 1102  
1103 1103 /// This allows subclasses to inject code before callbacks but after parse
... ... @@ -1326,7 +1326,7 @@ public:
1326 1326 return subcommand->help(wid, prev);
1327 1327  
1328 1328 std::stringstream out;
1329   - out << prog_discription << std::endl;
  1329 + out << prog_description << std::endl;
1330 1330 out << "Usage: " << prev;
1331 1331  
1332 1332 // Check for options
... ... @@ -1346,18 +1346,18 @@ public:
1346 1346 for(const Option &opt : options)
1347 1347 if(opt.positional()) {
1348 1348 out << " " << opt.help_positional();
1349   - if(opt.has_discription())
  1349 + if(opt.has_description())
1350 1350 pos=true;
1351 1351 }
1352 1352  
1353 1353 out << std::endl << std::endl;
1354 1354  
1355   - // Positional discriptions
  1355 + // Positional descriptions
1356 1356 if(pos) {
1357 1357 out << "Positionals:" << std::endl;
1358 1358 for(const Option &opt : options)
1359   - if(opt.positional() && opt.has_discription())
1360   - detail::format_help(out, opt.get_pname(), opt.get_discription(), wid);
  1359 + if(opt.positional() && opt.has_description())
  1360 + detail::format_help(out, opt.get_pname(), opt.get_description(), wid);
1361 1361 out << std::endl;
1362 1362  
1363 1363 }
... ... @@ -1368,7 +1368,7 @@ public:
1368 1368 out << "Options:" << std::endl;
1369 1369 for(const Option &opt : options) {
1370 1370 if(opt.nonpositional())
1371   - detail::format_help(out, opt.help_name(), opt.get_discription(), wid);
  1371 + detail::format_help(out, opt.help_name(), opt.get_description(), wid);
1372 1372  
1373 1373 }
1374 1374 out << std::endl;
... ... @@ -1378,7 +1378,7 @@ public:
1378 1378 if(subcommands.size()> 0) {
1379 1379 out << "Subcommands:" << std::endl;
1380 1380 for(const std::unique_ptr<App> &com : subcommands)
1381   - detail::format_help(out, com->get_name(), com->prog_discription, wid);
  1381 + detail::format_help(out, com->get_name(), com->prog_description, wid);
1382 1382 }
1383 1383 return out.str();
1384 1384 }
... ...