Commit fedf9d7b57297765c18b95aecc9625f0104a0fde

Authored by Jarryd Beck
1 parent fd5cdfd5

Refactor parser

Major refactor of the parsing code organisation to improve encapsulation
and not modify the input arguments. The returned result no longer has
pointers into the original option specification.
include/cxxopts.hpp
@@ -30,6 +30,7 @@ THE SOFTWARE. @@ -30,6 +30,7 @@ THE SOFTWARE.
30 #include <exception> 30 #include <exception>
31 #include <iostream> 31 #include <iostream>
32 #include <limits> 32 #include <limits>
  33 +#include <list>
33 #include <map> 34 #include <map>
34 #include <memory> 35 #include <memory>
35 #include <regex> 36 #include <regex>
@@ -1013,6 +1014,7 @@ namespace cxxopts @@ -1013,6 +1014,7 @@ namespace cxxopts
1013 , m_value(std::move(val)) 1014 , m_value(std::move(val))
1014 , m_count(0) 1015 , m_count(0)
1015 { 1016 {
  1017 + m_hash = std::hash<std::string>{}(m_long + m_short);
1016 } 1018 }
1017 1019
1018 OptionDetails(const OptionDetails& rhs) 1020 OptionDetails(const OptionDetails& rhs)
@@ -1058,12 +1060,20 @@ namespace cxxopts @@ -1058,12 +1060,20 @@ namespace cxxopts
1058 return m_long; 1060 return m_long;
1059 } 1061 }
1060 1062
  1063 + size_t
  1064 + hash() const
  1065 + {
  1066 + return m_hash;
  1067 + }
  1068 +
1061 private: 1069 private:
1062 std::string m_short; 1070 std::string m_short;
1063 std::string m_long; 1071 std::string m_long;
1064 String m_desc; 1072 String m_desc;
1065 std::shared_ptr<const Value> m_value; 1073 std::shared_ptr<const Value> m_value;
1066 int m_count; 1074 int m_count;
  1075 +
  1076 + size_t m_hash;
1067 }; 1077 };
1068 1078
1069 struct HelpOptionDetails 1079 struct HelpOptionDetails
@@ -1198,45 +1208,65 @@ namespace cxxopts @@ -1198,45 +1208,65 @@ namespace cxxopts
1198 std::string m_value; 1208 std::string m_value;
1199 }; 1209 };
1200 1210
  1211 + using ParsedHashMap = std::unordered_map<size_t, OptionValue>;
  1212 + using NameHashMap = std::unordered_map<std::string, size_t>;
  1213 +
