| #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 |