Michael Uleysky
8 years ago
1 changed files with 297 additions and 0 deletions
@ -0,0 +1,297 @@ |
|||||||
|
#ifndef MODGMT_PARAM_H |
||||||
|
#define MODGMT_PARAM_H |
||||||
|
#include "modgmt_strcomp.h" |
||||||
|
#include "common.h" |
||||||
|
|
||||||
|
// Common ancestor for all parameters
|
||||||
|
template<class Converter, bool Optional> |
||||||
|
class Parameter |
||||||
|
{ |
||||||
|
using ValueType=typename Converter::ValueType; |
||||||
|
std::string name; // Used for error reporting
|
||||||
|
bool initialised; |
||||||
|
Converter conv; |
||||||
|
ValueType val; |
||||||
|
|
||||||
|
template<bool hasDefault, class Conv=Converter> |
||||||
|
struct Adapter; |
||||||
|
template<class Conv> |
||||||
|
struct Adapter<true,Conv> {static const typename Conv::ValueType& Val(const Conv& cnv) {return cnv.Default();}}; |
||||||
|
template<class Conv> |
||||||
|
struct Adapter<false,Conv> {static typename Conv::ValueType&& Val(const Conv& cnv) {return std::move(typename Conv::ValueType());}}; |
||||||
|
|
||||||
|
template<class C> |
||||||
|
struct CheckDefault |
||||||
|
{ |
||||||
|
private: |
||||||
|
static void detect(...); |
||||||
|
template<class T> static decltype(std::declval<T>().Default()) detect(T); |
||||||
|
public: |
||||||
|
static constexpr bool value=std::is_same<ValueType, typename std::decay<decltype(detect(std::declval<C>()))>::type>::value; |
||||||
|
}; |
||||||
|
|
||||||
|
protected: |
||||||
|
Parameter(Parameter&&) = delete; |
||||||
|
Parameter(const Parameter&) = delete; |
||||||
|
Parameter() = delete; |
||||||
|
|
||||||
|
template<class... Args> |
||||||
|
Parameter(std::string&& str, Args... args): name(std::move(str)), initialised(false), conv(args...) |
||||||
|
{ |
||||||
|
val=Adapter<CheckDefault<Converter>::value>::Val(conv); |
||||||
|
} |
||||||
|
template<class... Args> |
||||||
|
Parameter(const std::string& str, Args... args): name(str), initialised(false), conv(args...) |
||||||
|
{ |
||||||
|
val=Adapter<CheckDefault<Converter>::value>::Val(conv); |
||||||
|
} |
||||||
|
|
||||||
|
void SetState(bool newini) {initialised=newini;} |
||||||
|
|
||||||
|
public: |
||||||
|
bool Init(const ObjectBase* p, std::string& err) |
||||||
|
{ |
||||||
|
std::string cerr; |
||||||
|
bool res=true; |
||||||
|
ValueType tval=conv.Convert(p,&res,cerr); |
||||||
|
SetState(res); |
||||||
|
if(res) val=std::move(tval); |
||||||
|
else err=std::move(cerr); |
||||||
|
return res; |
||||||
|
} |
||||||
|
bool Initialised() const {return initialised;} |
||||||
|
bool Exist() const {return initialised;} |
||||||
|
const std::string& Name() const {return name;} |
||||||
|
constexpr bool isOptional() const {return Optional;} |
||||||
|
const ValueType& Value() {return val;} |
||||||
|
const ValueType* operator->() const {return &val;} |
||||||
|
operator const ValueType&() {return val;} |
||||||
|
void Reset() {initialised=false; val=std::move(Adapter<CheckDefault<Converter>::value>::Val(conv));} |
||||||
|
|
||||||
|
static constexpr bool optional=Optional; |
||||||
|
using ConverterType=Converter; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
// Class for parameter which must be in named pair
|
||||||
|
template <class Converter, bool O> |
||||||
|
class NamedParameter: public Parameter<Converter,O>, public TemplateComparator |
||||||
|
{ |
||||||
|
public: |
||||||
|
using AcceptableObject=void; |
||||||
|
template<class... Args> |
||||||
|
NamedParameter(const std::string& t, Args... args):Parameter<Converter,O>(t,args...),TemplateComparator(t) {} |
||||||
|
template<class... Args> |
||||||
|
NamedParameter(std::pair<std::string,std::string>&& t, Args... args):Parameter<Converter,O>(std::move(t.first),args...),TemplateComparator(std::move(t.second)) {} |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
// Class for parameter which can be in named pair or as object of type Object
|
||||||
|
template <class Converter, bool O, class Object> |
||||||
|
class NamedFreeParameter: public NamedParameter<Converter,O> |
||||||
|
{ |
||||||
|
public: |
||||||
|
using AcceptableObject=Object; |
||||||
|
template<class... Args> |
||||||
|
NamedFreeParameter(Args... args):NamedParameter<Converter,O>(args...) {} |
||||||
|
}; |
||||||
|
|
||||||
|
// Class for parameter which can be in some positions in list
|
||||||
|
template <class Converter, bool O> |
||||||
|
class PositionalParameter: public Parameter<Converter,O> |
||||||
|
{ |
||||||
|
public: |
||||||
|
template<class... Args> |
||||||
|
PositionalParameter(Args... args):Parameter<Converter,O>(args...) {} |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
// Base class for ParseNamedParameters and ParsePositionalParameters
|
||||||
|
class ParseParameters |
||||||
|
{ |
||||||
|
protected: |
||||||
|
std::string err; |
||||||
|
ParseParameters():err() {} |
||||||
|
ParseParameters(const ParseParameters&) = delete; |
||||||
|
ParseParameters(ParseParameters&&) = delete; |
||||||
|
|
||||||
|
public: |
||||||
|
std::string Error() const {return err;} |
||||||
|
bool Ok() const {return err.empty();} |
||||||
|
operator bool() const {return Ok();} |
||||||
|
}; |
||||||
|
|
||||||
|
// Parsing positional parameters
|
||||||
|
class ParsePositionalParameters: public ParseParameters |
||||||
|
{ |
||||||
|
ParsePositionalParameters() = delete; |
||||||
|
ParsePositionalParameters(const ParsePositionalParameters&) = delete; |
||||||
|
ParsePositionalParameters(ParsePositionalParameters&&) = delete; |
||||||
|
|
||||||
|
// Main parsing function
|
||||||
|
template <class Par, class... Args> |
||||||
|
void Parse(ObjectList::IndexType i, const ObjectList* ol, Par& param, Args&... args) |
||||||
|
{ |
||||||
|
// Check types of arguments
|
||||||
|
static_assert(std::is_same<PositionalParameter<typename Par::ConverterType, Par::optional>,Par>::value,"ParsePositionalParameters argument(s) must be PositionalParameter"); |
||||||
|
|
||||||
|
// Check if parameter already initialised. This is code error.
|
||||||
|
if(param.Initialised()) |
||||||
|
{ |
||||||
|
err="Parameter "+param.Name()+" already initialised. This is code error."; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// List is ended
|
||||||
|
if(i>=ol->Size()) |
||||||
|
{ |
||||||
|
// Parameter is optional, skip it
|
||||||
|
if(Par::optional) Parse(i,ol,args...); |
||||||
|
// Parameter is required, this is an error
|
||||||
|
else err="Parameter "+param.Name()+" is required, but can't be setted because list is ended"; |
||||||
|
} |
||||||
|
// Initialise from some list element
|
||||||
|
else |
||||||
|
{ |
||||||
|
bool res=param.Init(ol->At(i),err); |
||||||
|
// All Ok, continue to next element in list
|
||||||
|
if(res) Parse(i+1,ol,args...); |
||||||
|
else |
||||||
|
{ |
||||||
|
// All Ok, optional parameter may be absent, try to initialise next parameter by same list element
|
||||||
|
if(Par::optional) Parse(i,ol,args...); |
||||||
|
// Error, required parameter not initialised
|
||||||
|
else err="Can't initialise parameter "+param.Name()+" from list element number "+ToString(i)+": "+err; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
// Bottom of recursion
|
||||||
|
void Parse(ObjectList::IndexType i, const ObjectList* ol) {if(i<ol->Size()) err="There are excess elements in list";} |
||||||
|
|
||||||
|
public: |
||||||
|
template <class... Args> |
||||||
|
ParsePositionalParameters(const ObjectList* ol, Args&... args) {Parse(0,ol,args...);} |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
// Parsing named parameters
|
||||||
|
class ParseNamedParameters: public ParseParameters |
||||||
|
{ |
||||||
|
ParseNamedParameters() = delete; |
||||||
|
ParseNamedParameters(const ParseNamedParameters&) = delete; |
||||||
|
ParseNamedParameters(ParseNamedParameters&&) = delete; |
||||||
|
|
||||||
|
// Parsing function for elements without names
|
||||||
|
// ob - object from which we initialise parameter param.
|
||||||
|
// init - is ob was already used for initialise something.
|
||||||
|
// args - other parameters
|
||||||
|
// Function try to initialise parameter param, if param accepted real type of ob for initialisation, else function try to initialise next parameter in args.
|
||||||
|
// Function return error if initialisation of parameter failed or if two parameters can be initialised from ob.
|
||||||
|
template <class Par, class... Args> |
||||||
|
bool ParseSingle(const ObjectBase* ob, bool init, Par& param, Args&... args) |
||||||
|
{ |
||||||
|
// Check types of arguments
|
||||||
|
static_assert(std::is_same<NamedParameter<typename Par::ConverterType, Par::optional>,Par>::value || std::is_same<NamedFreeParameter<typename Par::ConverterType, Par::optional, typename Par::AcceptableObject>,Par>::value,"ParseNamedParameters argument(s) must be NamedParameter or NamedFreeParameter"); |
||||||
|
|
||||||
|
OBType<typename Par::AcceptableObject> o(ob); |
||||||
|
if(o && init) |
||||||
|
{ |
||||||
|
err="Object of type "+ob->Type()+" used for initialisation of two parameters. This is code error."; |
||||||
|
return false; |
||||||
|
} |
||||||
|
if(!o) return ParseSingle(ob,init,args...); // Type mismatch, goto next parameter
|
||||||
|
if(param.Initialised()) |
||||||
|
{ |
||||||
|
err="Parameter "+param.Name()+" can't be initialised from object of type "+ob->Type()+" because it already initialised."; |
||||||
|
return false; |
||||||
|
} |
||||||
|
std::string ierr; |
||||||
|
if(!param.Init(ob,ierr)) |
||||||
|
{ |
||||||
|
err="Parameter "+param.Name()+" can't be initialised from object of type "+ob->Type()+": "+ierr; |
||||||
|
return false; |
||||||
|
} |
||||||
|
return ParseSingle(ob,true,args...); |
||||||
|
} |
||||||
|
// Bottom of recursion
|
||||||
|
bool ParseSingle(const ObjectBase* ob, bool init) const {return true;} |
||||||
|
|
||||||
|
// Parsing function for elements in pairs
|
||||||
|
// op - pair from which we initialise parameter param.
|
||||||
|
// pname - name of parameter already initialised from op (or empty string).
|
||||||
|
// args - other parameters
|
||||||
|
// Function try to initialise parameter param, if pair name corresponding to param template, else function try to initialise next parameter in args.
|
||||||
|
// Function return error if initialisation of parameter failed or if two parameters can be initialised from op.
|
||||||
|
template <class Par, class... Args> |
||||||
|
bool ParsePair(const ObjectPair* op, std::string& pname, Par& param, Args&... args) |
||||||
|
{ |
||||||
|
// Check types of arguments
|
||||||
|
static_assert(std::is_same<NamedParameter<typename Par::ConverterType, Par::optional>,Par>::value || std::is_same<NamedFreeParameter<typename Par::ConverterType, Par::optional, typename Par::AcceptableObject>,Par>::value,"ParseNamedParameters argument(s) must be NamedParameter or NamedFreeParameter"); |
||||||
|
|
||||||
|
bool cmp=param.Compare(op->Name()); |
||||||
|
if(cmp && !pname.empty()) |
||||||
|
{ |
||||||
|
err="Element "+op->Name()+" can be used for initialisation of two parameters: "+pname+" and "+param.Name(); |
||||||
|
return false; |
||||||
|
} |
||||||
|
if(!cmp) return ParsePair(op,pname,args...); // Name mismatch, goto next parameter
|
||||||
|
pname=param.Name(); |
||||||
|
if(param.Initialised()) |
||||||
|
{ |
||||||
|
err="Parameter "+param.Name()+" can't be initialised from element "+op->Name()+" because it already initialised."; |
||||||
|
return false; |
||||||
|
} |
||||||
|
std::string ierr; |
||||||
|
if(!param.Init(op->Value(),ierr)) |
||||||
|
{ |
||||||
|
err="Parameter "+param.Name()+" can't be initialised from element "+op->Name()+": "+ierr; |
||||||
|
return false; |
||||||
|
} |
||||||
|
return ParsePair(op,pname,args...); |
||||||
|
} |
||||||
|
// Bottom of recursion
|
||||||
|
bool ParsePair(const ObjectPair* op, std::string& pname) const {return true;} |
||||||
|
|
||||||
|
template <class Par, class... Args> |
||||||
|
void CheckRequired(const Par& param, Args&... args) |
||||||
|
{ |
||||||
|
if((Par::optional || param.Initialised())) CheckRequired(args...); |
||||||
|
else err="Parameter "+param.Name()+" is required, but not initialised"; |
||||||
|
} |
||||||
|
// Bottom of recursion
|
||||||
|
void CheckRequired() const {} |
||||||
|
|
||||||
|
public: |
||||||
|
template <class... Args> |
||||||
|
ParseNamedParameters(const ObjectList* ol, Args&... args) |
||||||
|
{ |
||||||
|
// Initialisation
|
||||||
|
std::string pname; |
||||||
|
for(ObjectList::const_iterator i=ol->begin();i!=ol->end();++i) |
||||||
|
{ |
||||||
|
pname.erase(); |
||||||
|
OBType<ObjectPair> p(*i); |
||||||
|
if( !(p?ParsePair(p,pname,args...):ParseSingle(*i,false,args...)) ) break; |
||||||
|
} |
||||||
|
if(!Ok()) return; // Error on initialisation phase
|
||||||
|
// Check if all required parameters are initialised
|
||||||
|
CheckRequired(args...); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
template<class Converter> |
||||||
|
using ONPar=NamedParameter<Converter,true>; |
||||||
|
template<class Converter> |
||||||
|
using RNPar=NamedParameter<Converter,false>; |
||||||
|
|
||||||
|
template<class Converter, class Object> |
||||||
|
using ONFPar=NamedFreeParameter<Converter,true,Object>; |
||||||
|
template<class Converter, class Object> |
||||||
|
using RNFPar=NamedFreeParameter<Converter,false,Object>; |
||||||
|
|
||||||
|
template<class Converter> |
||||||
|
using OPosPar=PositionalParameter<Converter,true>; |
||||||
|
template<class Converter> |
||||||
|
using RPosPar=PositionalParameter<Converter,false>; |
||||||
|
|
||||||
|
#endif |
Loading…
Reference in new issue