1201 class ParseResult 1214 class ParseResult
1202 { 1215 {
1203 public: 1216 public:
1204 1217
1205 - ParseResult(  
1206 - std::shared_ptr<  
1207 - std::unordered_map<std::string, std::shared_ptr<OptionDetails>>  
1208 - >,  
1209 - std::vector<std::string>,  
1210 - bool allow_unrecognised,  
1211 - int&, const char**&); 1218 + ParseResult() {}
  1219 +
  1220 + ParseResult(const ParseResult&) = default;
  1221 +
  1222 + ParseResult(NameHashMap&& keys, ParsedHashMap&& values, std::vector<KeyValue> sequential, std::vector<std::string>&& unmatched_args)
  1223 + : m_keys(std::move(keys))
  1224 + , m_values(std::move(values))
  1225 + , m_sequential(std::move(sequential))
  1226 + , m_unmatched(std::move(unmatched_args))
  1227 + {
  1228 + }
  1229 +
  1230 + ParseResult& operator=(ParseResult&&) = default;
  1231 + ParseResult& operator=(const ParseResult&) = default;
1212 1232
1213 size_t 1233 size_t
1214 count(const std::string& o) const 1234 count(const std::string& o) const
1215 { 1235 {
1216 - auto iter = m_options->find(o);  
1217 - if (iter == m_options->end()) 1236 + auto iter = m_keys.find(o);
  1237 + if (iter == m_keys.end())
1218 { 1238 {
1219 return 0; 1239 return 0;
1220 } 1240 }
1221 1241
1222 - auto riter = m_results.find(iter->second); 1242 + auto viter = m_values.find(iter->second);
  1243 +
  1244 + if (viter == m_values.end())
  1245 + {
  1246 + return 0;
  1247 + }
1223 1248
1224 - return riter->second.count(); 1249 + return viter->second.count();
1225 } 1250 }
1226 1251
1227 const OptionValue& 1252 const OptionValue&
1228 operator[](const std::string& option) const 1253 operator[](const std::string& option) const
1229 { 1254 {
1230 - auto iter = m_options->find(option); 1255 + auto iter = m_keys.find(option);
1231 1256
1232 - if (iter == m_options->end()) 1257 + if (iter == m_keys.end())
1233 { 1258 {
1234 throw_or_mimic<option_not_present_exception>(option); 1259 throw_or_mimic<option_not_present_exception>(option);
1235 } 1260 }
1236 1261
1237 - auto riter = m_results.find(iter->second); 1262 + auto viter = m_values.find(iter->second);
  1263 +
  1264 + if (viter == m_values.end())
  1265 + {
  1266 + throw_or_mimic<option_not_present_exception>(option);
  1267 + }
1238 1268
1239 - return riter->second; 1269 + return viter->second;
1240 } 1270 }
1241 1271
1242 const std::vector<KeyValue>& 1272 const std::vector<KeyValue>&
@@ -1245,49 +1275,17 @@ namespace cxxopts @@ -1245,49 +1275,17 @@ namespace cxxopts
1245 return m_sequential; 1275 return m_sequential;
1246 } 1276 }
1247 1277
1248 - private:  
1249 -  
1250 - void  
1251 - parse(int& argc, const char**& argv);  
1252 -  
1253 - void  
1254 - add_to_option(const std::string& option, const std::string& arg);  
1255 -  
1256 - bool  
1257 - consume_positional(const std::string& a);  
1258 -  
1259 - void  
1260 - parse_option  
1261 - (  
1262 - const std::shared_ptr<OptionDetails>& value,  
1263 - const std::string& name,  
1264 - const std::string& arg = ""  
1265 - );  
1266 -  
1267 - void  
1268 - parse_default(const std::shared_ptr<OptionDetails>& details);  
1269 -  
1270 - void  
1271 - checked_parse_arg  
1272 - (  
1273 - int argc,  
1274 - const char* argv[],  
1275 - int& current,  
1276 - const std::shared_ptr<OptionDetails>& value,  
1277 - const std::string& name  
1278 - );  
1279 -  
1280 - const std::shared_ptr<  
1281 - std::unordered_map<std::string, std::shared_ptr<OptionDetails>>  
1282 - > m_options;  
1283 - std::vector<std::string> m_positional;  
1284 - std::vector<std::string>::iterator m_next_positional;  
1285 - std::unordered_set<std::string> m_positional_set;  
1286 - std::unordered_map<std::shared_ptr<OptionDetails>, OptionValue> m_results;  
1287 -  
1288 - bool m_allow_unrecognised; 1278 + const std::vector<std::string>&
  1279 + unmatched() const
  1280 + {
  1281 + return m_unmatched;
  1282 + }
1289 1283
  1284 + private:
  1285 + NameHashMap m_keys;
  1286 + ParsedHashMap m_values;
1290 std::vector<KeyValue> m_sequential; 1287 std::vector<KeyValue> m_sequential;
  1288 + std::vector<std::string> m_unmatched;
