diff --git a/cmdopt.cpp b/cmdopt.cpp index 6e497238..4a311840 100644 --- a/cmdopt.cpp +++ b/cmdopt.cpp @@ -20,6 +20,7 @@ #include #include +#include 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 -count::count (std::string _name, T &_data): - value (std::move (_name), _data) +count::count (std::string _name, std::string _description, T &_data): + value (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); +} diff --git a/cmdopt.hpp b/cmdopt.hpp index 8d0d39ee..9f5e587b 100644 --- a/cmdopt.hpp +++ b/cmdopt.hpp @@ -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 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 class count : public value { public: - count (std::string name, T&); + count (std::string name, std::string description, T&); using value::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; using long_t = std::tuple; @@ -148,7 +154,12 @@ namespace util { namespace cmdopt { std::vector m_short; std::vector m_long; - std::vector> m_options; + std::vector< + std::tuple< + char, + std::string, + std::unique_ptr> + > m_options; }; } } diff --git a/cmdopt.ipp b/cmdopt.ipp index 7d997a14..0b2d37ee 100644 --- a/cmdopt.ipp +++ b/cmdopt.ipp @@ -24,8 +24,8 @@ namespace util { namespace cmdopt { /////////////////////////////////////////////////////////////////////////// template - option::value::value (std::string _name, T &_data): - base (std::move (_name)), + option::value::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 (longname, std::forward (args)...); + auto handler = std::make_unique (longname, description, std::forward (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; }