|
|
|
@ -3,7 +3,9 @@
|
|
|
|
|
#include <set> |
|
|
|
|
#include <string> |
|
|
|
|
#include <vector> |
|
|
|
|
#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<bool Optional> |
|
|
|
|
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 Converter, bool O> |
|
|
|
|
class NamedParameter: public Converter, public Parameter<O> |
|
|
|
|
{ |
|
|
|
|
NamedParameter() = delete; |
|
|
|
|
NamedParameter(const NamedParameter&) = delete; |
|
|
|
|
|
|
|
|
|
using Parameter<O>::Initialised; |
|
|
|
|
|
|
|
|
|
protected: |
|
|
|
|
TemplateComparator compar; |
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
typedef Converter ConverterType; |
|
|
|
|
// Constructor specifies template for comparison
|
|
|
|
|
template<class N, class T> |
|
|
|
|
NamedParameter(N&& n, T&& t):compar(std::forward(t)) {Parameter<O>(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<class Converter> |
|
|
|
|
using ONPar=NamedParameter<Converter,true>; |
|
|
|
|
template<class Converter> |
|
|
|
|
using RNPar=NamedParameter<Converter,false>; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Class for parameter which can be in named pair or not
|
|
|
|
|
template <class Converter, bool O> |
|
|
|
|
class NamedFreeParameter: public NamedParameter<Converter,O> |
|
|
|
|
{ |
|
|
|
|
NamedFreeParameter() = delete; |
|
|
|
|
NamedFreeParameter(const NamedFreeParameter&) = delete; |
|
|
|
|
|
|
|
|
|
using NamedParameter<Converter,O>::compar; |
|
|
|
|
using NamedParameter<Converter,O>::Initialised; |
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
typedef Converter ConverterType; |
|
|
|
|
// Constructor specifies template for comparison
|
|
|
|
|
template<class N, class T> |
|
|
|
|
NamedFreeParameter(N&& n, T&& t):NamedParameter<Converter,O>(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<class Converter> |
|
|
|
|
using ONFPar=NamedFreeParameter<Converter,true>; |
|
|
|
|
template<class Converter> |
|
|
|
|
using RNFPar=NamedFreeParameter<Converter,false>; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Class for parameter which can be in some positions in list
|
|
|
|
|
template <class Converter, bool O> |
|
|
|
|
class PositionalParameter: public Converter, public Parameter<O> |
|
|
|
|
{ |
|
|
|
|
using IType=ObjectList::IndexType; |
|
|
|
|
PositionalParameter() = delete; |
|
|
|
|
PositionalParameter(const PositionalParameter&) = delete; |
|
|
|
|
std::set<IType> positions; |
|
|
|
|
IType initpos; |
|
|
|
|
|
|
|
|
|
using Parameter<O>::Initialised; |
|
|
|
|
using Parameter<O>::SetState; |
|
|
|
|
|
|
|
|
|
// Constructor specifies list of possible positions
|
|
|
|
|
PositionalParameter(IType i):initpos(0) {positions.insert(i);} |
|
|
|
|
template<class... Args> |
|
|
|
|
PositionalParameter(IType i, Args... args): PositionalParameter(args...) {positions.insert(i);} |
|
|
|
|
public: |
|
|
|
|
typedef Converter ConverterType; |
|
|
|
|
template<class S> |
|
|
|
|
PositionalParameter(S&& str, IType i):initpos(0) {Parameter<O>(std::forward(str)); positions.insert(i);} |
|
|
|
|
template<class S, class... Args> |
|
|
|
|
PositionalParameter(S&& str, IType i, Args... args): PositionalParameter(args...) {Parameter<O>(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<class Converter> |
|
|
|
|
using OPosPar=PositionalParameter<Converter,true>; |
|
|
|
|
template<class Converter> |
|
|
|
|
using RPosPar=PositionalParameter<Converter,false>; |
|
|
|
|
|
|
|
|
|
// Parsing positional parameters
|
|
|
|
|
// Generic template
|
|
|
|
|
template<class... Args> |
|
|
|
|
class ParsePositionalParameters; |
|
|
|
|
|
|
|
|
|
// Recursive template
|
|
|
|
|
template<class Arg, class... Args> |
|
|
|
|
class ParsePositionalParameters<Arg, Args...>: ParsePositionalParameters<Args...> |
|
|
|
|
{ |
|
|
|
|
typedef PositionalParameter<typename Arg::ConverterType, Arg::optional> PType; |
|
|
|
|
static_assert(std::is_same<PType,Arg>::value,"ParsePositionalParameters template argument(s) must be PositionalParameter"); |
|
|
|
|
PType& par; |
|
|
|
|
|
|
|
|
|
ParsePositionalParameters() = delete; |
|
|
|
|
ParsePositionalParameters(const ParsePositionalParameters&) = delete; |
|
|
|
|
ParsePositionalParameters(ParsePositionalParameters&&) = delete; |
|
|
|
|
|
|
|
|
|
protected: |
|
|
|
|
template<class Str> |
|
|
|
|
void SetError(Str&& str) {ParsePositionalParameters<Args...>::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<Args...>::TryInit(i,ob); |
|
|
|
|
} |
|
|
|
|
bool Check() |
|
|
|
|
{ |
|
|
|
|
if(!(par.Initialised() || par.isOptional())) |
|
|
|
|
{ |
|
|
|
|
SetError("Parameter "+par.Name()+" required, but not set"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
return ParsePositionalParameters<Args...>::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<Args...>::isUsed(i,used); |
|
|
|
|
|
|
|
|
|
if(used) // Error, two parameters initialised from one position
|
|
|
|
|
{ |
|
|
|
|
SetError(par.Name()); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
auto res=ParsePositionalParameters<Args...>::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...>(args...);} |
|
|
|
|
|
|
|
|
|
std::string Error() const {return ParsePositionalParameters<Args...>::Error();} |
|
|
|
|
|
|
|
|
|
bool Parse(const ObjectList* list) |
|
|
|
|
{ |
|
|
|
|
std::string err; |
|
|
|
|
for(ObjectList::IndexType i=0; i<list->Size(); 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; i<list->Size(); 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<class Str> |
|
|
|
|
void SetError(Str&& str) {err=std::forward(str);} |
|
|
|
|
|
|
|
|
|
std::string Error() const {return err;} |
|
|
|
|
}; |
|
|
|
|