diff --git a/modules/gmt/modgmt_strcomp.h b/modules/gmt/modgmt_strcomp.h index 143822c..187d3a9 100644 --- a/modules/gmt/modgmt_strcomp.h +++ b/modules/gmt/modgmt_strcomp.h @@ -3,7 +3,9 @@ #include #include #include +#include "common.h" +// Compare string with template class TemplateComparator { TemplateComparator() = delete; @@ -65,3 +67,268 @@ class TemplateComparator InitCursors(root.get()); } }; + + +// Common ancestor for all parameters +template +class Parameter +{ + std::string name; + bool initialised; + + protected: + Parameter() = default; + Parameter(const Parameter&) = delete; + + Parameter(std::string&& str): name(std::move(str)), initialised(false) {} + Parameter(const std::string& str): name(str), initialised(false) {} + + void SetState(bool newini) {initialised=newini;} + + public: + bool Initialised() const {return initialised;} + const std::string& Name() {return name;} + constexpr bool isOptional() const {return Optional;} + + static constexpr bool optional=Optional; +}; + +// Class for parameter which must be in named pair +template +class NamedParameter: public Converter, public Parameter +{ + NamedParameter() = delete; + NamedParameter(const NamedParameter&) = delete; + + using Parameter::Initialised; + + protected: + TemplateComparator compar; + + public: + typedef Converter ConverterType; + // Constructor specifies template for comparison + template + NamedParameter(N&& n, T&& t):compar(std::forward(t)) {Parameter(std::forward(n));} + + // Return error in case of repeated initialisation + bool Init(const ObjectBase* p, const std::string& str, std::string& err) + { + if(!compar.Compare(str)) return true; + if(Initialised()) return !Converter::Convert(p,err); + else SetState(Converter::Convert(p,err)); + return true; + } +}; + +template +using ONPar=NamedParameter; +template +using RNPar=NamedParameter; + + +// Class for parameter which can be in named pair or not +template +class NamedFreeParameter: public NamedParameter +{ + NamedFreeParameter() = delete; + NamedFreeParameter(const NamedFreeParameter&) = delete; + + using NamedParameter::compar; + using NamedParameter::Initialised; + + public: + typedef Converter ConverterType; + // Constructor specifies template for comparison + template + NamedFreeParameter(N&& n, T&& t):NamedParameter(std::forward(n),std::forward(t)) {} + + // Return error in case of repeated initialisation + bool Init(const ObjectBase* p, const std::string& str) + { + if(!compar.Compare(str)) return true; + if(Initialised()) return !Converter::Convert(p); + else SetState(Converter::Convert(p)); + return true; + } + + bool Init(const ObjectBase* p) + { + if(Initialised()) return !Converter::Convert(p); + else SetState(Converter::Convert(p)); + return true; + } +}; + +template +using ONFPar=NamedFreeParameter; +template +using RNFPar=NamedFreeParameter; + + +// Class for parameter which can be in some positions in list +template +class PositionalParameter: public Converter, public Parameter +{ + using IType=ObjectList::IndexType; + PositionalParameter() = delete; + PositionalParameter(const PositionalParameter&) = delete; + std::set positions; + IType initpos; + + using Parameter::Initialised; + using Parameter::SetState; + + // Constructor specifies list of possible positions + PositionalParameter(IType i):initpos(0) {positions.insert(i);} + template + PositionalParameter(IType i, Args... args): PositionalParameter(args...) {positions.insert(i);} + public: + typedef Converter ConverterType; + template + PositionalParameter(S&& str, IType i):initpos(0) {Parameter(std::forward(str)); positions.insert(i);} + template + PositionalParameter(S&& str, IType i, Args... args): PositionalParameter(args...) {Parameter(std::forward(str)); positions.insert(i);} + + // Return error in case of repeated initialisation + bool Init(IType i, const ObjectBase* p, std::string& err) + { + std::string cerr; + if(positions.cend()==positions.find(i)) return true; + if(Initialised()) return !Converter::Convert(p,cerr); + else + { + bool res=Converter::Convert(p,cerr); + if(res) initpos=i; + else + { + if(1==positions.size() && !O) // Only one position for required parameter. Error message must describe reason of failed conversion + { + err=std::move(cerr); + return false; + } + } + SetState(res); + } + return true; + } + + IType InitialisedFromPosition() const {return initpos;} +}; + +template +using OPosPar=PositionalParameter; +template +using RPosPar=PositionalParameter; + +// Parsing positional parameters +// Generic template +template +class ParsePositionalParameters; + +// Recursive template +template +class ParsePositionalParameters: ParsePositionalParameters +{ + typedef PositionalParameter PType; + static_assert(std::is_same::value,"ParsePositionalParameters template argument(s) must be PositionalParameter"); + PType& par; + + ParsePositionalParameters() = delete; + ParsePositionalParameters(const ParsePositionalParameters&) = delete; + ParsePositionalParameters(ParsePositionalParameters&&) = delete; + + protected: + template + void SetError(Str&& str) {ParsePositionalParameters::SetError(std::forward(str));} + bool TryInit(ObjectList::IndexType i, const ObjectBase* ob) + { + std::string err; + bool res=par.Init(i,ob,err); + if(!res) SetError(err.empty()?("Ambiguous definition of parameter "+par.Name()):("Parameter "+par.Name()+" in position "+ToString(i)+": "+err)); + return res && ParsePositionalParameters::TryInit(i,ob); + } + bool Check() + { + if(!(par.Initialised() || par.isOptional())) + { + SetError("Parameter "+par.Name()+" required, but not set"); + return false; + } + return ParsePositionalParameters::Check(); + } + + // Return value: number of parameters initialised from position i (count only first two) + size_t isUsed(ObjectList::IndexType i, bool used=false) const + { + if(!par.Initialised() || (par.InitialisedFromPosition()!=i)) return ParsePositionalParameters::isUsed(i,used); + + if(used) // Error, two parameters initialised from one position + { + SetError(par.Name()); + return 1; + } + + auto res=ParsePositionalParameters::isUsed(i,true); + if(res==1) // Two parameters initialised from one position, name of second parameter in error string + { + SetError("Parameters "+Error()+" and "+par.Name()+" are initialised from object in position "+ToString(i)); + return 2; + } + else return 1; // All ok + } + + public: + ParsePositionalParameters(PType& p, Args... args):par(p) {ParsePositionalParameters(args...);} + + std::string Error() const {return ParsePositionalParameters::Error();} + + bool Parse(const ObjectList* list) + { + std::string err; + for(ObjectList::IndexType i=0; iSize(); i++) + { + if(!TryInit(i,list->At(i),err)) + { + SetError("Error parsing argument "+ToString(i)+": "+err); + return false; + } + } + if(!Check()) return false; + + for(ObjectList::IndexType i=0; iSize(); i++) + { + auto n=isUsed(i); + if(n>1) return false; // Error message already set + if(0==n) + { + SetError("Unused object in position "+ToString(i)); + return false; + } + } + return true; + } +}; + + +// Bottom of recursion +template<> +class ParsePositionalParameters<> +{ + std::string err; + + ParsePositionalParameters(const ParsePositionalParameters&) = delete; + ParsePositionalParameters(ParsePositionalParameters&&) = delete; + + protected: + ParsePositionalParameters() = default; + + bool TryInit(ObjectList::IndexType i, const ObjectBase* ob) const {return true;} + size_t isUsed(ObjectList::IndexType i, bool used=false) const {return 0;} + bool Check() const {return true;} + + template + void SetError(Str&& str) {err=std::forward(str);} + + std::string Error() const {return err;} +};