You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
105 lines
2.5 KiB
C++
105 lines
2.5 KiB
C++
#include "options.hpp"
|
|
|
|
#include <charconv>
|
|
#include <iostream>
|
|
|
|
template<> bool FromString(std::string_view sv)
|
|
{
|
|
if (sv == "1" || sv == "true" || sv == "t" || sv == "yes" || sv == "n")
|
|
return true;
|
|
else if (sv == "0" || sv == "false" || sv == "f" || sv == "no" || sv == "n")
|
|
return false;
|
|
else
|
|
throw ParseError("Invalid bool");
|
|
}
|
|
|
|
template <typename T>
|
|
static T FromStringIntCommon(std::string_view sv)
|
|
{
|
|
T res;
|
|
auto [p, ec] = std::from_chars(sv.begin(), sv.end(), res);
|
|
if (p != sv.end() || ec != std::errc())
|
|
throw ParseError("Invalid number");
|
|
return res;
|
|
}
|
|
|
|
#define FROM_STRING_SPEC(x) \
|
|
template<> x FromString(std::string_view sv) \
|
|
{ return FromStringIntCommon<x>(sv); }
|
|
INT_TYPES(FROM_STRING_SPEC)
|
|
#undef FROM_STRING_SPEC
|
|
|
|
// TODO: port this to from_chars when lazy libc++ developers finally implement
|
|
// float support in from_chars
|
|
template <typename T, T (*Fun)(const char*, char**)>
|
|
static T FromStringFloatCommon(std::string_view sv)
|
|
{
|
|
if (sv.empty()) throw ParseError("Missing number");
|
|
|
|
// strtod and friends need a null terminated string, we can't guarantee that
|
|
// so make an std::string to force null termination.
|
|
std::string in{sv};
|
|
char* endptr;
|
|
|
|
auto res = Fun(in.c_str(), &endptr);
|
|
if (std::size_t(endptr - in.c_str()) != sv.size())
|
|
throw ParseError("Invalid number");
|
|
return res;
|
|
}
|
|
|
|
template<> float FromString(std::string_view sv)
|
|
{ return FromStringFloatCommon<float, strtof>(sv); }
|
|
template<> double FromString(std::string_view sv)
|
|
{ return FromStringFloatCommon<double, strtod>(sv); }
|
|
|
|
namespace Detail
|
|
{
|
|
void ParseOneOption(
|
|
std::string_view key, const std::function<ParseCallback>& cb)
|
|
{
|
|
while (key.starts_with('-')) key = key.substr(1);
|
|
|
|
if (auto n = key.find_first_of('='); n != std::string_view::npos)
|
|
{
|
|
auto val = key.substr(n+1);
|
|
key = key.substr(0, n);
|
|
cb(key, val);
|
|
}
|
|
else
|
|
cb(key, "");
|
|
}
|
|
}
|
|
|
|
bool HandleOption(
|
|
std::span<Option> opts, std::string_view key, std::string_view val)
|
|
{
|
|
for (const auto& o : opts)
|
|
if (o.key == key)
|
|
{
|
|
o.apply(val);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void PrintHelp(std::span<const Option> opts)
|
|
{
|
|
for (const auto& o : opts)
|
|
{
|
|
std::cerr << "--" << o.key;
|
|
if (!o.value_placeholder.empty()) std::cerr << '=' << o.value_placeholder;
|
|
std::cerr << ": " << o.help << '\n';
|
|
}
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
try { return Main(argc, argv); }
|
|
catch (Exit e) { return e.code; }
|
|
catch (const std::exception& e)
|
|
{
|
|
std::cerr << "Fatal error: " << e.what() << std::endl;
|
|
return 2;
|
|
}
|
|
}
|