Skip to content
Snippets Groups Projects
Select Git revision
  • master
  • UD213
2 results

__init__.py

Blame
  • variables.h 12.90 KiB
    #ifndef LIBWCCL_VARIABLES_H
    #define LIBWCCL_VARIABLES_H
    
    #include <libwccl/values/bool.h>
    #include <libwccl/exception.h>
    #include <libwccl/values/position.h>
    #include <libwccl/values/strset.h>
    #include <libwccl/values/tset.h>
    #include <iostream>
    #include <map>
    #include <string>
    #include <cassert>
    #include <boost/mpl/assert.hpp>
    #include <boost/mpl/count.hpp>
    #include <boost/mpl/for_each.hpp>
    #include <boost/mpl/list.hpp>
    #include <boost/shared_ptr.hpp>
    #include <boost/make_shared.hpp>
    #include <boost/static_assert.hpp>
    #include <boost/type_traits/is_base_of.hpp> 
    #include <boost/type_traits/is_same.hpp>
    
    namespace Wccl {
    
    namespace detail {
    
    /**
     * Helper class template for the Variables class to facilitate
     * per-type maps.
     */
    template<typename T>
    class Vmap
    {
    public:
    	typedef std::map< std::string, boost::shared_ptr<T> > map_t;
    protected:
    	Vmap() : map_() {}
    	boost::shared_ptr<T> get(const std::string& s) const {
    		typename map_t::const_iterator i = map_.find(s);
    		if (i != map_.end()) {
    			return i->second;
    		} else {
    			return boost::shared_ptr<T>();
    		}
    	}
    	map_t map_;
    };
    
    } /* end ns detail */
    
    /**
     * Exception class for variable type mismatches
     */
    class VariableTypeMismatch : public WcclError
    {
    public:
    	VariableTypeMismatch(const std::string& varname)
    	 : WcclError("Variable type mismatch"), varname(varname)
    	{}
    
    	~VariableTypeMismatch() throw() {}
    
    	std::string varname;
    };
    
    /**
     * Exception class for invalid variable name errors in situations where
     * it is not possible to return a NULL-equivalent
     */
    class InvalidVariableName : public WcclError
    {
    public:
    	InvalidVariableName(const std::string& varname)
    	 : WcclError("Invalid variable name"), varname(varname)
    	{}
    
    	~InvalidVariableName() throw() {}
    
    	std::string varname;
    };
    
    /**
     * Base class for the "fast" variable accessors.
     */
    class BaseAccessor
    {
    public:
    	/// Variable name getter
    	const std::string get_name() const {
    		return varname_;
    	}
    protected:
    	/// Protected ctor, only constructed by derived classes
    	BaseAccessor(const std::string& varname)
    		: varname_(varname)
    	{
    	}
    
    	std::string varname_;
    };
    
    /**
     * A "fast" accessor class for getting a variable off a Variables object.
     *
     * It should be faster to create an accessor object from the variables name and
     * then use it multiple times than to use the name itself often.
     *
     * @todo the above is not the case yet.
     *
     * Objects of the accessor class can only be created by the Variables object,
     * are valid only for that Variables object and its copies, and might stop
     * working if the Variables object is touched in the wrong way in between the
     * creation and use of a VariableAccessor. UB is meant by "stop working".
     * Bad touch is not yet fully defined, removing a variable certainly is one.
     */
    template<typename T>
    class VariableAccessor : public BaseAccessor
    {
    public:
    
    protected:
    	/// Protected ctor, only created by Variables::create_accessor
    	VariableAccessor(const std::string& varname)
    		: BaseAccessor(varname)
    	{
    	}
    
    	friend class Variables;
    };
    
    /**
     * A class for holding variables.
     *
     * Variables are values with a name. They can any of the allowed types,
     * but only one variable, of one type, can exist under one name, so it is
     * not allowed to have an ambiguous variable name.
     * 
     * Values are kept as shared pointers and returned as such.
     *
     * Variables are manipulated by name using specific type overloads, and also
     * by the Value overload that, if possible, works across all valid types.
     * Some functions can only work for a concrete Value derived class, they are
     * marked as such and guarded by compile-time checks.
     *
     * Values are subtypes of the Value class, valid types are listed as arguments
     * of instantiations of the detail::Vmap base class template, and in the type
     * list below. Adding a new subtype should only take adding it to these two
     * places: inherit from another Vmap, add to typelist.
     */
    class Variables : detail::Vmap<Value>
    	, detail::Vmap<Bool>
    	, detail::Vmap<Position>
    	, detail::Vmap<StrSet>
    	, detail::Vmap<TSet>
    	, boost::noncopyable
    {
    public:
    	/// Valid value types, should match the inheritance.
    	/// the type Value must be first, order of other items is not important
    	typedef boost::mpl::list<Value,
    		Bool, Position, StrSet, TSet> types;
    
    	/// Constructor, creates an empty instance.
    	Variables();
    
    	/** Per-type size checker.
    	 *
    	 * Use the Value type to count all variables. In C++0x, Value would be
    	 * the default template argument.
    	 */
    	template<typename T>
    	int size() const {
    		BOOST_MPL_ASSERT(( boost::mpl::count<types, T> ));
    		return detail::Vmap<T>::map_.size();
    	}
    
    	/** Set all values to their default value.
    	  *
    	  * Effectively iterates through all variables and assigns them their
    	  * respective type's default-constructed value.
    	  */
    	void reset_values();
    
    	/** Per-type all variables accesor.
    	 *
    	 * Allows iterating through all variables of a given type (or all variables
    	 * if the type is Value). Values may be modified, the variable names or
    	 * shared pointers themselves cannot, use put etc. for that.
    	 */
    	template<typename T>
    	const typename detail::Vmap<T>::map_t get_all() const {
    		BOOST_MPL_ASSERT(( boost::mpl::count<types, T> ));
    		return detail::Vmap<T>::map_;
    	}
    
    	/** Variables cloning.
    	 *
    	 * A clone has the same variable names, with distinct underlying Value
    	 * objects, and the same values in these objects.
    	 */
    	Variables* clone() const;
    
    	/** Get a variable.
    	 *
    	 * Returns the variable with the given name, or NULL if it does not exist.
    	 * Use a specialied version with the requested type, or the general Value 
    	 * type if looking for any variable.
    	 * 
    	 * If T is not Value and the variable exists but has a different type,
    	 * returns NULL as if the variable did not exist.
    	 */
    	template<typename T>
    	boost::shared_ptr<T> get(const std::string& s) const {
    		BOOST_MPL_ASSERT(( boost::mpl::count<types, T> ));
    		return detail::Vmap<T>::get(s);
    	}
    
    	/** Get a variable, throwing version.
    	 *
    	 * Returns a valid pointer to the variable with the given name, as get(),
    	 * or throws if it is not found. Never returns NULL.
    	 *
    	 * May throw either VariableTypeMismatch or InvalidVariableName.
    	 */
    	template<typename T>
    	boost::shared_ptr<T> get_or_throw(const std::string& s) const {
    		BOOST_MPL_ASSERT(( boost::mpl::count<types, T> ));
    		boost::shared_ptr<T> r = detail::Vmap<T>::get(s);
    		if (r) {
    			return r;
    		} else {
    			if (detail::Vmap<Value>::get(s)) {
    				throw VariableTypeMismatch(s);
    			} else {
    				throw InvalidVariableName(s);
    			}
    		}
    	}
    
    	/** Convenience function to get the actual underlying Value of a variable.
    	 *
    	 * Will throw on errors like get_or_throw would. Returns whatever the
    	 * Value referenced returns in its get_value.
    	 */
    	template<typename T>
    	const typename T::value_type& get_value(const std::string& s) const {
    		return get_or_throw<T>(s)->get_value();
    	}
    
    	/** Create a "fast" accessor for a variable by name.
    	 *
    	 * Returns a special object which is valid for use in get_fast, which when
    	 * used should be faster than calling get<T>(s) repeatedly.
    	 *
    	 * If the variable name is invalid or teh type doesn't match, an exception
    	 * will be thrown, either VariableTypeMismatch or InvalidVariableName.
    	 */
    	template<typename T>
    	VariableAccessor<T> create_accessor(const std::string& s) {
    		BOOST_MPL_ASSERT(( boost::mpl::count<types, T> ));
    		if (!get<T>(s)) {
    			if (get<Value>(s)) {
    				throw VariableTypeMismatch(s);
    			} else {
    				throw InvalidVariableName(s);
    			}
    		}
    		return VariableAccessor<T>(s);
    	}
    
    	/** Get a variable using a "fast" accessor.
    	 *
    	 * The accessor is required to have been created by this particular object,
    	 * or a copy (either way), and the Variables object must have not had
    	 * variables removed (adding new variables should not invalidate accessors).
    	 *
    	 * If the VariableAccessor comes form a different Variables object or there
    	 * have been invalidating changes to this Variables object, behavior is not
    	 * well defined. You might get a NULL, a different variable than you
    	 * expected, or a segfault.
    	 */
    	template<typename T>
    	boost::shared_ptr<T> get_fast(const VariableAccessor<T>& a) const {
    		BOOST_MPL_ASSERT(( boost::mpl::count<types, T> ));
    		return get<T>(a.get_name());
    	}
    
    	/** Get a variable, possibly default-creatng it.
    	 *
    	 * Returns the variable with the given name, or, if it does not exist,
    	 * creates a new empty variable of the type given as the template
    	 * parameter and returns that.
    	 *
    	 * The variable must be of the requested type, otherwise
    	 * VariableTypeMismatch is thrown.
    	 * 
    	 * The function cannot be called if T is Value since T must be
    	 * constructible.
    	 */
    	template<typename T>
    	boost::shared_ptr<T> get_put(const std::string& s); 
    
    	/** Add a variable with the given name and the given value.
    	 *
    	 * The type should be normally deduced by the compiler, and should be 
    	 * a subtype of Value. Value itself can not be used, but @see put_any.
    	 *
    	 * If the variable does not exist, it is created.
    	 *
    	 * If it already exists it is replaced provided that the current type
    	 * matches (if it does not, VariableTypeMismatch is thrown). This
    	 * replacement is different from a set(s, v) since the original
    	 * value is not modified.
    	 */
    	template<typename T>
    	void put(const std::string& s, const boost::shared_ptr<T>& v);
    
    	/** Add a variable with the given name and the given value, dynamically
    	 * deducing the actual type.
    	 *
    	 * Prefer put<T> with a concrete T since it will be faster.
    	 *
    	 * @see the put version with a concrete type for semantics since
    	 * it is called eventually.
    	 *
    	 * The first matching type (where a dynamic_cast succeeds) is used.
    	 * If the passed Value cannot be converted to a valid subtype,
    	 * VariableTypeMismatch is thrown.
    	 */
    	void put_any(const std::string& s, const boost::shared_ptr<Value>& v);
    
    	/** Remove a variable assuming it has the given type.
    	 *
    	 * When T is Value, equivalent to del_any.
    	 *
    	 * @returns true if the variable was removed, false otherwise
    	 */
    	template<typename T>
    	bool del(const std::string& s);
    
    	/** Remove a variable regardless of it's type.
    	 *
    	 * Prefer del<T> with a concrete T as it will be faster.
    	 *
    	 * @returns true if the variable was removed, false otherwise
    	 */
    	bool del_any(const std::string& s);
    
    	/** Put<T> convenience overload for raw pointers that wraps them in a
    	 * shared_ptr (thus taking ownership).
    	 */
    	template<typename T>
    	void put(const std::string& s, T* v) {
    		put(s, boost::shared_ptr<T>(v));
    	}
    
    	/** Convenience function for putting raw values (not pointers)
    	 * that will allocate a copy as a shared pointer.
    	 */
    	template<typename T>
    	void put(const std::string& s, const T& v) {
    		put(s, boost::make_shared<T>(v));
    	}
    
    	/** Set an existing variable to the given value, or create a new variable.
    	 *
    	 * If the variable with the given name does not exist, it is created.
    	 * If it exists, it is set to the passed value (modified in-place).
    	 * This is in contrast to put(s, v) which would leave the original value
    	 * unchanged.
    	 */
    	template<typename T>
    	void set(const std::string& s, const T& v);
    
    	struct AccessHelper;
    	friend struct Variables::AccessHelper;
    	struct AccessHelper
    	{
    		template<typename T>
    		typename detail::Vmap<T>::map_t& access() {
    			return vars.get_all_nonconst<T>();
    		}
    		Variables& vars;
    	private:
    		AccessHelper(Variables& v) : vars(v) {}
    		friend class Variables;
    	};
    private:
    	template<typename T>
    	typename detail::Vmap<T>::map_t& get_all_nonconst() {
    		return detail::Vmap<T>::map_;
    	}
    };
    
    /* implementation */
    
    template<typename T> inline
    boost::shared_ptr<T> Variables::get_put(const std::string& s)
    {
    	BOOST_MPL_ASSERT(( boost::mpl::count<types, T> ));
    	boost::shared_ptr<T> v = get<T>(s);
    	if (!v) {
    		if (!get<Value>(s)) {
    			v.reset(new T());
    			put(s, v);
    		} else {
    			throw VariableTypeMismatch(s);
    		}
    	}
    	return v;
    }
    
    
    template<typename T> inline
    void Variables::put(const std::string& s, const boost::shared_ptr<T>& v) {
    	BOOST_MPL_ASSERT(( boost::mpl::count<types, T> ));
    	// disable put<Value>
    	BOOST_MPL_ASSERT_NOT(( boost::is_same<T, Value> ));
    	using detail::Vmap;
    	typename Vmap<T>::map_t::iterator i = Vmap<T>::map_.find(s);
    	typename Vmap<Value>::map_t::iterator vi = Vmap<Value>::map_.find(s);
    	if (i != Vmap<T>::map_.end()) {
    		assert(vi != Vmap<Value>::map_.end()); //invariant
    		i->second = v;
    		vi->second = v;
    	} else if (vi == Vmap<Value>::map_.end()) {
    		Vmap<T>::map_[s] = v;
    		Vmap<Value>::map_[s] = v;
    	} else {
    		throw VariableTypeMismatch(s);
    	}
    }
    
    template<typename T> inline
    bool Variables::del(const std::string &s)
    {
    	BOOST_MPL_ASSERT(( boost::mpl::count<types, T> ));
    	if (detail::Vmap<T>::map_.erase(s)) {
    		bool was_in_values = detail::Vmap<Value>::map_.erase(s);
    		assert(was_in_values);
    		return true;
    	} else {
    		return false;
    	}
    }
    
    template<> inline
    bool Variables::del<Value>(const std::string &s)
    {
    	return del_any(s);
    }
    
    template<typename T> inline
    void Variables::set(const std::string& s, const T& v)
    {
    	BOOST_MPL_ASSERT(( boost::mpl::count<types, T> ));
    	boost::shared_ptr<T> p = get<T>(s);
    	if (p) {
    		*p = v;
    	} else {
    		put(s, v);
    	}
    }
    
    } /* end ns Wccl */
    
    #endif // LIBWCCL_VARIABLES_H