66#include < regex>
77#include < memory>
88#include < iostream>
9+ #include < sstream>
10+ #include < iomanip>
911
1012namespace cppcommandline
1113{
@@ -93,6 +95,28 @@ class Option
9395 return val;
9496 }
9597
98+ std::string defaultValueAsString () const
99+ {
100+ std::ostringstream stream;
101+
102+ switch (d->type )
103+ {
104+ case Type::Bool: d->defaultValue .b ? stream << " true" : stream << " false" ; break ;
105+ case Type::Double: stream << d->defaultValue .d ; break ;
106+ case Type::Integer: stream << d->defaultValue .i ; break ;
107+ case Type::LongLong: stream << d->defaultValue .l ; break ;
108+ case Type::String: stream << d->defaultStringValue ; break ;
109+ case Type::Undefined: break ;
110+ }
111+
112+ return stream.str ();
113+ }
114+
115+ bool hasDefaultValue () const
116+ {
117+ return d->defaulted ;
118+ }
119+
96120 template <typename T>
97121 T *boundValue () const
98122 {
@@ -114,7 +138,6 @@ class Option
114138
115139 Option &asShortName (std::string shortName)
116140 {
117-
118141 if (d->longName .empty ())
119142 throw std::logic_error (" Short name cannot be defined for positional arguments." );
120143 else if (!isShortName (shortName))
@@ -192,7 +215,7 @@ class Option
192215 std::vector<std::string>::const_iterator it = argument+1 ;
193216
194217 if (it == end)
195- throw (std::logic_error (" Missing value for option '" + longName () + " '" ));
218+ throw (std::logic_error (" Missing value for option '" + ( longName (). empty () ? " [positional] " : longName () ) + " '" ));
196219 else if (setValue (*it))
197220 argument += 2 ;
198221 }
@@ -299,7 +322,7 @@ class Option
299322 *d->valueBinding .s = value;
300323 break ;
301324 case Type::Undefined:
302- throw (std::logic_error (" Bind value undefined for option '" + longName () + " '" ));
325+ throw (std::logic_error (" Bind value undefined for option '" + ( longName (). empty () ? " [positional] " : longName () ) + " '" ));
303326 break ;
304327 }
305328 }
@@ -424,6 +447,16 @@ template<> Option::Type Option::getType<double>() const { return Type::Double; }
424447class Parser
425448{
426449public:
450+ bool helpEnabled () const
451+ {
452+ return mHelp ;
453+ }
454+
455+ void disableHelp ()
456+ {
457+ mHelp = false ;
458+ }
459+
427460 std::string command () const
428461 {
429462 return mCommand ;
@@ -448,6 +481,8 @@ class Parser
448481
449482 void parse (int argc, char **argv)
450483 {
484+ try
485+ {
451486 if (argc == 0 )
452487 throw (std::logic_error (" Missing mandatory first command line argument" ));
453488 else
@@ -466,7 +501,44 @@ class Parser
466501 mArgs = std::vector<std::string>();
467502
468503 for (int i = 1 ; i < argc; i++)
469- mArgs .emplace_back (std::string (argv[i]));
504+ mArgs .emplace_back (argv[i]);
505+
506+ if (mHelp )
507+ {
508+ std::vector<std::string> helpNames = {" --help" , " -h" };
509+
510+ if (std::find_first_of (mArgs .cbegin (), mArgs .cend (), helpNames.cbegin (), helpNames.cend ()) != mArgs .cend ())
511+ {
512+ std::cout << " Usage: " << mAppName << " [options]" << std::endl;
513+ std::cout << " Options:" << std::endl;
514+
515+ for (const Option& option : mOptions )
516+ {
517+ std::ostringstream str;
518+
519+ if (option.isPositional ())
520+ str << " [positional]" ;
521+ else
522+ str << " -" << option.shortName () << " , --" << option.longName ();
523+
524+ std::cout << std::left << std::setw (25 ) << str.str ();
525+
526+ str = std::ostringstream ();
527+
528+ if (option.isRequired ())
529+ str << " [required]" ;
530+ else if (option.hasDefaultValue ())
531+ str << " [default=" << option.defaultValueAsString () << " ]" ;
532+ else
533+ str << " [optional]" ;
534+
535+ std::cout << std::setw (20 ) << str.str () << option.description () << std::endl;
536+ }
537+
538+ std::cout << std::endl;
539+ return ;
540+ }
541+ }
470542
471543 std::vector<Option*> options;
472544
@@ -492,13 +564,23 @@ class Parser
492564 }
493565
494566 if (arg == start)
495- throw (std::logic_error (" No option set for argument '" + *arg + " '" ));
567+ throw (std::logic_error (" No option matches argument '" + *arg + " '" ));
496568 }
497569
498570 for (Option *option : options)
499571 {
500572 if (option->isRequired ())
501- throw (std::logic_error (" Option '" + option->longName () + " ' was set as required but did not match any arguments" ));
573+ throw (std::logic_error (" Option '" + (option->longName ().empty () ? " [positional]" : option->longName ()) + " ' was set as required but did not match any arguments" ));
574+ }
575+ }
576+ catch (std::logic_error &e)
577+ {
578+ std::cout << " Error parsing command line arguments: " << e.what () << std::endl;
579+
580+ if (mHelp )
581+ std::cout << " Use --help or -h to list the command line options." << std::endl;
582+
583+ throw e;
502584 }
503585 }
504586
@@ -507,6 +589,7 @@ class Parser
507589 std::string mAppName ;
508590 std::vector<std::string> mArgs ;
509591 std::vector<Option> mOptions ;
592+ bool mHelp = true ;
510593};
511594
512595}
0 commit comments