Skip to content
Snippets Groups Projects
options.h 10.3 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>
David Flitney's avatar
David Flitney committed
#include <map>
David Flitney's avatar
David Flitney committed

#define POSIX_SOURCE 1

David Flitney's avatar
David Flitney committed
namespace Utilities {

David Flitney's avatar
David Flitney committed
  bool string_to_T(bool &b, const string& s); 
  bool string_to_T(string& d, const string& s); 
  bool string_to_T(int& i, const string& s); 
  bool string_to_T(float& v, const string& s); 
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.
    */
David Flitney's avatar
David Flitney committed
    virtual bool set_value(const string& vs) = 0;

    virtual ~BaseOption() {}

  private:
    string key_, help_text_;
    ArgFlag arg_flag_;

  protected:
    bool unset_, compulsory_;
  };

David Flitney's avatar
David Flitney committed
  ostream& operator<<(ostream &os, const BaseOption& o);

  /**
     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.
    */
David Flitney's avatar
David Flitney committed
    bool set_value(const string& vs) { 
      if(string_to_T(value_, vs))
	unset_ = false;
      return !unset_;
    }

    /**
       @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
   */
David Flitney's avatar
David Flitney committed
  class X_UnknownOption: public X_OptionError {
David Flitney's avatar
David Flitney committed
    X_UnknownOption(const string& s) throw(): 
      str_(s+":unknown option!") {};
    virtual const char * what() const throw() {
David Flitney's avatar
David Flitney committed
      return str_.c_str();
David Flitney's avatar
David Flitney committed

  private:
    string str_;
  };

  /**
     @package Exceptions
   */
  class X_AlreadySet: public X_OptionError {
  public:
    X_AlreadySet(const string& s) throw(): 
      str_(s+":already set!") {};
    virtual const char * what() const throw() {
      return str_.c_str();
    }

  private:
    string str_;
  };

  /**
     If an option should have had an argument but didn't then throw this
     exception.
     @package Exceptions
   */
David Flitney's avatar
David Flitney committed
  class X_MissingArgument: public X_OptionError {
David Flitney's avatar
David Flitney committed
    X_MissingArgument(const string& s) throw(): 
      str_(s+":missing argument!") {};
    virtual const char * what() const throw() {
David Flitney's avatar
David Flitney committed
      return str_.c_str();
David Flitney's avatar
David Flitney committed

  private:
    string str_;
  };

  /**
     @package Exceptions
   */
  class X_InvalidArgument: public X_OptionError {
  public:
    X_InvalidArgument(const string& o, const string& v) throw(): 
      str_(o+":invalid argument "+v+"!") {};
    virtual const char * what() const throw() {
      return str_.c_str();
    }

  private:
    string str_;
  };

  /**
     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:
David Flitney's avatar
David Flitney committed

    static OptionParser* Instance();

    /**
       @param o An option to be added to the parser
    */
David Flitney's avatar
David Flitney committed
    void add(const string& key, BaseOption& o) { options_[key] = &o; }
David Flitney's avatar
David Flitney committed
    void usage(const string& p, const string& e);
    /**
       @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.
    */
David Flitney's avatar
David Flitney committed
    unsigned int parse_command_line(unsigned int argc, char **argv);
    BaseOption* operator () (const string& key) { return options_[key]; }
    virtual ~OptionParser() {}

  protected:
    /**
    */
    OptionParser() {}
David Flitney's avatar
David Flitney committed

David Flitney's avatar
David Flitney committed
    /**
       @param optstr A string which should match one of the option strings
       registered with the add method.
       @return Pointer to the matching option or NULL if a match wasn't found.
    */
    BaseOption* find_matching_option(const string& optstr);
    /**
       @param optstr A string which should match one of the option strings
       registered with the add method.
       @param valstr A string which can be used to set the options value
       if applicable.
       @return true on success.
    */
    unsigned int parse_option(const string& optstr, const string& valstr)
      throw(X_AlreadySet, X_UnknownOption, 
	    X_MissingArgument, X_InvalidArgument);
    /**
       @param str A string of the form --option[=value].
       @return true on success.
    */
    unsigned int parse_long_option(const string& str);

    static OptionParser *instance_;
    typedef map<string, BaseOption *> Options;
David Flitney's avatar
David Flitney committed

David Flitney's avatar
David Flitney committed
#endif