#ifndef _library__settingvar__hpp__included__ #define _library__settingvar__hpp__included__ #include #include #include #include "threads.hpp" #include "string.hpp" #include 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 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 get_all(); /** * Enumerate valid keys. * * Returns: The set of actually valid keys. */ std::set 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 badcache; }; /** * Enumeration. */ struct enumeration { enumeration(std::initializer_list 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 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 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 variable : public base { typedef typename model::valtype_t valtype_t; variable(const variable&); variable& operator=(const variable&); 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 supervariable : public superbase { typedef typename model::valtype_t valtype_t; supervariable(const supervariable&); supervariable& operator=(const supervariable&); 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(grp, iname, hname, defaultvalue, true); } /** * Read value in instance. */ valtype_t operator()(group& grp) { base* b = &grp[iname]; variable* m = dynamic_cast*>(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* m = dynamic_cast*>(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 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 description& description_get( model_bool X) { static description x; static bool init = false; if(!init) { x.type = description::T_BOOLEAN; init = true; } return x; } /** * Model: Integer. */ template 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(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 description& description_get(model_int 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 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(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 description& description_get(model_enumerated X) { static description x; static bool init = false; if(!init) { x.type = description::T_ENUMERATION; x._enumeration = e; init = true; } return x; } } #endif