lsnes/include/library/settingvar.hpp

625 lines
12 KiB
C++

#ifndef _library__settingvar__hpp__included__
#define _library__settingvar__hpp__included__
#include <string>
#include <map>
#include <set>
#include "threads.hpp"
#include "string.hpp"
#include <string>
namespace settingvar
{
class base;
class group;
class description;
class set;
class superbase;
threads::rlock& get_setting_lock();
/**
* A settings listener.
*/
struct listener
{
/**
* Destructor.
*/
virtual ~listener() throw();
/**
* Listen for setting changing value.
*/
virtual void on_setting_change(group& _group, const base& val) = 0;
};
/**
* A set of setting variables.
*/
class set
{
public:
/**
* Set add/drop listener.
*/
class listener
{
public:
/**
* Dtor.
*/
virtual ~listener();
/**
* New item in set.
*/
virtual void create(set& s, const std::string& name, superbase& svar) = 0;
/**
* Deleted item from set.
*/
virtual void destroy(set& s, const std::string& name) = 0;
/**
* Destroyed the entiere set.
*/
virtual void kill(set& s) = 0;
};
/**
* Create a set.
*/
set();
/**
* Destructor.
*/
~set();
/**
* Register a supervariable.
*/
void do_register(const std::string& name, superbase& info);
/**
* Unregister a supervariable.
*/
void do_unregister(const std::string& name, superbase& info);
/**
* Add a callback on new supervariable.
*/
void add_callback(listener& listener) throw(std::bad_alloc);
/**
* Drop a callback on new supervariable.
*/
void drop_callback(listener& listener);
private:
char dummy;
};
/**
* Group of setting variables.
*/
class group
{
public:
/**
* Constructor.
*/
group() throw(std::bad_alloc);
/**
* Destructor.
*/
~group() throw();
/**
* Get all settings.
*/
std::set<std::string> get_settings_set() throw(std::bad_alloc);
/**
* Get setting.
*/
base& operator[](const std::string& name);
/**
* Add a listener.
*/
void add_listener(struct listener& _listener) throw(std::bad_alloc);
/**
* Remove a listener.
*/
void remove_listener(struct listener& _listener) throw(std::bad_alloc);
/**
* Register a setting.
*/
void do_register(const std::string& name, base& _setting) throw(std::bad_alloc);
/**
* Unregister a setting.
*/
void do_unregister(const std::string& name, base& _setting) throw(std::bad_alloc);
/**
* Fire listener.
*/
void fire_listener(base& var) throw();
/**
* Add a set of settings.
*/
void add_set(set& s) throw(std::bad_alloc);
/**
* Remove a set of settings.
*/
void drop_set(set& s);
private:
/**
* Set listener.
*/
class xlistener : public set::listener
{
public:
xlistener(group& _grp);
~xlistener();
void create(set& s, const std::string& name, superbase& sb);
void destroy(set& s, const std::string& name);
void kill(set& s);
private:
group& grp;
} _listener;
};
/**
* Write-trough value cache.
*/
class cache
{
public:
/**
* Constructor.
*/
cache(group& grp);
/**
* Enumerate contents.
*
* Note: This reads cached values in perference to actual values.
*/
std::map<std::string, std::string> get_all();
/**
* Enumerate valid keys.
*
* Returns: The set of actually valid keys.
*/
std::set<std::string> get_keys();
/**
* Set a value.
*
* Parameter name: Name of the setting.
* Parameter value: New value for the setting.
* Parameter allow_invalid: Cache value if invalid, instead of throwing.
* Throws std::runtime_error: Failed to set the setting and invalid values not allowed.
*
* Note: If setting has cached value and setting it succeeds, the cached value is cleared.
*/
void set(const std::string& name, const std::string& value, bool allow_invalid = false) throw(std::bad_alloc,
std::runtime_error);
/**
* Get a value.
*
* Parameter name: Name of the setting.
* Return: Actual value of the setting.
* Throws std::runtime_error: Setting doesn't exist.
*/
std::string get(const std::string& name) throw(std::bad_alloc, std::runtime_error);
/**
* Get descriptor for.
*/
const description& get_description(const std::string& name) throw(std::bad_alloc,
std::runtime_error);
/**
* Get human-readable name.
*
* Parameter name: Name of the setting.
* Return: Human-readable name of the setting.
* Throws std::runtime_error: Setting doesn't exist.
*/
std::string get_hname(const std::string& name) throw(std::bad_alloc, std::runtime_error);
private:
group& grp;
std::map<std::string, std::string> badcache;
};
/**
* Enumeration.
*/
struct enumeration
{
enumeration(std::initializer_list<const char*> v)
{
unsigned x = 0;
for(auto i : v) {
values[bound = x++] = i;
}
}
std::string get(unsigned val) { return values.count(val) ? values[val] : ""; }
unsigned max_val() { return bound; }
private:
std::map<unsigned, std::string> values;
unsigned bound;
};
/**
* Description of setting.
*/
struct description
{
enum _type
{
T_BOOLEAN,
T_NUMERIC,
T_STRING,
T_PATH,
T_ENUMERATION
};
_type type;
int64_t min_val;
int64_t max_val;
enumeration* _enumeration;
};
/**
* Get the description.
*/
template<class T> static class description& description_get(T dummy);
/**
* Supervariable.
*/
class superbase
{
public:
/**
* Constructor.
*/
void _superbase(set& _s, const std::string& iname) throw(std::bad_alloc);
/**
* Destructor.
*/
virtual ~superbase() throw();
/**
* Make a variable.
*/
virtual base* make(group& grp) = 0;
/**
* Notify set death.
*/
void set_died();
private:
set* s;
std::string iname;
};
/**
* Setting variable.
*/
class base
{
public:
/**
* Constructor.
*/
base(group& _group, const std::string& iname, const std::string& hname, bool dynamic) throw(std::bad_alloc);
/**
* Destructor.
*/
virtual ~base() throw();
/**
* Set setting.
*/
virtual void str(const std::string& val) throw(std::runtime_error, std::bad_alloc) = 0;
/**
* Get setting.
*/
virtual std::string str() const throw(std::runtime_error, std::bad_alloc) = 0;
/**
* Get setting name.
*/
const std::string& get_iname() const throw() { return iname; }
const std::string& get_hname() const throw() { return hname; }
/**
* Get setting description.
*/
virtual const description& get_description() const throw() = 0;
/**
* Notify group death.
*/
void group_died();
protected:
base(const base&);
base& operator=(const base&);
group* sgroup;
std::string iname;
std::string hname;
bool is_dynamic;
};
/**
* Setting variable.
*/
template<class model> class variable : public base
{
typedef typename model::valtype_t valtype_t;
variable(const variable<model>&);
variable<model>& operator=(const variable<model>&);
public:
/**
* Constructor.
*/
variable(group& sgroup, const std::string& iname, const std::string& hname,
valtype_t defaultvalue, bool dynamic = false)
: base(sgroup, iname, hname, dynamic)
{
value = defaultvalue;
}
/**
* Destructor.
*/
virtual ~variable() throw()
{
}
/**
* Set setting.
*/
void str(const std::string& val) throw(std::runtime_error, std::bad_alloc)
{
{
threads::arlock h(get_setting_lock());
value = model::read(val);
}
sgroup->fire_listener(*this);
}
/**
* Get setting.
*/
std::string str() const throw(std::runtime_error, std::bad_alloc)
{
threads::arlock h(get_setting_lock());
return model::write(value);
}
/**
* Set setting.
*/
void set(valtype_t _value) throw(std::runtime_error, std::bad_alloc)
{
{
threads::arlock h(get_setting_lock());
if(!model::valid(value))
throw std::runtime_error("Invalid value");
value = _value;
}
sgroup->fire_listener(*this);
}
/**
* Get setting.
*/
valtype_t get() throw(std::bad_alloc)
{
threads::arlock h(get_setting_lock());
return model::transform(value);
}
/**
* Get setting.
*/
operator valtype_t()
{
return get();
}
/**
* Get setting description.
*/
const description& get_description() const throw()
{
return description_get(dummy);
}
private:
valtype_t value;
model dummy;
};
/**
* Supervariable with model.
*/
template<class model> class supervariable : public superbase
{
typedef typename model::valtype_t valtype_t;
supervariable(const supervariable<model>&);
supervariable<model>& operator=(const supervariable<model>&);
public:
/**
* Constructor.
*/
supervariable(set& _s, const std::string& _iname, const std::string& _hname, valtype_t _defaultvalue)
throw(std::bad_alloc)
: s(_s)
{
iname = _iname;
hname = _hname;
defaultvalue = _defaultvalue;
_superbase(_s, iname);
}
/**
* Destructor.
*/
~supervariable() throw()
{
}
/**
* Make a variable.
*/
base* make(group& grp)
{
return new variable<model>(grp, iname, hname, defaultvalue, true);
}
/**
* Read value in instance.
*/
valtype_t operator()(group& grp)
{
base* b = &grp[iname];
variable<model>* m = dynamic_cast<variable<model>*>(b);
if(!m)
throw std::runtime_error("No such setting in target group");
return m->get();
}
/**
* Write value in instance.
*/
void operator()(group& grp, valtype_t val)
{
base* b = &grp[iname];
variable<model>* m = dynamic_cast<variable<model>*>(b);
if(!m)
throw std::runtime_error("No such setting in target group");
m->set(val);
}
private:
set& s;
std::string iname;
std::string hname;
valtype_t defaultvalue;
};
/**
* Yes-no.
*/
struct yes_no
{
static const char* enable;
static const char* disable;
};
/**
* Model: Boolean.
*/
template<typename values> struct model_bool
{
typedef bool valtype_t;
static bool valid(bool val) { return true; /* Any boolean is valid boolean. */ }
static bool read(const std::string& val)
{
int x = string_to_bool(val);
if(x < 0)
throw std::runtime_error("Invalid boolean value");
return (x != 0);
}
static std::string write(bool val)
{
return val ? values::enable : values::disable;
}
static bool transform(bool val) { return val; }
};
template<typename values> description& description_get(
model_bool<values> X)
{
static description x;
static bool init = false;
if(!init) {
x.type = description::T_BOOLEAN;
init = true;
}
return x;
}
/**
* Model: Integer.
*/
template<int32_t minimum, int32_t maximum> struct model_int
{
typedef int32_t valtype_t;
static bool valid(int32_t val) { return (val >= minimum && val <= maximum); }
static int32_t read(const std::string& val)
{
int x = parse_value<int32_t>(val);
if(x < minimum || x > maximum)
(stringfmt() << "Value out of range (" << minimum << " to " << maximum << ")").throwex();
return x;
}
static std::string write(int32_t val)
{
return (stringfmt() << val).str();
}
static int transform(int val) { return val; }
};
template<int32_t m, int32_t M> description& description_get(model_int<m, M> X)
{
static description x;
static bool init = false;
if(!init) {
x.type = description::T_NUMERIC;
x.min_val = m;
x.max_val = M;
init = true;
}
return x;
}
/**
* Model: Path.
*/
struct model_path
{
typedef std::string valtype_t;
static bool valid(std::string val) { return true; /* Any boolean is valid boolean. */ }
static std::string read(const std::string& val)
{
return val;
}
static std::string write(std::string val)
{
return val;
}
static std::string transform(std::string val)
{
return (val != "") ? val : ".";
}
};
template<> description& description_get(model_path X)
{
static description x;
static bool init = false;
if(!init) {
x.type = description::T_PATH;
init = true;
}
return x;
}
/**
* Model: Enumerated.
*/
template<enumeration* e> struct model_enumerated
{
typedef unsigned valtype_t;
static bool valid(unsigned val) { return (val <= e->max_val()); }
static unsigned read(const std::string& val)
{
for(unsigned i = 0; i <= e->max_val(); i++)
if(val == e->get(i))
return i;
unsigned x = parse_value<unsigned>(val);
if(x > e->max_val())
(stringfmt() << "Value out of range (0 to " << e->max_val() << ")").throwex();
return x;
}
static std::string write(unsigned val)
{
return e->get(val);
}
static int transform(int val) { return val; }
};
template<enumeration* e> description& description_get(model_enumerated<e> X)
{
static description x;
static bool init = false;
if(!init) {
x.type = description::T_ENUMERATION;
x._enumeration = e;
init = true;
}
return x;
}
}
#endif