Commit 387e51dc1623cfbec81ec121b622c5cfd40518e2
Merge branch 'master' into default_values
Conflicts: src/cxxopts.hpp
Showing
4 changed files
with
248 additions
and
42 deletions
CMakeLists.txt
| ... | ... | @@ -23,5 +23,20 @@ project(cxxopts) |
| 23 | 23 | |
| 24 | 24 | set(VERSION "0.0.1") |
| 25 | 25 | |
| 26 | +set(CXXOPTS_LINKER_LIBRARIES "") | |
| 27 | + | |
| 28 | +set(CXXOPTS_USE_UNICODE_HELP FALSE CACHE BOOL "Use ICU Unicode library") | |
| 29 | + | |
| 30 | +if(CXXOPTS_USE_UNICODE_HELP) | |
| 31 | + | |
| 32 | + find_package(PkgConfig) | |
| 33 | + | |
| 34 | + pkg_check_modules(ICU REQUIRED icu-uc) | |
| 35 | + | |
| 36 | + set(CXXOPTS_LINKER_LIBRARIES "${ICU_LIBRARIES}") | |
| 37 | + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCXXOPTS_USE_UNICODE") | |
| 38 | + | |
| 39 | +endif() | |
| 40 | + | |
| 26 | 41 | add_subdirectory(src) |
| 27 | 42 | ... | ... |
src/CMakeLists.txt
src/cxxopts.hpp
| ... | ... | @@ -34,6 +34,184 @@ THE SOFTWARE. |
| 34 | 34 | #include <string> |
| 35 | 35 | #include <vector> |
| 36 | 36 | |
| 37 | +#ifdef CXXOPTS_USE_UNICODE | |
| 38 | +#include <unicode/unistr.h> | |
| 39 | + | |
| 40 | +namespace cxxopts | |
| 41 | +{ | |
| 42 | + typedef icu::UnicodeString String; | |
| 43 | + | |
| 44 | + inline | |
| 45 | + String | |
| 46 | + toLocalString(std::string s) | |
| 47 | + { | |
| 48 | + return icu::UnicodeString::fromUTF8(s); | |
| 49 | + } | |
| 50 | + | |
| 51 | + class UnicodeStringIterator : public | |
| 52 | + std::iterator<std::forward_iterator_tag, int32_t> | |
| 53 | + { | |
| 54 | + public: | |
| 55 | + | |
| 56 | + UnicodeStringIterator(const icu::UnicodeString* s, int32_t pos) | |
| 57 | + : s(s) | |
| 58 | + , i(pos) | |
| 59 | + { | |
| 60 | + } | |
| 61 | + | |
| 62 | + value_type | |
| 63 | + operator*() const | |
| 64 | + { | |
| 65 | + return s->char32At(i); | |
| 66 | + } | |
| 67 | + | |
| 68 | + bool | |
| 69 | + operator==(const UnicodeStringIterator& rhs) const | |
| 70 | + { | |
| 71 | + return s == rhs.s && i == rhs.i; | |
| 72 | + } | |
| 73 | + | |
| 74 | + bool | |
| 75 | + operator!=(const UnicodeStringIterator& rhs) const | |
| 76 | + { | |
| 77 | + return !(*this == rhs); | |
| 78 | + } | |
| 79 | + | |
| 80 | + UnicodeStringIterator& | |
| 81 | + operator++() | |
| 82 | + { | |
| 83 | + ++i; | |
| 84 | + return *this; | |
| 85 | + } | |
| 86 | + | |
| 87 | + UnicodeStringIterator | |
| 88 | + operator+(int32_t v) | |
| 89 | + { | |
| 90 | + return UnicodeStringIterator(s, i + v); | |
| 91 | + } | |
| 92 | + | |
| 93 | + private: | |
| 94 | + const icu::UnicodeString* s; | |
| 95 | + int32_t i; | |
| 96 | + }; | |
| 97 | + | |
| 98 | + inline | |
| 99 | + String& | |
| 100 | + stringAppend(String&s, String a) | |
| 101 | + { | |
| 102 | + return s.append(std::move(a)); | |
| 103 | + } | |
| 104 | + | |
| 105 | + inline | |
| 106 | + String& | |
| 107 | + stringAppend(String& s, int n, UChar32 c) | |
| 108 | + { | |
| 109 | + for (int i = 0; i != n; ++i) | |
| 110 | + { | |
| 111 | + s.append(c); | |
| 112 | + } | |
| 113 | + | |
| 114 | + return s; | |
| 115 | + } | |
| 116 | + | |
| 117 | + template <typename Iterator> | |
| 118 | + String& | |
| 119 | + stringAppend(String& s, Iterator begin, Iterator end) | |
| 120 | + { | |
| 121 | + while (begin != end) | |
| 122 | + { | |
| 123 | + s.append(*begin); | |
| 124 | + ++begin; | |
| 125 | + } | |
| 126 | + | |
| 127 | + return s; | |
| 128 | + } | |
| 129 | + | |
| 130 | + inline | |
| 131 | + size_t | |
| 132 | + stringLength(const String& s) | |
| 133 | + { | |
| 134 | + return s.length(); | |
| 135 | + } | |
| 136 | + | |
| 137 | + inline | |
| 138 | + std::string | |
| 139 | + toUTF8String(const String& s) | |
| 140 | + { | |
| 141 | + std::string result; | |
| 142 | + s.toUTF8String(result); | |
| 143 | + | |
| 144 | + return result; | |
| 145 | + } | |
| 146 | +} | |
| 147 | + | |
| 148 | +namespace std | |
| 149 | +{ | |
| 150 | + cxxopts::UnicodeStringIterator | |
| 151 | + begin(const icu::UnicodeString& s) | |
| 152 | + { | |
| 153 | + return cxxopts::UnicodeStringIterator(&s, 0); | |
| 154 | + } | |
| 155 | + | |
| 156 | + cxxopts::UnicodeStringIterator | |
| 157 | + end(const icu::UnicodeString& s) | |
| 158 | + { | |
| 159 | + return cxxopts::UnicodeStringIterator(&s, s.length()); | |
| 160 | + } | |
| 161 | +} | |
| 162 | + | |
| 163 | +#else | |
| 164 | + | |
| 165 | +namespace cxxopts | |
| 166 | +{ | |
| 167 | + typedef std::string String; | |
| 168 | + | |
| 169 | + template <typename T> | |
| 170 | + T | |
| 171 | + toLocalString(T&& t) | |
| 172 | + { | |
| 173 | + return t; | |
| 174 | + } | |
| 175 | + | |
| 176 | + inline | |
| 177 | + size_t | |
| 178 | + stringLength(const String& s) | |
| 179 | + { | |
| 180 | + return s.length(); | |
| 181 | + } | |
| 182 | + | |
| 183 | + inline | |
| 184 | + String& | |
| 185 | + stringAppend(String&s, String a) | |
| 186 | + { | |
| 187 | + return s.append(std::move(a)); | |
| 188 | + } | |
| 189 | + | |
| 190 | + inline | |
| 191 | + String& | |
| 192 | + stringAppend(String& s, int n, char c) | |
| 193 | + { | |
| 194 | + return s.append(n, c); | |
| 195 | + } | |
| 196 | + | |
| 197 | + template <typename Iterator> | |
| 198 | + String& | |
| 199 | + stringAppend(String& s, Iterator begin, Iterator end) | |
| 200 | + { | |
| 201 | + return s.append(begin, end); | |
| 202 | + } | |
| 203 | + | |
| 204 | + template <typename T> | |
| 205 | + std::string | |
| 206 | + toUTF8String(T&& t) | |
| 207 | + { | |
| 208 | + return std::forward<T>(t); | |
| 209 | + } | |
| 210 | + | |
| 211 | +} | |
| 212 | + | |
| 213 | +#endif | |
| 214 | + | |
| 37 | 215 | namespace cxxopts |
| 38 | 216 | { |
| 39 | 217 | class Value : public std::enable_shared_from_this<Value> |
| ... | ... | @@ -354,7 +532,7 @@ namespace cxxopts |
| 354 | 532 | public: |
| 355 | 533 | OptionDetails |
| 356 | 534 | ( |
| 357 | - const std::string& description, | |
| 535 | + const String& description, | |
| 358 | 536 | std::shared_ptr<const Value> value |
| 359 | 537 | ) |
| 360 | 538 | : m_desc(description) |
| ... | ... | @@ -363,7 +541,7 @@ namespace cxxopts |
| 363 | 541 | { |
| 364 | 542 | } |
| 365 | 543 | |
| 366 | - const std::string& | |
| 544 | + const String& | |
| 367 | 545 | description() const |
| 368 | 546 | { |
| 369 | 547 | return m_desc; |
| ... | ... | @@ -407,7 +585,7 @@ namespace cxxopts |
| 407 | 585 | } |
| 408 | 586 | |
| 409 | 587 | private: |
| 410 | - std::string m_desc; | |
| 588 | + String m_desc; | |
| 411 | 589 | std::shared_ptr<const Value> m_value; |
| 412 | 590 | int m_count; |
| 413 | 591 | }; |
| ... | ... | @@ -416,7 +594,7 @@ namespace cxxopts |
| 416 | 594 | { |
| 417 | 595 | std::string s; |
| 418 | 596 | std::string l; |
| 419 | - std::string desc; | |
| 597 | + String desc; | |
| 420 | 598 | bool has_arg; |
| 421 | 599 | bool has_default; |
| 422 | 600 | std::string default_value; |
| ... | ... | @@ -435,7 +613,7 @@ namespace cxxopts |
| 435 | 613 | |
| 436 | 614 | Options(std::string program, std::string help_string = "") |
| 437 | 615 | : m_program(std::move(program)) |
| 438 | - , m_help_string(std::move(help_string)) | |
| 616 | + , m_help_string(toLocalString(std::move(help_string))) | |
| 439 | 617 | { |
| 440 | 618 | } |
| 441 | 619 | |
| ... | ... | @@ -454,7 +632,7 @@ namespace cxxopts |
| 454 | 632 | const std::string& group, |
| 455 | 633 | const std::string& s, |
| 456 | 634 | const std::string& l, |
| 457 | - const std::string& desc, | |
| 635 | + std::string desc, | |
| 458 | 636 | std::shared_ptr<const Value> value |
| 459 | 637 | ); |
| 460 | 638 | |
| ... | ... | @@ -531,11 +709,11 @@ namespace cxxopts |
| 531 | 709 | ); |
| 532 | 710 | |
| 533 | 711 | inline |
| 534 | - std::string | |
| 712 | + String | |
| 535 | 713 | help_one_group(const std::string& group) const; |
| 536 | 714 | |
| 537 | 715 | std::string m_program; |
| 538 | - std::string m_help_string; | |
| 716 | + String m_help_string; | |
| 539 | 717 | |
| 540 | 718 | std::map<std::string, std::shared_ptr<OptionDetails>> m_options; |
| 541 | 719 | std::string m_positional; |
| ... | ... | @@ -580,12 +758,12 @@ namespace cxxopts |
| 580 | 758 | constexpr int OPTION_DESC_GAP = 2; |
| 581 | 759 | |
| 582 | 760 | std::basic_regex<char> option_matcher |
| 583 | - ("--([[:alpha:]][-_[:alpha:]]+)(=(.*))?|-([a-zA-Z]+)"); | |
| 761 | + ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([a-zA-Z]+)"); | |
| 584 | 762 | |
| 585 | 763 | std::basic_regex<char> option_specifier |
| 586 | - ("(([a-zA-Z]),)?([a-zA-Z][-_a-zA-Z]+)"); | |
| 764 | + ("(([a-zA-Z]),)?([a-zA-Z0-9][-_a-zA-Z0-9]+)"); | |
| 587 | 765 | |
| 588 | - std::string | |
| 766 | + String | |
| 589 | 767 | format_option |
| 590 | 768 | ( |
| 591 | 769 | const HelpOptionDetails& o |
| ... | ... | @@ -594,11 +772,11 @@ namespace cxxopts |
| 594 | 772 | auto& s = o.s; |
| 595 | 773 | auto& l = o.l; |
| 596 | 774 | |
| 597 | - std::string result = " "; | |
| 775 | + String result = " "; | |
| 598 | 776 | |
| 599 | 777 | if (s.size() > 0) |
| 600 | 778 | { |
| 601 | - result += "-" + s + ","; | |
| 779 | + result += "-" + toLocalString(s) + ","; | |
| 602 | 780 | } |
| 603 | 781 | else |
| 604 | 782 | { |
| ... | ... | @@ -607,7 +785,7 @@ namespace cxxopts |
| 607 | 785 | |
| 608 | 786 | if (l.size() > 0) |
| 609 | 787 | { |
| 610 | - result += " --" + l; | |
| 788 | + result += " --" + toLocalString(l); | |
| 611 | 789 | } |
| 612 | 790 | |
| 613 | 791 | if (o.has_arg) |
| ... | ... | @@ -623,23 +801,23 @@ namespace cxxopts |
| 623 | 801 | return result; |
| 624 | 802 | } |
| 625 | 803 | |
| 626 | - std::string | |
| 804 | + String | |
| 627 | 805 | format_description |
| 628 | 806 | ( |
| 629 | - const std::string& text, | |
| 807 | + const String& text, | |
| 630 | 808 | int start, |
| 631 | 809 | int width |
| 632 | 810 | ) |
| 633 | 811 | { |
| 634 | - std::string result; | |
| 812 | + String result; | |
| 635 | 813 | |
| 636 | - auto current = text.begin(); | |
| 814 | + auto current = std::begin(text); | |
| 637 | 815 | auto startLine = current; |
| 638 | 816 | auto lastSpace = current; |
| 639 | 817 | |
| 640 | 818 | int size = 0; |
| 641 | 819 | |
| 642 | - while (current != text.end()) | |
| 820 | + while (current != std::end(text)) | |
| 643 | 821 | { |
| 644 | 822 | if (*current == ' ') |
| 645 | 823 | { |
| ... | ... | @@ -650,17 +828,17 @@ namespace cxxopts |
| 650 | 828 | { |
| 651 | 829 | if (lastSpace == startLine) |
| 652 | 830 | { |
| 653 | - result.append(startLine, current + 1); | |
| 654 | - result.append("\n"); | |
| 655 | - result.append(start, ' '); | |
| 831 | + stringAppend(result, startLine, current + 1); | |
| 832 | + stringAppend(result, "\n"); | |
| 833 | + stringAppend(result, start, ' '); | |
| 656 | 834 | startLine = current + 1; |
| 657 | 835 | lastSpace = startLine; |
| 658 | 836 | } |
| 659 | 837 | else |
| 660 | 838 | { |
| 661 | - result.append(startLine, lastSpace); | |
| 662 | - result.append("\n"); | |
| 663 | - result.append(start, ' '); | |
| 839 | + stringAppend(result, startLine, lastSpace); | |
| 840 | + stringAppend(result, "\n"); | |
| 841 | + stringAppend(result, start, ' '); | |
| 664 | 842 | startLine = lastSpace + 1; |
| 665 | 843 | } |
| 666 | 844 | size = 0; |
| ... | ... | @@ -674,7 +852,7 @@ namespace cxxopts |
| 674 | 852 | } |
| 675 | 853 | |
| 676 | 854 | //append whatever is left |
| 677 | - result.append(startLine, current); | |
| 855 | + stringAppend(result, startLine, current); | |
| 678 | 856 | |
| 679 | 857 | return result; |
| 680 | 858 | } |
| ... | ... | @@ -921,11 +1099,12 @@ Options::add_option |
| 921 | 1099 | const std::string& group, |
| 922 | 1100 | const std::string& s, |
| 923 | 1101 | const std::string& l, |
| 924 | - const std::string& desc, | |
| 1102 | + std::string desc, | |
| 925 | 1103 | std::shared_ptr<const Value> value |
| 926 | 1104 | ) |
| 927 | 1105 | { |
| 928 | - auto option = std::make_shared<OptionDetails>(desc, value); | |
| 1106 | + auto stringDesc = toLocalString(std::move(desc)); | |
| 1107 | + auto option = std::make_shared<OptionDetails>(stringDesc, value); | |
| 929 | 1108 | |
| 930 | 1109 | if (s.size() > 0) |
| 931 | 1110 | { |
| ... | ... | @@ -939,7 +1118,8 @@ Options::add_option |
| 939 | 1118 | |
| 940 | 1119 | //add the help details |
| 941 | 1120 | auto& options = m_help[group]; |
| 942 | - options.options.emplace_back(HelpOptionDetails{s, l, desc, | |
| 1121 | + | |
| 1122 | + options.options.emplace_back(HelpOptionDetails{s, l, stringDesc, | |
| 943 | 1123 | value->has_arg(), value->has_default(), value->get_default_value()}); |
| 944 | 1124 | } |
| 945 | 1125 | |
| ... | ... | @@ -958,10 +1138,10 @@ Options::add_one_option |
| 958 | 1138 | } |
| 959 | 1139 | } |
| 960 | 1140 | |
| 961 | -std::string | |
| 1141 | +String | |
| 962 | 1142 | Options::help_one_group(const std::string& g) const |
| 963 | 1143 | { |
| 964 | - typedef std::vector<std::pair<std::string, std::string>> OptionHelp; | |
| 1144 | + typedef std::vector<std::pair<String, String>> OptionHelp; | |
| 965 | 1145 | |
| 966 | 1146 | auto group = m_help.find(g); |
| 967 | 1147 | if (group == m_help.end()) |
| ... | ... | @@ -973,18 +1153,19 @@ Options::help_one_group(const std::string& g) const |
| 973 | 1153 | |
| 974 | 1154 | size_t longest = 0; |
| 975 | 1155 | |
| 976 | - std::string result; | |
| 1156 | + String result; | |
| 977 | 1157 | |
| 978 | 1158 | if (!g.empty()) |
| 979 | 1159 | { |
| 980 | - result += " " + g + " options:\n\n"; | |
| 1160 | + result += toLocalString(" " + g + " options:\n\n"); | |
| 981 | 1161 | } |
| 982 | 1162 | |
| 983 | 1163 | for (const auto& o : group->second.options) |
| 984 | 1164 | { |
| 985 | 1165 | auto s = format_option(o); |
| 986 | 1166 | longest = std::max(longest, s.size()); |
| 987 | - format.push_back(std::make_pair(s, std::string())); | |
| 1167 | + longest = std::max(longest, stringLength(s)); | |
| 1168 | + format.push_back(std::make_pair(s, String())); | |
| 988 | 1169 | } |
| 989 | 1170 | |
| 990 | 1171 | longest = std::min(longest, static_cast<size_t>(OPTION_LONGEST)); |
| ... | ... | @@ -998,15 +1179,16 @@ Options::help_one_group(const std::string& g) const |
| 998 | 1179 | auto d = format_description(o.desc, longest + OPTION_DESC_GAP, allowed); |
| 999 | 1180 | |
| 1000 | 1181 | result += fiter->first; |
| 1001 | - if (fiter->first.size() > longest) | |
| 1182 | + if (stringLength(fiter->first) > longest) | |
| 1002 | 1183 | { |
| 1003 | 1184 | result += "\n"; |
| 1004 | - result += std::string(longest + OPTION_DESC_GAP, ' '); | |
| 1185 | + result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' ')); | |
| 1005 | 1186 | } |
| 1006 | 1187 | else |
| 1007 | 1188 | { |
| 1008 | - result += std::string(longest + OPTION_DESC_GAP - fiter->first.size(), | |
| 1009 | - ' '); | |
| 1189 | + result += toLocalString(std::string(longest + OPTION_DESC_GAP - | |
| 1190 | + stringLength(fiter->first), | |
| 1191 | + ' ')); | |
| 1010 | 1192 | } |
| 1011 | 1193 | result += d; |
| 1012 | 1194 | result += "\n"; |
| ... | ... | @@ -1020,15 +1202,19 @@ Options::help_one_group(const std::string& g) const |
| 1020 | 1202 | std::string |
| 1021 | 1203 | Options::help(const std::vector<std::string>& groups) const |
| 1022 | 1204 | { |
| 1023 | - std::string result = "Usage:\n " + m_program + " [OPTION...]" | |
| 1205 | + String result = "Usage:\n " + toLocalString(m_program) + " [OPTION...]" | |
| 1024 | 1206 | + m_help_string + "\n\n"; |
| 1025 | 1207 | |
| 1026 | - for (const auto& g : groups) | |
| 1208 | + for (std::size_t i = 0; i < groups.size(); ++i) | |
| 1027 | 1209 | { |
| 1028 | - result += help_one_group(g); | |
| 1210 | + result += help_one_group(groups[i]); | |
| 1211 | + if (i < groups.size() - 1) | |
| 1212 | + { | |
| 1213 | + result += "\n"; | |
| 1214 | + } | |
| 1029 | 1215 | } |
| 1030 | 1216 | |
| 1031 | - return result; | |
| 1217 | + return toUTF8String(result); | |
| 1032 | 1218 | } |
| 1033 | 1219 | |
| 1034 | 1220 | } | ... | ... |
src/example.cpp
| ... | ... | @@ -48,6 +48,10 @@ int main(int argc, char* argv[]) |
| 48 | 48 | ("help", "Print help") |
| 49 | 49 | ("int", "An integer", cxxopts::value<int>()) |
| 50 | 50 | ("option_that_is_too_long_for_the_help", "A very long option") |
| 51 | + #ifdef CXXOPTS_USE_UNICODE | |
| 52 | + ("unicode", u8"A help option with non-ascii: à. Here the size of the" | |
| 53 | + " string should be correct") | |
| 54 | + #endif | |
| 51 | 55 | ; |
| 52 | 56 | |
| 53 | 57 | options.add_options("Group") | ... | ... |