#ifndef MODGMT_PARAM_H #define MODGMT_PARAM_H #include "modgmt_strcomp.h" #include "common.h" // Common ancestor for all parameters template class Parameter { using ValueType=typename Converter::ValueType; std::string name; // Used for error reporting bool initialised; Converter conv; ValueType val; template struct Adapter; template struct Adapter {static const typename Conv::ValueType& Val(const Conv& cnv) {return cnv.Default();}}; template struct Adapter {static typename Conv::ValueType Val(const Conv& cnv) {return typename Conv::ValueType();}}; template struct CheckDefault { private: static void detect(...); template static decltype(std::declval().Default()) detect(T); public: static constexpr bool value=std::is_same()))>::type>::value; }; protected: Parameter(Parameter&&) = delete; Parameter(const Parameter&) = delete; Parameter() = delete; template Parameter(std::string&& str, Args... args): name(std::move(str)), initialised(false), conv(args...) { val=Adapter::value>::Val(conv); } template Parameter(const std::string& str, Args... args): name(str), initialised(false), conv(args...) { val=Adapter::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::value>::Val(conv));} static constexpr bool optional=Optional; using ConverterType=Converter; }; // Class for parameter which must be in named pair template class NamedParameter: public TemplateComparator, public Parameter { public: using AcceptableObject=void; template NamedParameter(const std::string& t, Args... args):TemplateComparator(t),Parameter(Template2Name(),args...) {} }; // Class for parameter which can be in named pair or as object of type Object template class NamedFreeParameter: public NamedParameter { public: using AcceptableObject=Object; template NamedFreeParameter(Args... args):NamedParameter(args...) {} }; // Class for parameter which can be in some positions in list template class PositionalParameter: public Parameter { public: template PositionalParameter(Args... args):Parameter(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 void Parse(ObjectList::IndexType i, ObjectList::IndexType max, const ObjectList* ol, Par& param, Args&... args) { // Check types of arguments static_assert(std::is_same,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 ParsePositionalParameters(const ObjectList* ol, PositionalParameter& p1, Args&... args) {Parse(0,ol->Size(),ol,p1,args...);} template ParsePositionalParameters(const ObjectList* ol, ObjectList::IndexType min, ObjectList::IndexType max, PositionalParameter& 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 bool ParseSingle(const ObjectBase* ob, bool init, Par& param, Args&... args) { // Check types of arguments static_assert(std::is_same,Par>::value || std::is_same,Par>::value,"ParseNamedParameters argument(s) must be NamedParameter or NamedFreeParameter"); OBType 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 bool ParsePair(const ObjectPair* op, std::string& pname, Par& param, Args&... args) { // Check types of arguments static_assert(std::is_same,Par>::value || std::is_same,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 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 ParseNamedParameters(const ObjectList* ol, Args&... args) { // Initialisation std::string pname; for(ObjectList::const_iterator i=ol->begin();i!=ol->end();++i) { pname.erase(); OBType 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 using ONPar=NamedParameter; template using RNPar=NamedParameter; template using ONFPar=NamedFreeParameter; template using RNFPar=NamedFreeParameter; template using OPosPar=PositionalParameter; template using RPosPar=PositionalParameter; #endif