Commit ca6e9f70eb6b5fab44504daf57d92a9412ab301c

Authored by Frank Schoenmann
Committed by jarro2783
1 parent 00e8ecc4

Allow unrecognised options. (#105)

Allows unrecognised options to be left alone by the parser without throwing an exception.
include/cxxopts.hpp
@@ -1095,6 +1095,7 @@ namespace cxxopts @@ -1095,6 +1095,7 @@ namespace cxxopts
1095 ParseResult( 1095 ParseResult(
1096 const std::unordered_map<std::string, std::shared_ptr<OptionDetails>>&, 1096 const std::unordered_map<std::string, std::shared_ptr<OptionDetails>>&,
1097 std::vector<std::string>, 1097 std::vector<std::string>,
  1098 + bool allow_unrecognised,
1098 int&, char**&); 1099 int&, char**&);
1099 1100
1100 size_t 1101 size_t
@@ -1174,6 +1175,8 @@ namespace cxxopts @@ -1174,6 +1175,8 @@ namespace cxxopts
1174 std::unordered_set<std::string> m_positional_set; 1175 std::unordered_set<std::string> m_positional_set;
1175 std::unordered_map<std::shared_ptr<OptionDetails>, OptionValue> m_results; 1176 std::unordered_map<std::shared_ptr<OptionDetails>, OptionValue> m_results;
1176 1177
  1178 + bool m_allow_unrecognised;
  1179 +
1177 std::vector<KeyValue> m_sequential; 1180 std::vector<KeyValue> m_sequential;
1178 }; 1181 };
1179 1182
@@ -1187,6 +1190,7 @@ namespace cxxopts @@ -1187,6 +1190,7 @@ namespace cxxopts
1187 , m_custom_help("[OPTION...]") 1190 , m_custom_help("[OPTION...]")
1188 , m_positional_help("positional parameters") 1191 , m_positional_help("positional parameters")
1189 , m_show_positional(false) 1192 , m_show_positional(false)
  1193 + , m_allow_unrecognised(false)
1190 , m_next_positional(m_positional.end()) 1194 , m_next_positional(m_positional.end())
1191 { 1195 {
1192 } 1196 }
@@ -1212,6 +1216,13 @@ namespace cxxopts @@ -1212,6 +1216,13 @@ namespace cxxopts
1212 return *this; 1216 return *this;
1213 } 1217 }
1214 1218
  1219 + Options&
  1220 + allow_unrecognised_options()
  1221 + {
  1222 + m_allow_unrecognised = true;
  1223 + return *this;
  1224 + }
  1225 +
1215 ParseResult 1226 ParseResult
1216 parse(int& argc, char**& argv); 1227 parse(int& argc, char**& argv);
1217 1228
@@ -1275,6 +1286,7 @@ namespace cxxopts @@ -1275,6 +1286,7 @@ namespace cxxopts
1275 std::string m_custom_help; 1286 std::string m_custom_help;
1276 std::string m_positional_help; 1287 std::string m_positional_help;
1277 bool m_show_positional; 1288 bool m_show_positional;
  1289 + bool m_allow_unrecognised;
1278 1290
1279 std::unordered_map<std::string, std::shared_ptr<OptionDetails>> m_options; 1291 std::unordered_map<std::string, std::shared_ptr<OptionDetails>> m_options;
1280 std::vector<std::string> m_positional; 1292 std::vector<std::string> m_positional;
@@ -1431,11 +1443,13 @@ ParseResult::ParseResult @@ -1431,11 +1443,13 @@ ParseResult::ParseResult
1431 ( 1443 (
1432 const std::unordered_map<std::string, std::shared_ptr<OptionDetails>>& options, 1444 const std::unordered_map<std::string, std::shared_ptr<OptionDetails>>& options,
1433 std::vector<std::string> positional, 1445 std::vector<std::string> positional,
  1446 + bool allow_unrecognised,
1434 int& argc, char**& argv 1447 int& argc, char**& argv
1435 ) 1448 )
1436 : m_options(options) 1449 : m_options(options)
1437 , m_positional(std::move(positional)) 1450 , m_positional(std::move(positional))
1438 , m_next_positional(m_positional.begin()) 1451 , m_next_positional(m_positional.begin())
  1452 +, m_allow_unrecognised(allow_unrecognised)
