Skip to content
Snippets Groups Projects
options.h 8.74 KiB
Newer Older
David Flitney's avatar
David Flitney committed
// $Id$
#if !defined(OPTIONS_H)
#define OPTIONS_H

David Flitney's avatar
David Flitney committed
#include <string>
#include <vector>

#define POSIX_SOURCE 1

David Flitney's avatar
David Flitney committed
  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() {}
David Flitney's avatar
David Flitney committed
  
    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&lt;bool&gt; verbose(string("-V,--verbose"), false,
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string("switch on diagnostic messages"),
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;false, no_argument);
     Option&lt;bool&gt help(string("-h,--help"), false,
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string("display this message"),
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;false, no_argument);
     Option&lt;float&gt dof(string("-d,--dof"), 100.0,
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string("number of degrees of freedom"),
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;true, requires_argument);
     Option&lt;string&gt mask(string("-m,--mask"), string("mask"),
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string("brain mask volume"),
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;true, requires_argument);
     Option&lt;string&gt resid(string("-r,--res"), string("res4d"),
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string("4d `residual-of-fit' image"),
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;true, requires_argument);<br>
     int main(unsigned int argc, char **argv) {<br>
     &nbsp;&nbsp;OptionParser options("options V1.0\nCopyright(c) University of Oxford 2000, Dave Flitney", "options -d &lt;number&gt; --mask &lt;filename&gt; --res &lt;filename&gt;");<br>
     &nbsp;&nbsp;try {<br>
     &nbsp;&nbsp;&nbsp;&nbsp;options.add(verbose);
     &nbsp;&nbsp;&nbsp;&nbsp;options.add(help);
     &nbsp;&nbsp;&nbsp;&nbsp;options.add(dof);
     &nbsp;&nbsp;&nbsp;&nbsp;options.add(mask);
     &nbsp;&nbsp;&nbsp;&nbsp;options.add(resid);<br>
     &nbsp;&nbsp;&nbsp;&nbsp;for(unsigned int a = options.parse_command_line(argc, argv); a < argc; a++)
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout << argv[a] << endl;<br>  
     &nbsp;&nbsp;&nbsp;&nbsp;if(help.value())
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;options.usage();<br>
     &nbsp;&nbsp;&nbsp;&nbsp;if(verbose.value()) {
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout << "verbose = " << verbose.value() << endl;
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout << "help = " << help.value() << endl;
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout << "dof = " << dof.value() << endl;
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout << "mask = " << mask.value() << endl;
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout << "resid = " << resid.value() << endl;
     &nbsp;&nbsp;&nbsp;&nbsp;}<br>
     &nbsp;&nbsp;} catch(X_OptionError& e) {
     &nbsp;&nbsp;&nbsp;&nbsp;options.usage();
     &nbsp;&nbsp;&nbsp;&nbsp;cerr << endl << e.what() << endl;
     &nbsp;&nbsp;} catch(std::exception &e) {
     &nbsp;&nbsp;&nbsp;&nbsp;cerr << e.what() << endl;
     &nbsp;&nbsp;}    
     }
     </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);
David Flitney's avatar
David Flitney committed
  
David Flitney's avatar
David Flitney committed

  private:
    unsigned int argc_; char **argv_;
    string progname_, example_;
    typedef vector<BaseOption *> Options;
    Options options_;
  };
David Flitney's avatar
David Flitney committed

David Flitney's avatar
David Flitney committed
#endif