Commit fedf9d7b57297765c18b95aecc9625f0104a0fde
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.
Showing
2 changed files
with
190 additions
and
135 deletions
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<std::string> options) | @@ -1832,21 +1865,21 @@ Options::parse_positional(std::initializer_list<std::string> 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& argc, const char**& argv) | @@ -1873,13 +1906,12 @@ ParseResult::parse(int& argc, const char**& 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& argc, const char**& argv) | @@ -1893,9 +1925,9 @@ ParseResult::parse(int& argc, const char**& 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& argc, const char**& argv) | @@ -1927,15 +1959,14 @@ ParseResult::parse(int& argc, const char**& 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& argc, const char**& argv) | @@ -1964,12 +1995,13 @@ ParseResult::parse(int& argc, const char**& 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& argc, const char**& argv) | @@ -1980,7 +2012,7 @@ ParseResult::parse(int& argc, const char**& 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& argc, const char**& argv) | @@ -1988,14 +2020,33 @@ ParseResult::parse(int& argc, const char**& 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("All positional", "[positional]") | @@ -154,7 +154,7 @@ TEST_CASE("All positional", "[positional]") | ||
| 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("Some positional explicit", "[positional]") | @@ -182,7 +182,7 @@ TEST_CASE("Some positional explicit", "[positional]") | ||
| 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("No positional with extras", "[positional]") | @@ -209,11 +209,10 @@ TEST_CASE("No positional with extras", "[positional]") | ||
| 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("Unrecognised options", "[options]") { | @@ -643,9 +642,9 @@ TEST_CASE("Unrecognised options", "[options]") { | ||
| 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 |