Skip to content

Commit ad03b46

Browse files
committed
Added example and help printing.
1 parent a193149 commit ad03b46

File tree

2 files changed

+100
-9
lines changed

2 files changed

+100
-9
lines changed

example/main.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1-
#include <QCoreApplication>
21
#include <cppcommandline.h>
32

43
int main(int argc, char **argv)
54
{
6-
QCoreApplication a(argc, argv);
7-
5+
cppcommandline::Parser commandLine;
6+
7+
int someoption = 0;
8+
std::string filename;
9+
bool flag = false;
10+
11+
commandLine.option().withDescription("A required filename").required().bindTo(filename);
12+
commandLine.option("someoption").asShortName("s").withDefaultValue(10).withDescription("My description of someoption").bindTo(someoption);
13+
commandLine.option("flag").withDefaultValue(false).withDescription("My flag").bindTo(flag);
14+
15+
commandLine.parse(argc, argv);
816
return 0;
917
}

include/cppcommandline.h

Lines changed: 89 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include <regex>
77
#include <memory>
88
#include <iostream>
9+
#include <sstream>
10+
#include <iomanip>
911

1012
namespace 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; }
424447
class Parser
425448
{
426449
public:
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

Comments
 (0)