1291 }; 1289 };
1292 1290
1293 struct Option 1291 struct Option
@@ -1312,9 +1310,66 @@ namespace cxxopts @@ -1312,9 +1310,66 @@ namespace cxxopts
1312 std::string arg_help_; 1310 std::string arg_help_;
1313 }; 1311 };
1314 1312
  1313 + using OptionMap = std::unordered_map<std::string, std::shared_ptr<OptionDetails>>;
  1314 + using PositionalList = std::vector<std::string>;
  1315 + using PositionalListIterator = PositionalList::const_iterator;
  1316 +
  1317 + class OptionParser
  1318 + {
  1319 + public:
  1320 + OptionParser(const OptionMap& options, const PositionalList& positional, bool allow_unrecognised)
  1321 + : m_options(options)
  1322 + , m_positional(positional)
  1323 + , m_allow_unrecognised(allow_unrecognised)
  1324 + {
  1325 + }
  1326 +
  1327 + ParseResult
  1328 + parse(int argc, const char** argv);
  1329 +
  1330 + bool
  1331 + consume_positional(const std::string& a, PositionalListIterator& next);
  1332 +
  1333 + void
  1334 + checked_parse_arg
  1335 + (
  1336 + int argc,
  1337 + const char* argv[],
  1338 + int& current,
  1339 + const std::shared_ptr<OptionDetails>& value,
  1340 + const std::string& name
  1341 + );
  1342 +
  1343 + void
  1344 + add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg);
  1345 +
  1346 + void
  1347 + parse_option
  1348 + (
  1349 + const std::shared_ptr<OptionDetails>& value,
  1350 + const std::string& name,
  1351 + const std::string& arg = ""
  1352 + );
  1353 +
  1354 + void
  1355 + parse_default(const std::shared_ptr<OptionDetails>& details);
  1356 +
  1357 + private:
  1358 +
  1359 + void finalise_aliases();
  1360 +
  1361 + const OptionMap& m_options;
  1362 + const PositionalList& m_positional;
  1363 +
  1364 + std::vector<KeyValue> m_sequential;
  1365 + bool m_allow_unrecognised;
  1366 +
  1367 + ParsedHashMap m_parsed;
  1368 + NameHashMap m_keys;
  1369 + };
  1370 +
1315 class Options 1371 class Options
1316 { 1372 {
1317 - using OptionMap = std::unordered_map<std::string, std::shared_ptr<OptionDetails>>;  
1318 public: 1373 public:
1319 1374
1320 explicit Options(std::string program, std::string help_string = "") 1375 explicit Options(std::string program, std::string help_string = "")
@@ -1325,7 +1380,6 @@ namespace cxxopts @@ -1325,7 +1380,6 @@ namespace cxxopts
1325 , m_show_positional(false) 1380 , m_show_positional(false)
1326 , m_allow_unrecognised(false) 1381 , m_allow_unrecognised(false)
1327 , m_options(std::make_shared<OptionMap>()) 1382 , m_options(std::make_shared<OptionMap>())
1328 - , m_next_positional(m_positional.end())  
1329 { 1383 {
1330 } 1384 }
1331 1385
@@ -1358,7 +1412,7 @@ namespace cxxopts @@ -1358,7 +1412,7 @@ namespace cxxopts
1358 } 1412 }
1359 1413
1360 ParseResult 1414 ParseResult
1361 - parse(int& argc, const char**& argv); 1415 + parse(int argc, const char** argv);
1362 1416
1363 OptionAdder 1417 OptionAdder
1364 add_options(std::string group = ""); 1418 add_options(std::string group = "");
@@ -1444,11 +1498,13 @@ namespace cxxopts @@ -1444,11 +1498,13 @@ namespace cxxopts
1444 1498
1445 std::shared_ptr<OptionMap> m_options; 1499 std::shared_ptr<OptionMap> m_options;
1446 std::vector<std::string> m_positional; 1500 std::vector<std::string> m_positional;
1447 - std::vector<std::string>::iterator m_next_positional;  
1448 std::unordered_set<std::string> m_positional_set; 1501 std::unordered_set<std::string> m_positional_set;
1449 1502
1450 //mapping from groups to help options 1503 //mapping from groups to help options
1451 std::map<std::string, HelpGroupDetails> m_help; 1504 std::map<std::string, HelpGroupDetails> m_help;
  1505 +
  1506 + std::list<OptionDetails> m_option_list;
  1507 + std::unordered_map<std::string, decltype(m_option_list)::iterator> m_option_map;
