#ifndef _DECOMMANDLINE_HPP #define _DECOMMANDLINE_HPP /*------------------------------------------------------------------------- * drawElements C++ Base Library * ----------------------------- * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Command line parser. *//*--------------------------------------------------------------------*/ #include "deDefs.hpp" #include <map> #include <string> #include <vector> #include <ostream> #include <typeinfo> #include <stdexcept> namespace de { namespace cmdline { //! Default parsing function template<typename ValueType> void parseType (const char* src, ValueType* dst); template<typename T> struct NamedValue { const char* name; T value; }; template<typename OptName> struct Option { typedef typename OptName::ValueType ValueType; typedef void (*ParseFunc) (const char* src, ValueType* dst); // \note All assumed to point to static memory. const char* shortName; const char* longName; const char* description; const char* defaultValue; //!< Default value (parsed from string), or null if should not be set // \note Either parse or namedValues must be null. ParseFunc parse; //!< Custom parsing function or null. const NamedValue<ValueType>* namedValues; //!< Named values or null. const NamedValue<ValueType>* namedValuesEnd; //!< Named value list end. //! Construct generic option (string, int, boolean). Option (const char* shortName_, const char* longName_, const char* description_, const char* defaultValue_ = DE_NULL) : shortName (shortName_) , longName (longName_) , description (description_) , defaultValue (defaultValue_) , parse (parseType<ValueType>) , namedValues (DE_NULL) , namedValuesEnd(0) { } //! Option with custom parsing function. Option (const char* shortName_, const char* longName_, const char* description_, ParseFunc parse_, const char* defaultValue_ = DE_NULL) : shortName (shortName_) , longName (longName_) , description (description_) , defaultValue (defaultValue_) , parse (parse_) , namedValues (DE_NULL) , namedValuesEnd(DE_NULL) { } //! Option that uses named values. Option (const char* shortName_, const char* longName_, const char* description_, const NamedValue<ValueType>* namedValues_, const NamedValue<ValueType>* namedValuesEnd_, const char* defaultValue_ = DE_NULL) : shortName (shortName_) , longName (longName_) , description (description_) , defaultValue (defaultValue_) , parse ((ParseFunc)DE_NULL) , namedValues (namedValues_) , namedValuesEnd(namedValuesEnd_) { } //! Option that uses named values. template<size_t NumNamedValues> Option (const char* shortName_, const char* longName_, const char* description_, const NamedValue<ValueType> (&namedValues_)[NumNamedValues], const char* defaultValue_ = DE_NULL) : shortName (shortName_) , longName (longName_) , description (description_) , defaultValue (defaultValue_) , parse ((ParseFunc)DE_NULL) , namedValues (DE_ARRAY_BEGIN(namedValues_)) , namedValuesEnd(DE_ARRAY_END(namedValues_)) { } }; template<class Option> struct OptTraits { typedef typename Option::ValueType ValueType; }; //! Default value lookup template<typename ValueType> inline void getTypeDefault (ValueType* dst) { *dst = ValueType(); } template<> void getTypeDefault<bool> (bool* dst); template<typename T> inline bool isBoolean (void) { return false; } template<> inline bool isBoolean<bool> (void) { return true; } //! Is argument boolean-only value? template<class Option> inline bool isBooleanOpt (void) { return isBoolean<typename OptTraits<Option>::ValueType>(); } namespace detail { using std::string; using std::vector; using std::map; // TypedFieldMap implementation template<class Name> struct TypedFieldTraits { // Generic implementation for cmdline. typedef typename OptTraits<Name>::ValueType ValueType; }; template<class Value> struct TypedFieldValueTraits { static void destroy (void* value) { delete (Value*)value; } }; class TypedFieldMap { public: TypedFieldMap (void); ~TypedFieldMap (void); bool empty (void) const { return m_fields.empty(); } void clear (void); template<typename Name> void set (typename TypedFieldTraits<Name>::ValueType* value); template<typename Name> void set (const typename TypedFieldTraits<Name>::ValueType& value); template<typename Name> bool contains (void) const; template<typename Name> const typename TypedFieldTraits<Name>::ValueType& get (void) const; private: TypedFieldMap (const TypedFieldMap&); TypedFieldMap& operator= (const TypedFieldMap&); typedef void (*DestroyFunc) (void*); struct Entry { void* value; DestroyFunc destructor; Entry (void) : value(DE_NULL), destructor(0) {} Entry (void* value_, DestroyFunc destructor_) : value(value_), destructor(destructor_) {} }; typedef std::map<const std::type_info*, Entry> Map; bool contains (const std::type_info* key) const; const Entry& get (const std::type_info* key) const; void set (const std::type_info* key, const Entry& value); Map m_fields; }; template<typename Name> inline void TypedFieldMap::set (typename TypedFieldTraits<Name>::ValueType* value) { set(&typeid(Name), Entry(value, &TypedFieldValueTraits<typename TypedFieldTraits<Name>::ValueType>::destroy)); } template<typename Name> void TypedFieldMap::set (const typename TypedFieldTraits<Name>::ValueType& value) { typename TypedFieldTraits<Name>::ValueType* copy = new typename TypedFieldTraits<Name>::ValueType(value); try { set<Name>(copy); } catch (...) { delete copy; throw; } } template<typename Name> inline bool TypedFieldMap::contains (void) const { return contains(&typeid(Name)); } template<typename Name> inline const typename TypedFieldTraits<Name>::ValueType& TypedFieldMap::get (void) const { return *static_cast<typename TypedFieldTraits<Name>::ValueType*>(get(&typeid(Name)).value); } class CommandLine; typedef void (*GenericParseFunc) (const char* src, void* dst); class Parser { public: Parser (void); ~Parser (void); template<class OptType> void addOption (const Option<OptType>& option); bool parse (int numArgs, const char* const* args, CommandLine* dst, std::ostream& err) const; void help (std::ostream& dst) const; private: Parser (const Parser&); Parser& operator= (const Parser&); struct OptInfo; typedef void (*DispatchParseFunc) (const OptInfo* info, const char* src, TypedFieldMap* dst); typedef void (*SetDefaultFunc) (TypedFieldMap* dst); struct OptInfo { const char* shortName; const char* longName; const char* description; const char* defaultValue; bool isFlag; //!< Set true for bool typed arguments that do not used named values. GenericParseFunc parse; const void* namedValues; const void* namedValuesEnd; size_t namedValueStride; DispatchParseFunc dispatchParse; SetDefaultFunc setDefault; OptInfo (void) : shortName (DE_NULL) , longName (DE_NULL) , description (DE_NULL) , defaultValue (DE_NULL) , isFlag (false) , parse (DE_NULL) , namedValues (DE_NULL) , namedValuesEnd (DE_NULL) , namedValueStride (0) , dispatchParse (DE_NULL) , setDefault (DE_NULL) {} }; void addOption (const OptInfo& option); template<typename OptName> static void dispatchParse (const OptInfo* info, const char* src, TypedFieldMap* dst); vector<OptInfo> m_options; }; template<class OptType> inline Parser& operator<< (Parser& parser, const Option<OptType>& option) { parser.addOption(option); return parser; } //! Find match by name. Throws exception if no match is found. const void* findNamedValueMatch (const char* src, const void* namedValues, const void* namedValuesEnd, size_t stride); template<typename OptType> void Parser::dispatchParse (const OptInfo* info, const char* src, TypedFieldMap* dst) { typename OptTraits<OptType>::ValueType* value = new typename OptTraits<OptType>::ValueType(); try { DE_ASSERT((!!info->parse) != (!!info->namedValues)); if (info->parse) { ((typename Option<OptType>::ParseFunc)(info->parse))(src, value); } else { const void* match = findNamedValueMatch(src, info->namedValues, info->namedValuesEnd, info->namedValueStride); *value = static_cast<const NamedValue<typename OptTraits<OptType>::ValueType>*>(match)->value; } dst->set<OptType>(value); } catch (...) { delete value; throw; } } template<typename OptType> void dispatchSetDefault (TypedFieldMap* dst) { typename OptTraits<OptType>::ValueType* value = new typename OptTraits<OptType>::ValueType(); try { getTypeDefault<typename OptTraits<OptType>::ValueType>(value); dst->set<OptType>(value); } catch (...) { delete value; throw; } } template<typename OptType> const char* getNamedValueName (const void* value) { const NamedValue<typename OptTraits<OptType>::ValueType>* typedVal = static_cast<const NamedValue<typename OptTraits<OptType>::ValueType> >(value); return typedVal->name; } template<typename OptType> void setFromNamedValue (const void* value, TypedFieldMap* dst) { const NamedValue<typename OptTraits<OptType>::ValueType>* typedVal = static_cast<const NamedValue<typename OptTraits<OptType>::ValueType> >(value); dst->set<OptType>(typedVal->value); } template<class OptType> void Parser::addOption (const Option<OptType>& option) { OptInfo opt; opt.shortName = option.shortName; opt.longName = option.longName; opt.description = option.description; opt.defaultValue = option.defaultValue; opt.isFlag = isBooleanOpt<OptType>() && !option.namedValues; opt.parse = (GenericParseFunc)option.parse; opt.namedValues = (const void*)option.namedValues; opt.namedValuesEnd = (const void*)option.namedValuesEnd; opt.namedValueStride = sizeof(*option.namedValues); opt.dispatchParse = dispatchParse<OptType>; if (opt.isFlag) opt.setDefault = dispatchSetDefault<OptType>; addOption(opt); } class CommandLine { public: CommandLine (void) {} ~CommandLine (void) {} void clear (void); const TypedFieldMap& getOptions (void) const { return m_options; } const vector<string>& getArgs (void) const { return m_args; } template<typename Option> bool hasOption (void) const { return m_options.contains<Option>(); } template<typename Option> const typename TypedFieldTraits<Option>::ValueType& getOption (void) const { return m_options.get<Option>(); } private: TypedFieldMap m_options; vector<string> m_args; friend class Parser; }; } // detail using detail::Parser; using detail::CommandLine; void selfTest (void); } // cmdline } // de #define DE_DECLARE_COMMAND_LINE_OPT(NAME, TYPE) struct NAME { typedef TYPE ValueType; } #endif // _DECOMMANDLINE_HPP