aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTaylor C. Richberger <taywee@gmx.com>2017-11-19 13:44:41 -0700
committerGitHub <noreply@github.com>2017-11-19 13:44:41 -0700
commit14202ddf76649027a7f3cf9399d455b97d96a215 (patch)
tree18dc18d21fe469007b7abe10ace094a8c6aac625
parentMerge pull request #45 from pavel-belikov/readme (diff)
parentadd /bigobj for msvc debug build (diff)
downloadargs.hxx-14202ddf76649027a7f3cf9399d455b97d96a215.tar.xz
Merge pull request #46 from pavel-belikov/additional-flag-description
Add default values and choices to help output
-rw-r--r--CMakeLists.txt2
-rw-r--r--args.hxx250
-rw-r--r--test.cxx62
3 files changed, 264 insertions, 50 deletions
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 ()
diff --git a/args.hxx b/args.hxx
index 7a4ccf7..f8caa8d 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,30 @@ 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 = "]";
+
+ /** 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
@@ -738,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<std::tuple<std::string, std::string, unsigned>> 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<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned indentLevel) const override
{
std::tuple<std::string, std::string, unsigned> 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) };
}
@@ -792,6 +886,30 @@ namespace args
}
};
+ namespace detail
+ {
+ template <typename T, typename = int>
+ struct IsConvertableToString : std::false_type {};
+
+ template <typename T>
+ struct IsConvertableToString<T, decltype(std::declval<std::ostringstream&>() << std::declval<T>(), int())> : std::true_type {};
+
+ template <typename T>
+ typename std::enable_if<IsConvertableToString<T>::value, std::string>::type
+ ToString(const T &value)
+ {
+ std::ostringstream s;
+ s << value;
+ return s.str();
+ }
+ template <typename T>
+ typename std::enable_if<!IsConvertableToString<T>::value, std::string>::type
+ ToString(const T &)
+ {
+ return {};
+ }
+ }
+
/** Base class for all flag options
*/
class FlagBase : public NamedBase
@@ -799,6 +917,33 @@ namespace args
protected:
const Matcher matcher;
+ virtual std::string GetNameString(const HelpParams &params) 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_)) {}
@@ -854,41 +999,11 @@ namespace args
std::string res = flag.str(params.shortPrefix, params.longPrefix);
if (!postfix.empty())
{
- res += " <" + postfix + ">";
- }
-
- return { IsRequired() ? res : "[" + res + "]" };
- }
-
- virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned indentLevel) const override
- {
- std::tuple<std::string, std::string, unsigned> 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 += "[" + postfix + "]";
- }
+ res += params.proglineValueOpen + postfix + params.proglineValueClose;
}
- std::get<0>(description) = std::move(flags);
- std::get<1>(description) = help;
- std::get<2>(description) = indentLevel;
- return { std::move(description) };
+ return { IsRequired() ? params.proglineRequiredOpen + res + params.proglineRequiredClose
+ : params.proglineNonrequiredOpen + res + params.proglineNonrequiredClose };
}
virtual bool HasFlag() const override
@@ -981,9 +1096,10 @@ namespace args
return true;
}
- virtual std::vector<std::string> GetProgramLine(const HelpParams &) const override
+ virtual std::vector<std::string> GetProgramLine(const HelpParams &params) 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
@@ -2668,13 +2784,19 @@ namespace args
{
protected:
T value;
+ T defaultValue;
+
+ virtual std::string GetDefaultString(const HelpParams&) const override
+ {
+ 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);
}
@@ -2703,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
@@ -2722,24 +2857,22 @@ namespace args
class ImplicitValueFlag : public ValueFlag<T, Reader>
{
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<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(implicitValue_), defaultValue(defaultValue_)
+ : ValueFlag<T, Reader>(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<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(defaultValue_), defaultValue(defaultValue_)
+ : ValueFlag<T, Reader>(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<T, Reader>(group_, name_, help_, std::move(matcher_), {}, options_), implicitValue(), defaultValue()
+ : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), {}, options_), implicitValue()
{
}
@@ -2760,12 +2893,6 @@ namespace args
ValueFlag<T, Reader>::ParseValue(value_);
}
}
-
- virtual void Reset() noexcept override
- {
- this->value = defaultValue;
- ValueFlag<T, Reader>::Reset();
- }
};
/** A variadic arguments accepting flag class
@@ -2993,6 +3120,31 @@ namespace args
T value;
Reader reader;
+ protected:
+ virtual std::string GetChoicesString(const HelpParams &) const override
+ {
+ std::string res;
+ if (detail::IsConvertableToString<K>::value)
+ {
+ std::vector<std::string> 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<K, T> &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<std::string>{"-v"}), std::runtime_error);
}
+TEST_CASE("Default values work as expected", "[args]")
+{
+ args::ArgumentParser p("parser");
+ args::ValueFlag<std::string> f(p, "name", "description", {'f', "foo"}, "abc");
+ args::MapFlag<std::string, int> 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