#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 #include #include #include #include #include namespace de { namespace cmdline { //! Default parsing function template void parseType (const char* src, ValueType* dst); template struct NamedValue { const char* name; T value; }; template 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* namedValues; //!< Named values or null. const NamedValue* 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) , 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* namedValues_, const NamedValue* 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 Option (const char* shortName_, const char* longName_, const char* description_, const NamedValue (&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 struct OptTraits { typedef typename Option::ValueType ValueType; }; //! Default value lookup template inline void getTypeDefault (ValueType* dst) { *dst = ValueType(); } template<> void getTypeDefault (bool* dst); template inline bool isBoolean (void) { return false; } template<> inline bool isBoolean (void) { return true; } //! Is argument boolean-only value? template inline bool isBooleanOpt (void) { return isBoolean::ValueType>(); } namespace detail { using std::string; using std::vector; using std::map; // TypedFieldMap implementation template struct TypedFieldTraits { // Generic implementation for cmdline. typedef typename OptTraits::ValueType ValueType; }; template 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 void set (typename TypedFieldTraits::ValueType* value); template void set (const typename TypedFieldTraits::ValueType& value); template bool contains (void) const; template const typename TypedFieldTraits::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 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 inline void TypedFieldMap::set (typename TypedFieldTraits::ValueType* value) { set(&typeid(Name), Entry(value, &TypedFieldValueTraits::ValueType>::destroy)); } template void TypedFieldMap::set (const typename TypedFieldTraits::ValueType& value) { typename TypedFieldTraits::ValueType* copy = new typename TypedFieldTraits::ValueType(value); try { set(copy); } catch (...) { delete copy; throw; } } template inline bool TypedFieldMap::contains (void) const { return contains(&typeid(Name)); } template inline const typename TypedFieldTraits::ValueType& TypedFieldMap::get (void) const { return *static_cast::ValueType*>(get(&typeid(Name)).value); } class CommandLine; typedef void (*GenericParseFunc) (const char* src, void* dst); class Parser { public: Parser (void); ~Parser (void); template void addOption (const Option& 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 static void dispatchParse (const OptInfo* info, const char* src, TypedFieldMap* dst); vector m_options; }; template inline Parser& operator<< (Parser& parser, const Option& 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 void Parser::dispatchParse (const OptInfo* info, const char* src, TypedFieldMap* dst) { typename OptTraits::ValueType* value = new typename OptTraits::ValueType(); try { DE_ASSERT((!!info->parse) != (!!info->namedValues)); if (info->parse) { ((typename Option::ParseFunc)(info->parse))(src, value); } else { const void* match = findNamedValueMatch(src, info->namedValues, info->namedValuesEnd, info->namedValueStride); *value = static_cast::ValueType>*>(match)->value; } dst->set(value); } catch (...) { delete value; throw; } } template void dispatchSetDefault (TypedFieldMap* dst) { typename OptTraits::ValueType* value = new typename OptTraits::ValueType(); try { getTypeDefault::ValueType>(value); dst->set(value); } catch (...) { delete value; throw; } } template const char* getNamedValueName (const void* value) { const NamedValue::ValueType>* typedVal = static_cast::ValueType> >(value); return typedVal->name; } template void setFromNamedValue (const void* value, TypedFieldMap* dst) { const NamedValue::ValueType>* typedVal = static_cast::ValueType> >(value); dst->set(typedVal->value); } template void Parser::addOption (const Option& option) { OptInfo opt; opt.shortName = option.shortName; opt.longName = option.longName; opt.description = option.description; opt.defaultValue = option.defaultValue; opt.isFlag = isBooleanOpt() && !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; if (opt.isFlag) opt.setDefault = dispatchSetDefault; addOption(opt); } class CommandLine { public: CommandLine (void) {} ~CommandLine (void) {} void clear (void); const TypedFieldMap& getOptions (void) const { return m_options; } const vector& getArgs (void) const { return m_args; } template bool hasOption (void) const { return m_options.contains