blob: 50bfca549012ac39f395a84e90c837c2d63cdd90 [file] [log] [blame]
#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