Commit 02c49388d7bee69aac4123a8efd692750c17bfd0

Authored by Henry Schreiner
Committed by GitHub
1 parent 8d7aefe2

Making mutable sets explicit (#200)

CHANGELOG.md
1 1 ## Version 1.7: Parse breakup (in progress)
2 2  
3 3 The parsing procedure now maps much more sensibly to complex, nested subcommand structures. Each phase of the parsing happens on all subcommands before moving on with the next phase of the parse. This allows several features, like required environment variables, to work properly even through subcommand boundaries.
4   -Passing the same subcommand multiple times is better supported. Several new features were added as well, including Windows style option support, parsing strings directly, and ignoring underscores in names.
  4 +Passing the same subcommand multiple times is better supported. Several new features were added as well, including Windows style option support, parsing strings directly, and ignoring underscores in names. Adding a set that you plan to change later must now be done with `add_mutable_set`.
5 5  
6 6 * Support Windows style options with `->allow_windows_style_options`. [#187] On by default on Windows. [#190]
7 7 * Added `parse(string)` to split up and parse a command-line style string directly. [#186]
... ... @@ -9,6 +9,7 @@ Passing the same subcommand multiple times is better supported. Several new feat
9 9 * The default INI Config will now add quotes to strings with spaces [#195]
10 10 * The default message now will mention the help-all flag also if present [#197]
11 11 * Added `->description` to set Option descriptions [#199]
  12 +* Mutating sets (introduced in Version 1.6) now have a clear add method, `add_mutable_set*`, since the set reference should not expire [#200]
12 13 * Subcommands now track how many times they were parsed in a parsing process. `count()` with no arguments will return the number of times a subcommand was encountered. [#179]
13 14 * Parsing is now done in phases: `shortcurcuits`, `ini`, `env`, `callbacks`, and `requirements`; all subcommands complete a phase before moving on. [#179]
14 15 * Calling parse multiple times is now officially supported without `clear` (automatic). [#179]
... ... @@ -28,6 +29,7 @@ Passing the same subcommand multiple times is better supported. Several new feat
28 29 [#197]: https://github.com/CLIUtils/CLI11/pull/197
29 30 [#195]: https://github.com/CLIUtils/CLI11/issues/195
30 31 [#199]: https://github.com/CLIUtils/CLI11/pull/199
  32 +[#200]: https://github.com/CLIUtils/CLI11/pull/200
31 33  
32 34 ## Version 1.6.2: Help-all
33 35  
... ...
README.md
... ... @@ -194,17 +194,19 @@ app.add_set(option_name,
194 194 set_of_possible_options,
195 195 help_string="",
196 196 default=false)
  197 +app.add_mutable_set(... // Set can change later, keeps reference
197 198  
198 199 app.add_set_ignore_case(... // String only
199   -
  200 +app.add__mutable_set_ignore_case(... // String only
200 201 app.add_set_ignore_underscore(... // String only
201   -
  202 +app.add__mutable_set_ignore_underscore(... // String only
202 203 app.add_set_ignore_case_underscore(... // String only
  204 +app.add_mutable_set_ignore_case_underscore(... // String only
203 205  
204 206 App* subcom = app.add_subcommand(name, description);
205 207 ```
206 208  
207   -An option name 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. If you want the default value to print in the help description, pass in `true` for the final parameter for `add_option` or `add_set`. The set options allow your users to pick from a set of predefined options; you can add an existing set if you need to modify the set later, or you can use an initializer list.
  209 +An option name 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. If you want the default value to print in the help description, pass in `true` for the final parameter for `add_option` or `add_set`. The set options allow your users to pick from a set of predefined options, and you can use an initializer list directly if you like. If you need to modify the set later, use the `mutable` forms.
208 210  
209 211 On a C++14 compiler, you can pass a callback function directly to `.add_flag`, while in C++11 mode you'll need to use `.add_flag_function` if you want a callback function. The function will be given the number of times the flag was passed. You can throw a relevant `CLI::ParseError` to signal a failure.
210 212  
... ...
include/CLI/App.hpp
... ... @@ -547,8 +547,8 @@ class App {
547 547 /// Add set of options (No default, temp reference, such as an inline set)
548 548 template <typename T>
549 549 Option *add_set(std::string option_name,
550   - T &member, ///< The selected member of the set
551   - const std::set<T> &&options, ///< The set of possibilities
  550 + T &member, ///< The selected member of the set
  551 + std::set<T> options, ///< The set of possibilities
552 552 std::string description = "") {
553 553  
554 554 std::string simple_name = CLI::detail::split(option_name, ',').at(0);
... ... @@ -566,12 +566,12 @@ class App {
566 566 return opt;
567 567 }
568 568  
569   - /// Add set of options (No default, non-temp reference, such as an existing set)
  569 + /// Add set of options (No default, set can be changed afterwords - do not destroy the set)
570 570 template <typename T>
571   - Option *add_set(std::string option_name,
572   - T &member, ///< The selected member of the set
573   - const std::set<T> &options, ///< The set of possibilities
574   - std::string description = "") {
  571 + Option *add_mutable_set(std::string option_name,
  572 + T &member, ///< The selected member of the set
  573 + const std::set<T> &options, ///< The set of possibilities
  574 + std::string description = "") {
575 575  
576 576 std::string simple_name = CLI::detail::split(option_name, ',').at(0);
577 577 CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
... ... @@ -588,11 +588,11 @@ class App {
588 588 return opt;
589 589 }
590 590  
591   - /// Add set of options (with default, R value, such as an inline set)
  591 + /// Add set of options (with default, static set, such as an inline set)
592 592 template <typename T>
593 593 Option *add_set(std::string option_name,
594   - T &member, ///< The selected member of the set
595   - const std::set<T> &&options, ///< The set of possibilities
  594 + T &member, ///< The selected member of the set
  595 + std::set<T> options, ///< The set of possibilities
596 596 std::string description,
597 597 bool defaulted) {
598 598  
... ... @@ -616,13 +616,13 @@ class App {
616 616 return opt;
617 617 }
618 618  
619   - /// Add set of options (with default, L value reference, such as an existing set)
  619 + /// Add set of options (with default, set can be changed afterwards - do not destroy the set)
620 620 template <typename T>
621   - Option *add_set(std::string option_name,
622   - T &member, ///< The selected member of the set
623   - const std::set<T> &options, ///< The set of possibilities
624   - std::string description,
625   - bool defaulted) {
  621 + Option *add_mutable_set(std::string option_name,
  622 + T &member, ///< The selected member of the set
  623 + const std::set<T> &options, ///< The set of possibilities
  624 + std::string description,
  625 + bool defaulted) {
626 626  
627 627 std::string simple_name = CLI::detail::split(option_name, ',').at(0);
628 628 CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
... ... @@ -643,10 +643,10 @@ class App {
643 643 return opt;
644 644 }
645 645  
646   - /// Add set of options, string only, ignore case (no default, R value)
  646 + /// Add set of options, string only, ignore case (no default, static set)
647 647 Option *add_set_ignore_case(std::string option_name,
648   - std::string &member, ///< The selected member of the set
649   - const std::set<std::string> &&options, ///< The set of possibilities
  648 + std::string &member, ///< The selected member of the set
  649 + std::set<std::string> options, ///< The set of possibilities
650 650 std::string description = "") {
651 651  
652 652 std::string simple_name = CLI::detail::split(option_name, ',').at(0);
... ... @@ -671,11 +671,12 @@ class App {
671 671 return opt;
672 672 }
673 673  
674   - /// Add set of options, string only, ignore case (no default, L value)
675   - Option *add_set_ignore_case(std::string option_name,
676   - std::string &member, ///< The selected member of the set
677   - const std::set<std::string> &options, ///< The set of possibilities
678   - std::string description = "") {
  674 + /// Add set of options, string only, ignore case (no default, set can be changed afterwards - do not destroy the
  675 + /// set)
  676 + Option *add_mutable_set_ignore_case(std::string option_name,
  677 + std::string &member, ///< The selected member of the set
  678 + const std::set<std::string> &options, ///< The set of possibilities
  679 + std::string description = "") {
679 680  
680 681 std::string simple_name = CLI::detail::split(option_name, ',').at(0);
681 682 CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
... ... @@ -699,10 +700,10 @@ class App {
699 700 return opt;
700 701 }
701 702  
702   - /// Add set of options, string only, ignore case (default, R value)
  703 + /// Add set of options, string only, ignore case (default, static set)
703 704 Option *add_set_ignore_case(std::string option_name,
704   - std::string &member, ///< The selected member of the set
705   - const std::set<std::string> &&options, ///< The set of possibilities
  705 + std::string &member, ///< The selected member of the set
  706 + std::set<std::string> options, ///< The set of possibilities
706 707 std::string description,
707 708 bool defaulted) {
708 709  
... ... @@ -730,12 +731,12 @@ class App {
730 731 return opt;
731 732 }
732 733  
733   - /// Add set of options, string only, ignore case (default, L value)
734   - Option *add_set_ignore_case(std::string option_name,
735   - std::string &member, ///< The selected member of the set
736   - const std::set<std::string> &options, ///< The set of possibilities
737   - std::string description,
738   - bool defaulted) {
  734 + /// Add set of options, string only, ignore case (default, set can be changed afterwards - do not destroy the set)
  735 + Option *add_mutable_set_ignore_case(std::string option_name,
  736 + std::string &member, ///< The selected member of the set
  737 + const std::set<std::string> &options, ///< The set of possibilities
  738 + std::string description,
  739 + bool defaulted) {
739 740  
740 741 std::string simple_name = CLI::detail::split(option_name, ',').at(0);
741 742 CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
... ... @@ -761,10 +762,10 @@ class App {
761 762 return opt;
762 763 }
763 764  
764   - /// Add set of options, string only, ignore underscore (no default, R value)
  765 + /// Add set of options, string only, ignore underscore (no default, static set)
765 766 Option *add_set_ignore_underscore(std::string option_name,
766   - std::string &member, ///< The selected member of the set
767   - const std::set<std::string> &&options, ///< The set of possibilities
  767 + std::string &member, ///< The selected member of the set
  768 + std::set<std::string> options, ///< The set of possibilities
768 769 std::string description = "") {
769 770  
770 771 std::string simple_name = CLI::detail::split(option_name, ',').at(0);
... ... @@ -789,11 +790,12 @@ class App {
789 790 return opt;
790 791 }
791 792  
792   - /// Add set of options, string only, ignore underscore (no default, L value)
793   - Option *add_set_ignore_underscore(std::string option_name,
794   - std::string &member, ///< The selected member of the set
795   - const std::set<std::string> &options, ///< The set of possibilities
796   - std::string description = "") {
  793 + /// Add set of options, string only, ignore underscore (no default, set can be changed afterwards - do not destroy
  794 + /// the set)
  795 + Option *add_mutable_set_ignore_underscore(std::string option_name,
  796 + std::string &member, ///< The selected member of the set
  797 + const std::set<std::string> &options, ///< The set of possibilities
  798 + std::string description = "") {
797 799  
798 800 std::string simple_name = CLI::detail::split(option_name, ',').at(0);
799 801 CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
... ... @@ -817,10 +819,10 @@ class App {
817 819 return opt;
818 820 }
819 821  
820   - /// Add set of options, string only, ignore underscore (default, R value)
  822 + /// Add set of options, string only, ignore underscore (default, static set)
821 823 Option *add_set_ignore_underscore(std::string option_name,
822   - std::string &member, ///< The selected member of the set
823   - const std::set<std::string> &&options, ///< The set of possibilities
  824 + std::string &member, ///< The selected member of the set
  825 + std::set<std::string> options, ///< The set of possibilities
824 826 std::string description,
825 827 bool defaulted) {
826 828  
... ... @@ -848,12 +850,13 @@ class App {
848 850 return opt;
849 851 }
850 852  
851   - /// Add set of options, string only, ignore underscore (default, L value)
852   - Option *add_set_ignore_underscore(std::string option_name,
853   - std::string &member, ///< The selected member of the set
854   - const std::set<std::string> &options, ///< The set of possibilities
855   - std::string description,
856   - bool defaulted) {
  853 + /// Add set of options, string only, ignore underscore (default, set can be changed afterwards - do not destroy the
  854 + /// set)
  855 + Option *add_mutable_set_ignore_underscore(std::string option_name,
  856 + std::string &member, ///< The selected member of the set
  857 + const std::set<std::string> &options, ///< The set of possibilities
  858 + std::string description,
  859 + bool defaulted) {
857 860  
858 861 std::string simple_name = CLI::detail::split(option_name, ',').at(0);
859 862 CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
... ... @@ -879,10 +882,10 @@ class App {
879 882 return opt;
880 883 }
881 884  
882   - /// Add set of options, string only, ignore underscore and case(no default, R value)
  885 + /// Add set of options, string only, ignore underscore and case (no default, static set)
883 886 Option *add_set_ignore_case_underscore(std::string option_name,
884   - std::string &member, ///< The selected member of the set
885   - const std::set<std::string> &&options, ///< The set of possibilities
  887 + std::string &member, ///< The selected member of the set
  888 + std::set<std::string> options, ///< The set of possibilities
886 889 std::string description = "") {
887 890  
888 891 std::string simple_name = CLI::detail::split(option_name, ',').at(0);
... ... @@ -907,11 +910,12 @@ class App {
907 910 return opt;
908 911 }
909 912  
910   - /// Add set of options, string only, ignore underscore and case(no default, L value)
911   - Option *add_set_ignore_case_underscore(std::string option_name,
912   - std::string &member, ///< The selected member of the set
913   - const std::set<std::string> &options, ///< The set of possibilities
914   - std::string description = "") {
  913 + /// Add set of options, string only, ignore underscore and case (no default, set can be changed afterwards - do not
  914 + /// destroy the set)
  915 + Option *add_mutable_set_ignore_case_underscore(std::string option_name,
  916 + std::string &member, ///< The selected member of the set
  917 + const std::set<std::string> &options, ///< The set of possibilities
  918 + std::string description = "") {
915 919  
916 920 std::string simple_name = CLI::detail::split(option_name, ',').at(0);
917 921 CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
... ... @@ -935,10 +939,10 @@ class App {
935 939 return opt;
936 940 }
937 941  
938   - /// Add set of options, string only, ignore underscore and case (default, R value)
  942 + /// Add set of options, string only, ignore underscore and case (default, static set)
939 943 Option *add_set_ignore_case_underscore(std::string option_name,
940   - std::string &member, ///< The selected member of the set
941   - const std::set<std::string> &&options, ///< The set of possibilities
  944 + std::string &member, ///< The selected member of the set
  945 + std::set<std::string> options, ///< The set of possibilities
942 946 std::string description,
943 947 bool defaulted) {
944 948  
... ... @@ -966,12 +970,13 @@ class App {
966 970 return opt;
967 971 }
968 972  
969   - /// Add set of options, string only, ignore underscore and case (default, L value)
970   - Option *add_set_ignore_case_underscore(std::string option_name,
971   - std::string &member, ///< The selected member of the set
972   - const std::set<std::string> &options, ///< The set of possibilities
973   - std::string description,
974   - bool defaulted) {
  973 + /// Add set of options, string only, ignore underscore and case (default, set can be changed afterwards - do not
  974 + /// destroy the set)
  975 + Option *add_mutable_set_ignore_case_underscore(std::string option_name,
  976 + std::string &member, ///< The selected member of the set
  977 + const std::set<std::string> &options, ///< The set of possibilities
  978 + std::string description,
  979 + bool defaulted) {
975 980  
976 981 std::string simple_name = CLI::detail::split(option_name, ',').at(0);
977 982 CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
... ...
tests/AppTest.cpp
... ... @@ -1168,12 +1168,12 @@ TEST_F(TApp, FailSet) {
1168 1168 EXPECT_THROW(run(), CLI::ConversionError);
1169 1169 }
1170 1170  
1171   -TEST_F(TApp, FailLValueSet) {
  1171 +TEST_F(TApp, FailMutableSet) {
1172 1172  
1173 1173 int choice;
1174 1174 std::set<int> vals{1, 2, 3};
1175   - app.add_set("-q,--quick", choice, vals);
1176   - app.add_set("-s,--slow", choice, vals, "", true);
  1175 + app.add_mutable_set("-q,--quick", choice, vals);
  1176 + app.add_mutable_set("-s,--slow", choice, vals, "", true);
1177 1177  
1178 1178 args = {"--quick=hello"};
1179 1179 EXPECT_THROW(run(), CLI::ConversionError);
... ... @@ -1206,12 +1206,11 @@ TEST_F(TApp, InSetIgnoreCase) {
1206 1206 EXPECT_THROW(run(), CLI::ArgumentMismatch);
1207 1207 }
1208 1208  
1209   -/*
1210   -TEST_F(TApp, InSetIgnoreCaseLValue) {
  1209 +TEST_F(TApp, InSetIgnoreCaseMutableValue) {
1211 1210  
1212 1211 std::set<std::string> options{"one", "Two", "THREE"};
1213 1212 std::string choice;
1214   - app.add_set_ignore_case("-q,--quick", choice, options);
  1213 + app.add_mutable_set_ignore_case("-q,--quick", choice, options);
1215 1214  
1216 1215 args = {"--quick", "One"};
1217 1216 run();
... ... @@ -1227,14 +1226,7 @@ TEST_F(TApp, InSetIgnoreCaseLValue) {
1227 1226  
1228 1227 options.clear();
1229 1228 args = {"--quick", "ThrEE"};
1230   - run();
1231   - EXPECT_EQ("THREE", choice); // this will now fail since options was cleared
1232   -
1233   - args = {"--quick", "four"};
1234 1229 EXPECT_THROW(run(), CLI::ConversionError);
1235   -
1236   - args = {"--quick=one", "--quick=two"};
1237   - EXPECT_THROW(run(), CLI::ArgumentMismatch);
1238 1230 }
1239 1231  
1240 1232 TEST_F(TApp, InSetIgnoreCasePointer) {
... ... @@ -1258,7 +1250,7 @@ TEST_F(TApp, InSetIgnoreCasePointer) {
1258 1250 delete options;
1259 1251 args = {"--quick", "ThrEE"};
1260 1252 run();
1261   - EXPECT_EQ("THREE", choice); // this could cause a seg fault
  1253 + EXPECT_EQ("THREE", choice); // this does not throw a segfault
1262 1254  
1263 1255 args = {"--quick", "four"};
1264 1256 EXPECT_THROW(run(), CLI::ConversionError);
... ... @@ -1266,7 +1258,7 @@ TEST_F(TApp, InSetIgnoreCasePointer) {
1266 1258 args = {"--quick=one", "--quick=two"};
1267 1259 EXPECT_THROW(run(), CLI::ArgumentMismatch);
1268 1260 }
1269   -*/
  1261 +
1270 1262 TEST_F(TApp, InSetIgnoreUnderscore) {
1271 1263  
1272 1264 std::string choice;
... ... @@ -1765,8 +1757,8 @@ TEST_F(TApp, AddRemoveSetItems) {
1765 1757 std::set<std::string> items{"TYPE1", "TYPE2", "TYPE3", "TYPE4", "TYPE5"};
1766 1758  
1767 1759 std::string type1, type2;
1768   - app.add_set("--type1", type1, items);
1769   - app.add_set("--type2", type2, items, "", true);
  1760 + app.add_mutable_set("--type1", type1, items);
  1761 + app.add_mutable_set("--type2", type2, items, "", true);
1770 1762  
1771 1763 args = {"--type1", "TYPE1", "--type2", "TYPE2"};
1772 1764  
... ... @@ -1796,8 +1788,8 @@ TEST_F(TApp, AddRemoveSetItemsNoCase) {
1796 1788 std::set<std::string> items{"TYPE1", "TYPE2", "TYPE3", "TYPE4", "TYPE5"};
1797 1789  
1798 1790 std::string type1, type2;
1799   - app.add_set_ignore_case("--type1", type1, items);
1800   - app.add_set_ignore_case("--type2", type2, items, "", true);
  1791 + app.add_mutable_set_ignore_case("--type1", type1, items);
  1792 + app.add_mutable_set_ignore_case("--type2", type2, items, "", true);
1801 1793  
1802 1794 args = {"--type1", "TYPe1", "--type2", "TyPE2"};
1803 1795  
... ...
tests/HelpTest.cpp
... ... @@ -795,7 +795,7 @@ TEST(THelp, ChangingSet) {
795 795  
796 796 std::set<int> vals{1, 2, 3};
797 797 int val;
798   - app.add_set("--val", val, vals);
  798 + app.add_mutable_set("--val", val, vals);
799 799  
800 800 std::string help = app.help();
801 801  
... ... @@ -816,7 +816,7 @@ TEST(THelp, ChangingSetDefaulted) {
816 816  
817 817 std::set<int> vals{1, 2, 3};
818 818 int val = 2;
819   - app.add_set("--val", val, vals, "", true);
  819 + app.add_mutable_set("--val", val, vals, "", true);
820 820  
821 821 std::string help = app.help();
822 822  
... ... @@ -836,7 +836,7 @@ TEST(THelp, ChangingCaselessSet) {
836 836  
837 837 std::set<std::string> vals{"1", "2", "3"};
838 838 std::string val;
839   - app.add_set_ignore_case("--val", val, vals);
  839 + app.add_mutable_set_ignore_case("--val", val, vals);
840 840  
841 841 std::string help = app.help();
842 842  
... ... @@ -857,7 +857,7 @@ TEST(THelp, ChangingCaselessSetDefaulted) {
857 857  
858 858 std::set<std::string> vals{"1", "2", "3"};
859 859 std::string val = "2";
860   - app.add_set_ignore_case("--val", val, vals, "", true);
  860 + app.add_mutable_set_ignore_case("--val", val, vals, "", true);
861 861  
862 862 std::string help = app.help();
863 863  
... ...