Simplify option_parser

This commit is contained in:
Andrzej Rybczak
2016-11-19 14:41:32 +01:00
parent 6d313a282b
commit 180c2d26fc
9 changed files with 480 additions and 690 deletions

View File

@@ -35,6 +35,18 @@
#include "utility/option_parser.h"
bool yes_no(std::string v)
{
if (v == "yes")
return true;
else if (v == "no")
return false;
else
invalid_value(v);
}
////////////////////////////////////////////////////////////////////////////////
bool option_parser::run(std::istream &is, bool ignore_errors)
{
// quoted value. leftmost and rightmost quotation marks are the delimiters.
@@ -52,10 +64,14 @@ bool option_parser::run(std::istream &is, bool ignore_errors)
auto it = m_parsers.find(option);
if (it != m_parsers.end())
{
try {
try
{
it->second.parse(match[2]);
} catch (std::exception &e) {
std::cerr << "Error while processing option \"" << option << "\": " << e.what() << "\n";
}
catch (std::exception &e)
{
std::cerr << "Error while processing option \"" << option
<< "\": " << e.what() << "\n";
if (!ignore_errors)
return false;
}
@@ -73,14 +89,19 @@ bool option_parser::run(std::istream &is, bool ignore_errors)
bool option_parser::initialize_undefined(bool ignore_errors)
{
for (auto &p : m_parsers)
for (auto &pp : m_parsers)
{
if (!p.second.defined())
auto &p = pp.second;
if (!p.used())
{
try {
p.second.run_default();
} catch (std::exception &e) {
std::cerr << "Error while initializing option \"" << p.first << "\": " << e.what() << "\n";
try
{
p.parse_default();
}
catch (std::exception &e)
{
std::cerr << "Error while initializing option \"" << pp.first
<< "\": " << e.what() << "\n";
if (ignore_errors)
return false;
}
@@ -88,15 +109,3 @@ bool option_parser::initialize_undefined(bool ignore_errors)
}
return true;
}
option_parser::worker yes_no(bool &arg, bool value)
{
return option_parser::worker([&arg](std::string &&v) {
if (v == "yes")
arg = true;
else if (v == "no")
arg = false;
else
throw std::runtime_error("invalid argument: " + v);
}, defaults_to(arg, value));
}

View File

@@ -33,152 +33,144 @@
#ifndef NCMPCPP_UTILITY_OPTION_PARSER_H
#define NCMPCPP_UTILITY_OPTION_PARSER_H
#include <boost/algorithm/string/trim.hpp>
#include <boost/tokenizer.hpp>
#include <boost/lexical_cast.hpp>
#include <cassert>
#include <stdexcept>
#include <unordered_map>
#include "utility/functional.h"
struct option_parser
[[noreturn]] inline void invalid_value(const std::string &v)
{
typedef std::function<void(std::string &&)> parser_t;
typedef std::function<void()> default_t;
throw std::runtime_error("invalid value: " + v);
}
template <typename DestT, typename SourceT>
struct assign_value_once
{
typedef DestT dest_type;
typedef typename std::decay<SourceT>::type source_type;
template <typename DestT>
DestT verbose_lexical_cast(std::string v)
{
try {
return boost::lexical_cast<DestT>(std::move(v));
} catch (boost::bad_lexical_cast &) {
invalid_value(v);
}
}
template <typename ArgT>
assign_value_once(DestT &dest, ArgT &&value)
: m_dest(dest), m_source(std::make_shared<source_type>(std::forward<ArgT>(value))) { }
template <typename ValueT, typename ConvertT>
std::vector<ValueT> list_of(const std::string &v, ConvertT convert)
{
std::vector<ValueT> result;
boost::tokenizer<boost::escaped_list_separator<char>> elems(v);
for (auto &value : elems)
result.push_back(convert(boost::trim_copy(value)));
if (result.empty())
throw std::runtime_error("empty list");
return result;
}
void operator()()
{
assert(m_source.get() != nullptr);
m_dest = std::move(*m_source);
m_source.reset();
}
template <typename ValueT>
std::vector<ValueT> list_of(const std::string &v)
{
return list_of<ValueT>(v, verbose_lexical_cast<ValueT>);
}
private:
DestT &m_dest;
std::shared_ptr<source_type> m_source;
};
bool yes_no(std::string v);
template <typename IntermediateT, typename DestT, typename TransformT>
struct parse_and_transform
{
template <typename ArgT>
parse_and_transform(DestT &dest, ArgT &&map)
: m_dest(dest), m_map(std::forward<ArgT>(map)) { }
void operator()(std::string &&v)
{
try {
m_dest = m_map(boost::lexical_cast<IntermediateT>(v));
} catch (boost::bad_lexical_cast &) {
throw std::runtime_error("invalid value: " + v);
}
}
private:
DestT &m_dest;
TransformT m_map;
};
////////////////////////////////////////////////////////////////////////////////
class option_parser
{
template <typename DestT>
struct worker
{
worker() { }
template <typename MapT>
worker(DestT *dest, MapT &&map)
: m_dest(dest), m_map(std::forward<MapT>(map)), m_dest_set(false)
{ }
template <typename ParserT, typename DefaultT>
worker(ParserT &&p, DefaultT &&d)
: m_defined(false), m_parser(std::forward<ParserT>(p))
, m_default(std::forward<DefaultT>(d)) { }
template <typename ValueT>
void parse(ValueT &&value)
void operator()(std::string value)
{
if (m_defined)
throw std::runtime_error("option already defined");
m_parser(std::forward<ValueT>(value));
m_defined = true;
}
bool defined() const
{
return m_defined;
}
void run_default()
{
if (m_defined)
throw std::runtime_error("option already defined");
m_default();
m_defined = true;
if (m_dest_set)
throw std::runtime_error("option already set");
assign<DestT, void>::apply(m_dest, m_map, value);
m_dest_set = true;
}
private:
bool m_defined;
parser_t m_parser;
default_t m_default;
template <typename ValueT, typename VoidT>
struct assign {
static void apply(ValueT *dest,
std::function<DestT(std::string)> &map,
std::string &value) {
*dest = map(std::move(value));
}
};
template <typename VoidT>
struct assign<void, VoidT> {
static void apply(void *,
std::function<void(std::string)> &map,
std::string &value) {
map(std::move(value));
}
};
DestT *m_dest;
std::function<DestT(std::string)> m_map;
bool m_dest_set;
};
template <typename ParserT, typename DefaultT>
void add(const std::string &option, ParserT &&p, DefaultT &&d)
struct parser {
template <typename StringT, typename SetterT>
parser(StringT &&default_, SetterT &&setter_)
: m_used(false)
, m_default_value(std::forward<StringT>(default_))
, m_worker(std::forward<SetterT>(setter_))
{ }
bool used() const
{
return m_used;
}
void parse(std::string v)
{
m_worker(std::move(v));
m_used = true;
}
void parse_default() const
{
assert(!m_used);
m_worker(m_default_value);
}
private:
bool m_used;
std::string m_default_value;
std::function<void(std::string)> m_worker;
};
std::unordered_map<std::string, parser> m_parsers;
public:
template <typename DestT, typename MapT>
void add(std::string option, DestT *dest, std::string default_, MapT &&map)
{
assert(m_parsers.count(option) == 0);
m_parsers[option] = worker(std::forward<ParserT>(p), std::forward<DefaultT>(d));
m_parsers.emplace(
std::move(option),
parser(
std::move(default_),
worker<DestT>(dest, std::forward<MapT>(map))));
}
template <typename WorkerT>
void add(const std::string &option, WorkerT &&w)
template <typename DestT>
void add(std::string option, DestT *dest, std::string default_)
{
assert(m_parsers.count(option) == 0);
m_parsers[option] = std::forward<WorkerT>(w);
add(std::move(option), dest, std::move(default_), verbose_lexical_cast<DestT>);
}
bool run(std::istream &is, bool ignore_errors);
bool initialize_undefined(bool ignore_errors);
private:
std::unordered_map<std::string, worker> m_parsers;
bool run(std::istream &is, bool warn_on_errors);
bool initialize_undefined(bool warn_on_errors);
};
template <typename IntermediateT, typename ArgT, typename TransformT>
option_parser::parser_t assign(ArgT &arg, TransformT &&map = id_())
{
return option_parser::parse_and_transform<IntermediateT, ArgT, TransformT>(
arg, std::forward<TransformT>(map)
);
}
template <typename ArgT, typename ValueT>
option_parser::default_t defaults_to(ArgT &arg, ValueT &&value)
{
return option_parser::assign_value_once<ArgT, ValueT>(
arg, std::forward<ValueT>(value)
);
}
template <typename IntermediateT, typename ArgT, typename ValueT, typename TransformT>
option_parser::worker assign_default(ArgT &arg, ValueT &&value, TransformT &&map)
{
return option_parser::worker(
assign<IntermediateT>(arg, std::forward<TransformT>(map)),
defaults_to(arg, map(std::forward<ValueT>(value)))
);
}
template <typename ArgT, typename ValueT>
option_parser::worker assign_default(ArgT &arg, ValueT &&value)
{
return assign_default<ArgT>(arg, std::forward<ValueT>(value), id_());
}
// workers for specific types
option_parser::worker yes_no(bool &arg, bool value);
#endif // NCMPCPP_UTILITY_OPTION_PARSER_H