]> git.tdb.fi Git - libs/core.git/blob - source/core/getopt.h
d3369c6b46fdf57b392b5a8dc56507fc6b79a773
[libs/core.git] / source / core / getopt.h
1 #ifndef MSP_CORE_GETOPT_H_
2 #define MSP_CORE_GETOPT_H_
3
4 #include <list>
5 #include <stdexcept>
6 #include <string>
7 #include <vector>
8 #include <msp/strings/lexicalcast.h>
9
10 namespace Msp {
11
12 class usage_error: public std::runtime_error
13 {
14 private:
15         std::string help_;
16
17 public:
18         usage_error(const std::string &w, const std::string &h = std::string()): std::runtime_error(w), help_(h) { }
19         ~usage_error() throw() { }
20
21         const char *help() const throw() { return help_.c_str(); }
22 };
23
24
25 class GetOpt
26 {
27 public:
28         enum ArgType
29         {
30                 NO_ARG,
31                 OPTIONAL_ARG,
32                 REQUIRED_ARG
33         };
34         
35         class Option
36         {
37         protected:
38                 Option() { }
39         public:
40                 virtual ~Option() { }
41
42                 virtual Option &set_help(const std::string &) = 0;
43                 virtual Option &set_help(const std::string &, const std::string &) = 0;
44                 virtual unsigned get_seen_count() const = 0;
45         };
46
47 private:
48         class OptBase: public Option
49         {
50         protected:
51                 char shrt;
52                 std::string lng;
53                 ArgType arg_type;
54                 unsigned seen_count;
55                 std::string help;
56                 std::string metavar;
57
58                 OptBase(char, const std::string &, ArgType);
59         public:
60                 virtual ~OptBase() { }
61
62                 virtual OptBase &set_help(const std::string &);
63                 virtual OptBase &set_help(const std::string &, const std::string &);
64                 char get_short() const { return shrt; }
65                 const std::string &get_long() const { return lng; }
66                 ArgType get_arg_type() const { return arg_type; }
67                 const std::string &get_help() const { return help; }
68                 const std::string &get_metavar() const { return metavar; }
69                 virtual unsigned get_seen_count() const { return seen_count; }
70                 void process();
71                 void process(const std::string &);
72         protected:
73                 virtual void store() = 0;
74                 virtual void store(const std::string &) = 0;
75         };
76
77         template<typename T>
78         class SimpleOption: public OptBase
79         {
80         private:
81                 T &data;
82
83         public:
84                 SimpleOption(char s, const std::string &l, T &d, ArgType a): OptBase(s, l, a), data(d) { }
85
86                 virtual void store() { }
87
88                 virtual void store(const std::string &a)
89                 {
90                         try
91                         {
92                                 data = lexical_cast<T>(a);
93                         }
94                         catch(const lexical_error &e)
95                         {
96                                 throw usage_error("Invalid argument for --"+lng+" ("+e.what()+")");
97                         }
98                 }
99         };
100
101         template<typename T>
102         class ListOption: public OptBase
103         {
104         private:
105                 T &data;
106
107         public:
108                 ListOption(char s, const std::string &l, T &d, ArgType a): OptBase(s, l, a), data(d)
109                 { if(arg_type!=REQUIRED_ARG) throw std::invalid_argument("ListOption arg_type!=REQUIRED"); }
110
111                 virtual void store() { }
112
113                 virtual void store(const std::string &a)
114                 {
115                         try
116                         {
117                                 data.push_back(lexical_cast<typename T::value_type>(a));
118                         }
119                         catch(const lexical_error &e)
120                         {
121                                 throw usage_error("Invalid argument for --"+lng+" ("+e.what()+")");
122                         }
123                 }
124         };
125
126         bool help;
127         std::list<OptBase *> opts;
128         std::vector<std::string> args;
129
130 public:
131         GetOpt();
132         ~GetOpt();
133
134         const std::vector<std::string> &get_args() const { return args; }
135
136         template<typename T>
137         Option &add_option(char s, const std::string &l, T &d, ArgType a = NO_ARG)
138         { return add_option(new SimpleOption<T>(s, l, d, a)); }
139         
140         template<typename T>
141         Option &add_option(char s, const std::string &l, std::list<T> &d, ArgType a = REQUIRED_ARG)
142         { return add_option(new ListOption<std::list<T> >(s, l, d, a)); }
143         
144         template<typename T>
145         Option &add_option(const std::string &l, T &d, ArgType a)
146         { return add_option(0, l, d, a); }
147
148 private:
149         OptBase &add_option(OptBase *);
150
151         OptBase &get_option(char);
152         OptBase &get_option(const std::string &);
153
154 public:
155         /** Processes argc/argv style command line arguments.  The contents of argv
156         will be unchanged; use get_args to access non-option arguments. */
157         void operator()(unsigned, const char *const *);
158
159 private:
160         /** Processes a long option.  Returns the number of arguments eaten. */
161         unsigned process_long(const char *const *);
162
163         /** Processes short options.  Returns the number of arguments eaten. */
164         unsigned process_short(const char *const *);
165
166 public:
167         /** Generates a single line that describes known options. */
168         std::string generate_usage(const std::string &) const;
169
170         /** Generates help for known options in tabular format, one option per
171         line.  The returned string will have a linefeed at the end. */
172         std::string generate_help() const;
173 };
174
175 template<> inline void GetOpt::SimpleOption<bool>::store()
176 { data = true; }
177
178 template<> inline void GetOpt::SimpleOption<unsigned>::store()
179 { ++data; }
180
181 template<> inline void GetOpt::SimpleOption<std::string>::store(const std::string &a)
182 { data = a; }
183
184 template<> inline void GetOpt::ListOption<std::list<std::string> >::store(const std::string &a)
185 { data.push_back(a); }
186
187 } // namespace Msp
188
189 #endif