Commit 9a90d969 authored by Dominik Charousset's avatar Dominik Charousset

Fix bug in options parser

parent ab400a9f
...@@ -67,6 +67,10 @@ detail::add_arg_functor<T> add_arg(std::vector<T>& storage) { ...@@ -67,6 +67,10 @@ detail::add_arg_functor<T> add_arg(std::vector<T>& storage) {
return {storage}; return {storage};
} }
/**
* Right-hand side of a match expression for a program option
* reading a boolean flag.
*/
inline std::function<void()> set_flag(bool& storage) { inline std::function<void()> set_flag(bool& storage) {
return [&] { storage = true; }; return [&] { storage = true; };
} }
...@@ -98,8 +102,7 @@ using opt_rvalue_builder = ...@@ -98,8 +102,7 @@ using opt_rvalue_builder =
using opt0_rvalue_builder = decltype(on(std::string{}) || on(std::string{})); using opt0_rvalue_builder = decltype(on(std::string{}) || on(std::string{}));
/** /**
* Left-hand side of a match expression for a program option with * Left-hand side of a match expression for a program option with one argument.
* one argument.
*/ */
inline opt_rvalue_builder on_opt1(char short_opt, inline opt_rvalue_builder on_opt1(char short_opt,
std::string long_opt, std::string long_opt,
...@@ -115,22 +118,8 @@ inline opt_rvalue_builder on_opt1(char short_opt, ...@@ -115,22 +118,8 @@ inline opt_rvalue_builder on_opt1(char short_opt,
const char short_flag_arr[] = {'-', short_opt, '\0' }; const char short_flag_arr[] = {'-', short_opt, '\0' };
const char* lhs_str = short_flag_arr; const char* lhs_str = short_flag_arr;
std::string prefix = "--"; std::string prefix = "--";
prefix += long_opt; prefix += std::move(long_opt);
prefix += "="; prefix += "=";
std::function<optional<std::string> (const std::string&)> kvp =
[prefix](const std::string& input) -> optional<std::string> {
if (std::equal(std::begin(prefix), std::end(prefix), std::begin(input))) {
return input.substr(prefix.size());
}
else if (std::equal(std::begin(prefix) + 1, std::end(prefix), std::begin(input))) {
return input.substr(prefix.size() - 1);
}
return none;
};
std::vector<std::string> opts;
opts.push_back(lhs_str);
opts.push_back("--" + long_opt);
opts.push_back("-" + long_opt);
return on(extract_longopt_arg(prefix)) || on(lhs_str, val<std::string>); return on(extract_longopt_arg(prefix)) || on(lhs_str, val<std::string>);
} }
...@@ -138,19 +127,19 @@ inline opt_rvalue_builder on_opt1(char short_opt, ...@@ -138,19 +127,19 @@ inline opt_rvalue_builder on_opt1(char short_opt,
* Left-hand side of a match expression for a program option with no argument. * Left-hand side of a match expression for a program option with no argument.
*/ */
inline opt0_rvalue_builder on_opt0(char short_opt, inline opt0_rvalue_builder on_opt0(char short_opt,
std::string long_opt, std::string long_opt,
options_description* desc = nullptr, options_description* desc = nullptr,
std::string help_text = "", std::string help_text = "",
std::string help_group = "general options") { std::string help_group = "general options") {
if (desc) { if (desc) {
option_info oinf{help_text, 0}; option_info oinf{help_text, 0};
(*desc)[help_group].insert(std::make_pair( (*desc)[help_group].insert(std::make_pair(std::make_pair(short_opt,
std::make_pair(short_opt, long_opt), long_opt),
std::move(oinf))); std::move(oinf)));
} }
const char short_flag_arr[] = {'-', short_opt, '\0' }; const char short_flag_arr[] = {'-', short_opt, '\0' };
std::string short_opt_string = short_flag_arr; std::string short_opt_string = short_flag_arr;
return on(long_opt) || on(short_opt_string); return on("--" + long_opt) || on(short_opt_string);
} }
/** /**
...@@ -162,7 +151,7 @@ std::function<void()> print_desc(options_description* desc, ...@@ -162,7 +151,7 @@ std::function<void()> print_desc(options_description* desc,
if (!desc) return; if (!desc) return;
if (desc->empty()) { if (desc->empty()) {
out << "please use '-h' or '--help' for a list " out << "please use '-h' or '--help' for a list "
"of available program options\n"; "of available program options\n";
} }
for (auto& opt_group : *desc) { for (auto& opt_group : *desc) {
out << opt_group.first << ":\n"; out << opt_group.first << ":\n";
...@@ -197,8 +186,8 @@ std::function<void()> print_desc(options_description* desc, ...@@ -197,8 +186,8 @@ std::function<void()> print_desc(options_description* desc,
* and then calls `exit(exit_reason). * and then calls `exit(exit_reason).
*/ */
inline std::function<void()> print_desc_and_exit(options_description* desc, inline std::function<void()> print_desc_and_exit(options_description* desc,
std::ostream& out = std::cout, std::ostream& out = std::cout,
int exit_reason = 0) { int exit_reason = 0) {
auto fun = print_desc(desc, out); auto fun = print_desc(desc, out);
return [=] { return [=] {
fun(); fun();
......
...@@ -49,89 +49,94 @@ struct conv_arg_impl<std::string> { ...@@ -49,89 +49,94 @@ struct conv_arg_impl<std::string> {
static inline result_type _(const std::string& arg) { return arg; } static inline result_type _(const std::string& arg) { return arg; }
}; };
template <bool> class opt1_rvalue_builder; template <class T>
struct conv_arg_impl<optional<T>> {
using result_type = optional<T>;
static inline result_type _(const std::string& arg) {
return conv_arg_impl<T>::_(arg);
}
};
template <bool>
class opt1_rvalue_builder;
template <class T> template <class T>
struct rd_arg_storage : ref_counted { struct rd_arg_storage : ref_counted {
T& storage; T& storage;
bool set; bool set;
std::string arg_name; std::string arg_name;
rd_arg_storage(T& r) : storage(r), set(false) { } rd_arg_storage(T& r) : storage(r), set(false) {
// nop
}
}; };
template <class T> template <class T>
class rd_arg_functor { class rd_arg_functor {
public:
template <bool> friend class opt1_rvalue_builder; template <bool>
friend class opt1_rvalue_builder;
using storage_type = rd_arg_storage<T>; using storage_type = rd_arg_storage<T>;
public:
rd_arg_functor(const rd_arg_functor&) = default; rd_arg_functor(const rd_arg_functor&) = default;
rd_arg_functor(T& storage) : m_storage(new storage_type(storage)) { } rd_arg_functor(T& storage) : m_storage(new storage_type(storage)) {
// nop
}
bool operator()(const std::string& arg) const { bool operator()(const std::string& arg) const {
if (m_storage->set) { if (m_storage->set) {
std::cerr << "*** error: " << m_storage->arg_name std::cerr << "*** error: " << m_storage->arg_name
<< " previously set to " << m_storage->storage << " already defined" << std::endl;
<< std::endl; return false;
} }
else { auto opt = conv_arg_impl<T>::_(arg);
auto opt = conv_arg_impl<T>::_(arg); if (!opt) {
if (opt) { std::cerr << "*** error: cannot convert \"" << arg << "\" to "
m_storage->storage = *opt; << detail::demangle(typeid(T).name())
m_storage->set = true; << " [option: \"" << m_storage->arg_name << "\"]"
return true; << std::endl;
} return false;
else {
std::cerr << "*** error: cannot convert \"" << arg << "\" to "
<< detail::demangle(typeid(T).name())
<< " [option: \"" << m_storage->arg_name << "\"]"
<< std::endl;
}
} }
return false; m_storage->storage = *opt;
m_storage->set = true;
return true;
} }
private: private:
intrusive_ptr<storage_type> m_storage; intrusive_ptr<storage_type> m_storage;
}; };
template <class T> template <class T>
class add_arg_functor { class add_arg_functor {
template <bool> friend class opt1_rvalue_builder;
public: public:
template <bool>
friend class opt1_rvalue_builder;
using value_type = std::vector<T>; using value_type = std::vector<T>;
using storage_type = rd_arg_storage<value_type>; using storage_type = rd_arg_storage<value_type>;
add_arg_functor(const add_arg_functor&) = default; add_arg_functor(const add_arg_functor&) = default;
add_arg_functor(value_type& storage) : m_storage(new storage_type(storage)) { } add_arg_functor(value_type& storage) : m_storage(new storage_type(storage)) {
// nop
}
bool operator()(const std::string& arg) const { bool operator()(const std::string& arg) const {
auto opt = conv_arg_impl<T>::_(arg); auto opt = conv_arg_impl<T>::_(arg);
if (opt) { if (!opt) {
m_storage->storage.push_back(*opt); std::cerr << "*** error: cannot convert \"" << arg << "\" to "
return true; << detail::demangle(typeid(T))
<< " [option: \"" << m_storage->arg_name << "\"]"
<< std::endl;
return false;
} }
std::cerr << "*** error: cannot convert \"" << arg << "\" to " m_storage->storage.push_back(*opt);
<< detail::demangle(typeid(T)) return true;
<< " [option: \"" << m_storage->arg_name << "\"]"
<< std::endl;
return false;
} }
private: private:
intrusive_ptr<storage_type> m_storage; intrusive_ptr<storage_type> m_storage;
}; };
template <class T> template <class T>
...@@ -147,9 +152,7 @@ using opt0_rvalue_builder = decltype(on<std::string>()); ...@@ -147,9 +152,7 @@ using opt0_rvalue_builder = decltype(on<std::string>());
template <bool HasShortOpt = true> template <bool HasShortOpt = true>
class opt1_rvalue_builder { class opt1_rvalue_builder {
public: public:
using left_type = decltype(on<std::string, std::string>()); using left_type = decltype(on<std::string, std::string>());
using right_type = using right_type =
...@@ -157,45 +160,50 @@ class opt1_rvalue_builder { ...@@ -157,45 +160,50 @@ class opt1_rvalue_builder {
template <class Left, typename Right> template <class Left, typename Right>
opt1_rvalue_builder(char sopt, std::string lopt, Left&& lhs, Right&& rhs) opt1_rvalue_builder(char sopt, std::string lopt, Left&& lhs, Right&& rhs)
: m_short(sopt), m_long(std::move(lopt)) : m_short(sopt),
, m_left(std::forward<Left>(lhs)), m_right(std::forward<Right>(rhs)) { } m_long(std::move(lopt)),
m_left(std::forward<Left>(lhs)),
m_right(std::forward<Right>(rhs)) {
// nop
}
template <class Expr> template <class Expr>
auto operator>>(Expr expr) auto operator>>(Expr expr)
-> decltype((*(static_cast<left_type*>(nullptr)) >> expr).or_else( -> decltype((*(static_cast<left_type*>(nullptr)) >> expr).or_else(
*(static_cast<right_type*>(nullptr)) >> expr)) const { *(static_cast<right_type*>(nullptr)) >> expr)) const {
inject_arg_name(expr); inject_arg_name(expr);
return (m_left >> expr).or_else(m_right >> expr); return (m_left >> expr).or_else(m_right >> expr);
} }
private: private:
template <class T> template <class T>
inline void inject_arg_name(rd_arg_functor<T>& expr) { void inject_arg_name(rd_arg_functor<T>& expr) {
expr.m_storage->arg_name = m_long; expr.m_storage->arg_name = m_long;
} }
template <class T> template <class T>
inline void inject_arg_name(const T&) { } void inject_arg_name(const T&) {
// using opts without rd_arg() or similar functor
}
char m_short; char m_short;
std::string m_long; std::string m_long;
left_type m_left; left_type m_left;
right_type m_right; right_type m_right;
}; };
template <> template <>
class opt1_rvalue_builder<false> { class opt1_rvalue_builder<false> {
public: public:
using sub_type = using sub_type =
decltype(on(std::function<optional<std::string> (const std::string&)>())); decltype(on(std::function<optional<std::string> (const std::string&)>()));
template <class SubType> template <class SubType>
opt1_rvalue_builder(std::string lopt, SubType&& sub) opt1_rvalue_builder(std::string lopt, SubType&& sub)
: m_long(std::move(lopt)), m_sub(std::forward<SubType>(sub)) { } : m_long(std::move(lopt)),
m_sub(std::forward<SubType>(sub)) {
// nop
}
template <class Expr> template <class Expr>
auto operator>>(Expr expr) auto operator>>(Expr expr)
...@@ -205,18 +213,18 @@ class opt1_rvalue_builder<false> { ...@@ -205,18 +213,18 @@ class opt1_rvalue_builder<false> {
} }
private: private:
template <class T> template <class T>
inline void inject_arg_name(rd_arg_functor<T>& expr) { inline void inject_arg_name(rd_arg_functor<T>& expr) {
expr.m_storage->arg_name = m_long; expr.m_storage->arg_name = m_long;
} }
template <class T> template <class T>
inline void inject_arg_name(const T&) { } inline void inject_arg_name(const T&) {
// using opts without rd_arg() or similar functor
}
std::string m_long; std::string m_long;
sub_type m_sub; sub_type m_sub;
}; };
} // namespace detail } // namespace detail
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment