1 #ifndef _DECOMMANDLINE_HPP
2 #define _DECOMMANDLINE_HPP
3 /*-------------------------------------------------------------------------
4 * drawElements C++ Base Library
5 * -----------------------------
6 *
7 * Copyright 2014 The Android Open Source Project
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 *
21 *//*!
22 * \file
23 * \brief Command line parser.
24 *//*--------------------------------------------------------------------*/
25
26 #include "deDefs.hpp"
27
28 #include <map>
29 #include <string>
30 #include <vector>
31 #include <ostream>
32 #include <typeinfo>
33 #include <stdexcept>
34
35 namespace de
36 {
37 namespace cmdline
38 {
39
40 //! Default parsing function
41 template <typename ValueType>
42 void parseType(const char *src, ValueType *dst);
43
44 template <typename T>
45 struct NamedValue
46 {
47 const char *name;
48 T value;
49 };
50
51 template <typename OptName>
52 struct Option
53 {
54 typedef typename OptName::ValueType ValueType;
55 typedef void (*ParseFunc)(const char *src, ValueType *dst);
56
57 // \note All assumed to point to static memory.
58 const char *shortName;
59 const char *longName;
60 const char *description;
61 const char *defaultValue; //!< Default value (parsed from string), or null if should not be set
62
63 // \note Either parse or namedValues must be null.
64 ParseFunc parse; //!< Custom parsing function or null.
65 const NamedValue<ValueType> *namedValues; //!< Named values or null.
66 const NamedValue<ValueType> *namedValuesEnd; //!< Named value list end.
67
68 //! Construct generic option (string, int, boolean).
Optionde::cmdline::Option69 Option(const char *shortName_, const char *longName_, const char *description_, const char *defaultValue_ = DE_NULL)
70 : shortName(shortName_)
71 , longName(longName_)
72 , description(description_)
73 , defaultValue(defaultValue_)
74 , parse(parseType<ValueType>)
75 , namedValues(DE_NULL)
76 , namedValuesEnd(0)
77 {
78 }
79
80 //! Option with custom parsing function.
Optionde::cmdline::Option81 Option(const char *shortName_, const char *longName_, const char *description_, ParseFunc parse_,
82 const char *defaultValue_ = DE_NULL)
83 : shortName(shortName_)
84 , longName(longName_)
85 , description(description_)
86 , defaultValue(defaultValue_)
87 , parse(parse_)
88 , namedValues(DE_NULL)
89 , namedValuesEnd(DE_NULL)
90 {
91 }
92
93 //! Option that uses named values.
Optionde::cmdline::Option94 Option(const char *shortName_, const char *longName_, const char *description_,
95 const NamedValue<ValueType> *namedValues_, const NamedValue<ValueType> *namedValuesEnd_,
96 const char *defaultValue_ = DE_NULL)
97 : shortName(shortName_)
98 , longName(longName_)
99 , description(description_)
100 , defaultValue(defaultValue_)
101 , parse((ParseFunc)DE_NULL)
102 , namedValues(namedValues_)
103 , namedValuesEnd(namedValuesEnd_)
104 {
105 }
106
107 //! Option that uses named values.
108 template <size_t NumNamedValues>
Optionde::cmdline::Option109 Option(const char *shortName_, const char *longName_, const char *description_,
110 const NamedValue<ValueType> (&namedValues_)[NumNamedValues], const char *defaultValue_ = DE_NULL)
111 : shortName(shortName_)
112 , longName(longName_)
113 , description(description_)
114 , defaultValue(defaultValue_)
115 , parse((ParseFunc)DE_NULL)
116 , namedValues(DE_ARRAY_BEGIN(namedValues_))
117 , namedValuesEnd(DE_ARRAY_END(namedValues_))
118 {
119 }
120 };
121
122 template <class Option>
123 struct OptTraits
124 {
125 typedef typename Option::ValueType ValueType;
126 };
127
128 //! Default value lookup
129 template <typename ValueType>
getTypeDefault(ValueType * dst)130 inline void getTypeDefault(ValueType *dst)
131 {
132 *dst = ValueType();
133 }
134
135 template <>
136 void getTypeDefault<bool>(bool *dst);
137
138 template <typename T>
isBoolean(void)139 inline bool isBoolean(void)
140 {
141 return false;
142 }
143 template <>
isBoolean(void)144 inline bool isBoolean<bool>(void)
145 {
146 return true;
147 }
148
149 //! Is argument boolean-only value?
150 template <class Option>
isBooleanOpt(void)151 inline bool isBooleanOpt(void)
152 {
153 return isBoolean<typename OptTraits<Option>::ValueType>();
154 }
155
156 namespace detail
157 {
158
159 using std::map;
160 using std::string;
161 using std::vector;
162
163 // TypedFieldMap implementation
164
165 template <class Name>
166 struct TypedFieldTraits
167 {
168 // Generic implementation for cmdline.
169 typedef typename OptTraits<Name>::ValueType ValueType;
170 };
171
172 template <class Value>
173 struct TypedFieldValueTraits
174 {
destroyde::cmdline::detail::TypedFieldValueTraits175 static void destroy(void *value)
176 {
177 delete (Value *)value;
178 }
179 };
180
181 class TypedFieldMap
182 {
183 public:
184 TypedFieldMap(void);
185 ~TypedFieldMap(void);
186
empty(void) const187 bool empty(void) const
188 {
189 return m_fields.empty();
190 }
191 void clear(void);
192
193 template <typename Name>
194 void set(typename TypedFieldTraits<Name>::ValueType *value);
195
196 template <typename Name>
197 void set(const typename TypedFieldTraits<Name>::ValueType &value);
198
199 template <typename Name>
200 bool contains(void) const;
201
202 template <typename Name>
203 const typename TypedFieldTraits<Name>::ValueType &get(void) const;
204
205 private:
206 TypedFieldMap(const TypedFieldMap &);
207 TypedFieldMap &operator=(const TypedFieldMap &);
208
209 typedef void (*DestroyFunc)(void *);
210
211 struct Entry
212 {
213 void *value;
214 DestroyFunc destructor;
215
Entryde::cmdline::detail::TypedFieldMap::Entry216 Entry(void) : value(DE_NULL), destructor(0)
217 {
218 }
Entryde::cmdline::detail::TypedFieldMap::Entry219 Entry(void *value_, DestroyFunc destructor_) : value(value_), destructor(destructor_)
220 {
221 }
222 };
223
224 typedef std::map<const std::type_info *, Entry> Map;
225
226 bool contains(const std::type_info *key) const;
227 const Entry &get(const std::type_info *key) const;
228 void set(const std::type_info *key, const Entry &value);
229
230 Map m_fields;
231 };
232
233 template <typename Name>
set(typename TypedFieldTraits<Name>::ValueType * value)234 inline void TypedFieldMap::set(typename TypedFieldTraits<Name>::ValueType *value)
235 {
236 set(&typeid(Name), Entry(value, &TypedFieldValueTraits<typename TypedFieldTraits<Name>::ValueType>::destroy));
237 }
238
239 template <typename Name>
set(const typename TypedFieldTraits<Name>::ValueType & value)240 void TypedFieldMap::set(const typename TypedFieldTraits<Name>::ValueType &value)
241 {
242 typename TypedFieldTraits<Name>::ValueType *copy = new typename TypedFieldTraits<Name>::ValueType(value);
243
244 try
245 {
246 set<Name>(copy);
247 }
248 catch (...)
249 {
250 delete copy;
251 throw;
252 }
253 }
254
255 template <typename Name>
contains(void) const256 inline bool TypedFieldMap::contains(void) const
257 {
258 return contains(&typeid(Name));
259 }
260
261 template <typename Name>
get(void) const262 inline const typename TypedFieldTraits<Name>::ValueType &TypedFieldMap::get(void) const
263 {
264 return *static_cast<typename TypedFieldTraits<Name>::ValueType *>(get(&typeid(Name)).value);
265 }
266
267 class CommandLine;
268
269 typedef void (*GenericParseFunc)(const char *src, void *dst);
270
271 class Parser
272 {
273 public:
274 Parser(void);
275 ~Parser(void);
276
277 template <class OptType>
278 void addOption(const Option<OptType> &option);
279
280 bool parse(int numArgs, const char *const *args, CommandLine *dst, std::ostream &err) const;
281
282 void help(std::ostream &dst) const;
283
284 private:
285 Parser(const Parser &);
286 Parser &operator=(const Parser &);
287
288 struct OptInfo;
289
290 typedef void (*DispatchParseFunc)(const OptInfo *info, const char *src, TypedFieldMap *dst);
291 typedef void (*SetDefaultFunc)(TypedFieldMap *dst);
292
293 struct OptInfo
294 {
295 const char *shortName;
296 const char *longName;
297 const char *description;
298 const char *defaultValue;
299 bool isFlag; //!< Set true for bool typed arguments that do not used named values.
300
301 GenericParseFunc parse;
302
303 const void *namedValues;
304 const void *namedValuesEnd;
305 size_t namedValueStride;
306
307 DispatchParseFunc dispatchParse;
308 SetDefaultFunc setDefault;
309
OptInfode::cmdline::detail::Parser::OptInfo310 OptInfo(void)
311 : shortName(DE_NULL)
312 , longName(DE_NULL)
313 , description(DE_NULL)
314 , defaultValue(DE_NULL)
315 , isFlag(false)
316 , parse(DE_NULL)
317 , namedValues(DE_NULL)
318 , namedValuesEnd(DE_NULL)
319 , namedValueStride(0)
320 , dispatchParse(DE_NULL)
321 , setDefault(DE_NULL)
322 {
323 }
324 };
325
326 void addOption(const OptInfo &option);
327
328 template <typename OptName>
329 static void dispatchParse(const OptInfo *info, const char *src, TypedFieldMap *dst);
330
331 vector<OptInfo> m_options;
332 };
333
334 template <class OptType>
operator <<(Parser & parser,const Option<OptType> & option)335 inline Parser &operator<<(Parser &parser, const Option<OptType> &option)
336 {
337 parser.addOption(option);
338 return parser;
339 }
340
341 //! Find match by name. Throws exception if no match is found.
342 const void *findNamedValueMatch(const char *src, const void *namedValues, const void *namedValuesEnd, size_t stride);
343
344 template <typename OptType>
dispatchParse(const OptInfo * info,const char * src,TypedFieldMap * dst)345 void Parser::dispatchParse(const OptInfo *info, const char *src, TypedFieldMap *dst)
346 {
347 typename OptTraits<OptType>::ValueType *value = new typename OptTraits<OptType>::ValueType();
348 try
349 {
350 DE_ASSERT((!!info->parse) != (!!info->namedValues));
351 if (info->parse)
352 {
353 ((typename Option<OptType>::ParseFunc)(info->parse))(src, value);
354 }
355 else
356 {
357 const void *match =
358 findNamedValueMatch(src, info->namedValues, info->namedValuesEnd, info->namedValueStride);
359 *value = static_cast<const NamedValue<typename OptTraits<OptType>::ValueType> *>(match)->value;
360 }
361 dst->set<OptType>(value);
362 }
363 catch (...)
364 {
365 delete value;
366 throw;
367 }
368 }
369
370 template <typename OptType>
dispatchSetDefault(TypedFieldMap * dst)371 void dispatchSetDefault(TypedFieldMap *dst)
372 {
373 typename OptTraits<OptType>::ValueType *value = new typename OptTraits<OptType>::ValueType();
374 try
375 {
376 getTypeDefault<typename OptTraits<OptType>::ValueType>(value);
377 dst->set<OptType>(value);
378 }
379 catch (...)
380 {
381 delete value;
382 throw;
383 }
384 }
385
386 template <typename OptType>
getNamedValueName(const void * value)387 const char *getNamedValueName(const void *value)
388 {
389 const NamedValue<typename OptTraits<OptType>::ValueType> *typedVal =
390 static_cast<const NamedValue<typename OptTraits<OptType>::ValueType>>(value);
391 return typedVal->name;
392 }
393
394 template <typename OptType>
setFromNamedValue(const void * value,TypedFieldMap * dst)395 void setFromNamedValue(const void *value, TypedFieldMap *dst)
396 {
397 const NamedValue<typename OptTraits<OptType>::ValueType> *typedVal =
398 static_cast<const NamedValue<typename OptTraits<OptType>::ValueType>>(value);
399 dst->set<OptType>(typedVal->value);
400 }
401
402 template <class OptType>
addOption(const Option<OptType> & option)403 void Parser::addOption(const Option<OptType> &option)
404 {
405 OptInfo opt;
406
407 opt.shortName = option.shortName;
408 opt.longName = option.longName;
409 opt.description = option.description;
410 opt.defaultValue = option.defaultValue;
411 opt.isFlag = isBooleanOpt<OptType>() && !option.namedValues;
412 opt.parse = (GenericParseFunc)option.parse;
413 opt.namedValues = (const void *)option.namedValues;
414 opt.namedValuesEnd = (const void *)option.namedValuesEnd;
415 opt.namedValueStride = sizeof(*option.namedValues);
416 opt.dispatchParse = dispatchParse<OptType>;
417
418 if (opt.isFlag)
419 opt.setDefault = dispatchSetDefault<OptType>;
420
421 addOption(opt);
422 }
423
424 class CommandLine
425 {
426 public:
CommandLine(void)427 CommandLine(void)
428 {
429 }
~CommandLine(void)430 ~CommandLine(void)
431 {
432 }
433
434 void clear(void);
435
getOptions(void) const436 const TypedFieldMap &getOptions(void) const
437 {
438 return m_options;
439 }
getArgs(void) const440 const vector<string> &getArgs(void) const
441 {
442 return m_args;
443 }
444
445 template <typename Option>
hasOption(void) const446 bool hasOption(void) const
447 {
448 return m_options.contains<Option>();
449 }
450
451 template <typename Option>
getOption(void) const452 const typename TypedFieldTraits<Option>::ValueType &getOption(void) const
453 {
454 return m_options.get<Option>();
455 }
456
457 bool helpSpecified(void) const;
458
459 private:
460 TypedFieldMap m_options;
461 vector<string> m_args;
462
463 friend class Parser;
464 };
465
466 } // namespace detail
467
468 using detail::CommandLine;
469 using detail::Parser;
470
471 void selfTest(void);
472
473 } // namespace cmdline
474 } // namespace de
475
476 #define DE_DECLARE_COMMAND_LINE_OPT(NAME, TYPE) \
477 struct NAME \
478 { \
479 typedef TYPE ValueType; \
480 }
481
482 #endif // _DECOMMANDLINE_HPP
483