]> Dogcows Code - chaz/yoink/blob - src/stlplus/subsystems/cli_parser.hpp
archiving support for releases
[chaz/yoink] / src / stlplus / subsystems / cli_parser.hpp
1 #ifndef STLPLUS_CLI_PARSER
2 #define STLPLUS_CLI_PARSER
3 ////////////////////////////////////////////////////////////////////////////////
4
5 // Author: Andy Rushton
6 // Copyright: (c) Southampton University 1999-2004
7 // (c) Andy Rushton 2004-2009
8 // License: BSD License, see ../docs/license.html
9
10 // A subsystem for managing command-line parsing, including using INI files to
11 // control the default options.
12
13 ////////////////////////////////////////////////////////////////////////////////
14 #include "subsystems_fixes.hpp"
15 #include "message_handler.hpp"
16 #include "ini_manager.hpp"
17 #include "smart_ptr.hpp"
18 #include <string>
19 #include <stdexcept>
20
21 namespace stlplus
22 {
23
24 ////////////////////////////////////////////////////////////////////////////////
25 // Internals
26
27 class cli_parser_data;
28
29 ////////////////////////////////////////////////////////////////////////////////
30 // declarations
31
32 // enum to define the basic behaviour of an argument
33 // - a switch is an option with no value but which can be switched on or off e.g. -help and -nohelp
34 // - a value is an option followed by a value e.g. -output results.txt
35 // (a default value can be removed by using the option as a negated switch e.g. -nooutput)
36 // - command-line values (i.e. any strings not preceded by '-') are treated
37 // internally as an option with no name and must be values
38 enum cli_kind_t {cli_switch_kind, cli_value_kind};
39
40 // the mode controls the behaviour if an option appears more than once in either the command-line or the ini files
41 // - a single mode option overrides all previous values so will only be found once in the parsed result
42 // - a multiple mode option can be repeated to define multiple values, but overrides values from ini files
43 // - a cumulative mode option is a multiple mode option which keeps ini file values as well
44 enum cli_mode_t {cli_single_mode, cli_multiple_mode, cli_cumulative_mode};
45
46 // There are two structures used for defining command-line parameters
47 // (1) a C struct which is used in a C array - this is used for declaring
48 // command-line parameters in a static declaration
49 // (2) a C++ class which is used in an STL vector - this is used for building
50 // command-line parameters within code
51
52 // The C struct for definitions
53 struct cli_definition_t
54 {
55 // the name of the option, e.g. "help"
56 const char* m_name;
57
58 // the kind of the option, e.g. cli_switch_kind
59 cli_kind_t m_kind;
60
61 // the mode e.g. cli_single_mode
62 cli_mode_t m_mode;
63
64 // the mnemonic for the message giving usage information for this option
65 const char* m_message;
66
67 // built-in default value - null if not present
68 const char* m_default;
69 };
70
71 // The C array of the C struct. The array must be terminated by END_CLI_DEFINITIONS.
72 typedef cli_definition_t cli_definitions_t [];
73 #define END_CLI_DEFINITIONS {0,stlplus::cli_switch_kind,stlplus::cli_single_mode,"",0}
74
75 // The C++ class for definitions
76 class cli_definition
77 {
78 public:
79 // constructor that allows a definition to be created in one line
80 cli_definition(const std::string& name, cli_kind_t kind, cli_mode_t mode,
81 const std::string& message, const std::string& default_value = std::string()) :
82 m_name(name), m_kind(kind), m_mode(mode), m_message(message), m_default(default_value) {}
83
84 // the name of the option, e.g. "help"
85 const std::string& name(void) const;
86
87 // the kind of the option, e.g. switch_kind
88 cli_kind_t kind(void) const;
89
90 // the mode e.g. single_mode
91 cli_mode_t mode(void) const;
92
93 // the mnemonic for the message giving usage
94 const std::string& message(void) const;
95
96 // built-in default value - empty string if not present
97 const std::string& default_value(void) const;
98
99 private:
100 std::string m_name;
101 cli_kind_t m_kind;
102 cli_mode_t m_mode;
103 std::string m_message;
104 std::string m_default;
105 };
106
107 // The C++ vector of the C++ class
108 typedef std::vector<cli_definition> cli_definitions;
109
110 //////////////////////////////////////////////////////////////////////////////
111 // exceptions that can be thrown by the CLI parser
112 // they are all derivatives of std::logic_error because all errors are predictable by code inspection
113 // a correct program will never throw an exception
114
115 // thrown if a command-line argument is accessed with the wrong mode - i.e. attempt to get the value of a switch
116 class cli_mode_error : public std::invalid_argument
117 {
118 public:
119 cli_mode_error(const std::string& arg) : std::invalid_argument(arg) {}
120 ~cli_mode_error(void) throw() {}
121 };
122
123 // similar to std::out_of_range thrown for using an index out of range
124 class cli_index_error : public std::out_of_range
125 {
126 public:
127 cli_index_error(const std::string& arg) : std::out_of_range(arg) {}
128 ~cli_index_error(void) throw() {}
129 };
130
131 // similar to std::invalid_argument - thrown for passing an illegal argument to a method
132 class cli_argument_error : public std::invalid_argument
133 {
134 public:
135 cli_argument_error(const std::string& arg) : std::invalid_argument(arg) {}
136 ~cli_argument_error(void) throw() {}
137 };
138
139 ////////////////////////////////////////////////////////////////////////////////
140
141 class cli_parser
142 {
143 public:
144 // Type definitions map the global type names onto convenient scoped names
145
146 typedef cli_kind_t kind_t;
147 typedef cli_mode_t mode_t;
148 typedef cli_definition_t definition_t;
149 typedef cli_definitions_t definitions_t;
150 typedef cli_definition definition;
151 typedef cli_definitions definitions;
152
153 ////////////////////////////////////////////////////////////////////////////////
154 // Methods
155
156 // various constructors
157
158 // you have a choice of either creating an uninitialised CLI parser and then
159 // calling separate functions to set it up or of calling one of the
160 // composite constructors. However, you must set up the error handler in the
161 // constructor.
162
163 // set up the parser with its error handler
164 // defer everything else
165 cli_parser(message_handler& errors)
166 throw();
167
168 // constructors using the C definitions_t structure
169
170 // set up the parser with the error handler and define all the command-line options
171 // defer default values and parameter parsing
172 cli_parser(cli_definitions_t, message_handler& errors)
173 throw(cli_mode_error);
174 // set up the parser with the error handler and define all the command-line
175 // options and their default from the ini files
176 // defer parameter parsing
177 cli_parser(cli_definitions_t, const ini_manager& defaults, const std::string& ini_section, message_handler& errors)
178 throw(cli_mode_error);
179 // set up the parser with the error handler and define all the command-line
180 // options no ini files used for default values, so only built-in defaults
181 // supported then parse the command line
182 cli_parser(char* argv[], cli_definitions_t, message_handler& errors)
183 throw(cli_mode_error,message_handler_id_error,message_handler_format_error);
184 // set up the parser with the error handler and define all the command-line
185 // options and their default from the ini files then parse the command line
186 cli_parser(char* argv[], cli_definitions_t, const ini_manager& defaults, const std::string& ini_section, message_handler& errors)
187 throw(cli_mode_error,message_handler_id_error,message_handler_format_error);
188
189 // constructors using the C++ definitions structure
190
191 // set up the parser with the error handler and define all the command-line
192 // options from a C array of structs
193 // defer default values and parameter parsing
194 cli_parser(cli_definitions, message_handler& errors)
195 throw(cli_mode_error);
196 // set up the parser with the error handler and define all the command-line
197 // options and their default from the ini files
198 // defer parameter parsing
199 cli_parser(cli_definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& errors)
200 throw(cli_mode_error);
201 // set up the parser with the error handler and define all the command-line
202 // options no ini files used for default values, so only built-in defaults
203 // supported then parse the command line
204 cli_parser(char* argv[], cli_definitions, message_handler& errors)
205 throw(cli_mode_error,message_handler_id_error,message_handler_format_error);
206 // set up the parser with the error handler and define all the command-line
207 // options and their default from the ini files then parse the command line
208 cli_parser(char* argv[], cli_definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& errors)
209 throw(cli_mode_error,message_handler_id_error,message_handler_format_error);
210
211 ~cli_parser(void)
212 throw();
213
214 // the separate functions for initialising the parser in steps. These are
215 // declared in the order of use. Firts, add definitions of command-line
216 // arguments. Then optionally load default values from ini files, then
217 // finally parse the command line.
218
219 // add a set of C definitions. The definitions will be given ID codes from 0
220 // to the number of elements - 1 in the array
221 void add_definitions(cli_definitions_t)
222 throw(cli_mode_error);
223 // add a single C definition, returning the ID code for it
224 unsigned add_definition(const definition_t&)
225 throw(cli_mode_error,cli_argument_error);
226 // add a set of C++ definitions. The definitions will be given ID codes from
227 // 0 to the number of elements - 1 in the array
228 void add_definitions(cli_definitions)
229 throw(cli_mode_error);
230 // add a single C++ definition, returning the ID code for it
231 unsigned add_definition(const definition&)
232 throw(cli_mode_error);
233
234 // All definitions have an optional built-in default value which is stored
235 // in the definition types above. However, these can optionally be
236 // overridden by a value from an ini file. If you want this functionality,
237 // call this function. If you don't want ini file handling, simply don't
238 // call it. The values will be searched for only in the named section of the
239 // ini file (sections are labelled by e.g. [vassemble]), so in this case you
240 // would specify the section name as "vassemble" (exclude the brackets).
241 void set_defaults(const ini_manager& defaults, const std::string& ini_section)
242 throw();
243
244 // the final stage of initialisation is to read the command-line and extract
245 // the values from it. If parse errors are found, this will report the
246 // errors using the error handler and return false.
247 bool parse(char* argv[])
248 throw(cli_argument_error,message_handler_id_error,message_handler_format_error);
249
250 // test for whether the CLI parser is still valid (no errors have happened)
251 // after the initialisation phase
252 bool valid(void)
253 throw();
254
255 // iteration functions avoiding the use of iterators. Just loop through the
256 // arguments from 0 to size()-1 and use the index of the loop to interrogate
257 // the command-line for the value at that position.
258
259 // the number of values to read, indexed 0 to size()-1
260 unsigned size(void) const
261 throw();
262
263 // the argument name
264 std::string name(unsigned i) const
265 throw(cli_index_error);
266 // the argument ID, that is, the offset into the original definitions
267 unsigned id(unsigned i) const
268 throw(cli_index_error);
269
270 // the kind (switch or value) and short-cut tests for the different kinds
271 cli_kind_t kind(unsigned i) const
272 throw(cli_index_error);
273 bool switch_kind(unsigned i) const
274 throw(cli_index_error);
275 bool value_kind(unsigned i) const
276 throw(cli_index_error);
277
278 // the mode (single, multiple, cumulative) and short-cut tests for the
279 // different modes - you rarely need to know this since it mainly controls
280 // the parsing
281 cli_mode_t mode(unsigned i) const
282 throw(cli_index_error);
283 bool single_mode(unsigned i) const
284 throw(cli_index_error);
285 bool multiple_mode(unsigned i) const
286 throw(cli_index_error);
287 bool cumulative_mode(unsigned i) const
288 throw(cli_index_error);
289
290 // get the switch's value, but only if the value is of switch kind
291 bool switch_value(unsigned i) const
292 throw(cli_mode_error,cli_index_error);
293
294 // get the option's value, but only if it is of value kind
295 std::string string_value(unsigned i) const
296 throw(cli_mode_error,cli_index_error);
297
298 // print the usage report - typically in response to the -help switch being on
299 void usage(void) const
300 throw(std::runtime_error);
301
302 private:
303 friend class cli_parser_data;
304 smart_ptr_nocopy<cli_parser_data> m_data;
305 };
306
307 } // end namespace stlplus
308
309 #endif
This page took 0.044882 seconds and 4 git commands to generate.