#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::map; using std::string; using std::vector; // 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