cmdopt: add a simple omnipresent help option

This commit is contained in:
Danny Robson 2015-09-11 19:55:32 +10:00
parent ad2f3261de
commit 736fe9a156
3 changed files with 90 additions and 27 deletions

View File

@ -20,6 +20,7 @@
#include <algorithm>
#include <cstring>
#include <iomanip>
using util::cmdopt::option::base;
using util::cmdopt::option::bytes;
@ -31,10 +32,11 @@ using util::cmdopt::parser;
///////////////////////////////////////////////////////////////////////////////
base::base (std::string _name):
m_name (_name),
base::base (std::string _name, std::string _description):
m_required (false),
m_seen (false)
m_seen (false),
m_name (std::move (_name)),
m_description (std::move (_description))
{ ; }
@ -77,13 +79,21 @@ base::finish (void)
//-----------------------------------------------------------------------------
std::string
const std::string&
base::name (void) const
{
return m_name;
}
//-----------------------------------------------------------------------------
const std::string&
base::description (void) const
{
return m_description;
}
//-----------------------------------------------------------------------------
bool
base::required (void) const
@ -117,8 +127,8 @@ base::seen (bool _seen)
///////////////////////////////////////////////////////////////////////////////
null::null (std::string _name):
base (std::move (_name))
null::null (std::string _name, std::string _description):
base (std::move (_name), std::move (_description))
{ ; }
@ -139,8 +149,8 @@ null::execute (const char *restrict)
///////////////////////////////////////////////////////////////////////////////
present::present (std::string _name, bool &_data):
base (std::move (_name)),
present::present (std::string _name, std::string _description, bool &_data):
base (std::move (_name), std::move (_description)),
m_data (_data)
{ ; }
@ -214,8 +224,8 @@ namespace util { namespace cmdopt { namespace option {
///////////////////////////////////////////////////////////////////////////////
template <typename T>
count<T>::count (std::string _name, T &_data):
value<T> (std::move (_name), _data)
count<T>::count (std::string _name, std::string _description, T &_data):
value<T> (std::move (_name), std::move (_description), _data)
{ ; }
@ -270,6 +280,7 @@ suffix_to_multiplier (char c)
}
//-----------------------------------------------------------------------------
void
bytes::execute (const char *restrict str)
{
@ -298,7 +309,7 @@ parser::scan (int argc, const char *const *argv)
CHECK (argv);
for (auto &j: m_options)
j->start ();
std::get<2> (j)->start ();
// start iterating after our program's name
int i = 1;
@ -319,7 +330,7 @@ parser::scan (int argc, const char *const *argv)
}
for (auto &j: m_options)
j->finish ();
std::get<2> (j)->finish ();
return i;
}
@ -343,6 +354,8 @@ parser::parse_long (int pos, int argc, const char *const *argv)
const char *last = start + strlen (start);
std::string key { start, eq ? eq : last };
if (key == "help")
print_help (argc, argv);
// find the handler
auto handle_pos = std::find_if (m_long.begin (),
@ -388,6 +401,8 @@ parser::parse_short (int pos, int argc, const char *const *argv)
if (len > 2 || pos + 1 == argc || argv[pos+1][0] == '-') {
for (size_t i = 1; i < len; ++i) {
auto letter = argv[pos][i];
if (letter == 'h')
print_help (argc, argv);
auto hpos = std::find_if (m_short.begin (),
m_short.end (),
@ -411,3 +426,43 @@ parser::parse_short (int pos, int argc, const char *const *argv)
return 2;
}
///////////////////////////////////////////////////////////////////////////////
void
parser::print_help (const int argc,
const char *const *argv) const
{
(void)argc;
CHECK_EQ (m_short.size (), m_options.size ());
CHECK_EQ (m_long.size (), m_options.size ());
if (m_options.empty ())
exit (0);
// find the longest long form argument so we can set field alignment
auto largest = std::max_element (m_long.begin (),
m_long.end (),
[] (const auto &a, const auto &b) {
return std::get<0> (a).size () < std::get<0> (b).size ();
});
int longwidth = std::get<0> (*largest).size ();
// field width requires an alignment. we don't care about preserving
// state as we're about to bail anyway
std::cout << std::left;
// print all the option info
std::cout << "usage: " << argv[0] << '\n';
for (auto &o: m_options) {
std::cout << '\t'
<< '-' << std::get<0> (o) << '\t'
<< std::setw (longwidth) << std::get<1> (o) << '\t'
<< std::setw (0) << std::get<2> (o)->description ()
<< '\n';
}
exit (0);
}

View File

@ -27,8 +27,10 @@
namespace util { namespace cmdopt {
namespace option {
class base {
protected:
base (std::string name, std::string description);
public:
base (std::string name);
virtual ~base ();
virtual void execute (void);
@ -36,7 +38,8 @@ namespace util { namespace cmdopt {
virtual void start (void);
virtual void finish (void);
std::string name (void) const;
const std::string& name (void) const;
const std::string& description (void) const;
bool required (void) const;
bool required (bool);
@ -45,15 +48,17 @@ namespace util { namespace cmdopt {
bool seen (bool);
private:
std::string m_name;
bool m_required;
bool m_seen;
std::string m_name;
std::string m_description;
};
class null : public base {
public:
null (std::string name);
explicit null (std::string name, std::string description);
virtual void execute (void) override;
virtual void execute (const char *restrict) override;
@ -62,7 +67,7 @@ namespace util { namespace cmdopt {
class present : public base {
public:
present (std::string name, bool&);
present (std::string name, std::string description, bool&);
using base::execute;
virtual void execute (void) override;
@ -77,7 +82,7 @@ namespace util { namespace cmdopt {
template <typename T>
class value : public base {
public:
value (std::string name, T&);
value (std::string name, std::string description, T&);
using base::execute;
void execute (const char *restrict) override;
@ -94,7 +99,7 @@ namespace util { namespace cmdopt {
template <typename T = unsigned>
class count : public value<T> {
public:
count (std::string name, T&);
count (std::string name, std::string description, T&);
using value<T>::execute;
void execute (void) override;
@ -141,6 +146,7 @@ namespace util { namespace cmdopt {
int parse_long (int pos, int argc, const char *const *argv);
int parse_short (int pos, int argc, const char *const *argv);
void print_help [[noreturn]] (int argc, const char *const *argv) const;
using short_t = std::tuple<char,option::base&>;
using long_t = std::tuple<std::string,option::base&>;
@ -148,7 +154,12 @@ namespace util { namespace cmdopt {
std::vector<short_t> m_short;
std::vector<long_t> m_long;
std::vector<std::unique_ptr<option::base>> m_options;
std::vector<
std::tuple<
char,
std::string,
std::unique_ptr<option::base>>
> m_options;
};
} }

View File

@ -24,8 +24,8 @@
namespace util { namespace cmdopt {
///////////////////////////////////////////////////////////////////////////
template <typename T>
option::value<T>::value (std::string _name, T &_data):
base (std::move (_name)),
option::value<T>::value (std::string _name, std::string _description, T &_data):
base (std::move (_name), std::move (_description)),
m_data (_data)
{ ; }
@ -88,16 +88,13 @@ namespace util { namespace cmdopt {
std::string description,
Args&&... args)
{
// TODO: make use of the description with the help option
(void)description;
auto handler = std::make_unique<T> (longname, std::forward<Args> (args)...);
auto handler = std::make_unique<T> (longname, description, std::forward<Args> (args)...);
T& ref = *handler;
m_short.emplace_back (shortname, ref);
m_long .emplace_back (longname, ref);
m_options.emplace_back (std::move (handler));
m_options.emplace_back (shortname, longname, std::move (handler));
return ref;
}