Commit 387e51dc1623cfbec81ec121b622c5cfd40518e2

Authored by Baptiste Wicht
2 parents 456ea951 8d7c4ea4

Merge branch 'master' into default_values

Conflicts:
	src/cxxopts.hpp
CMakeLists.txt
@@ -23,5 +23,20 @@ project(cxxopts) @@ -23,5 +23,20 @@ project(cxxopts)
23 23
24 set(VERSION "0.0.1") 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 add_subdirectory(src) 41 add_subdirectory(src)
27 42
src/CMakeLists.txt
@@ -21,5 +21,6 @@ @@ -21,5 +21,6 @@
21 add_executable(example example.cpp) 21 add_executable(example example.cpp)
22 22
23 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") 23 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall")
  24 +target_link_libraries(example ${CXXOPTS_LINKER_LIBRARIES})
24 25
25 install(FILES cxxopts.hpp DESTINATION include) 26 install(FILES cxxopts.hpp DESTINATION include)
src/cxxopts.hpp
@@ -34,6 +34,184 @@ THE SOFTWARE. @@ -34,6 +34,184 @@ THE SOFTWARE.
34 #include <string> 34 #include <string>
35 #include <vector> 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 namespace cxxopts 215 namespace cxxopts
38 { 216 {
39 class Value : public std::enable_shared_from_this<Value> 217 class Value : public std::enable_shared_from_this<Value>
@@ -354,7 +532,7 @@ namespace cxxopts @@ -354,7 +532,7 @@ namespace cxxopts
354 public: 532 public:
355 OptionDetails 533 OptionDetails
356 ( 534 (
357 - const std::string& description, 535 + const String& description,
358 std::shared_ptr<const Value> value 536 std::shared_ptr<const Value> value
359 ) 537 )
360 : m_desc(description) 538 : m_desc(description)
@@ -363,7 +541,7 @@ namespace cxxopts @@ -363,7 +541,7 @@ namespace cxxopts
363 { 541 {
364 } 542 }
365 543
366 - const std::string& 544 + const String&
367 description() const 545 description() const
368 { 546 {
369 return m_desc; 547 return m_desc;
@@ -407,7 +585,7 @@ namespace cxxopts @@ -407,7 +585,7 @@ namespace cxxopts
407 } 585 }
408 586
409 private: 587 private:
410 - std::string m_desc; 588 + String m_desc;
411 std::shared_ptr<const Value> m_value; 589 std::shared_ptr<const Value> m_value;
412 int m_count; 590 int m_count;
413 }; 591 };
@@ -416,7 +594,7 @@ namespace cxxopts @@ -416,7 +594,7 @@ namespace cxxopts
416 { 594 {
417 std::string s; 595 std::string s;
418 std::string l; 596 std::string l;
419 - std::string desc; 597 + String desc;
420 bool has_arg; 598 bool has_arg;
421 bool has_default; 599 bool has_default;
422 std::string default_value; 600 std::string default_value;
@@ -435,7 +613,7 @@ namespace cxxopts @@ -435,7 +613,7 @@ namespace cxxopts
435 613
436 Options(std::string program, std::string help_string = "") 614 Options(std::string program, std::string help_string = "")
437 : m_program(std::move(program)) 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,7 +632,7 @@ namespace cxxopts
454 const std::string& group, 632 const std::string& group,
455 const std::string& s, 633 const std::string& s,
456 const std::string& l, 634 const std::string& l,
457 - const std::string& desc, 635 + std::string desc,
458 std::shared_ptr<const Value> value 636 std::shared_ptr<const Value> value
459 ); 637 );
460 638
@@ -531,11 +709,11 @@ namespace cxxopts @@ -531,11 +709,11 @@ namespace cxxopts
531 ); 709 );
532 710
533 inline 711 inline
534 - std::string 712 + String
535 help_one_group(const std::string& group) const; 713 help_one_group(const std::string& group) const;
536 714
537 std::string m_program; 715 std::string m_program;
538 - std::string m_help_string; 716 + String m_help_string;
539 717
540 std::map<std::string, std::shared_ptr<OptionDetails>> m_options; 718 std::map<std::string, std::shared_ptr<OptionDetails>> m_options;
541 std::string m_positional; 719 std::string m_positional;
@@ -580,12 +758,12 @@ namespace cxxopts @@ -580,12 +758,12 @@ namespace cxxopts
580 constexpr int OPTION_DESC_GAP = 2; 758 constexpr int OPTION_DESC_GAP = 2;
581 759
582 std::basic_regex<char> option_matcher 760 std::basic_regex<char> option_matcher
583 - ("--([[:alpha:]][-_[:alpha:]]+)(=(.*))?|-([a-zA-Z]+)"); 761 + ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([a-zA-Z]+)");
584 762
585 std::basic_regex<char> option_specifier 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 format_option 767 format_option
590 ( 768 (
591 const HelpOptionDetails& o 769 const HelpOptionDetails& o
@@ -594,11 +772,11 @@ namespace cxxopts @@ -594,11 +772,11 @@ namespace cxxopts
594 auto& s = o.s; 772 auto& s = o.s;
595 auto& l = o.l; 773 auto& l = o.l;
596 774
597 - std::string result = " "; 775 + String result = " ";
598 776
599 if (s.size() > 0) 777 if (s.size() > 0)
600 { 778 {
601 - result += "-" + s + ","; 779 + result += "-" + toLocalString(s) + ",";
602 } 780 }
603 else 781 else
604 { 782 {
@@ -607,7 +785,7 @@ namespace cxxopts @@ -607,7 +785,7 @@ namespace cxxopts
607 785
608 if (l.size() > 0) 786 if (l.size() > 0)
609 { 787 {
610 - result += " --" + l; 788 + result += " --" + toLocalString(l);
611 } 789 }
612 790
613 if (o.has_arg) 791 if (o.has_arg)
@@ -623,23 +801,23 @@ namespace cxxopts @@ -623,23 +801,23 @@ namespace cxxopts
623 return result; 801 return result;
624 } 802 }
625 803
626 - std::string 804 + String
627 format_description 805 format_description
628 ( 806 (
629 - const std::string& text, 807 + const String& text,
630 int start, 808 int start,
631 int width 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 auto startLine = current; 815 auto startLine = current;
638 auto lastSpace = current; 816 auto lastSpace = current;
639 817
640 int size = 0; 818 int size = 0;
641 819
642 - while (current != text.end()) 820 + while (current != std::end(text))
643 { 821 {
644 if (*current == ' ') 822 if (*current == ' ')
645 { 823 {
@@ -650,17 +828,17 @@ namespace cxxopts @@ -650,17 +828,17 @@ namespace cxxopts
650 { 828 {
651 if (lastSpace == startLine) 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 startLine = current + 1; 834 startLine = current + 1;
657 lastSpace = startLine; 835 lastSpace = startLine;
658 } 836 }
659 else 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 startLine = lastSpace + 1; 842 startLine = lastSpace + 1;
665 } 843 }
666 size = 0; 844 size = 0;
@@ -674,7 +852,7 @@ namespace cxxopts @@ -674,7 +852,7 @@ namespace cxxopts
674 } 852 }
675 853
676 //append whatever is left 854 //append whatever is left
677 - result.append(startLine, current); 855 + stringAppend(result, startLine, current);
678 856
679 return result; 857 return result;
680 } 858 }
@@ -921,11 +1099,12 @@ Options::add_option @@ -921,11 +1099,12 @@ Options::add_option
921 const std::string& group, 1099 const std::string& group,
922 const std::string& s, 1100 const std::string& s,
923 const std::string& l, 1101 const std::string& l,
924 - const std::string& desc, 1102 + std::string desc,
925 std::shared_ptr<const Value> value 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 if (s.size() > 0) 1109 if (s.size() > 0)
931 { 1110 {
@@ -939,7 +1118,8 @@ Options::add_option @@ -939,7 +1118,8 @@ Options::add_option
939 1118
940 //add the help details 1119 //add the help details
941 auto& options = m_help[group]; 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 value->has_arg(), value->has_default(), value->get_default_value()}); 1123 value->has_arg(), value->has_default(), value->get_default_value()});
944 } 1124 }
945 1125
@@ -958,10 +1138,10 @@ Options::add_one_option @@ -958,10 +1138,10 @@ Options::add_one_option
958 } 1138 }
959 } 1139 }
960 1140
961 -std::string 1141 +String
962 Options::help_one_group(const std::string& g) const 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 auto group = m_help.find(g); 1146 auto group = m_help.find(g);
967 if (group == m_help.end()) 1147 if (group == m_help.end())
@@ -973,18 +1153,19 @@ Options::help_one_group(const std::string&amp; g) const @@ -973,18 +1153,19 @@ Options::help_one_group(const std::string&amp; g) const
973 1153
974 size_t longest = 0; 1154 size_t longest = 0;
975 1155
976 - std::string result; 1156 + String result;
977 1157
978 if (!g.empty()) 1158 if (!g.empty())
979 { 1159 {
980 - result += " " + g + " options:\n\n"; 1160 + result += toLocalString(" " + g + " options:\n\n");
981 } 1161 }
982 1162
983 for (const auto& o : group->second.options) 1163 for (const auto& o : group->second.options)
984 { 1164 {
985 auto s = format_option(o); 1165 auto s = format_option(o);
986 longest = std::max(longest, s.size()); 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 longest = std::min(longest, static_cast<size_t>(OPTION_LONGEST)); 1171 longest = std::min(longest, static_cast<size_t>(OPTION_LONGEST));
@@ -998,15 +1179,16 @@ Options::help_one_group(const std::string&amp; g) const @@ -998,15 +1179,16 @@ Options::help_one_group(const std::string&amp; g) const
998 auto d = format_description(o.desc, longest + OPTION_DESC_GAP, allowed); 1179 auto d = format_description(o.desc, longest + OPTION_DESC_GAP, allowed);
999 1180
1000 result += fiter->first; 1181 result += fiter->first;
1001 - if (fiter->first.size() > longest) 1182 + if (stringLength(fiter->first) > longest)
1002 { 1183 {
1003 result += "\n"; 1184 result += "\n";
1004 - result += std::string(longest + OPTION_DESC_GAP, ' '); 1185 + result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' '));
1005 } 1186 }
1006 else 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 result += d; 1193 result += d;
1012 result += "\n"; 1194 result += "\n";
@@ -1020,15 +1202,19 @@ Options::help_one_group(const std::string&amp; g) const @@ -1020,15 +1202,19 @@ Options::help_one_group(const std::string&amp; g) const
1020 std::string 1202 std::string
1021 Options::help(const std::vector<std::string>& groups) const 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 + m_help_string + "\n\n"; 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,6 +48,10 @@ int main(int argc, char* argv[])
48 ("help", "Print help") 48 ("help", "Print help")
49 ("int", "An integer", cxxopts::value<int>()) 49 ("int", "An integer", cxxopts::value<int>())
50 ("option_that_is_too_long_for_the_help", "A very long option") 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 options.add_options("Group") 57 options.add_options("Group")