1 #ifndef MSP_CORE_GETOPT_H_
2 #define MSP_CORE_GETOPT_H_
8 #include <msp/strings/lexicalcast.h>
12 class usage_error: public std::runtime_error
18 usage_error(const std::string &w, const std::string &h = std::string()): std::runtime_error(w), help_(h) { }
19 ~usage_error() throw() { }
21 const char *help() const throw() { return help_.c_str(); }
26 Command line option processor. Both short and long options are supported, with
27 optional and required arguments. Automatic help text generation is also
30 Short options begin with a single dash and are identified by a single letter.
31 Multiple short options may be grouped if they take no arguments; for example,
32 the string "-abc" could be interpreted as having the options 'a', 'b' and 'c'.
33 If the option takes an argument and there are unused characters in the argv
34 element, then those characters are interpreted as the argument. Otherwise the
35 next element is taken as the argument. An optional argument must be given in
38 Long options begin with a double dash and are identified by an arbitrary
39 string. An argument can be specified either in the same argv element,
40 separated by an equals sign, or in the next element. As with short options,
41 an optional argument must be in the same element.
43 A single option may have both alternative forms, but must always have at least
44 a long form. This is to encourage self-documenting options; it's much easier
45 to remember words than letters.
47 A built-in --help option is provided and will output a list of options and
48 their associated help texts. An application may override this by providing
49 its own option with the same name.
68 /// Sets help text for the option.
69 virtual Option &set_help(const std::string &) = 0;
71 /** Sets help text for the option, with a placeholder metavariable. The
72 metavariable is used to denote the argument in the option list. */
73 virtual Option &set_help(const std::string &, const std::string &) = 0;
75 virtual Option &bind_seen_count(unsigned &) = 0;
77 /// Returns the number of times this option was seen on the command line.
78 virtual unsigned get_seen_count() const = 0;
82 class OptBase: public Option
89 unsigned *ext_seen_count;
93 OptBase(char, const std::string &, ArgType);
95 virtual ~OptBase() { }
97 virtual OptBase &set_help(const std::string &);
98 virtual OptBase &set_help(const std::string &, const std::string &);
99 virtual OptBase &bind_seen_count(unsigned &);
100 char get_short() const { return shrt; }
101 const std::string &get_long() const { return lng; }
102 ArgType get_arg_type() const { return arg_type; }
103 const std::string &get_help() const { return help; }
104 const std::string &get_metavar() const { return metavar; }
105 virtual unsigned get_seen_count() const { return seen_count; }
107 void process(const std::string &);
109 virtual void store() = 0;
110 virtual void store(const std::string &) = 0;
114 class SimpleOption: public OptBase
120 SimpleOption(char s, const std::string &l, T &d, ArgType a): OptBase(s, l, a), data(d) { }
122 virtual void store() { }
124 virtual void store(const std::string &a)
128 data = lexical_cast<T>(a);
130 catch(const lexical_error &e)
132 throw usage_error("Invalid argument for --"+lng+" ("+e.what()+")");
138 class ListOption: public OptBase
144 ListOption(char s, const std::string &l, T &d, ArgType a): OptBase(s, l, a), data(d)
145 { if(arg_type!=REQUIRED_ARG) throw std::invalid_argument("ListOption arg_type!=REQUIRED"); }
147 virtual void store() { }
149 virtual void store(const std::string &a)
153 data.push_back(lexical_cast<typename T::value_type>(a));
155 catch(const lexical_error &e)
157 throw usage_error("Invalid argument for --"+lng+" ("+e.what()+")");
163 std::list<OptBase *> opts;
164 std::vector<std::string> args;
170 /// Returns any non-option arguments encountered during processing.
171 const std::vector<std::string> &get_args() const { return args; }
173 /** Adds an option with both short and long forms. Processing depends on
174 the type of the destination variable and whether an argument is taken or
175 not. With an argument, the value is lexical_cast to appropriate type and
176 stored in the destination. Without an argument, a bool will be set to true
177 and an unsigned will be incremented; any other type will be ignored. */
179 Option &add_option(char s, const std::string &l, T &d, ArgType a = NO_ARG)
180 { return add_option(new SimpleOption<T>(s, l, d, a)); }
182 /** Adds an option with both short and long forms. The option may be
183 specified multiple times, and the argument from each occurrence is stored in
184 the list. The argument type must be REQUIRED_ARG. */
186 Option &add_option(char s, const std::string &l, std::list<T> &d, ArgType a = REQUIRED_ARG)
187 { return add_option(new ListOption<std::list<T> >(s, l, d, a)); }
189 /** Adds an option with only a long form. */
191 Option &add_option(const std::string &l, T &d, ArgType a)
192 { return add_option(0, l, d, a); }
195 OptBase &add_option(OptBase *);
197 OptBase &get_option(char);
198 OptBase &get_option(const std::string &);
201 /** Processes argc/argv style command line arguments. The contents of argv
202 will be unchanged; use get_args to access non-option arguments. */
203 void operator()(unsigned, const char *const *);
206 /** Processes a long option. Returns the number of arguments eaten. */
207 unsigned process_long(const char *const *);
209 /** Processes short options. Returns the number of arguments eaten. */
210 unsigned process_short(const char *const *);
213 /** Generates a single line that describes known options. */
214 std::string generate_usage(const std::string &) const;
216 /** Generates help for known options in tabular format, one option per
217 line. The returned string will have a linefeed at the end. */
218 std::string generate_help() const;
221 template<> inline void GetOpt::SimpleOption<bool>::store()
224 template<> inline void GetOpt::SimpleOption<unsigned>::store()
227 template<> inline void GetOpt::SimpleOption<std::string>::store(const std::string &a)
230 template<> inline void GetOpt::ListOption<std::list<std::string> >::store(const std::string &a)
231 { data.push_back(a); }