351 lines
6.1 KiB
C++
351 lines
6.1 KiB
C++
#include <regex>
|
||
#include <set>
|
||
#include <map>
|
||
#include <exception>
|
||
#include <boost/any.hpp>
|
||
#include <sstream>
|
||
|
||
namespace cxxopts
|
||
{
|
||
using boost::any;
|
||
|
||
class Value
|
||
{
|
||
public:
|
||
|
||
virtual void
|
||
parse(const std::string& text) const = 0;
|
||
|
||
virtual bool
|
||
has_arg() const = 0;
|
||
};
|
||
|
||
namespace values
|
||
{
|
||
template <typename T>
|
||
void
|
||
parse_value(const std::string& text, T& value)
|
||
{
|
||
std::istringstream is(text);
|
||
is >> value;
|
||
}
|
||
|
||
template <typename T>
|
||
bool
|
||
value_has_arg(bool*)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
template <typename T>
|
||
bool
|
||
value_has_arg(T*)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
template <typename T>
|
||
class default_value : public Value
|
||
{
|
||
public:
|
||
default_value()
|
||
: m_result(std::make_shared<T>())
|
||
, m_store(m_result.get())
|
||
{
|
||
}
|
||
|
||
default_value(T* t)
|
||
: m_store(t)
|
||
{
|
||
}
|
||
|
||
void
|
||
parse(const std::string& text) const
|
||
{
|
||
parse_value(text, *m_store);
|
||
}
|
||
|
||
bool
|
||
has_arg() const
|
||
{
|
||
return value_has_arg(m_store);
|
||
}
|
||
|
||
const T&
|
||
get() const
|
||
{
|
||
if (m_store == nullptr)
|
||
{
|
||
return *m_result;
|
||
}
|
||
else
|
||
{
|
||
return *m_store;
|
||
}
|
||
}
|
||
|
||
private:
|
||
std::shared_ptr<T> m_result;
|
||
T* m_store;
|
||
};
|
||
|
||
}
|
||
|
||
template <typename T>
|
||
std::shared_ptr<Value>
|
||
value()
|
||
{
|
||
return std::make_shared<values::default_value<T>>();
|
||
}
|
||
|
||
extern std::basic_regex<char> option_matcher;
|
||
|
||
extern std::basic_regex<char> option_specifier;
|
||
|
||
class MessageException
|
||
;
|
||
|
||
class OptionException : public std::exception
|
||
{
|
||
public:
|
||
OptionException(const std::string& message)
|
||
: m_message(message)
|
||
{
|
||
}
|
||
|
||
virtual const char*
|
||
what() const noexcept
|
||
{
|
||
return m_message.c_str();
|
||
}
|
||
|
||
private:
|
||
std::string m_message;
|
||
};
|
||
|
||
class OptionSpecException : public OptionException
|
||
{
|
||
public:
|
||
|
||
OptionSpecException(const std::string& message)
|
||
: OptionException(message)
|
||
{
|
||
}
|
||
};
|
||
|
||
class OptionParseException : public OptionException
|
||
{
|
||
public:
|
||
OptionParseException(const std::string& message)
|
||
: OptionException(message)
|
||
{
|
||
}
|
||
};
|
||
|
||
class option_exists_error : public OptionSpecException
|
||
{
|
||
public:
|
||
option_exists_error(const std::string& option)
|
||
: OptionSpecException(u8"Option ‘" + option + u8"’ already exists")
|
||
{
|
||
}
|
||
};
|
||
|
||
class invalid_option_format_error : public OptionSpecException
|
||
{
|
||
public:
|
||
invalid_option_format_error(const std::string& format)
|
||
: OptionSpecException(u8"Invalid option format ‘" + format + u8"’")
|
||
{
|
||
}
|
||
};
|
||
|
||
class option_not_exists_exception : public OptionParseException
|
||
{
|
||
public:
|
||
option_not_exists_exception(const std::string& option)
|
||
: OptionParseException(u8"Option ‘" + option + u8"’ does not exist")
|
||
{
|
||
}
|
||
};
|
||
|
||
class missing_argument_exception : public OptionParseException
|
||
{
|
||
public:
|
||
missing_argument_exception(const std::string& option)
|
||
: OptionParseException(u8"Option ‘" + option + u8"’ is missing an argument")
|
||
{
|
||
}
|
||
};
|
||
|
||
class option_requires_argument_exception : public OptionParseException
|
||
{
|
||
public:
|
||
option_requires_argument_exception(const std::string& option)
|
||
: OptionParseException(u8"Option ‘" + option + u8"’ requires an argument")
|
||
{
|
||
}
|
||
};
|
||
|
||
class option_not_has_argument_exception : public OptionParseException
|
||
{
|
||
public:
|
||
option_not_has_argument_exception
|
||
(
|
||
const std::string& option,
|
||
const std::string& arg
|
||
)
|
||
: OptionParseException(
|
||
u8"Option ‘" + option + u8"’ does not take an argument, but argument‘"
|
||
+ arg + "’ given")
|
||
{
|
||
}
|
||
};
|
||
|
||
class option_not_present_exception : public OptionParseException
|
||
{
|
||
public:
|
||
option_not_present_exception(const std::string& option)
|
||
: OptionParseException(u8"Option ‘" + option + u8"’ not present")
|
||
{
|
||
}
|
||
};
|
||
|
||
class OptionAdder;
|
||
|
||
class OptionDetails
|
||
{
|
||
public:
|
||
OptionDetails
|
||
(
|
||
const std::string& description,
|
||
std::shared_ptr<const Value> value
|
||
)
|
||
: m_desc(description)
|
||
, m_value(value)
|
||
, m_count(0)
|
||
{
|
||
}
|
||
|
||
const std::string&
|
||
description() const
|
||
{
|
||
return m_desc;
|
||
}
|
||
|
||
bool
|
||
has_arg() const
|
||
{
|
||
return m_value->has_arg();
|
||
}
|
||
|
||
void
|
||
parse(const std::string& text)
|
||
{
|
||
m_value->parse(text);
|
||
++m_count;
|
||
}
|
||
|
||
int
|
||
count() const
|
||
{
|
||
return m_count;
|
||
}
|
||
|
||
template <typename T>
|
||
const T&
|
||
as() const
|
||
{
|
||
return dynamic_cast<const values::default_value<T>&>(*m_value).get();
|
||
}
|
||
|
||
private:
|
||
std::string m_desc;
|
||
std::shared_ptr<const Value> m_value;
|
||
int m_count;
|
||
};
|
||
|
||
class Options
|
||
{
|
||
public:
|
||
|
||
void
|
||
parse(int& argc, char**& argv);
|
||
|
||
OptionAdder
|
||
add_options();
|
||
|
||
int
|
||
count(const std::string& o) const
|
||
{
|
||
auto iter = m_options.find(o);
|
||
if (iter == m_options.end())
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
return iter->second->count();
|
||
}
|
||
|
||
const OptionDetails&
|
||
operator[](const std::string& option) const
|
||
{
|
||
auto iter = m_options.find(option);
|
||
|
||
if (iter == m_options.end())
|
||
{
|
||
throw option_not_present_exception(option);
|
||
}
|
||
|
||
return *iter->second;
|
||
}
|
||
|
||
private:
|
||
friend class OptionAdder;
|
||
|
||
void
|
||
parse_option
|
||
(
|
||
std::shared_ptr<OptionDetails> value,
|
||
const std::string& name,
|
||
const std::string& arg = ""
|
||
);
|
||
|
||
void
|
||
checked_parse_arg
|
||
(
|
||
int argc,
|
||
char* argv[],
|
||
int argPos,
|
||
std::shared_ptr<OptionDetails> value,
|
||
const std::string& name
|
||
);
|
||
|
||
|
||
std::map<std::string, std::shared_ptr<OptionDetails>> m_options;
|
||
};
|
||
|
||
class OptionAdder
|
||
{
|
||
public:
|
||
|
||
OptionAdder(Options& options)
|
||
: m_options(options)
|
||
{
|
||
}
|
||
|
||
OptionAdder&
|
||
operator()
|
||
(
|
||
const std::string& opts,
|
||
const std::string& desc,
|
||
std::shared_ptr<const Value> value
|
||
= ::cxxopts::value<bool>()
|
||
);
|
||
|
||
private:
|
||
Options& m_options;
|
||
};
|
||
|
||
}
|