297 lines
10 KiB

#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 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 TemplateComparator, public Parameter<Converter,O>
{
public:
using AcceptableObject=void;
template<class... Args>
NamedParameter(const std::string& t, Args... args):TemplateComparator(t),Parameter<Converter,O>(Template2Name(),args...) {}
};
// 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, ObjectList::IndexType max, 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>=max)
{
// Parameter is optional, skip it
if(Par::optional) Parse(i,max,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,max,ol,args...);
else
{
// All Ok, optional parameter may be absent, try to initialise next parameter by same list element
if(Par::optional) Parse(i,max,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, ObjectList::IndexType max, const ObjectList* ol) {if(i<max) err="There are excess elements in list";}
public:
template <class Converter, bool optional, class... Args>
ParsePositionalParameters(const ObjectList* ol, PositionalParameter<Converter,optional>& p1, Args&... args) {Parse(0,ol->Size(),ol,p1,args...);}
template <class Converter, bool optional, class... Args>
ParsePositionalParameters(const ObjectList* ol, ObjectList::IndexType min, ObjectList::IndexType max, PositionalParameter<Converter,optional>& p1, Args&... args) {Parse(min,std::min(max,ol->Size()),ol,p1,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