1452 }; 1508 };
1453 1509
1454 class OptionAdder 1510 class OptionAdder
@@ -1610,24 +1666,6 @@ namespace cxxopts @@ -1610,24 +1666,6 @@ namespace cxxopts
1610 } // namespace 1666 } // namespace
1611 1667
1612 inline 1668 inline
1613 -ParseResult::ParseResult  
1614 -(  
1615 - std::shared_ptr<  
1616 - std::unordered_map<std::string, std::shared_ptr<OptionDetails>>  
1617 - > options,  
1618 - std::vector<std::string> positional,  
1619 - bool allow_unrecognised,  
1620 - int& argc, const char**& argv  
1621 -)  
1622 -: m_options(std::move(options))  
1623 -, m_positional(std::move(positional))  
1624 -, m_next_positional(m_positional.begin())  
1625 -, m_allow_unrecognised(allow_unrecognised)  
1626 -{  
1627 - parse(argc, argv);  
1628 -}  
1629 -  
1630 -inline  
1631 void 1669 void
1632 Options::add_options 1670 Options::add_options
1633 ( 1671 (
@@ -1706,21 +1744,24 @@ OptionAdder::operator() @@ -1706,21 +1744,24 @@ OptionAdder::operator()
1706 1744
1707 inline 1745 inline
1708 void 1746 void
1709 -ParseResult::parse_default(const std::shared_ptr<OptionDetails>& details) 1747 +OptionParser::parse_default(const std::shared_ptr<OptionDetails>& details)
1710 { 1748 {
1711 - m_results[details].parse_default(details); 1749 + // TODO: remove the duplicate code here
  1750 + auto& store = m_parsed[details->hash()];
  1751 + store.parse_default(details);
1712 } 1752 }
1713 1753
1714 inline 1754 inline
1715 void 1755 void
1716 -ParseResult::parse_option 1756 +OptionParser::parse_option
1717 ( 1757 (
1718 const std::shared_ptr<OptionDetails>& value, 1758 const std::shared_ptr<OptionDetails>& value,
1719 const std::string& /*name*/, 1759 const std::string& /*name*/,
1720 const std::string& arg 1760 const std::string& arg
1721 ) 1761 )
1722 { 1762 {
1723 - auto& result = m_results[value]; 1763 + auto hash = value->hash();
  1764 + auto& result = m_parsed[hash];
1724 result.parse(value, arg); 1765 result.parse(value, arg);
1725 1766
1726 m_sequential.emplace_back(value->long_name(), arg); 1767 m_sequential.emplace_back(value->long_name(), arg);
@@ -1728,7 +1769,7 @@ ParseResult::parse_option @@ -1728,7 +1769,7 @@ ParseResult::parse_option
1728 1769
1729 inline 1770 inline
1730 void 1771 void
1731 -ParseResult::checked_parse_arg 1772 +OptionParser::checked_parse_arg
1732 ( 1773 (
1733 int argc, 1774 int argc,
1734 const char* argv[], 1775 const char* argv[],
@@ -1764,43 +1805,36 @@ ParseResult::checked_parse_arg @@ -1764,43 +1805,36 @@ ParseResult::checked_parse_arg
1764 1805
1765 inline 1806 inline
1766 void 1807 void
1767 -ParseResult::add_to_option(const std::string& option, const std::string& arg) 1808 +OptionParser::add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg)
1768 { 1809 {
1769 - auto iter = m_options->find(option);  
1770 -  
1771 - if (iter == m_options->end())  
1772 - {  
1773 - throw_or_mimic<option_not_exists_exception>(option);  
1774 - }  
1775 -  
1776 parse_option(iter->second, option, arg); 1810 parse_option(iter->second, option, arg);
1777 } 1811 }
1778 1812
1779 inline 1813 inline
1780 bool 1814 bool
1781 -ParseResult::consume_positional(const std::string& a) 1815 +OptionParser::consume_positional(const std::string& a, PositionalListIterator& next)
1782 { 1816 {
1783 - while (m_next_positional != m_positional.end()) 1817 + while (next != m_positional.end())
1784 { 1818 {
1785 - auto iter = m_options->find(*m_next_positional);  
1786 - if (iter != m_options->end()) 1819 + auto iter = m_options.find(*next);
  1820 + if (iter != m_options.end())
1787 { 1821 {
1788 - auto& result = m_results[iter->second]; 1822 + auto& result = m_parsed[iter->second->hash()];
1789 if (!iter->second->value().is_container()) 1823 if (!iter->second->value().is_container())
1790 { 1824 {
1791 if (result.count() == 0) 1825 if (result.count() == 0)
1792 { 1826 {
1793 - add_to_option(*m_next_positional, a);  
1794 - ++m_next_positional; 1827 + add_to_option(iter, *next, a);
  1828 + ++next;
1795 return true; 1829 return true;
1796 } 1830 }
1797 - ++m_next_positional; 1831 + ++next;
1798 continue; 1832 continue;
1799 } 1833 }
1800 - add_to_option(*m_next_positional, a); 1834 + add_to_option(iter, *next, a);
1801 return true; 1835 return true;
1802 } 1836 }
1803 - throw_or_mimic<option_not_exists_exception>(*m_next_positional); 1837 + throw_or_mimic<option_not_exists_exception>(*next);
1804 } 1838 }
1805 1839
1806 return false; 1840 return false;
@@ -1818,7 +1852,6 @@ void @@ -1818,7 +1852,6 @@ void
1818 Options::parse_positional(std::vector<std::string> options) 1852 Options::parse_positional(std::vector<std::string> options)
1819 { 1853 {
1820 m_positional = std::move(options); 1854 m_positional = std::move(options);
1821 - m_next_positional = m_positional.begin();  
1822 1855
1823 m_positional_set.insert(m_positional.begin(), m_positional.end()); 1856 m_positional_set.insert(m_positional.begin(), m_positional.end());
1824 } 1857 }
@@ -1832,21 +1865,21 @@ Options::parse_positional(std::initializer_list&lt;std::string&gt; options) @@ -1832,21 +1865,21 @@ Options::parse_positional(std::initializer_list&lt;std::string&gt; options)
1832 1865
1833 inline 1866 inline
1834 ParseResult 1867 ParseResult
1835 -Options::parse(int& argc, const char**& argv) 1868 +Options::parse(int argc, const char** argv)
1836 { 1869 {
1837 - ParseResult result(m_options, m_positional, m_allow_unrecognised, argc, argv);  
1838 - return result; 1870 + OptionParser parser(*m_options, m_positional, m_allow_unrecognised);
  1871 +
  1872 + return parser.parse(argc, argv);
1839 } 1873 }
1840 1874
1841 -inline  
1842 -void  
1843 -ParseResult::parse(int& argc, const char**& argv) 1875 +inline ParseResult
  1876 +OptionParser::parse(int argc, const char** argv)
1844 { 1877 {
1845 int current = 1; 1878 int current = 1;
1846 -  
1847 - int nextKeep = 1;  
1848 -  
1849 bool consume_remaining = false; 1879 bool consume_remaining = false;
  1880 + PositionalListIterator next_positional = m_positional.begin();
  1881 +
  1882 + std::vector<std::string> unmatched;
1850 1883
1851 while (current != argc) 1884 while (current != argc)
1852 { 1885 {
@@ -1873,13 +1906,12 @@ ParseResult::parse(int&amp; argc, const char**&amp; argv) @@ -1873,13 +1906,12 @@ ParseResult::parse(int&amp; argc, const char**&amp; argv)
1873 1906
1874 //if true is returned here then it was consumed, otherwise it is 1907 //if true is returned here then it was consumed, otherwise it is
1875 //ignored 1908 //ignored
1876 - if (consume_positional(argv[current])) 1909 + if (consume_positional(argv[current], next_positional))
1877 { 1910 {
1878 } 1911 }
1879 else 1912 else
1880 { 1913 {
1881 - argv[nextKeep] = argv[current];  
1882 - ++nextKeep; 1914 + unmatched.push_back(argv[current]);
1883 } 1915 }
1884 //if we return from here then it was parsed successfully, so continue 1916 //if we return from here then it was parsed successfully, so continue
1885 } 1917 }
@@ -1893,9 +1925,9 @@ ParseResult::parse(int&amp; argc, const char**&amp; argv) @@ -1893,9 +1925,9 @@ ParseResult::parse(int&amp; argc, const char**&amp; argv)
1893 for (std::size_t i = 0; i != s.size(); ++i) 1925 for (std::size_t i = 0; i != s.size(); ++i)
1894 { 1926 {
1895 std::string name(1, s[i]); 1927 std::string name(1, s[i]);
1896 - auto iter = m_options->find(name); 1928 + auto iter = m_options.find(name);
1897 1929
1898 - if (iter == m_options->end()) 1930 + if (iter == m_options.end())
1899 { 1931 {
1900 if (m_allow_unrecognised) 1932 if (m_allow_unrecognised)
1901 { 1933 {
@@ -1927,15 +1959,14 @@ ParseResult::parse(int&amp; argc, const char**&amp; argv) @@ -1927,15 +1959,14 @@ ParseResult::parse(int&amp; argc, const char**&amp; argv)
1927 { 1959 {
1928 const std::string& name = result[1]; 1960 const std::string& name = result[1];
1929 1961
1930 - auto iter = m_options->find(name); 1962 + auto iter = m_options.find(name);
1931 1963
1932 - if (iter == m_options->end()) 1964 + if (iter == m_options.end())
1933 { 1965 {
1934 if (m_allow_unrecognised) 1966 if (m_allow_unrecognised)
1935 { 1967 {
1936 // keep unrecognised options in argument list, skip to next argument 1968 // keep unrecognised options in argument list, skip to next argument
1937 - argv[nextKeep] = argv[current];  
1938 - ++nextKeep; 1969 + unmatched.push_back(argv[current]);
1939 ++current; 1970 ++current;
1940 continue; 1971 continue;
1941 } 1972 }
@@ -1964,12 +1995,13 @@ ParseResult::parse(int&amp; argc, const char**&amp; argv) @@ -1964,12 +1995,13 @@ ParseResult::parse(int&amp; argc, const char**&amp; argv)
1964 ++current; 1995 ++current;
1965 } 1996 }
1966 1997
1967 - for (auto& opt : *m_options) 1998 + for (auto& opt : m_options)
1968 { 1999 {
1969 auto& detail = opt.second; 2000 auto& detail = opt.second;
1970 const auto& value = detail->value(); 2001 const auto& value = detail->value();
1971 2002
1972 - auto& store = m_results[detail]; 2003 + //auto& store = m_results[detail];
  2004 + auto& store = m_parsed[detail->hash()];
1973 2005
1974 if(value.has_default() && !store.count() && !store.has_default()){ 2006 if(value.has_default() && !store.count() && !store.has_default()){
1975 parse_default(detail); 2007 parse_default(detail);
@@ -1980,7 +2012,7 @@ ParseResult::parse(int&amp; argc, const char**&amp; argv) @@ -1980,7 +2012,7 @@ ParseResult::parse(int&amp; argc, const char**&amp; argv)
1980 { 2012 {
1981 while (current < argc) 2013 while (current < argc)
1982 { 2014 {
1983 - if (!consume_positional(argv[current])) { 2015 + if (!consume_positional(argv[current], next_positional)) {
1984 break; 2016 break;
1985 } 2017 }
1986 ++current; 2018 ++current;
@@ -1988,14 +2020,33 @@ ParseResult::parse(int&amp; argc, const char**&amp; argv) @@ -1988,14 +2020,33 @@ ParseResult::parse(int&amp; argc, const char**&amp; argv)
1988 2020
1989 //adjust argv for any that couldn't be swallowed 2021 //adjust argv for any that couldn't be swallowed
1990 while (current != argc) { 2022 while (current != argc) {
1991 - argv[nextKeep] = argv[current];  
1992 - ++nextKeep; 2023 + unmatched.push_back(argv[current]);
1993 ++current; 2024 ++current;
1994 } 2025 }
1995 } 2026 }
1996 2027
1997 - argc = nextKeep; 2028 + finalise_aliases();
1998 2029
  2030 + ParseResult parsed(std::move(m_keys), std::move(m_parsed), std::move(m_sequential), std::move(unmatched));
  2031 + return parsed;
  2032 +}
  2033 +
  2034 +inline
  2035 +void
  2036 +OptionParser::finalise_aliases()
  2037 +{
  2038 + for (auto& option: m_options)
  2039 + {
  2040 + auto& detail = *option.second;
  2041 + auto hash = detail.hash();
  2042 + //if (m_parsed.find(hash) != m_parsed.end())
  2043 + {
  2044 + m_keys[detail.short_name()] = hash;
  2045 + m_keys[detail.long_name()] = hash;
  2046 + }
  2047 +
  2048 + m_parsed.emplace(hash, OptionValue());
  2049 + }
1999 } 2050 }
2000 2051
2001 inline 2052 inline
@@ -2034,6 +2085,11 @@ Options::add_option @@ -2034,6 +2085,11 @@ Options::add_option
2034 add_one_option(l, option); 2085 add_one_option(l, option);
2035 } 2086 }
2036 2087
  2088 + m_option_list.push_front(*option.get());
  2089 + auto iter = m_option_list.begin();
  2090 + m_option_map[s] = iter;
  2091 + m_option_map[l] = iter;
  2092 +
2037 //add the help details 2093 //add the help details
2038 auto& options = m_help[group]; 2094 auto& options = m_help[group];
2039 2095
test/options.cpp
@@ -154,7 +154,7 @@ TEST_CASE(&quot;All positional&quot;, &quot;[positional]&quot;) @@ -154,7 +154,7 @@ TEST_CASE(&quot;All positional&quot;, &quot;[positional]&quot;)
154 154
155 auto result = options.parse(argc, argv); 155 auto result = options.parse(argc, argv);
156 156
157 - REQUIRE(argc == 1); 157 + CHECK(result.unmatched().size() == 0);
158 REQUIRE(positional.size() == 3); 158 REQUIRE(positional.size() == 3);
159 159
160 CHECK(positional[0] == "a"); 160 CHECK(positional[0] == "a");
@@ -182,7 +182,7 @@ TEST_CASE(&quot;Some positional explicit&quot;, &quot;[positional]&quot;) @@ -182,7 +182,7 @@ TEST_CASE(&quot;Some positional explicit&quot;, &quot;[positional]&quot;)
182 182
183 auto result = options.parse(argc, argv); 183 auto result = options.parse(argc, argv);
184 184
185 - CHECK(argc == 1); 185 + CHECK(result.unmatched().size() == 0);
186 CHECK(result.count("output")); 186 CHECK(result.count("output"));
187 CHECK(result["input"].as<std::string>() == "b"); 187 CHECK(result["input"].as<std::string>() == "b");
188 CHECK(result["output"].as<std::string>() == "a"); 188 CHECK(result["output"].as<std::string>() == "a");
@@ -209,11 +209,10 @@ TEST_CASE(&quot;No positional with extras&quot;, &quot;[positional]&quot;) @@ -209,11 +209,10 @@ TEST_CASE(&quot;No positional with extras&quot;, &quot;[positional]&quot;)
209 auto old_argv = argv; 209 auto old_argv = argv;
210 auto old_argc = argc; 210 auto old_argc = argc;
211 211
212 - options.parse(argc, argv); 212 + auto result = options.parse(argc, argv);
213 213
214 - REQUIRE(argc == old_argc - 1);  
215 - CHECK(argv[0] == std::string("extras"));  
216 - CHECK(argv[1] == std::string("a")); 214 + auto& unmatched = result.unmatched();
  215 + CHECK((unmatched == std::vector<std::string>{"a", "b", "c", "d"}));
217 } 216 }
218 217
219 TEST_CASE("Positional not valid", "[positional]") { 218 TEST_CASE("Positional not valid", "[positional]") {
@@ -643,9 +642,9 @@ TEST_CASE(&quot;Unrecognised options&quot;, &quot;[options]&quot;) { @@ -643,9 +642,9 @@ TEST_CASE(&quot;Unrecognised options&quot;, &quot;[options]&quot;) {
643 642
644 SECTION("After allowing unrecognised options") { 643 SECTION("After allowing unrecognised options") {
645 options.allow_unrecognised_options(); 644 options.allow_unrecognised_options();
646 - CHECK_NOTHROW(options.parse(argc, argv));  
647 - REQUIRE(argc == 3);  
648 - CHECK_THAT(argv[1], Catch::Equals("--unknown")); 645 + auto result = options.parse(argc, argv);
  646 + auto& unmatched = result.unmatched();
  647 + CHECK((unmatched == std::vector<std::string>{"--unknown", "--another_unknown"}));
649 } 648 }
650 } 649 }
651 650