Commit c04f8a5bb9cabc552e690d8277d4939544813304

Authored by RonxBulld
Committed by GitHub
1 parent 17451028

Fully compatible with GCC4.8 compilation system. (#285)

* -Wsuggest-override is not supported by gcc before 5.0
* GCC prior to 5.0 should ignore not only -Wnon-virtual-dtor but also -Weffc++, otherwise non-virtual destructor problems will still be reported.
* The `#pragma GCC diagnostic push' should be used before setting up the temporary environment.
* When using GCC4.8, use manual lexical analysis instead of regular expressions.
* Add gcc4.8 stuff to travis file.
.travis.yml
@@ -51,6 +51,20 @@ matrix: @@ -51,6 +51,20 @@ matrix:
51 - g++-5 51 - g++-5
52 sources: *sources 52 sources: *sources
53 - os: linux 53 - os: linux
  54 + env: COMPILER=g++-4.8
  55 + addons:
  56 + apt:
  57 + packages:
  58 + - g++-4.8
  59 + sources: *sources
  60 + - os: linux
  61 + env: COMPILER=g++-4.8 UNICODE_OPTIONS=-DCXXOPTS_USE_UNICODE_HELP=Yes
  62 + addons:
  63 + apt:
  64 + packages:
  65 + - g++-4.8
  66 + sources: *sources
  67 + - os: linux
54 env: COMPILER=clang++-3.8 CXXFLAGS=-stdlib=libc++ 68 env: COMPILER=clang++-3.8 CXXFLAGS=-stdlib=libc++
55 addons: 69 addons:
56 apt: 70 apt:
include/cxxopts.hpp
@@ -33,13 +33,23 @@ THE SOFTWARE. @@ -33,13 +33,23 @@ THE SOFTWARE.
33 #include <list> 33 #include <list>
34 #include <map> 34 #include <map>
35 #include <memory> 35 #include <memory>
36 -#include <regex>  
37 #include <sstream> 36 #include <sstream>
38 #include <string> 37 #include <string>
39 #include <unordered_map> 38 #include <unordered_map>
40 #include <unordered_set> 39 #include <unordered_set>
41 #include <utility> 40 #include <utility>
42 #include <vector> 41 #include <vector>
  42 +#include <algorithm>
  43 +
  44 +#if defined(__GNUC__) && !defined(__clang__)
  45 +# if (__GNUC__ * 10 + __GNUC_MINOR__) < 49
  46 +# define CXXOPTS_NO_REGEX true
  47 +# endif
  48 +#endif
  49 +
  50 +#ifndef CXXOPTS_NO_REGEX
  51 +# include <regex>
  52 +#endif // CXXOPTS_NO_REGEX
43 53
44 #ifdef __cpp_lib_optional 54 #ifdef __cpp_lib_optional
45 #include <optional> 55 #include <optional>
@@ -91,6 +101,14 @@ namespace cxxopts @@ -91,6 +101,14 @@ namespace cxxopts
91 return icu::UnicodeString::fromUTF8(std::move(s)); 101 return icu::UnicodeString::fromUTF8(std::move(s));
92 } 102 }
93 103
  104 +#if defined(__GNUC__)
  105 +// GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we want to silence it:
  106 +// warning: base class 'class std::enable_shared_from_this<cxxopts::Value>' has accessible non-virtual destructor
  107 +#pragma GCC diagnostic push
  108 +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
  109 +#pragma GCC diagnostic ignored "-Weffc++"
  110 +// This will be ignored under other compilers like LLVM clang.
  111 +#endif
94 class UnicodeStringIterator : public 112 class UnicodeStringIterator : public
95 std::iterator<std::forward_iterator_tag, int32_t> 113 std::iterator<std::forward_iterator_tag, int32_t>
96 { 114 {
@@ -137,6 +155,9 @@ namespace cxxopts @@ -137,6 +155,9 @@ namespace cxxopts
137 const icu::UnicodeString* s; 155 const icu::UnicodeString* s;
138 int32_t i; 156 int32_t i;
139 }; 157 };
  158 +#if defined(__GNUC__)
  159 +#pragma GCC diagnostic pop
  160 +#endif
140 161
141 inline 162 inline
142 String& 163 String&
@@ -519,15 +540,257 @@ namespace cxxopts @@ -519,15 +540,257 @@ namespace cxxopts
519 540
520 namespace values 541 namespace values
521 { 542 {
522 - namespace 543 + namespace parser_tool
523 { 544 {
524 - std::basic_regex<char> integer_pattern  
525 - ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)");  
526 - std::basic_regex<char> truthy_pattern  
527 - ("(t|T)(rue)?|1");  
528 - std::basic_regex<char> falsy_pattern  
529 - ("(f|F)(alse)?|0");  
530 - } // namespace 545 + struct IntegerDesc
  546 + {
  547 + std::string negative = "";
  548 + std::string base = "";
  549 + std::string value = "";
  550 + };
  551 + struct ArguDesc {
  552 + std::string arg_name = "";
  553 + bool grouping = false;
  554 + bool set_value = false;
  555 + std::string value = "";
  556 + };
  557 +#ifdef CXXOPTS_NO_REGEX
  558 + inline IntegerDesc SplitInteger(const std::string &text)
  559 + {
  560 + if (text.empty())
  561 + {
  562 + throw_or_mimic<argument_incorrect_type>(text);
  563 + }
  564 + IntegerDesc desc;
  565 + const char *pdata = text.c_str();
  566 + if (*pdata == '-')
  567 + {
  568 + pdata += 1;
  569 + desc.negative = "-";
  570 + }
  571 + if (strncmp(pdata, "0x", 2) == 0)
  572 + {
  573 + pdata += 2;
  574 + desc.base = "0x";
  575 + }
  576 + if (*pdata != '\0')
  577 + {
  578 + desc.value = std::string(pdata);
  579 + }
  580 + else
  581 + {
  582 + throw_or_mimic<argument_incorrect_type>(text);
  583 + }
  584 + return desc;
  585 + }
  586 +
  587 + inline bool IsTrueText(const std::string &text)
  588 + {
  589 + const char *pdata = text.c_str();
  590 + if (*pdata == 't' || *pdata == 'T')
  591 + {
  592 + pdata += 1;
  593 + if (strncmp(pdata, "rue\0", 4) == 0)
  594 + {
  595 + return true;
  596 + }
  597 + }
  598 + else if (strncmp(pdata, "1\0", 2) == 0)
  599 + {
  600 + return true;
  601 + }
  602 + return false;
  603 + }
  604 +
  605 + inline bool IsFalseText(const std::string &text)
  606 + {
  607 + const char *pdata = text.c_str();
  608 + if (*pdata == 'f' || *pdata == 'F')
  609 + {
  610 + pdata += 1;
  611 + if (strncmp(pdata, "alse\0", 5) == 0)
  612 + {
  613 + return true;
  614 + }
  615 + }
  616 + else if (strncmp(pdata, "0\0", 2) == 0)
  617 + {
  618 + return true;
  619 + }
  620 + return false;
  621 + }
  622 +
  623 + inline std::pair<std::string, std::string> SplitSwitchDef(const std::string &text)
  624 + {
  625 + std::string short_sw, long_sw;
  626 + const char *pdata = text.c_str();
  627 + if (isalnum(*pdata) && *(pdata + 1) == ',') {
  628 + short_sw = std::string(1, *pdata);
  629 + pdata += 2;
  630 + }
  631 + while (*pdata == ' ') { pdata += 1; }
  632 + if (isalnum(*pdata)) {
  633 + const char *store = pdata;
  634 + pdata += 1;
  635 + while (isalnum(*pdata) || *pdata == '-' || *pdata == '_') {
  636 + pdata += 1;
  637 + }
  638 + if (*pdata == '\0') {
  639 + long_sw = std::string(store, pdata - store);
  640 + } else {
  641 + throw_or_mimic<invalid_option_format_error>(text);
  642 + }
  643 + }
  644 + return std::pair<std::string, std::string>(short_sw, long_sw);
  645 + }
  646 +
  647 + inline ArguDesc ParseArgument(const char *arg, bool &matched)
  648 + {
  649 + ArguDesc argu_desc;
  650 + const char *pdata = arg;
  651 + matched = false;
  652 + if (strncmp(pdata, "--", 2) == 0)
  653 + {
  654 + pdata += 2;
  655 + if (isalnum(*pdata))
  656 + {
  657 + argu_desc.arg_name.push_back(*pdata);
  658 + pdata += 1;
  659 + while (isalnum(*pdata) || *pdata == '-' || *pdata == '_')
  660 + {
  661 + argu_desc.arg_name.push_back(*pdata);
  662 + pdata += 1;
  663 + }
  664 + if (argu_desc.arg_name.length() > 1)
  665 + {
  666 + if (*pdata == '=')
  667 + {
  668 + argu_desc.set_value = true;
  669 + pdata += 1;
  670 + if (*pdata != '\0')
  671 + {
  672 + argu_desc.value = std::string(pdata);
  673 + }
  674 + matched = true;
  675 + }
  676 + else if (*pdata == '\0')
  677 + {
  678 + matched = true;
  679 + }
  680 + }
  681 + }
  682 + }
  683 + else if (strncmp(pdata, "-", 1) == 0)
  684 + {
  685 + pdata += 1;
  686 + argu_desc.grouping = true;
  687 + while (isalnum(*pdata))
  688 + {
  689 + argu_desc.arg_name.push_back(*pdata);
  690 + pdata += 1;
  691 + }
  692 + matched = !argu_desc.arg_name.empty() && *pdata == '\0';
  693 + }
  694 + return argu_desc;
  695 + }
  696 +
  697 +#else // CXXOPTS_NO_REGEX
  698 +
  699 + namespace
  700 + {
  701 +
  702 + std::basic_regex<char> integer_pattern
  703 + ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)");
  704 + std::basic_regex<char> truthy_pattern
  705 + ("(t|T)(rue)?|1");
  706 + std::basic_regex<char> falsy_pattern
  707 + ("(f|F)(alse)?|0");
  708 +
  709 + std::basic_regex<char> option_matcher
  710 + ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)");
  711 + std::basic_regex<char> option_specifier
  712 + ("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?");
  713 +
  714 + } // namespace
  715 +
  716 + inline IntegerDesc SplitInteger(const std::string &text)
  717 + {
  718 + std::smatch match;
  719 + std::regex_match(text, match, integer_pattern);
  720 +
  721 + if (match.length() == 0)
  722 + {
  723 + throw_or_mimic<argument_incorrect_type>(text);
  724 + }
  725 +
  726 + IntegerDesc desc;
  727 + desc.negative = match[1];
  728 + desc.base = match[2];
  729 + desc.value = match[3];
  730 +
  731 + if (match.length(4) > 0)
  732 + {
  733 + desc.base = match[5];
  734 + desc.value = "0";
  735 + return desc;
  736 + }
  737 +
  738 + return desc;
  739 + }
  740 +
  741 + inline bool IsTrueText(const std::string &text)
  742 + {
  743 + std::smatch result;
  744 + std::regex_match(text, result, truthy_pattern);
  745 + return !result.empty();
  746 + }
  747 +
  748 + inline bool IsFalseText(const std::string &text)
  749 + {
  750 + std::smatch result;
  751 + std::regex_match(text, result, falsy_pattern);
  752 + return !result.empty();
  753 + }
  754 +
  755 + inline std::pair<std::string, std::string> SplitSwitchDef(const std::string &text)
  756 + {
  757 + std::match_results<const char*> result;
  758 + std::regex_match(text.c_str(), result, option_specifier);
  759 + if (result.empty())
  760 + {
  761 + throw_or_mimic<invalid_option_format_error>(text);
  762 + }
  763 +
  764 + const std::string& short_sw = result[2];
  765 + const std::string& long_sw = result[3];
  766 +
  767 + return std::pair<std::string, std::string>(short_sw, long_sw);
  768 + }
  769 +
  770 + inline ArguDesc ParseArgument(const char *arg, bool &matched)
  771 + {
  772 + std::match_results<const char*> result;
  773 + std::regex_match(arg, result, option_matcher);
  774 + matched = !result.empty();
  775 +
  776 + ArguDesc argu_desc;
  777 + if (matched) {
  778 + argu_desc.arg_name = result[1].str();
  779 + argu_desc.set_value = result[2].length() > 0;
  780 + argu_desc.value = result[3].str();
  781 + if (result[4].length() > 0)
  782 + {
  783 + argu_desc.grouping = true;
  784 + argu_desc.arg_name = result[4].str();
  785 + }
  786 + }
  787 +
  788 + return argu_desc;
  789 + }
  790 +
  791 +#endif // CXXOPTS_NO_REGEX
  792 +#undef CXXOPTS_NO_REGEX
  793 + }
531 794
532 namespace detail 795 namespace detail
533 { 796 {
@@ -595,45 +858,32 @@ namespace cxxopts @@ -595,45 +858,32 @@ namespace cxxopts
595 void 858 void
596 integer_parser(const std::string& text, T& value) 859 integer_parser(const std::string& text, T& value)
597 { 860 {
598 - std::smatch match;  
599 - std::regex_match(text, match, integer_pattern);  
600 -  
601 - if (match.length() == 0)  
602 - {  
603 - throw_or_mimic<argument_incorrect_type>(text);  
604 - }  
605 -  
606 - if (match.length(4) > 0)  
607 - {  
608 - value = 0;  
609 - return;  
610 - } 861 + parser_tool::IntegerDesc int_desc = parser_tool::SplitInteger(text);
611 862
612 using US = typename std::make_unsigned<T>::type; 863 using US = typename std::make_unsigned<T>::type;
613 -  
614 constexpr bool is_signed = std::numeric_limits<T>::is_signed; 864 constexpr bool is_signed = std::numeric_limits<T>::is_signed;
615 - const bool negative = match.length(1) > 0;  
616 - const uint8_t base = match.length(2) > 0 ? 16 : 10;  
617 865
618 - auto value_match = match[3]; 866 + const bool negative = int_desc.negative.length() > 0;
  867 + const uint8_t base = int_desc.base.length() > 0 ? 16 : 10;
  868 + const std::string & value_match = int_desc.value;
619 869
620 US result = 0; 870 US result = 0;
621 871
622 - for (auto iter = value_match.first; iter != value_match.second; ++iter) 872 + for (char ch : value_match)
623 { 873 {
624 US digit = 0; 874 US digit = 0;
625 875
626 - if (*iter >= '0' && *iter <= '9') 876 + if (ch >= '0' && ch <= '9')
627 { 877 {
628 - digit = static_cast<US>(*iter - '0'); 878 + digit = static_cast<US>(ch - '0');
629 } 879 }
630 - else if (base == 16 && *iter >= 'a' && *iter <= 'f') 880 + else if (base == 16 && ch >= 'a' && ch <= 'f')
631 { 881 {
632 - digit = static_cast<US>(*iter - 'a' + 10); 882 + digit = static_cast<US>(ch - 'a' + 10);
633 } 883 }
634 - else if (base == 16 && *iter >= 'A' && *iter <= 'F') 884 + else if (base == 16 && ch >= 'A' && ch <= 'F')
635 { 885 {
636 - digit = static_cast<US>(*iter - 'A' + 10); 886 + digit = static_cast<US>(ch - 'A' + 10);
637 } 887 }
638 else 888 else
639 { 889 {
@@ -731,17 +981,13 @@ namespace cxxopts @@ -731,17 +981,13 @@ namespace cxxopts
731 void 981 void
732 parse_value(const std::string& text, bool& value) 982 parse_value(const std::string& text, bool& value)
733 { 983 {
734 - std::smatch result;  
735 - std::regex_match(text, result, truthy_pattern);  
736 -  
737 - if (!result.empty()) 984 + if (parser_tool::IsTrueText(text))
738 { 985 {
739 value = true; 986 value = true;
740 return; 987 return;
741 } 988 }
742 989
743 - std::regex_match(text, result, falsy_pattern);  
744 - if (!result.empty()) 990 + if (parser_tool::IsFalseText(text))
745 { 991 {
746 value = false; 992 value = false;
747 return; 993 return;
@@ -1579,12 +1825,6 @@ namespace cxxopts @@ -1579,12 +1825,6 @@ namespace cxxopts
1579 constexpr size_t OPTION_LONGEST = 30; 1825 constexpr size_t OPTION_LONGEST = 30;
1580 constexpr size_t OPTION_DESC_GAP = 2; 1826 constexpr size_t OPTION_DESC_GAP = 2;
1581 1827
1582 - std::basic_regex<char> option_matcher  
1583 - ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)");  
1584 -  
1585 - std::basic_regex<char> option_specifier  
1586 - ("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?");  
1587 -  
1588 String 1828 String
1589 format_option 1829 format_option
1590 ( 1830 (
@@ -1794,37 +2034,30 @@ OptionAdder::operator() @@ -1794,37 +2034,30 @@ OptionAdder::operator()
1794 std::string arg_help 2034 std::string arg_help
1795 ) 2035 )
1796 { 2036 {
1797 - std::match_results<const char*> result;  
1798 - std::regex_match(opts.c_str(), result, option_specifier); 2037 + std::string short_sw, long_sw;
  2038 + std::tie(short_sw, long_sw) = values::parser_tool::SplitSwitchDef(opts);
1799 2039
1800 - if (result.empty()) 2040 + if (!short_sw.length() && !long_sw.length())
1801 { 2041 {
1802 throw_or_mimic<invalid_option_format_error>(opts); 2042 throw_or_mimic<invalid_option_format_error>(opts);
1803 } 2043 }
1804 -  
1805 - const auto& short_match = result[2];  
1806 - const auto& long_match = result[3];  
1807 -  
1808 - if (!short_match.length() && !long_match.length())  
1809 - {  
1810 - throw_or_mimic<invalid_option_format_error>(opts);  
1811 - } else if (long_match.length() == 1 && short_match.length()) 2044 + else if (long_sw.length() == 1 && short_sw.length())
1812 { 2045 {
1813 throw_or_mimic<invalid_option_format_error>(opts); 2046 throw_or_mimic<invalid_option_format_error>(opts);
1814 } 2047 }
1815 2048
1816 auto option_names = [] 2049 auto option_names = []
1817 ( 2050 (
1818 - const std::sub_match<const char*>& short_,  
1819 - const std::sub_match<const char*>& long_ 2051 + const std::string &short_,
  2052 + const std::string &long_
1820 ) 2053 )
1821 { 2054 {
1822 if (long_.length() == 1) 2055 if (long_.length() == 1)
1823 { 2056 {
1824 - return std::make_tuple(long_.str(), short_.str()); 2057 + return std::make_tuple(long_, short_);
1825 } 2058 }
1826 - return std::make_tuple(short_.str(), long_.str());  
1827 - }(short_match, long_match); 2059 + return std::make_tuple(short_, long_);
  2060 + }(short_sw, long_sw);
1828 2061
1829 m_options.add_option 2062 m_options.add_option
1830 ( 2063 (
@@ -1986,11 +2219,11 @@ OptionParser::parse(int argc, const char* const* argv) @@ -1986,11 +2219,11 @@ OptionParser::parse(int argc, const char* const* argv)
1986 ++current; 2219 ++current;
1987 break; 2220 break;
1988 } 2221 }
  2222 + bool matched = false;
  2223 + values::parser_tool::ArguDesc argu_desc =
  2224 + values::parser_tool::ParseArgument(argv[current], matched);
1989 2225
1990 - std::match_results<const char*> result;  
1991 - std::regex_match(argv[current], result, option_matcher);  
1992 -  
1993 - if (result.empty()) 2226 + if (!matched)
1994 { 2227 {
1995 //not a flag 2228 //not a flag
1996 2229
@@ -2015,9 +2248,9 @@ OptionParser::parse(int argc, const char* const* argv) @@ -2015,9 +2248,9 @@ OptionParser::parse(int argc, const char* const* argv)
2015 else 2248 else
2016 { 2249 {
2017 //short or long option? 2250 //short or long option?
2018 - if (result[4].length() != 0) 2251 + if (argu_desc.grouping)
2019 { 2252 {
2020 - const std::string& s = result[4]; 2253 + const std::string& s = argu_desc.arg_name;
2021 2254
2022 for (std::size_t i = 0; i != s.size(); ++i) 2255 for (std::size_t i = 0; i != s.size(); ++i)
2023 { 2256 {
@@ -2052,9 +2285,9 @@ OptionParser::parse(int argc, const char* const* argv) @@ -2052,9 +2285,9 @@ OptionParser::parse(int argc, const char* const* argv)
2052 } 2285 }
2053 } 2286 }
2054 } 2287 }
2055 - else if (result[1].length() != 0) 2288 + else if (argu_desc.arg_name.length() != 0)
2056 { 2289 {
2057 - const std::string& name = result[1]; 2290 + const std::string& name = argu_desc.arg_name;
2058 2291
2059 auto iter = m_options.find(name); 2292 auto iter = m_options.find(name);
2060 2293
@@ -2074,11 +2307,11 @@ OptionParser::parse(int argc, const char* const* argv) @@ -2074,11 +2307,11 @@ OptionParser::parse(int argc, const char* const* argv)
2074 auto opt = iter->second; 2307 auto opt = iter->second;
2075 2308
2076 //equals provided for long option? 2309 //equals provided for long option?
2077 - if (result[2].length() != 0) 2310 + if (argu_desc.set_value)
2078 { 2311 {
2079 //parse the option given 2312 //parse the option given
2080 2313
2081 - parse_option(opt, name, result[3]); 2314 + parse_option(opt, name, argu_desc.value);
2082 } 2315 }
2083 else 2316 else
2084 { 2317 {