diff --git a/modules/gmt/modgmt_param.h b/modules/gmt/modgmt_param.h new file mode 100644 index 0000000..7907f97 --- /dev/null +++ b/modules/gmt/modgmt_param.h @@ -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 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 std::move(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 Parameter, public TemplateComparator +{ + public: + using AcceptableObject=void; + template + NamedParameter(const std::string& t, Args... args):Parameter(t,args...),TemplateComparator(t) {} + template + NamedParameter(std::pair&& t, Args... args):Parameter(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 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, 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>=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(iSize()) err="There are excess elements in list";} + + public: + template + 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 + 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