Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show default value for arg in help message #92

Merged
merged 11 commits into from
Dec 15, 2020
79 changes: 67 additions & 12 deletions include/argparse/argparse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,57 @@ struct is_container<
template <typename T>
static constexpr bool is_container_v = is_container<T>::value;

template <typename T, typename _ = void>
struct is_streamable : std::false_type {};

template <typename T>
struct is_streamable<
T, std::conditional_t<
false, decltype(std::declval<std::ostream>() << std::declval<T>()),
void>> : std::true_type {};

template <typename T>
static constexpr bool is_streamable_v = is_streamable<T>::value;

template <typename T>
static constexpr bool is_representable_v =
is_streamable_v<T> || is_container_v<T>;

constexpr size_t repr_max_container_size = 5;

template <typename T> std::string repr(T const &val) {
if constexpr (std::is_same_v<T, bool>) {
return val ? "true" : "false";
} else if constexpr (std::is_convertible_v<T, std::string_view>) {
return '"' + std::string{std::string_view{val}} + '"';
} else if constexpr (is_container_v<T>) {
std::stringstream out;
out << "{";
const auto size = val.size();
if (size > 1) {
out << repr(*val.begin());
std::for_each(
std::next(val.begin()),
std::next(val.begin(), std::min(size, repr_max_container_size) - 1),
[&out](const auto &v) { out << " " << repr(v); });
if (size <= repr_max_container_size)
out << " ";
else
out << "...";
}
if (size > 0)
out << repr(*std::prev(val.end()));
out << "}";
return out.str();
} else if constexpr (is_streamable_v<T>) {
std::stringstream out;
out << val;
return out.str();
} else {
return "<not representable>";
}
}

namespace {

template <typename T> constexpr bool standard_signed_integer = false;
Expand All @@ -90,7 +141,7 @@ template <> constexpr bool standard_unsigned_integer<unsigned long int> = true;
template <>
constexpr bool standard_unsigned_integer<unsigned long long int> = true;

}
} // namespace

template <typename T>
constexpr bool standard_integer =
Expand Down Expand Up @@ -197,7 +248,7 @@ template <> constexpr auto generic_strtod<float> = strtof;
template <> constexpr auto generic_strtod<double> = strtod;
template <> constexpr auto generic_strtod<long double> = strtold;

}
} // namespace

template <class T> inline auto do_strtod(std::string const &s) -> T {
if (isspace(static_cast<unsigned char>(s[0])) || s[0] == '+')
Expand Down Expand Up @@ -294,8 +345,9 @@ class Argument {
return *this;
}

Argument &default_value(std::any aDefaultValue) {
mDefaultValue = std::move(aDefaultValue);
template <typename T> Argument &default_value(T &&aDefaultValue) {
mDefaultValueRepr = details::repr(aDefaultValue);
mDefaultValue = std::forward<T>(aDefaultValue);
return *this;
}

Expand Down Expand Up @@ -485,6 +537,8 @@ class Argument {
stream << nameStream.str() << "\t" << argument.mHelp;
if (argument.mIsRequired && !argument.mDefaultValue.has_value())
stream << "[Required]";
if (argument.mDefaultValue.has_value())
stream << " [default: " << argument.mDefaultValueRepr << "]";
stream << "\n";
return stream;
}
Expand Down Expand Up @@ -735,6 +789,7 @@ class Argument {
std::string_view mUsedName;
std::string mHelp;
std::any mDefaultValue;
std::string mDefaultValueRepr;
std::any mImplicitValue;
using valued_action = std::function<std::any(const std::string &)>;
using void_action = std::function<void(const std::string &)>;
Expand All @@ -750,11 +805,10 @@ class Argument {

class ArgumentParser {
public:
explicit ArgumentParser(std::string aProgramName = {}, std::string aVersion = "1.0")
explicit ArgumentParser(std::string aProgramName = {},
std::string aVersion = "1.0")
: mProgramName(std::move(aProgramName)), mVersion(std::move(aVersion)) {
add_argument("-h", "--help")
.help("shows help message and exits")
.nargs(0);
add_argument("-h", "--help").help("shows help message and exits").nargs(0);
add_argument("-v", "--version")
.help("prints version information and exits")
.nargs(0);
Expand Down Expand Up @@ -850,7 +904,8 @@ class ArgumentParser {
* @throws std::logic_error if the option has no value
* @throws std::bad_any_cast if the option is not of type T
*/
template <typename T = std::string> T get(std::string_view aArgumentName) const {
template <typename T = std::string>
T get(std::string_view aArgumentName) const {
return (*this)[aArgumentName].get<T>();
}

Expand Down Expand Up @@ -889,7 +944,7 @@ class ArgumentParser {
}
stream << "\n\n";

if(!parser.mDescription.empty())
if (!parser.mDescription.empty())
stream << parser.mDescription << "\n\n";

if (!parser.mPositionalArguments.empty())
Expand All @@ -909,7 +964,7 @@ class ArgumentParser {
stream << mOptionalArgument;
}

if(!parser.mEpilog.empty())
if (!parser.mEpilog.empty())
stream << parser.mEpilog << "\n\n";
}

Expand Down Expand Up @@ -963,7 +1018,7 @@ class ArgumentParser {
std::cout << *this;
std::exit(0);
}
// the second optional argument is --version
// the second optional argument is --version
else if (tArgument == std::next(mOptionalArguments.begin(), 1)) {
std::cout << mVersion << "\n";
std::exit(0);
Expand Down