You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
432 lines
11 KiB
432 lines
11 KiB
#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
|