From 4365759b32515727ffee54c48e888457b3e837e5 Mon Sep 17 00:00:00 2001 From: Pavel Belikov Date: Sat, 18 Nov 2017 12:55:05 +0300 Subject: add more HelpParams for different strings used in usage/options sections --- args.hxx | 45 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/args.hxx b/args.hxx index 7a4ccf7..ed9325b 100644 --- a/args.hxx +++ b/args.hxx @@ -575,6 +575,30 @@ namespace args */ std::string proglineCommand = "COMMAND"; + /** The prefix for progline value + */ + std::string proglineValueOpen = " <"; + + /** The postfix for progline value + */ + std::string proglineValueClose = ">"; + + /** The prefix for progline required argument + */ + std::string proglineRequiredOpen = ""; + + /** The postfix for progline required argument + */ + std::string proglineRequiredClose = ""; + + /** The prefix for progline non-required argument + */ + std::string proglineNonrequiredOpen = "["; + + /** The postfix for progline non-required argument + */ + std::string proglineNonrequiredClose = "]"; + /** Show flags in program line */ bool proglineShowFlags = false; @@ -602,6 +626,15 @@ namespace args /** Add newline before flag description */ bool addNewlineBeforeDescription = false; + + /** The prefix for option value + */ + std::string valueOpen = "["; + + /** The postfix for option value + */ + std::string valueClose = "]"; + }; /** Base class for all match types @@ -854,10 +887,11 @@ namespace args std::string res = flag.str(params.shortPrefix, params.longPrefix); if (!postfix.empty()) { - res += " <" + postfix + ">"; + res += params.proglineValueOpen + postfix + params.proglineValueClose; } - return { IsRequired() ? res : "[" + res + "]" }; + return { IsRequired() ? params.proglineRequiredOpen + res + params.proglineRequiredClose + : params.proglineNonrequiredOpen + res + params.proglineNonrequiredClose }; } virtual std::vector> GetDescription(const HelpParams ¶ms, const unsigned indentLevel) const override @@ -881,7 +915,7 @@ namespace args if (!postfix.empty() && (!useValueNameOnce || it + 1 == flagStrings.end())) { flags += flag.isShort ? params.shortSeparator : params.longSeparator; - flags += "[" + postfix + "]"; + flags += params.valueOpen + postfix + params.valueClose; } } @@ -981,9 +1015,10 @@ namespace args return true; } - virtual std::vector GetProgramLine(const HelpParams &) const override + virtual std::vector GetProgramLine(const HelpParams ¶ms) const override { - return { IsRequired() ? Name() : "[" + Name() + ']' }; + return { IsRequired() ? params.proglineRequiredOpen + Name() + params.proglineRequiredClose + : params.proglineNonrequiredOpen + Name() + params.proglineNonrequiredClose }; } virtual void Validate(const std::string &, const std::string &) const override -- cgit v1.2.1 From 4b02649e9fee72a66f17a1b96e90ef4f4e053aeb Mon Sep 17 00:00:00 2001 From: Pavel Belikov Date: Sat, 18 Nov 2017 15:27:23 +0300 Subject: add choices and default strings to argument description --- args.hxx | 207 +++++++++++++++++++++++++++++++++++++++++++++++++-------------- test.cxx | 62 +++++++++++++++++++ 2 files changed, 224 insertions(+), 45 deletions(-) diff --git a/args.hxx b/args.hxx index ed9325b..223525b 100644 --- a/args.hxx +++ b/args.hxx @@ -635,6 +635,21 @@ namespace args */ std::string valueClose = "]"; + /** Add choices to argument description + */ + bool addChoices = false; + + /** The prefix for choices + */ + std::string choiceString = "\nOne of: "; + + /** Add default values to argument description + */ + bool addDefault = false; + + /** The prefix for default values + */ + std::string defaultString = "\nDefault: "; }; /** Base class for all match types @@ -771,17 +786,63 @@ namespace args protected: const std::string name; bool kickout = false; + std::string defaultString; + bool defaultStringManual = false; + std::string choicesString; + bool choicesStringManual = false; + + virtual std::string GetDefaultString(const HelpParams&) const { return {}; } + + virtual std::string GetChoicesString(const HelpParams&) const { return {}; } + + virtual std::string GetNameString(const HelpParams&) const { return Name(); } + + void AddDescriptionPostfix(std::string &dest, const bool isManual, const std::string &manual, bool isGenerated, const std::string &generated, const std::string &str) const + { + if (isManual && !manual.empty()) + { + dest += str; + dest += manual; + } + else if (!isManual && isGenerated && !generated.empty()) + { + dest += str; + dest += generated; + } + } public: NamedBase(const std::string &name_, const std::string &help_, Options options_ = {}) : Base(help_, options_), name(name_) {} virtual ~NamedBase() {} - virtual std::vector> GetDescription(const HelpParams &, const unsigned indentLevel) const override + /** Sets default value string that will be added to argument description. + * Use empty string to disable it for this argument. + */ + void HelpDefault(const std::string &str) + { + defaultStringManual = true; + defaultString = str; + } + + /** Sets choices string that will be added to argument description. + * Use empty string to disable it for this argument. + */ + void HelpChoices(const std::string &str) + { + choicesStringManual = true; + choicesString = str; + } + + virtual std::vector> GetDescription(const HelpParams ¶ms, const unsigned indentLevel) const override { std::tuple description; - std::get<0>(description) = Name(); + std::get<0>(description) = GetNameString(params); std::get<1>(description) = help; std::get<2>(description) = indentLevel; + + AddDescriptionPostfix(std::get<1>(description), choicesStringManual, choicesString, params.addChoices, GetChoicesString(params), params.choiceString); + AddDescriptionPostfix(std::get<1>(description), defaultStringManual, defaultString, params.addDefault, GetDefaultString(params), params.defaultString); + return { std::move(description) }; } @@ -825,6 +886,30 @@ namespace args } }; + namespace detail + { + template + struct IsConvertableToString : std::false_type {}; + + template + struct IsConvertableToString() << std::declval(), int())> : std::true_type {}; + + template + typename std::enable_if::value, std::string>::type + ToString(const T &value) + { + std::ostringstream s; + s << value; + return s.str(); + } + template + typename std::enable_if::value, std::string>::type + ToString(const T &) + { + return {}; + } + } + /** Base class for all flag options */ class FlagBase : public NamedBase @@ -832,6 +917,33 @@ namespace args protected: const Matcher matcher; + virtual std::string GetNameString(const HelpParams ¶ms) const override + { + const std::string postfix = !params.showValueName || NumberOfArguments() == 0 ? std::string() : Name(); + std::string flags; + const auto flagStrings = matcher.GetFlagStrings(); + const bool useValueNameOnce = flagStrings.size() == 1 ? false : params.useValueNameOnce; + for (auto it = flagStrings.begin(); it != flagStrings.end(); ++it) + { + auto &flag = *it; + if (it != flagStrings.begin()) + { + flags += ", "; + } + + flags += flag.isShort ? params.shortPrefix : params.longPrefix; + flags += flag.str(); + + if (!postfix.empty() && (!useValueNameOnce || it + 1 == flagStrings.end())) + { + flags += flag.isShort ? params.shortSeparator : params.longSeparator; + flags += params.valueOpen + postfix + params.valueClose; + } + } + + return flags; + } + public: FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : NamedBase(name_, help_, extraError_ ? Options::Single : Options()), matcher(std::move(matcher_)) {} @@ -894,37 +1006,6 @@ namespace args : params.proglineNonrequiredOpen + res + params.proglineNonrequiredClose }; } - virtual std::vector> GetDescription(const HelpParams ¶ms, const unsigned indentLevel) const override - { - std::tuple description; - const std::string postfix = !params.showValueName || NumberOfArguments() == 0 ? std::string() : Name(); - std::string flags; - const auto flagStrings = matcher.GetFlagStrings(); - const bool useValueNameOnce = flagStrings.size() == 1 ? false : params.useValueNameOnce; - for (auto it = flagStrings.begin(); it != flagStrings.end(); ++it) - { - auto &flag = *it; - if (it != flagStrings.begin()) - { - flags += ", "; - } - - flags += flag.isShort ? params.shortPrefix : params.longPrefix; - flags += flag.str(); - - if (!postfix.empty() && (!useValueNameOnce || it + 1 == flagStrings.end())) - { - flags += flag.isShort ? params.shortSeparator : params.longSeparator; - flags += params.valueOpen + postfix + params.valueClose; - } - } - - std::get<0>(description) = std::move(flags); - std::get<1>(description) = help; - std::get<2>(description) = indentLevel; - return { std::move(description) }; - } - virtual bool HasFlag() const override { return true; @@ -2703,13 +2784,19 @@ namespace args { protected: T value; + T defaultValue; + + virtual std::string GetDefaultString(const HelpParams&) const + { + return detail::ToString(defaultValue); + } private: Reader reader; public: - ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_, Options options_): ValueFlagBase(name_, help_, std::move(matcher_), options_), value(defaultValue_) + ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_, Options options_): ValueFlagBase(name_, help_, std::move(matcher_), options_), value(defaultValue_), defaultValue(defaultValue_) { group_.Add(*this); } @@ -2738,12 +2825,25 @@ namespace args #endif } + virtual void Reset() noexcept override + { + ValueFlagBase::Reset(); + value = defaultValue; + } + /** Get the value */ T &Get() noexcept { return value; } + + /** Get the default value + */ + const T &GetDefault() noexcept + { + return defaultValue; + } }; /** An optional argument-accepting flag class @@ -2757,24 +2857,22 @@ namespace args class ImplicitValueFlag : public ValueFlag { protected: - T implicitValue; - T defaultValue; public: ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &implicitValue_, const T &defaultValue_ = T(), Options options_ = {}) - : ValueFlag(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(implicitValue_), defaultValue(defaultValue_) + : ValueFlag(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(implicitValue_) { } ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), Options options_ = {}) - : ValueFlag(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(defaultValue_), defaultValue(defaultValue_) + : ValueFlag(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(defaultValue_) { } ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) - : ValueFlag(group_, name_, help_, std::move(matcher_), {}, options_), implicitValue(), defaultValue() + : ValueFlag(group_, name_, help_, std::move(matcher_), {}, options_), implicitValue() { } @@ -2795,12 +2893,6 @@ namespace args ValueFlag::ParseValue(value_); } } - - virtual void Reset() noexcept override - { - this->value = defaultValue; - ValueFlag::Reset(); - } }; /** A variadic arguments accepting flag class @@ -3028,6 +3120,31 @@ namespace args T value; Reader reader; + protected: + virtual std::string GetChoicesString(const HelpParams &) const override + { + std::string res; + if (detail::IsConvertableToString::value) + { + std::vector values; + for (const auto &p : map) + { + values.push_back(detail::ToString(p.first)); + } + + std::sort(values.begin(), values.end()); + for (const auto &s : values) + { + if (!res.empty()) + { + res += ", "; + } + res += s; + } + } + return res; + } + public: MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map &map_, const T &defaultValue_, Options options_): ValueFlagBase(name_, help_, std::move(matcher_), options_), map(map_), value(defaultValue_) diff --git a/test.cxx b/test.cxx index 44cde49..a8d1676 100644 --- a/test.cxx +++ b/test.cxx @@ -1088,6 +1088,68 @@ TEST_CASE("ActionFlag works as expected", "[args]") REQUIRE_THROWS_AS(p.ParseArgs(std::vector{"-v"}), std::runtime_error); } +TEST_CASE("Default values work as expected", "[args]") +{ + args::ArgumentParser p("parser"); + args::ValueFlag f(p, "name", "description", {'f', "foo"}, "abc"); + args::MapFlag b(p, "name", "description", {'b', "bar"}, {{"a", 1}, {"b", 2}, {"c", 3}}); + p.Prog("prog"); + REQUIRE(p.Help() == R"( prog {OPTIONS} + + parser + + OPTIONS: + + -f[name], --foo=[name] description + -b[name], --bar=[name] description + +)"); + + p.helpParams.addDefault = true; + p.helpParams.addChoices = true; + + REQUIRE(p.Help() == R"( prog {OPTIONS} + + parser + + OPTIONS: + + -f[name], --foo=[name] description + Default: abc + -b[name], --bar=[name] description + One of: a, b, c + +)"); + + f.HelpDefault("123"); + b.HelpChoices("1, 2, 3"); + REQUIRE(p.Help() == R"( prog {OPTIONS} + + parser + + OPTIONS: + + -f[name], --foo=[name] description + Default: 123 + -b[name], --bar=[name] description + One of: 1, 2, 3 + +)"); + + f.HelpDefault({}); + b.HelpChoices({}); + REQUIRE(p.Help() == R"( prog {OPTIONS} + + parser + + OPTIONS: + + -f[name], --foo=[name] description + -b[name], --bar=[name] description + +)"); +} + #undef ARGS_HXX #define ARGS_TESTNAMESPACE #define ARGS_NOEXCEPT -- cgit v1.2.1 From db569937810dc2121a60fe2593faccc804bb1bed Mon Sep 17 00:00:00 2001 From: Pavel Belikov Date: Sun, 19 Nov 2017 21:31:16 +0300 Subject: fix missing override --- args.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/args.hxx b/args.hxx index 223525b..f8caa8d 100644 --- a/args.hxx +++ b/args.hxx @@ -2786,7 +2786,7 @@ namespace args T value; T defaultValue; - virtual std::string GetDefaultString(const HelpParams&) const + virtual std::string GetDefaultString(const HelpParams&) const override { return detail::ToString(defaultValue); } -- cgit v1.2.1 From da44bdad5e965d5461f007289b3d18a7992be4f5 Mon Sep 17 00:00:00 2001 From: Pavel Belikov Date: Sun, 19 Nov 2017 21:31:29 +0300 Subject: add /bigobj for msvc debug build --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dfcbaa0..81864d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ target_link_libraries(argstest args) set_property(TARGET argstest PROPERTY CXX_STANDARD 11) if (MSVC) - target_compile_options(argstest PRIVATE /W4 /WX) + target_compile_options(argstest PRIVATE /W4 /WX /bigobj) else () target_compile_options(argstest PRIVATE -Wall -Wextra -Werror -pedantic -Wshadow -Wunused-parameter) endif () -- cgit v1.2.1