Support binary unit prefixes in command line arguments

This commit is contained in:
Eelco Dolstra 2021-01-08 12:51:19 +01:00
parent 6548b89cc4
commit 17beae299d
4 changed files with 29 additions and 27 deletions

View file

@ -219,10 +219,8 @@ LegacyArgs::LegacyArgs(const std::string & programName,
.description = description, .description = description,
.labels = {"n"}, .labels = {"n"},
.handler = {[=](std::string s) { .handler = {[=](std::string s) {
if (auto n = string2Int<unsigned int>(s)) auto n = string2IntWithUnitPrefix<uint64_t>(s);
settings.set(dest, std::to_string(*n)); settings.set(dest, std::to_string(n));
else
throw UsageError("'%s' is not an integer", s);
}} }}
}); });
}; };

View file

@ -57,22 +57,7 @@ template<class N> N getIntArg(const string & opt,
{ {
++i; ++i;
if (i == end) throw UsageError("'%1%' requires an argument", opt); if (i == end) throw UsageError("'%1%' requires an argument", opt);
string s = *i; return string2IntWithUnitPrefix<N>(*i);
N multiplier = 1;
if (allowUnit && !s.empty()) {
char u = std::toupper(*s.rbegin());
if (std::isalpha(u)) {
if (u == 'K') multiplier = 1ULL << 10;
else if (u == 'M') multiplier = 1ULL << 20;
else if (u == 'G') multiplier = 1ULL << 30;
else if (u == 'T') multiplier = 1ULL << 40;
else throw UsageError("invalid unit specifier '%1%'", u);
s.resize(s.size() - 1);
}
}
if (auto n = string2Int<N>(s))
return *n * multiplier;
throw UsageError("'%1%' requires an integer argument", opt);
} }

View file

@ -87,10 +87,7 @@ protected:
template<class I> template<class I>
Handler(I * dest) Handler(I * dest)
: fun([=](std::vector<std::string> ss) { : fun([=](std::vector<std::string> ss) {
if (auto n = string2Int<I>(ss[0])) *dest = string2IntWithUnitPrefix<I>(ss[0]);
*dest = *n;
else
throw UsageError("'%s' is not an integer", ss[0]);
}) })
, arity(1) , arity(1)
{ } { }

View file

@ -401,12 +401,34 @@ template<class N>
std::optional<N> string2Int(const std::string & s) std::optional<N> string2Int(const std::string & s)
{ {
if (s.substr(0, 1) == "-" && !std::numeric_limits<N>::is_signed) if (s.substr(0, 1) == "-" && !std::numeric_limits<N>::is_signed)
return {}; return std::nullopt;
std::istringstream str(s); std::istringstream str(s);
N n; N n;
str >> n; str >> n;
if (str && str.get() == EOF) return n; if (str && str.get() == EOF) return n;
return {}; return std::nullopt;
}
/* Like string2Int(), but support an optional suffix 'K', 'M', 'G' or
'T' denoting a binary unit prefix. */
template<class N>
N string2IntWithUnitPrefix(std::string s)
{
N multiplier = 1;
if (!s.empty()) {
char u = std::toupper(*s.rbegin());
if (std::isalpha(u)) {
if (u == 'K') multiplier = 1ULL << 10;
else if (u == 'M') multiplier = 1ULL << 20;
else if (u == 'G') multiplier = 1ULL << 30;
else if (u == 'T') multiplier = 1ULL << 40;
else throw UsageError("invalid unit specifier '%1%'", u);
s.resize(s.size() - 1);
}
}
if (auto n = string2Int<N>(s))
return *n * multiplier;
throw UsageError("'%s' is not an integer", s);
} }
/* Parse a string into a float. */ /* Parse a string into a float. */
@ -417,7 +439,7 @@ std::optional<N> string2Float(const string & s)
N n; N n;
str >> n; str >> n;
if (str && str.get() == EOF) return n; if (str && str.get() == EOF) return n;
return {}; return std::nullopt;
} }