1439 { 1453 {
1440 parse(argc, argv); 1454 parse(argc, argv);
1441 } 1455 }
@@ -1641,7 +1655,7 @@ inline @@ -1641,7 +1655,7 @@ inline
1641 ParseResult 1655 ParseResult
1642 Options::parse(int& argc, char**& argv) 1656 Options::parse(int& argc, char**& argv)
1643 { 1657 {
1644 - ParseResult result(m_options, m_positional, argc, argv); 1658 + ParseResult result(m_options, m_positional, m_allow_unrecognised, argc, argv);
1645 return result; 1659 return result;
1646 } 1660 }
1647 1661
@@ -1697,7 +1711,15 @@ ParseResult::parse(int&amp; argc, char**&amp; argv) @@ -1697,7 +1711,15 @@ ParseResult::parse(int&amp; argc, char**&amp; argv)
1697 1711
1698 if (iter == m_options.end()) 1712 if (iter == m_options.end())
1699 { 1713 {
1700 - throw option_not_exists_exception(name); 1714 + if (m_allow_unrecognised)
  1715 + {
  1716 + continue;
  1717 + }
  1718 + else
  1719 + {
  1720 + //error
  1721 + throw option_not_exists_exception(name);
  1722 + }
1701 } 1723 }
1702 1724
1703 auto value = iter->second; 1725 auto value = iter->second;
@@ -1726,7 +1748,19 @@ ParseResult::parse(int&amp; argc, char**&amp; argv) @@ -1726,7 +1748,19 @@ ParseResult::parse(int&amp; argc, char**&amp; argv)
1726 1748
1727 if (iter == m_options.end()) 1749 if (iter == m_options.end())
1728 { 1750 {
1729 - throw option_not_exists_exception(name); 1751 + if (m_allow_unrecognised)
  1752 + {
  1753 + // keep unrecognised options in argument list, skip to next argument
  1754 + argv[nextKeep] = argv[current];
  1755 + ++nextKeep;
  1756 + ++current;
  1757 + continue;
  1758 + }
  1759 + else
  1760 + {
  1761 + //error
  1762 + throw option_not_exists_exception(name);
  1763 + }
1730 } 1764 }
1731 1765
1732 auto opt = iter->second; 1766 auto opt = iter->second;
src/example.cpp
@@ -37,7 +37,9 @@ int main(int argc, char* argv[]) @@ -37,7 +37,9 @@ int main(int argc, char* argv[])
37 37
38 bool apple = false; 38 bool apple = false;
39 39
40 - options.add_options() 40 + options
  41 + .allow_unrecognised_options()
  42 + .add_options()
41 ("a,apple", "an apple", cxxopts::value<bool>(apple)) 43 ("a,apple", "an apple", cxxopts::value<bool>(apple))
42 ("b,bob", "Bob") 44 ("b,bob", "Bob")
43 ("t,true", "True", cxxopts::value<bool>()->default_value("true")) 45 ("t,true", "True", cxxopts::value<bool>()->default_value("true"))
test/options.cpp
@@ -473,3 +473,33 @@ TEST_CASE(&quot;std::optional&quot;, &quot;[optional]&quot;) { @@ -473,3 +473,33 @@ TEST_CASE(&quot;std::optional&quot;, &quot;[optional]&quot;) {
473 CHECK(*optional == "foo"); 473 CHECK(*optional == "foo");
474 } 474 }
475 #endif 475 #endif
  476 +
  477 +TEST_CASE("Unrecognised options", "[options]") {
  478 + cxxopts::Options options("unknown_options", " - test unknown options");
  479 +
  480 + options.add_options()
  481 + ("long", "a long option")
  482 + ("s,short", "a short option");
  483 +
  484 + Argv av({
  485 + "unknown_options",
  486 + "--unknown",
  487 + "--long",
  488 + "-su",
  489 + "--another_unknown",
  490 + });
  491 +
  492 + char** argv = av.argv();
  493 + auto argc = av.argc();
  494 +
  495 + SECTION("Default behaviour") {
  496 + CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::option_not_exists_exception);
  497 + }
  498 +
  499 + SECTION("After allowing unrecognised options") {
  500 + options.allow_unrecognised_options();
  501 + CHECK_NOTHROW(options.parse(argc, argv));
  502 + REQUIRE(argc == 3);
  503 + CHECK_THAT(argv[1], Catch::Equals("--unknown"));
  504 + }
  505 +}
476 \ No newline at end of file 506 \ No newline at end of file