# 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