Special command line option classes added

This commit is contained in:
johan
2013-08-11 21:49:11 +02:00
parent 29f6e41923
commit cc3baec022
8 changed files with 559 additions and 306 deletions

View File

@@ -1,18 +1,18 @@
/* (C) 2011 Viktor Lofgren
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* (C) 2011 Viktor Lofgren
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "getoptpp.h"
#include <stdexcept>
@@ -23,175 +23,207 @@ using namespace std;
namespace vlofgren {
/*
*
* Class OptionsParser
*
*
*/
*
* Class OptionsParser
*
*
*/
OptionsParser::OptionsParser(const char* programDesc) : fprogramDesc(programDesc) {}
OptionsParser::~OptionsParser() {}
ParameterSet& OptionsParser::getParameters() {
return parameters;
return parameters;
}
void OptionsParser::parse(int argc, const char* argv[]) throw(runtime_error)
{
argv0 = argv[0];
argv0 = argv[0];
if(argc == 1) return;
if(argc == 1) return;
vector<string> v(&argv[1], &argv[argc]);
vector<string> v(&argv[1], &argv[argc]);
ParserState state(*this, v);
ParserState state(*this, v);
for(; !state.end(); state.advance()) {
for(; !state.end(); state.advance()) {
std::list<Parameter*>::iterator i;
std::list<Parameter*>::iterator i;
for(i = parameters.parameters.begin();
i != parameters.parameters.end(); i++)
{
if((*i)->receive(state)) break;
}
for(i = parameters.parameters.begin();
i != parameters.parameters.end(); i++)
{
int n = 0;
try
{
n = (*i)->receive(state);
}
catch(Parameter::ExpectedArgument &)
{
throw Parameter::ExpectedArgument(state.get() + ": expected an argument");
}
catch(Parameter::UnexpectedArgument &)
{
throw Parameter::UnexpectedArgument(state.get() + ": did not expect an argument");
}
catch(Switchable::SwitchingError &)
{
throw Parameter::ParameterRejected(state.get() + ": parameter already set");
}
catch(Parameter::ParameterRejected & pr) {
std::string what = pr.what();
if(what.length())
{
throw Parameter::ParameterRejected(state.get() + ": " + what);
}
throw Parameter::ParameterRejected(state.get() + " (unspecified error)");
}
if(i == parameters.parameters.end()) {
std::string file = state.get();
if(file == "--") {
state.advance();
break;
}
else if(file.at(0) == '-')
throw Parameter::ParameterRejected(string("Bad parameter: ") + file);
else files.push_back(state.get());
}
}
for (int j = 1; j < n; ++j)
{
state.advance();
}
if(!state.end()) for(; !state.end(); state.advance()) {
files.push_back(state.get());
}
if(n != 0)
{
break;
}
}
if(i == parameters.parameters.end()) {
std::string file = state.get();
if(file == "--") {
state.advance();
break;
}
else if(file.at(0) == '-')
throw Parameter::ParameterRejected(string("Bad parameter: ") + file);
else files.push_back(state.get());
}
}
if(!state.end()) for(; !state.end(); state.advance()) {
files.push_back(state.get());
}
}
void OptionsParser::usage() const {
cerr << "Usage: " << programName() << " arguments" << endl;
cerr << fprogramDesc << endl;
cerr << "Usage: " << programName() << " [OPTIONS]" << endl << endl;
cerr << fprogramDesc << endl << endl;
cerr << "Parameters: " << endl;
cerr << "Parameters: " << endl;
std::list<Parameter*>::const_iterator i;
for(i = parameters.parameters.begin();
i != parameters.parameters.end(); i++)
{
cerr.width(30);
cerr << std::left << " " + (*i)->usageLine();
std::list<Parameter*>::const_iterator i;
for(i = parameters.parameters.begin();
i != parameters.parameters.end(); i++)
{
cerr.width(30);
cerr << std::left << " " + (*i)->usageLine();
cerr.width(40);
cerr << std::left << (*i)->description() << endl;
}
cerr.width(40);
cerr << std::left << (*i)->description() << endl;
}
}
const vector<string>& OptionsParser::getFiles() const {
return files;
return files;
}
const string& OptionsParser::programName() const {
return argv0;
return argv0;
}
/*
* Parameter set
*
*
*/
* Parameter set
*
*
*/
ParameterSet::ParameterSet(const ParameterSet& ps) {
throw new runtime_error("ParameterSet not copyable");
throw new runtime_error("ParameterSet not copyable");
}
ParameterSet::~ParameterSet() {
for(std::list<Parameter*>::iterator i = parameters.begin();
i != parameters.end(); i++)
{
delete *i;
}
for(std::list<Parameter*>::iterator i = parameters.begin();
i != parameters.end(); i++)
{
delete *i;
}
}
/* The typical use case for command line arguments makes linear searching completely
* acceptable here.
*/
* acceptable here.
*/
Parameter& ParameterSet::operator[](char c) const {
for(std::list<Parameter*>::const_iterator i = parameters.begin(); i!= parameters.end(); i++) {
if((*i)->shortOption() == c) return *(*i);
}
throw out_of_range("ParameterSet["+c+string("]"));
for(std::list<Parameter*>::const_iterator i = parameters.begin(); i!= parameters.end(); i++) {
if((*i)->shortOption() == c) return *(*i);
}
throw out_of_range("ParameterSet["+c+string("]"));
}
Parameter& ParameterSet::operator[](const string& param) const {
for(std::list<Parameter*>::const_iterator i = parameters.begin(); i!= parameters.end(); i++) {
if((*i)->longOption() == param) return *(*i);
}
throw out_of_range("ParameterSet["+param+"]");
for(std::list<Parameter*>::const_iterator i = parameters.begin(); i!= parameters.end(); i++) {
if((*i)->longOption() == param) return *(*i);
}
throw out_of_range("ParameterSet["+param+"]");
}
/*
*
* Class ParserState
*
*
*/
*
* Class ParserState
*
*
*/
ParserState::ParserState(OptionsParser &opts, vector<string>& args) :
opts(opts), arguments(args), iterator(args.begin())
opts(opts), arguments(args), iterator(args.begin())
{
}
const string ParserState::peek() const {
vector<string>::const_iterator next = iterator+1;
if(next != arguments.end()) return *next;
else return "";
vector<string>::const_iterator next = iterator+1;
if(next != arguments.end()) return *next;
else return "";
}
const string ParserState::get() const {
if(!end()) return *iterator;
else return "";
if(!end()) return *iterator;
else return "";
}
void ParserState::advance() {
iterator++;
iterator++;
}
bool ParserState::end() const {
return iterator == arguments.end();
return iterator == arguments.end();
}
/*
*
* Class Parameter
*
*
*/
*
* Class Parameter
*
*
*/
Parameter::Parameter(char shortOption, const std::string & longOption, const std::string & description) :
fshortOption(shortOption), flongOption(longOption), fdescription(description)
fshortOption(shortOption), flongOption(longOption), fdescription(description)
{
}
Parameter::~Parameter() {}
@@ -202,11 +234,11 @@ bool Parameter::hasShortOption() const { return fshortOption != 0x0; }
char Parameter::shortOption() const { assert(hasShortOption()); return fshortOption; }
/*
*
* Class Switchable
*
*
*/
*
* Class Switchable
*
*
*/
bool Switchable::isSet() const { return fset; }
Switchable::~Switchable() {};
@@ -217,108 +249,88 @@ MultiSwitchable::~MultiSwitchable() {}
void UniquelySwitchable::set() throw (Switchable::SwitchingError) {
if(UniquelySwitchable::isSet()) throw Switchable::SwitchingError();
fset = true;
if(UniquelySwitchable::isSet()) throw Switchable::SwitchingError();
fset = true;
}
UniquelySwitchable::~UniquelySwitchable() {}
PresettableUniquelySwitchable::~PresettableUniquelySwitchable() {}
bool PresettableUniquelySwitchable::isSet() const {
return UniquelySwitchable::isSet() || fpreset.isSet();
return UniquelySwitchable::isSet() || fpreset.isSet();
}
void PresettableUniquelySwitchable::set() throw (Switchable::SwitchingError)
{
UniquelySwitchable::set();
UniquelySwitchable::set();
}
void PresettableUniquelySwitchable::preset() {
fpreset.set();
fpreset.set();
}
/*
*
* Class SwitchParameter
*
*
*/
SwitchParameter::SwitchParameter(char shortOption, const char *longOption,
const char* description) : CommonParameter<MultiSwitchable>(shortOption, longOption, description) {}
SwitchParameter::~SwitchParameter() {}
void SwitchParameter::receiveSwitch() throw(Parameter::ParameterRejected) {
set();
}
void SwitchParameter::receiveArgument(const string &arg) throw(Parameter::ParameterRejected) {
throw UnexpectedArgument();
}
/*
*
* PODParameter specializations
*
*
*
*/
*
* PODParameter specializations
*
*
*
*/
template<>
PODParameter<string>::PODParameter(char shortOption, const char *longOption,
const char* description) : CommonParameter<PresettableUniquelySwitchable>(shortOption, longOption, description) {
preset();
const char* description) : CommonParameter<PresettableUniquelySwitchable>(shortOption, longOption, description) {
preset();
}
template<>
int PODParameter<int>::validate(const string &s) throw(Parameter::ParameterRejected)
{
// This is sadly necessary for strto*-functions to operate on
// const char*. The function doesn't write to the memory, though,
// so it's quite safe.
// This is sadly necessary for strto*-functions to operate on
// const char*. The function doesn't write to the memory, though,
// so it's quite safe.
char* cstr = const_cast<char*>(s.c_str());
if(*cstr == '\0') throw ParameterRejected("No argument given");
char* cstr = const_cast<char*>(s.c_str());
if(*cstr == '\0') throw ParameterRejected("No argument given");
long l = strtol(cstr, &cstr, 10);
if(*cstr != '\0') throw ParameterRejected("Expected int");
long l = strtol(cstr, &cstr, 10);
if(*cstr != '\0') throw ParameterRejected("Expected int");
if(l > INT_MAX || l < INT_MIN) {
throw ParameterRejected("Expected int");
}
if(l > INT_MAX || l < INT_MIN) {
throw ParameterRejected("Expected int");
}
return l;
return l;
}
template<>
long PODParameter<long>::validate(const string &s) throw(Parameter::ParameterRejected)
{
char* cstr = const_cast<char*>(s.c_str());
if(*cstr == '\0') throw ParameterRejected("No argument given");
char* cstr = const_cast<char*>(s.c_str());
if(*cstr == '\0') throw ParameterRejected("No argument given");
long l = strtol(cstr, &cstr, 10);
if(*cstr != '\0') throw ParameterRejected("Expected long");
long l = strtol(cstr, &cstr, 10);
if(*cstr != '\0') throw ParameterRejected("Expected long");
return l;
return l;
}
template<>
double PODParameter<double>::validate(const string &s) throw(Parameter::ParameterRejected)
{
char* cstr = const_cast<char*>(s.c_str());
if(*cstr == '\0') throw ParameterRejected("No argument given");
char* cstr = const_cast<char*>(s.c_str());
if(*cstr == '\0') throw ParameterRejected("No argument given");
double d = strtod(cstr, &cstr);
if(*cstr != '\0') throw ParameterRejected("Expected double");
double d = strtod(cstr, &cstr);
if(*cstr != '\0') throw ParameterRejected("Expected double");
return d;
return d;
}
template<>
string PODParameter<string>::validate(const string &s) throw(Parameter::ParameterRejected)
{
return s;
return s;
}

