diff --git a/options.h b/options.h index dd74db2e5573a7a225e071f9305e3527bbe279c1..31aabcbc3db5819bfec286d53ea3bed8fd02d544 100644 --- a/options.h +++ b/options.h @@ -2,84 +2,276 @@ #if !defined(OPTIONS_H) #define OPTIONS_H +#include <stdexcept> #include <string> #include <vector> #define POSIX_SOURCE 1 -class BaseOption { -public: +namespace Options { + typedef enum { no_argument = 0, requires_argument, optional_argument } ArgFlag; + + /** + Provides behaviour common to all option types. Actual options are + declared using the templated Option class. The + OptionParser class can be used to parse command lines. + @see Option + @see OptionParser + @author Dave Flitney + @version 1.0b, Nov., 2000. + @package Options + */ + class BaseOption { + public: + /** + @param k comma seperated list of key aliases + @param ht the help text to be printed for this option + @param c if true then this option is compulsory + @param f one of no_argument, requires_argument, optional_argument + to indicate what arguments should be supplied + */ + BaseOption(const string& k, const string& ht, bool c, ArgFlag f): + key_(k), help_text_(ht), arg_flag_(f), + unset_(true), compulsory_(c) {} + + /** + @return true if the option is compulsory + */ + bool compulsory() { return compulsory_; } + /** + @return true if the option requires an argument + */ + bool required() { return arg_flag_ == requires_argument; } + /** + @return true if the option has an optional argument + */ + bool optional() { return arg_flag_ == optional_argument; } + /** + @return true if the option has an argument at all + */ + bool has_arg() { return arg_flag_ != no_argument; } + /** + @return true if the option has been set + */ + bool set() { return !unset_; } + /** + @return true if the option remains unset + */ + bool unset() { return unset_; } + /* + @param arg A command line argument to be compared against + the list of possible keys for this option. + @return True if a match is found. + */ + bool matches(const string& arg); + /* + @return This options key string. + */ + const string& key() const { return key_; } + /* + @return This options help text. + */ + const string& help_text() const { return help_text_; } + + /* + @param Sets the value for this option. Is overridden in the type + specific template class Option. + */ + virtual void value(const string& vs) = 0; + + virtual ~BaseOption() {} + + private: + string key_, help_text_; + ArgFlag arg_flag_; + + protected: + bool unset_, compulsory_; + }; + + void string_to_T(bool &b, const string& s); + void string_to_T(string& d, const string& s); + void string_to_T(int& i, const string& s); + void string_to_T(float& v, const string& s); + + /** + Template class adding type specific behaviour to BaseOption. Define + one of these per program supported option. + @author Dave Flitney + @version 1.0b, Nov., 2000. + @package Options + @see BaseOption + */ + template<class T> class Option: public BaseOption { + public: + /** + @param k Comma seperated list of key aliases + @param v Default value for this option + @param ht Help text to be printed when outputting usage + @param c If true then this option is compulsory + @param f This options argument requirements + */ + Option(const string& k, const T& v, const string& ht, + bool c, ArgFlag f = no_argument): + BaseOption(k, ht, c, f), default_(v), value_(v) {} + + + /** + @param vs The value string which needs to be parsed to set + this options value. The overloaded function string_to_T must be defined + for type T. + @changes Clears the unset_ flag. + */ + void value(const string& vs) { + string_to_T(value_, vs); unset_ = false; + } + + /** + @return The options value. + */ + const T& value() { return value_; } + /** + @return The options default value. + */ + const T& default_value() { return default_; } + + virtual ~Option() {} + + private: + Option() {} - BaseOption(const string& k, const string& ht, bool c, ArgFlag f): - key_(k), help_text_(ht), arg_flag_(f), - unset_(true), compulsory_(c) {} - - bool compulsory() { return compulsory_; } - bool required() { return arg_flag_ == requires_argument; } - bool optional() { return arg_flag_ == optional_argument; } - bool set() { return !unset_; } - bool unset() { return unset_; } - bool has_arg() { return arg_flag_ != no_argument; } - bool matches(const string& arg); - const string& key() const { return key_; } - const string& help_text() const { return help_text_; } - - virtual void value(const string& vs) = 0; - - virtual ~BaseOption() {} - -private: - string key_, help_text_; - ArgFlag arg_flag_; - -protected: - bool unset_, compulsory_; -}; - -void string_to_T(bool &b, const string& s); -void string_to_T(string& d, const string& s); -void string_to_T(int& i, const string& s); -void string_to_T(float& v, const string& s); - -template<class T> class Option: public BaseOption { -public: - Option(const string& k, const T& v, const string& ht, - bool c, ArgFlag f = no_argument): - BaseOption(k, ht, c, f), default_(v), value_(v) {} - - void value(const string& vs) { - string_to_T(value_, vs); unset_ = false; - } - const T& value() { return value_; } - const T& default_value() { return default_; } - virtual ~Option() {} - -private: - Option() {} - - T default_, value_; -}; - -class OptionParser { -public: - OptionParser(const string& p, const string& e): progname_(p), example_(e) {} - - void add(BaseOption& o) { options_.push_back(&o); } - void usage(); - BaseOption * operator[](const string& key); - bool check_compulsory_arguments(bool verbose=false); - unsigned int parse_command_line(unsigned int argc, char **argv); + T default_, value_; + }; + + /** + Throw this exception if an error occured inside the Options package. + @package Exceptions + */ + class X_OptionError: public std::exception { + public: + X_OptionError() throw() {}; + virtual const char * what() const throw() { + return "There were errors parsing the command line!"; + } + }; + + /** + Throw this exception if no matching option was found during parsing. + @package Exceptions + */ + class X_UnknownOptions: public X_OptionError { + public: + X_UnknownOptions() throw() {}; + virtual const char * what() const throw() { + return "There were unknown options!"; + } + }; + + /** + If an option should have had an argument but didn't then throw this + exception. + @package Exceptions + */ + class X_MissingArguments: public X_OptionError { + public: + X_MissingArguments() throw() {}; + virtual const char * what() const throw() { + return "There were missing arguments!"; + } + }; + + /** + A class for parsing command line arguments into Option objects. + @author Dave Flitney + @version 1.0b, Nov., 2000. + @package Options + @see BaseOption + @see Option + @example + <pre> + Option<bool> verbose(string("-V,--verbose"), false, + string("switch on diagnostic messages"), + false, no_argument); + Option<bool> help(string("-h,--help"), false, + string("display this message"), + false, no_argument); + Option<float> dof(string("-d,--dof"), 100.0, + string("number of degrees of freedom"), + true, requires_argument); + Option<string> mask(string("-m,--mask"), string("mask"), + string("brain mask volume"), + true, requires_argument); + Option<string> resid(string("-r,--res"), string("res4d"), + string("4d `residual-of-fit' image"), + true, requires_argument);<br> + int main(unsigned int argc, char **argv) {<br> + OptionParser options("options V1.0\nCopyright(c) University of Oxford 2000, Dave Flitney", "options -d <number> --mask <filename> --res <filename>");<br> + try {<br> + options.add(verbose); + options.add(help); + options.add(dof); + options.add(mask); + options.add(resid);<br> + for(unsigned int a = options.parse_command_line(argc, argv); a < argc; a++) + cout << argv[a] << endl;<br> + if(help.value()) + options.usage();<br> + if(verbose.value()) { + cout << "verbose = " << verbose.value() << endl; + cout << "help = " << help.value() << endl; + cout << "dof = " << dof.value() << endl; + cout << "mask = " << mask.value() << endl; + cout << "resid = " << resid.value() << endl; + }<br> + } catch(X_OptionError& e) { + options.usage(); + cerr << endl << e.what() << endl; + } catch(std::exception &e) { + cerr << e.what() << endl; + } + } + </pre> + */ + class OptionParser { + public: + /** + @param p The program identifier string + @param e Example usage string + */ + OptionParser(const string& p, const string& e): progname_(p), example_(e) {} + + /** + @param o An option to be added to the parser + */ + void add(BaseOption& o) { options_.push_back(&o); } + + void usage(); + /** + @param verbose If set then this method will carp about any option which + is marked as compulsory but hasn't been set + @return true if all compulsory arguments have been set and false otherwise + */ + bool check_compulsory_arguments(bool verbose=false); + /** + The parameters, argc and argv, should normally be those passed to + main via the command line shell. + @param argc The argument count. + @param argv The vector of argument strings. + */ + unsigned int parse_command_line(unsigned int argc, char **argv) + throw(X_OptionError, X_UnknownOptions, X_MissingArguments); - ~OptionParser() {} + ~OptionParser() {} -private: - unsigned int argc_; char **argv_; - string progname_, example_; - typedef vector<BaseOption *> Options; - Options options_; -}; + private: + unsigned int argc_; char **argv_; + string progname_, example_; + typedef vector<BaseOption *> Options; + Options options_; + }; +} #endif