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 default should be T()
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_, const char* defaultValue_ = DE_NULL)
82 : shortName (shortName_)
83 , longName (longName_)
84 , description (description_)
85 , defaultValue (defaultValue_)
86 , parse (parse_)
87 , namedValues (DE_NULL)
88 , namedValuesEnd(DE_NULL)
89 {
90 }
91
92 //! Option that uses named values.
Optionde::cmdline::Option93 Option (const char* shortName_, const char* longName_, const char* description_, const NamedValue<ValueType>* namedValues_, const NamedValue<ValueType>* namedValuesEnd_, const char* defaultValue_ = DE_NULL)
94 : shortName (shortName_)
95 , longName (longName_)
96 , description (description_)
97 , defaultValue (defaultValue_)
98 , parse ((ParseFunc)DE_NULL)
99 , namedValues (namedValues_)
100 , namedValuesEnd(namedValuesEnd_)
101 {
102 }
103
104 //! Option that uses named values.
105 template<size_t NumNamedValues>
Optionde::cmdline::Option106 Option (const char* shortName_, const char* longName_, const char* description_, const NamedValue<ValueType> (&namedValues_)[NumNamedValues], const char* defaultValue_ = DE_NULL)
107 : shortName (shortName_)
108 , longName (longName_)
109 , description (description_)
110 , defaultValue (defaultValue_)
111 , parse ((ParseFunc)DE_NULL)
112 , namedValues (DE_ARRAY_BEGIN(namedValues_))
113 , namedValuesEnd(DE_ARRAY_END(namedValues_))
114 {
115 }
116 };
117
118 template<class Option>
119 struct OptTraits
120 {
121 typedef typename Option::ValueType ValueType;
122 };
123
124 //! Default value lookup
125 template<typename ValueType>
getTypeDefault(ValueType * dst)126 inline void getTypeDefault (ValueType* dst)
127 {
128 *dst = ValueType();
129 }
130
131 template<> void getTypeDefault<bool> (bool* dst);
132
isBoolean(void)133 template<typename T> inline bool isBoolean (void) { return false; }
isBoolean(void)134 template<> inline bool isBoolean<bool> (void) { return true; }
135
136 //! Is argument boolean-only value?
isBooleanOpt(void)137 template<class Option> inline bool isBooleanOpt (void) { return isBoolean<typename OptTraits<Option>::ValueType>(); }
138
139 namespace detail
140 {
141
142 using std::string;
143 using std::vector;
144 using std::map;
145
146 // TypedFieldMap implementation
147
148 template<class Name>
149 struct TypedFieldTraits
150 {
151 // Generic implementation for cmdline.
152 typedef typename OptTraits<Name>::ValueType ValueType;
153 };
154
155 template<class Value>
156 struct TypedFieldValueTraits
157 {
destroyde::cmdline::detail::TypedFieldValueTraits158 static void destroy (void* value) { delete (Value*)value; }
159 };
160
161 class TypedFieldMap
162 {
163 public:
164 TypedFieldMap (void);
165 ~TypedFieldMap (void);
166
empty(void) const167 bool empty (void) const { return m_fields.empty(); }
clear(void)168 void clear (void) { m_fields.clear(); }
169
170 template<typename Name>
171 void set (typename TypedFieldTraits<Name>::ValueType* value);
172
173 template<typename Name>
174 void set (const typename TypedFieldTraits<Name>::ValueType& value);
175
176 template<typename Name>
177 bool contains (void) const;
178
179 template<typename Name>
180 const typename TypedFieldTraits<Name>::ValueType&
181 get (void) const;
182
183 private:
184 TypedFieldMap (const TypedFieldMap&);
185 TypedFieldMap& operator= (const TypedFieldMap&);
186
187 typedef void (*DestroyFunc) (void*);
188
189 struct Entry
190 {
191 void* value;
192 DestroyFunc destructor;
193
Entryde::cmdline::detail::TypedFieldMap::Entry194 Entry (void) : value(DE_NULL), destructor(0) {}
Entryde::cmdline::detail::TypedFieldMap::Entry195 Entry (void* value_, DestroyFunc destructor_) : value(value_), destructor(destructor_) {}
196 };
197
198 typedef std::map<const std::type_info*, Entry> Map;
199
200 bool contains (const std::type_info* key) const;
201 const Entry& get (const std::type_info* key) const;
202 void set (const std::type_info* key, const Entry& value);
203
204 Map m_fields;
205 };
206
207 template<typename Name>
set(typename TypedFieldTraits<Name>::ValueType * value)208 inline void TypedFieldMap::set (typename TypedFieldTraits<Name>::ValueType* value)
209 {
210 set(&typeid(Name), Entry(value, &TypedFieldValueTraits<typename TypedFieldTraits<Name>::ValueType>::destroy));
211 }
212
213 template<typename Name>
set(const typename TypedFieldTraits<Name>::ValueType & value)214 void TypedFieldMap::set (const typename TypedFieldTraits<Name>::ValueType& value)
215 {
216 typename TypedFieldTraits<Name>::ValueType* copy = new typename TypedFieldTraits<Name>::ValueType(value);
217
218 try
219 {
220 set<Name>(copy);
221 }
222 catch (...)
223 {
224 delete copy;
225 throw;
226 }
227 }
228
229 template<typename Name>
contains(void) const230 inline bool TypedFieldMap::contains (void) const
231 {
232 return contains(&typeid(Name));
233 }
234
235 template<typename Name>
get(void) const236 inline const typename TypedFieldTraits<Name>::ValueType& TypedFieldMap::get (void) const
237 {
238 return *static_cast<typename TypedFieldTraits<Name>::ValueType*>(get(&typeid(Name)).value);
239 }
240
241 class CommandLine;
242
243 typedef void (*GenericParseFunc) (const char* src, void* dst);
244
245 class Parser
246 {
247 public:
248 Parser (void);
249 ~Parser (void);
250
251 template<class OptType>
252 void addOption (const Option<OptType>& option);
253
254 bool parse (int numArgs, const char* const* args, CommandLine* dst, std::ostream& err) const;
255
256 void help (std::ostream& dst) const;
257
258 private:
259 Parser (const Parser&);
260 Parser& operator= (const Parser&);
261
262 struct OptInfo;
263
264 typedef void (*DispatchParseFunc) (const OptInfo* info, const char* src, TypedFieldMap* dst);
265 typedef void (*GetDefaultFunc) (TypedFieldMap* dst);
266
267 struct OptInfo
268 {
269 const char* shortName;
270 const char* longName;
271 const char* description;
272 const char* defaultValue;
273 bool isFlag; //!< Set true for bool typed arguments that do not used named values.
274
275 GenericParseFunc parse;
276
277 const void* namedValues;
278 const void* namedValuesEnd;
279 size_t namedValueStride;
280
281 DispatchParseFunc dispatchParse;
282 GetDefaultFunc getDefault;
283
OptInfode::cmdline::detail::Parser::OptInfo284 OptInfo (void)
285 : shortName (DE_NULL)
286 , longName (DE_NULL)
287 , description (DE_NULL)
288 , defaultValue (DE_NULL)
289 , isFlag (false)
290 , parse (DE_NULL)
291 , namedValues (DE_NULL)
292 , namedValuesEnd (DE_NULL)
293 , namedValueStride (0)
294 , dispatchParse (DE_NULL)
295 , getDefault (DE_NULL)
296 {}
297 };
298
299 void addOption (const OptInfo& option);
300
301 template<typename OptName>
302 static void dispatchParse (const OptInfo* info, const char* src, TypedFieldMap* dst);
303
304 vector<OptInfo> m_options;
305 };
306
307 template<class OptType>
operator <<(Parser & parser,const Option<OptType> & option)308 inline Parser& operator<< (Parser& parser, const Option<OptType>& option)
309 {
310 parser.addOption(option);
311 return parser;
312 }
313
314 //! Find match by name. Throws exception if no match is found.
315 const void* findNamedValueMatch (const char* src, const void* namedValues, const void* namedValuesEnd, size_t stride);
316
317 template<typename OptType>
dispatchParse(const OptInfo * info,const char * src,TypedFieldMap * dst)318 void Parser::dispatchParse (const OptInfo* info, const char* src, TypedFieldMap* dst)
319 {
320 typename OptTraits<OptType>::ValueType* value = new typename OptTraits<OptType>::ValueType();
321 try
322 {
323 DE_ASSERT((!!info->parse) != (!!info->namedValues));
324 if (info->parse)
325 {
326 ((typename Option<OptType>::ParseFunc)(info->parse))(src, value);
327 }
328 else
329 {
330 const void* match = findNamedValueMatch(src, info->namedValues, info->namedValuesEnd, info->namedValueStride);
331 *value = static_cast<const NamedValue<typename OptTraits<OptType>::ValueType>*>(match)->value;
332 }
333 dst->set<OptType>(value);
334 }
335 catch (...)
336 {
337 delete value;
338 throw;
339 }
340 }
341
342 template<typename OptType>
dispatchGetDefault(TypedFieldMap * dst)343 void dispatchGetDefault (TypedFieldMap* dst)
344 {
345 typename OptTraits<OptType>::ValueType* value = new typename OptTraits<OptType>::ValueType();
346 try
347 {
348 getTypeDefault<typename OptTraits<OptType>::ValueType>(value);
349 dst->set<OptType>(value);
350 }
351 catch (...)
352 {
353 delete value;
354 throw;
355 }
356 }
357
358 template<typename OptType>
getNamedValueName(const void * value)359 const char* getNamedValueName (const void* value)
360 {
361 const NamedValue<typename OptTraits<OptType>::ValueType>* typedVal = static_cast<const NamedValue<typename OptTraits<OptType>::ValueType> >(value);
362 return typedVal->name;
363 }
364
365 template<typename OptType>
setFromNamedValue(const void * value,TypedFieldMap * dst)366 void setFromNamedValue (const void* value, TypedFieldMap* dst)
367 {
368 const NamedValue<typename OptTraits<OptType>::ValueType>* typedVal = static_cast<const NamedValue<typename OptTraits<OptType>::ValueType> >(value);
369 dst->set<OptType>(typedVal->value);
370 }
371
372 template<class OptType>
addOption(const Option<OptType> & option)373 void Parser::addOption (const Option<OptType>& option)
374 {
375 OptInfo opt;
376
377 opt.shortName = option.shortName;
378 opt.longName = option.longName;
379 opt.description = option.description;
380 opt.defaultValue = option.defaultValue;
381 opt.isFlag = isBooleanOpt<OptType>() && !option.namedValues;
382 opt.parse = (GenericParseFunc)option.parse;
383 opt.namedValues = (const void*)option.namedValues;
384 opt.namedValuesEnd = (const void*)option.namedValuesEnd;
385 opt.namedValueStride = sizeof(*option.namedValues);
386 opt.dispatchParse = dispatchParse<OptType>;
387 opt.getDefault = dispatchGetDefault<OptType>;
388
389 addOption(opt);
390 }
391
392 class CommandLine
393 {
394 public:
CommandLine(void)395 CommandLine (void) {}
~CommandLine(void)396 ~CommandLine (void) {}
397
398 void clear (void);
399
getOptions(void) const400 const TypedFieldMap& getOptions (void) const { return m_options; }
getArgs(void) const401 const vector<string>& getArgs (void) const { return m_args; }
402
403 template<typename Option>
404 const typename TypedFieldTraits<Option>::ValueType&
getOption(void) const405 getOption (void) const { return m_options.get<Option>(); }
406
407 private:
408 TypedFieldMap m_options;
409 vector<string> m_args;
410
411 friend class Parser;
412 };
413
414 } // detail
415
416 using detail::Parser;
417 using detail::CommandLine;
418
419 void selfTest (void);
420
421 } // cmdline
422 } // de
423
424 #define DE_DECLARE_COMMAND_LINE_OPT(NAME, TYPE) struct NAME { typedef TYPE ValueType; }
425
426 #endif // _DECOMMANDLINE_HPP
427