View File

@@ -203,7 +203,7 @@ protected:
* iterator that technically allows for more complex grammar than what is
* presently used.
*/
virtual bool receive(ParserState& state) throw(ParameterRejected) = 0;
virtual int receive(ParserState& state) throw(ParameterRejected) = 0;
friend class OptionsParser;
@@ -250,20 +250,9 @@ protected:
* receiveSwitch() or receiveArgument() accordingly.
*
* @param state The current argument being parsed.
* @return The number of parameters taken from the input
*/
virtual bool receive(ParserState& state) throw(ParameterRejected);
/**
* Called when a parameter does not have an argument, e.g.
* either -f or --foo
*/
virtual void receiveSwitch() throw (ParameterRejected) = 0;
/**
* Called when a parameter does have an argument, .e.g
* -fbar or --foo=bar
*/
virtual void receiveArgument(const std::string& argument) throw (ParameterRejected) = 0;
virtual int receive(ParserState& state) throw(ParameterRejected) = 0;
};
/** This class (used as a mixin) defines how a parameter
@@ -344,15 +333,15 @@ private:
/* Parameter that does not take an argument, and throws an exception
* if an argument is given */
class SwitchParameter : public CommonParameter<MultiSwitchable> {
template<typename SwitchingBehavior=MultiSwitchable>
class SwitchParameter : public CommonParameter<SwitchingBehavior> {
public:
SwitchParameter(char shortOption, const char *longOption,
const char* description);
virtual ~SwitchParameter();
protected:
virtual void receiveSwitch() throw (Parameter::ParameterRejected);
virtual void receiveArgument(const std::string& argument) throw (Parameter::ParameterRejected);
virtual int receive(ParserState& state) throw(Parameter::ParameterRejected);
};
/** Plain-Old-Data parameter. Performs input validation.
@@ -363,8 +352,8 @@ protected:
* Specifically, you need to specialize validate().
*/
template<typename T>
class PODParameter : public CommonParameter<PresettableUniquelySwitchable> {
template<typename T, typename SwitchingBehavior=PresettableUniquelySwitchable>
class PODParameter : public CommonParameter<SwitchingBehavior> {
public:
PODParameter(char shortOption, const char *longOption,
const char* description);
@@ -383,14 +372,14 @@ public:
std::string usageLine() const;
protected:
virtual int receive(ParserState& state) throw(Parameter::ParameterRejected);
/** Validation function for the data type.
*
* @throw ParameterRejected if the argument does not conform to this data type.
* @return the value corresponding to the argument.
*/
virtual T validate(const std::string& s) throw (ParameterRejected);
virtual void receiveArgument(const std::string &argument) throw(ParameterRejected);
virtual void receiveSwitch() throw (Parameter::ParameterRejected);
virtual T validate(const std::string& s) throw (Parameter::ParameterRejected);
T value;
};

View File

@@ -53,155 +53,125 @@ template<typename SwitchingBehavior>
std::string CommonParameter<SwitchingBehavior>::usageLine() const {
std::stringstream strstr;
strstr.width(10);
if (hasShortOption())
{
strstr << std::left<< std::string("-") + shortOption();
strstr << "-" << shortOption() << ", ";
}
else
{
strstr << " ";
strstr << " ";
}
strstr.width(20);
strstr << std::left << "--" + longOption();
strstr.width(20);
strstr << std::left << "--" + longOption();
return strstr.str();
return strstr.str();
}
/*
*
* Class SwitchParameter
*
*
*/
template<typename SwitchingBehavior>
bool CommonParameter<SwitchingBehavior>::receive(ParserState& state) throw(Parameter::ParameterRejected) {
SwitchParameter<SwitchingBehavior>::SwitchParameter(char shortOption, const char *longOption,
const char* description) : CommonParameter<SwitchingBehavior>(shortOption, longOption, description) {}
template<typename SwitchingBehavior>
SwitchParameter<SwitchingBehavior>::~SwitchParameter() {}
template<typename SwitchingBehavior>
int SwitchParameter<SwitchingBehavior>::receive(ParserState& state) throw(Parameter::ParameterRejected) {
const std::string arg = state.get();
try {
if(arg.at(0) != '-') return false;
if(arg.at(0) != '-') return false;
if(arg.at(1) == '-') { /* Long form parameter */
try {
unsigned int eq = arg.find_first_of("=");
if ((arg.at(1) == '-' && arg.substr(2) == this->longOption()) ||
(this->hasShortOption() && arg.at(1) == this->shortOption() && arg.length() == 2))
{
this->set();
return 1;
}
if(eq == std::string::npos) {
if(arg.substr(2) != longOption())
return false;
this->receiveSwitch();
} else {
if(arg.substr(2, eq-2) != longOption())
return false;
this->receiveArgument(arg.substr(eq+1));
}
return true;
} catch(Parameter::ExpectedArgument &ea) {
throw ExpectedArgument("--" + longOption() + ": expected an argument");
} catch(Parameter::UnexpectedArgument &ua) {
throw UnexpectedArgument("--" + longOption() + ": did not expect an argument");
} catch(Switchable::SwitchingError &e) {
throw ParameterRejected("--" + longOption() + ": parameter already set");
} catch(Parameter::ParameterRejected &pr) {
std::string what = pr.what();
if(what.length())
throw Parameter::ParameterRejected("--" + longOption() + ": " + what);
throw Parameter::ParameterRejected("--" + longOption() + " (unspecified error)");
}
}
else if (hasShortOption())
{
try {
if(arg.at(1) == shortOption()) {
/* Matched argument on the form -f or -fsomething */
if(arg.length() == 2) { /* -f */
this->receiveSwitch();
return true;
} else { /* -fsomething */
this->receiveArgument(arg.substr(2));
return true;
}
}
} catch(Parameter::ExpectedArgument &ea) {
throw ExpectedArgument(std::string("-") + shortOption() + ": expected an argument");
} catch(Parameter::UnexpectedArgument &ua) {
throw UnexpectedArgument(std::string("-") + shortOption() + ": did not expect an argument");
} catch(Switchable::SwitchingError &e) {
throw ParameterRejected(std::string("-") + shortOption() + ": parameter already set");
}
}
} catch(std::out_of_range& o) {
return false;
}
return false;
return 0;
}
/*
* PODParameter stuff
*
*/
template<typename T>
PODParameter<T>::PODParameter(char shortOption, const char *longOption,
template<typename T, typename SwitchingBehavior>
PODParameter<T, SwitchingBehavior>::PODParameter(char shortOption, const char *longOption,
const char* description) : CommonParameter<PresettableUniquelySwitchable>(shortOption, longOption, description) {}
template<typename T>
PODParameter<T>::~PODParameter() {}
template<typename T, typename SwitchingBehavior>
PODParameter<T, SwitchingBehavior>::~PODParameter() {}
template<typename T>
PODParameter<T>::operator T() const { return getValue(); }
template<typename T, typename SwitchingBehavior>
PODParameter<T, SwitchingBehavior>::operator T() const { return getValue(); }
template<typename T>
void PODParameter<T>::setDefault(T value) {
template<typename T, typename SwitchingBehavior>
void PODParameter<T, SwitchingBehavior>::setDefault(T value) {
PresettableUniquelySwitchable::preset();
this->value = value;
}
template<typename T>
T PODParameter<T>::getValue() const {
if(!isSet()) {
throw runtime_error(
std::string("Attempting to retreive the argument of parameter") + longOption() + " but it hasn't been set!");
template<typename T, typename SwitchingBehavior>
T PODParameter<T, SwitchingBehavior>::getValue() const {
if(!this->isSet()) {
throw std::runtime_error(
std::string("Attempting to retreive the argument of parameter") + this->longOption() + " but it hasn't been set!");
}
return value;
}
template<typename T>
std::string PODParameter<T>::usageLine() const {
template<typename T, typename SwitchingBehavior>
std::string PODParameter<T, SwitchingBehavior>::usageLine() const {
std::stringstream strstr;
strstr.width(10);
if (hasShortOption())
if (this->hasShortOption())
{
strstr << std::left << std::string("-") + shortOption() +"arg";
strstr << "-" << this->shortOption() << ", ";
}
else
{
strstr << "";
strstr << " ";
}
strstr.width(20);
strstr << std::left << "--" + longOption() + "=arg";
strstr << std::left << "--" + this->longOption() + " <arg>";
return strstr.str();
}
template<typename T>
void PODParameter<T>::receiveSwitch() throw (Parameter::ParameterRejected) {
throw Parameter::ExpectedArgument();
}
template<typename T, typename SwitchingBehavior>
int PODParameter<T, SwitchingBehavior>::receive(ParserState& state) throw(Parameter::ParameterRejected) {
template<typename T>
void PODParameter<T>::receiveArgument(const std::string &argument) throw(Parameter::ParameterRejected) {
set();
value = this->validate(argument);
}
const std::string arg = state.get();
if(arg.at(0) != '-') return false;
if((arg.at(1) == '-' && arg.substr(2) == this->longOption()) ||
(this->hasShortOption() && arg.at(1) == this->shortOption() && arg.length() == 2))
{
// retrieve the argument
std::string arg1 = state.peek();
if (arg1.length() == 0)
{
throw Parameter::ExpectedArgument(arg + ": expected an argument");
return 1;
}
this->set();
value = this->validate(arg1);
return 2;
}
return 0;
}
#endif