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
35namespace de
36{
37namespace cmdline
38{
39
40//! Default parsing function
41template<typename ValueType>
42void parseType (const char* src, ValueType* dst);
43
44template<typename T>
45struct NamedValue
46{
47	const char*	name;
48	T			value;
49};
50
51template<typename OptName>
52struct 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).
69	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.
81	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.
93	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>
106	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
118template<class Option>
119struct OptTraits
120{
121	typedef typename Option::ValueType ValueType;
122};
123
124//! Default value lookup
125template<typename ValueType>
126inline void getTypeDefault (ValueType* dst)
127{
128	*dst = ValueType();
129}
130
131template<> void getTypeDefault<bool> (bool* dst);
132
133template<typename T>	inline bool isBoolean		(void) { return false;	}
134template<>				inline bool isBoolean<bool>	(void) { return true;	}
135
136//! Is argument boolean-only value?
137template<class Option>	inline bool isBooleanOpt	(void) { return isBoolean<typename OptTraits<Option>::ValueType>(); }
138
139namespace detail
140{
141
142using std::string;
143using std::vector;
144using std::map;
145
146// TypedFieldMap implementation
147
148template<class Name>
149struct TypedFieldTraits
150{
151	// Generic implementation for cmdline.
152	typedef typename OptTraits<Name>::ValueType	ValueType;
153};
154
155template<class Value>
156struct TypedFieldValueTraits
157{
158	static void destroy (void* value) { delete (Value*)value; }
159};
160
161class TypedFieldMap
162{
163public:
164						TypedFieldMap			(void);
165						~TypedFieldMap			(void);
166
167	bool				empty					(void) const	{ return m_fields.empty();	}
168	void				clear					(void);
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
183private:
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
194		Entry (void) : value(DE_NULL), destructor(0) {}
195		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
207template<typename Name>
208inline void TypedFieldMap::set (typename TypedFieldTraits<Name>::ValueType* value)
209{
210	set(&typeid(Name), Entry(value, &TypedFieldValueTraits<typename TypedFieldTraits<Name>::ValueType>::destroy));
211}
212
213template<typename Name>
214void 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
229template<typename Name>
230inline bool TypedFieldMap::contains (void) const
231{
232	return contains(&typeid(Name));
233}
234
235template<typename Name>
236inline const typename TypedFieldTraits<Name>::ValueType& TypedFieldMap::get (void) const
237{
238	return *static_cast<typename TypedFieldTraits<Name>::ValueType*>(get(&typeid(Name)).value);
239}
240
241class CommandLine;
242
243typedef void (*GenericParseFunc) (const char* src, void* dst);
244
245class Parser
246{
247public:
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
258private:
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		(*SetDefaultFunc)			(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		SetDefaultFunc			setDefault;
283
284		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			, setDefault		(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
307template<class OptType>
308inline 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.
315const void* findNamedValueMatch (const char* src, const void* namedValues, const void* namedValuesEnd, size_t stride);
316
317template<typename OptType>
318void 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
342template<typename OptType>
343void dispatchSetDefault (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
358template<typename OptType>
359const 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
365template<typename OptType>
366void 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
372template<class OptType>
373void 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
388	if (opt.isFlag)
389		opt.setDefault		= dispatchSetDefault<OptType>;
390
391	addOption(opt);
392}
393
394class CommandLine
395{
396public:
397								CommandLine		(void) {}
398								~CommandLine	(void) {}
399
400	void						clear			(void);
401
402	const TypedFieldMap&		getOptions		(void) const	{ return m_options;	}
403	const vector<string>&		getArgs			(void) const	{ return m_args;	}
404
405	template<typename Option>
406	bool						hasOption		(void) const	{ return m_options.contains<Option>();	}
407
408	template<typename Option>
409	const typename TypedFieldTraits<Option>::ValueType&
410								getOption		(void) const	{ return m_options.get<Option>();		}
411
412private:
413	TypedFieldMap				m_options;
414	vector<string>				m_args;
415
416	friend class Parser;
417};
418
419} // detail
420
421using detail::Parser;
422using detail::CommandLine;
423
424void selfTest (void);
425
426} // cmdline
427} // de
428
429#define DE_DECLARE_COMMAND_LINE_OPT(NAME, TYPE) struct NAME { typedef TYPE ValueType; }
430
431#endif // _DECOMMANDLINE_HPP
432