From 63af78443b2bb2e8c12941230bf69eb268d7fde3 Mon Sep 17 00:00:00 2001 From: Baptiste Wicht Date: Sun, 26 Oct 2014 19:12:32 +0100 Subject: [PATCH 1/8] Add support for alphanumeric options --- src/cxxopts.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cxxopts.hpp b/src/cxxopts.hpp index f3908c6..073e39c 100644 --- a/src/cxxopts.hpp +++ b/src/cxxopts.hpp @@ -497,10 +497,10 @@ namespace cxxopts constexpr int OPTION_DESC_GAP = 2; std::basic_regex option_matcher - ("--([[:alpha:]][-_[:alpha:]]+)(=(.*))?|-([a-zA-Z]+)"); + ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([a-zA-Z]+)"); std::basic_regex option_specifier - ("(([a-zA-Z]),)?([a-zA-Z][-_a-zA-Z]+)"); + ("(([a-zA-Z]),)?([a-zA-Z0-9][-_a-zA-Z0-9]+)"); std::string format_option From a525bb1eefd63c1af0810eecac5fa5a47b13978c Mon Sep 17 00:00:00 2001 From: Baptiste Wicht Date: Sun, 26 Oct 2014 19:18:41 +0100 Subject: [PATCH 2/8] Add an eol between each option groups --- src/cxxopts.hpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cxxopts.hpp b/src/cxxopts.hpp index f3908c6..3910daf 100644 --- a/src/cxxopts.hpp +++ b/src/cxxopts.hpp @@ -909,9 +909,13 @@ Options::help(const std::vector& groups) const std::string result = "Usage:\n " + m_program + " [OPTION...]" + m_help_string + "\n\n"; - for (const auto& g : groups) + for (std::size_t i = 0; i < groups.size(); ++i) { - result += help_one_group(g); + result += help_one_group(groups[i]); + if (i < groups.size() - 1) + { + result += "\n"; + } } return result; From f514d7d71e2aa288eef6952165ea826f95496091 Mon Sep 17 00:00:00 2001 From: Jarryd Beck Date: Mon, 27 Oct 2014 13:30:22 +1100 Subject: [PATCH 3/8] start converting to String for Unicode --- src/cxxopts.hpp | 57 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/src/cxxopts.hpp b/src/cxxopts.hpp index 5e9b9d5..d168f10 100644 --- a/src/cxxopts.hpp +++ b/src/cxxopts.hpp @@ -34,6 +34,37 @@ THE SOFTWARE. #include #include +#ifdef CXXOPTS_USE_UNICODE +#include + +namespace cxxopts +{ + typedef icu::UnicodeString String; + + inline + String + toLocalString(std::string s) + { + return icu::UnicodeString::fromUTF8(s); + } +} + +#else + +namespace cxxopts +{ + typedef std::string String; + + inline + String + toLocalString(std::string s) + { + return std::move(s); + } +} + +#endif + namespace cxxopts { class Value @@ -284,7 +315,7 @@ namespace cxxopts public: OptionDetails ( - const std::string& description, + const String& description, std::shared_ptr value ) : m_desc(description) @@ -293,7 +324,7 @@ namespace cxxopts { } - const std::string& + const String& description() const { return m_desc; @@ -326,7 +357,7 @@ namespace cxxopts } private: - std::string m_desc; + String m_desc; std::shared_ptr m_value; int m_count; }; @@ -335,7 +366,7 @@ namespace cxxopts { std::string s; std::string l; - std::string desc; + String desc; bool has_arg; }; @@ -371,7 +402,7 @@ namespace cxxopts const std::string& group, const std::string& s, const std::string& l, - const std::string& desc, + std::string desc, std::shared_ptr value ); @@ -502,7 +533,7 @@ namespace cxxopts std::basic_regex option_specifier ("(([a-zA-Z]),)?([a-zA-Z0-9][-_a-zA-Z0-9]+)"); - std::string + String format_option ( const std::string& s, @@ -534,15 +565,15 @@ namespace cxxopts return result; } - std::string + String format_description ( - const std::string& text, + const String& text, int start, int width ) { - std::string result; + String result; auto current = text.begin(); auto startLine = current; @@ -808,11 +839,12 @@ Options::add_option const std::string& group, const std::string& s, const std::string& l, - const std::string& desc, + std::string desc, std::shared_ptr value ) { - auto option = std::make_shared(desc, value); + auto stringDesc = toLocalString(std::move(desc)); + auto option = std::make_shared(stringDesc, value); if (s.size() > 0) { @@ -826,7 +858,8 @@ Options::add_option //add the help details auto& options = m_help[group]; - options.options.emplace_back(HelpOptionDetails{s, l, desc, value->has_arg()}); + options.options. + emplace_back(HelpOptionDetails{s, l, stringDesc, value->has_arg()}); } void From 9d1a4dfcdc55021fefcade42271eca0fdbc6cf65 Mon Sep 17 00:00:00 2001 From: Jarryd Beck Date: Mon, 27 Oct 2014 15:20:12 +1100 Subject: [PATCH 4/8] more icu::UnicodeString --- src/cxxopts.hpp | 106 ++++++++++++++++++++++++++++++++++++++++-------- src/example.cpp | 2 + 2 files changed, 90 insertions(+), 18 deletions(-) diff --git a/src/cxxopts.hpp b/src/cxxopts.hpp index d168f10..eaf98e1 100644 --- a/src/cxxopts.hpp +++ b/src/cxxopts.hpp @@ -35,7 +35,7 @@ THE SOFTWARE. #include #ifdef CXXOPTS_USE_UNICODE -#include +#include namespace cxxopts { @@ -47,6 +47,68 @@ namespace cxxopts { return icu::UnicodeString::fromUTF8(s); } + + class UnicodeStringIterator : public + std::iterator + { + public: + + UnicodeStringIterator(const icu::UnicodeString* s, int32_t pos) + : s(s) + , i(pos) + { + } + + value_type + operator*() const + { + return s->char32At(i); + } + + bool + operator==(const UnicodeStringIterator& rhs) const + { + return s == rhs.s && i == rhs.i; + } + + bool + operator!=(const UnicodeStringIterator& rhs) const + { + return !(*this == rhs); + } + + UnicodeStringIterator& + operator++() + { + ++i; + return *this; + } + + UnicodeStringIterator + operator+(int32_t v) + { + return UnicodeStringIterator(s, i + v); + } + + private: + const icu::UnicodeString* s; + int32_t i; + }; +} + +namespace std +{ + cxxopts::UnicodeStringIterator + begin(const icu::UnicodeString& s) + { + return cxxopts::UnicodeStringIterator(&s, 0); + } + + cxxopts::UnicodeStringIterator + end(const icu::UnicodeString& s) + { + return cxxopts::UnicodeStringIterator(&s, s.length()); + } } #else @@ -55,11 +117,18 @@ namespace cxxopts { typedef std::string String; - inline - String - toLocalString(std::string s) + template + T + toLocalString(T&& t) { - return std::move(s); + return t; + } + + inline + size_t + stringLength(const String& s) + { + return s.length(); } } @@ -437,7 +506,7 @@ namespace cxxopts parse_positional(std::string option); inline - std::string + String help(const std::vector& groups = {""}) const; private: @@ -479,7 +548,7 @@ namespace cxxopts ); inline - std::string + String help_one_group(const std::string& group) const; std::string m_program; @@ -541,11 +610,11 @@ namespace cxxopts bool has_arg ) { - std::string result = " "; + String result = " "; if (s.size() > 0) { - result += "-" + s + ","; + result += "-" + toLocalString(s) + ","; } else { @@ -554,7 +623,7 @@ namespace cxxopts if (l.size() > 0) { - result += " --" + l; + result += " --" + toLocalString(l); } if (has_arg) @@ -575,13 +644,13 @@ namespace cxxopts { String result; - auto current = text.begin(); + auto current = std::begin(text); auto startLine = current; auto lastSpace = current; int size = 0; - while (current != text.end()) + while (current != std::end(text)) { if (*current == ' ') { @@ -877,7 +946,7 @@ Options::add_one_option } } -std::string +String Options::help_one_group(const std::string& g) const { typedef std::vector> OptionHelp; @@ -892,7 +961,7 @@ Options::help_one_group(const std::string& g) const size_t longest = 0; - std::string result; + String result; if (!g.empty()) { @@ -902,7 +971,7 @@ Options::help_one_group(const std::string& g) const for (const auto& o : group->second.options) { auto s = format_option(o.s, o.l, o.has_arg); - longest = std::max(longest, s.size()); + longest = std::max(longest, stringLength(s)); format.push_back(std::make_pair(s, std::string())); } @@ -917,14 +986,15 @@ Options::help_one_group(const std::string& g) const auto d = format_description(o.desc, longest + OPTION_DESC_GAP, allowed); result += fiter->first; - if (fiter->first.size() > longest) + if (stringLength(fiter->first) > longest) { result += "\n"; result += std::string(longest + OPTION_DESC_GAP, ' '); } else { - result += std::string(longest + OPTION_DESC_GAP - fiter->first.size(), + result += std::string(longest + OPTION_DESC_GAP - + stringLength(fiter->first), ' '); } result += d; @@ -936,7 +1006,7 @@ Options::help_one_group(const std::string& g) const return result; } -std::string +String Options::help(const std::vector& groups) const { std::string result = "Usage:\n " + m_program + " [OPTION...]" diff --git a/src/example.cpp b/src/example.cpp index 989d40c..d94d932 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -24,6 +24,8 @@ THE SOFTWARE. #include +#define CXXOPTS_USE_UNICODE + #include "cxxopts.hpp" int main(int argc, char* argv[]) From 9509ca94f2ccbdb6d78ea5370b8af0b1f21f4dbd Mon Sep 17 00:00:00 2001 From: Jarryd Beck Date: Mon, 27 Oct 2014 16:45:13 +1100 Subject: [PATCH 5/8] unicode --- CMakeLists.txt | 4 ++ src/CMakeLists.txt | 1 + src/cxxopts.hpp | 116 +++++++++++++++++++++++++++++++++++++-------- 3 files changed, 102 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d5ea0e6..e785ba4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,5 +23,9 @@ project(cxxopts) set(VERSION "0.0.1") +find_package(PkgConfig) + +pkg_check_modules(ICU REQUIRED icu-uc) + add_subdirectory(src) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3f24779..c2b2ff9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,5 +21,6 @@ add_executable(example example.cpp) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") +target_link_libraries(example ${ICU_LIBRARIES}) install(FILES cxxopts.hpp DESTINATION include) diff --git a/src/cxxopts.hpp b/src/cxxopts.hpp index eaf98e1..c2f99bc 100644 --- a/src/cxxopts.hpp +++ b/src/cxxopts.hpp @@ -94,6 +94,55 @@ namespace cxxopts const icu::UnicodeString* s; int32_t i; }; + + inline + String& + stringAppend(String&s, String a) + { + return s.append(std::move(a)); + } + + inline + String& + stringAppend(String& s, int n, UChar32 c) + { + for (int i = 0; i != n; ++i) + { + s.append(c); + } + + return s; + } + + template + String& + stringAppend(String& s, Iterator begin, Iterator end) + { + while (begin != end) + { + s.append(*begin); + ++begin; + } + + return s; + } + + inline + size_t + stringLength(const String& s) + { + return s.length(); + } + + inline + std::string + toUTF8String(const String& s) + { + std::string result; + s.toUTF8String(result); + + return result; + } } namespace std @@ -130,6 +179,35 @@ namespace cxxopts { return s.length(); } + + inline + String& + stringAppend(String&s, String a) + { + return s.append(std::move(a)); + } + + inline + String& + stringAppend(String& s, int n, char c) + { + return s.append(n, c); + } + + template + String& + stringAppend(String& s, Iterator begin, Iterator end) + { + return s.append(begin, end); + } + + template + std::string + toUTF8String(T&& t) + { + return std::forward(t); + } + } #endif @@ -452,7 +530,7 @@ namespace cxxopts Options(std::string program, std::string help_string = "") : m_program(std::move(program)) - , m_help_string(std::move(help_string)) + , m_help_string(toLocalString(std::move(help_string))) { } @@ -506,7 +584,7 @@ namespace cxxopts parse_positional(std::string option); inline - String + std::string help(const std::vector& groups = {""}) const; private: @@ -552,7 +630,7 @@ namespace cxxopts help_one_group(const std::string& group) const; std::string m_program; - std::string m_help_string; + String m_help_string; std::map> m_options; std::string m_positional; @@ -661,17 +739,17 @@ namespace cxxopts { if (lastSpace == startLine) { - result.append(startLine, current + 1); - result.append("\n"); - result.append(start, ' '); + stringAppend(result, startLine, current + 1); + stringAppend(result, "\n"); + stringAppend(result, start, ' '); startLine = current + 1; lastSpace = startLine; } else { - result.append(startLine, lastSpace); - result.append("\n"); - result.append(start, ' '); + stringAppend(result, startLine, lastSpace); + stringAppend(result, "\n"); + stringAppend(result, start, ' '); startLine = lastSpace + 1; } size = 0; @@ -685,7 +763,7 @@ namespace cxxopts } //append whatever is left - result.append(startLine, current); + stringAppend(result, startLine, current); return result; } @@ -949,7 +1027,7 @@ Options::add_one_option String Options::help_one_group(const std::string& g) const { - typedef std::vector> OptionHelp; + typedef std::vector> OptionHelp; auto group = m_help.find(g); if (group == m_help.end()) @@ -965,14 +1043,14 @@ Options::help_one_group(const std::string& g) const if (!g.empty()) { - result += " " + g + " options:\n\n"; + result += toLocalString(" " + g + " options:\n\n"); } for (const auto& o : group->second.options) { auto s = format_option(o.s, o.l, o.has_arg); longest = std::max(longest, stringLength(s)); - format.push_back(std::make_pair(s, std::string())); + format.push_back(std::make_pair(s, String())); } longest = std::min(longest, static_cast(OPTION_LONGEST)); @@ -989,13 +1067,13 @@ Options::help_one_group(const std::string& g) const if (stringLength(fiter->first) > longest) { result += "\n"; - result += std::string(longest + OPTION_DESC_GAP, ' '); + result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' ')); } else { - result += std::string(longest + OPTION_DESC_GAP - + result += toLocalString(std::string(longest + OPTION_DESC_GAP - stringLength(fiter->first), - ' '); + ' ')); } result += d; result += "\n"; @@ -1006,10 +1084,10 @@ Options::help_one_group(const std::string& g) const return result; } -String +std::string Options::help(const std::vector& groups) const { - std::string result = "Usage:\n " + m_program + " [OPTION...]" + String result = "Usage:\n " + toLocalString(m_program) + " [OPTION...]" + m_help_string + "\n\n"; for (std::size_t i = 0; i < groups.size(); ++i) @@ -1021,7 +1099,7 @@ Options::help(const std::vector& groups) const } } - return result; + return toUTF8String(result); } } From 3707c91a0156e9893201c50c15466008cbdc0dad Mon Sep 17 00:00:00 2001 From: Jarryd Beck Date: Mon, 27 Oct 2014 16:46:16 +1100 Subject: [PATCH 6/8] unicode help --- src/example.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/example.cpp b/src/example.cpp index d94d932..cfffb11 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -48,6 +48,7 @@ int main(int argc, char* argv[]) ("help", "Print help") ("int", "An integer", cxxopts::value()) ("option_that_is_too_long_for_the_help", "A very long option") + ("unicode", u8"A help option with an accent: à") ; options.add_options("Group") From 8234766bcadabd32fb548a4b2a4c5a18bff8a864 Mon Sep 17 00:00:00 2001 From: Jarryd Beck Date: Mon, 27 Oct 2014 16:47:21 +1100 Subject: [PATCH 7/8] better help example --- src/example.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/example.cpp b/src/example.cpp index cfffb11..526045c 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -48,7 +48,8 @@ int main(int argc, char* argv[]) ("help", "Print help") ("int", "An integer", cxxopts::value()) ("option_that_is_too_long_for_the_help", "A very long option") - ("unicode", u8"A help option with an accent: à") + ("unicode", u8"A help option with non-ascii: à. Here the length of the" + " string should be correct") ; options.add_options("Group") From 8d7c4ea43e25a3187662506040b826a167e7110c Mon Sep 17 00:00:00 2001 From: Jarryd Beck Date: Mon, 27 Oct 2014 22:06:24 +1100 Subject: [PATCH 8/8] unicode configuration --- CMakeLists.txt | 15 +++++++++++++-- src/CMakeLists.txt | 2 +- src/example.cpp | 6 +++--- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e785ba4..34e6bed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,9 +23,20 @@ project(cxxopts) set(VERSION "0.0.1") -find_package(PkgConfig) +set(CXXOPTS_LINKER_LIBRARIES "") -pkg_check_modules(ICU REQUIRED icu-uc) +set(CXXOPTS_USE_UNICODE_HELP FALSE CACHE BOOL "Use ICU Unicode library") + +if(CXXOPTS_USE_UNICODE_HELP) + + find_package(PkgConfig) + + pkg_check_modules(ICU REQUIRED icu-uc) + + set(CXXOPTS_LINKER_LIBRARIES "${ICU_LIBRARIES}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCXXOPTS_USE_UNICODE") + +endif() add_subdirectory(src) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c2b2ff9..625abe9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,6 +21,6 @@ add_executable(example example.cpp) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") -target_link_libraries(example ${ICU_LIBRARIES}) +target_link_libraries(example ${CXXOPTS_LINKER_LIBRARIES}) install(FILES cxxopts.hpp DESTINATION include) diff --git a/src/example.cpp b/src/example.cpp index 526045c..c6500c6 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -24,8 +24,6 @@ THE SOFTWARE. #include -#define CXXOPTS_USE_UNICODE - #include "cxxopts.hpp" int main(int argc, char* argv[]) @@ -48,8 +46,10 @@ int main(int argc, char* argv[]) ("help", "Print help") ("int", "An integer", cxxopts::value()) ("option_that_is_too_long_for_the_help", "A very long option") - ("unicode", u8"A help option with non-ascii: à. Here the length of the" + #ifdef CXXOPTS_USE_UNICODE + ("unicode", u8"A help option with non-ascii: à. Here the size of the" " string should be correct") + #endif ; options.add_options("Group")