Skip to content
Snippets Groups Projects
variables.h 10.9 KiB
Newer Older
#ifndef LIBWCCL_VARIABLES_H
#define LIBWCCL_VARIABLES_H

#include <libwccl/values/bool.h>
#include <libwccl/exception.h>
#include <libwccl/values/position.h>
ilor's avatar
ilor committed
#include <libwccl/values/strset.h>
ilor's avatar
ilor committed
#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>
 * Helper class template for the Variables class to facilitate
 * per-type maps.
template<typename T>
class Vmap
{
protected:
	typedef std::map< std::string, boost::shared_ptr<T> > map_t;
	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
	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;
};

/**
ilor's avatar
ilor committed
 * Base class for the "fast" variable accessors.
ilor's avatar
ilor committed
class BaseAccessor
{
public:
	/// Variable name getter
	const std::string get_name() const {
		return varname_;
	}
protected:
	/// Protected ctor, only constructed by derived classes
ilor's avatar
ilor committed
	BaseAccessor(const std::string& varname)
ilor's avatar
ilor committed
 * A "fast" accessor class for getting a variable off a Variables object.
ilor's avatar
ilor committed
 * 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.
 *
ilor's avatar
ilor committed
 * 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
ilor's avatar
ilor committed
 * 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.
ilor's avatar
ilor committed
class VariableAccessor : public BaseAccessor
ilor's avatar
ilor committed
	/// Protected ctor, only created by Variables::create_accessor
	VariableAccessor(const std::string& varname)
		: BaseAccessor(varname)
/**
 * 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
ilor's avatar
ilor committed
 * places: inherit from another Vmap, add to typelist.
 */
class Variables : detail::Vmap<Value>
	, detail::Vmap<Bool>
	, detail::Vmap<Position>
ilor's avatar
ilor committed
	, detail::Vmap<StrSet>
ilor's avatar
ilor committed
	, detail::Vmap<TSet>
ilor's avatar
ilor committed
//	, boost::noncopyable
{
public:
	/// Valid value types, should match the inheritance.
	/// the type Value must be first, order of other items is not important
ilor's avatar
ilor committed
	typedef boost::mpl::list<Value,
		Bool, Position, StrSet, TSet> types;

	/// Constructor, creates an empty instance.
	/** 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();
	}

ilor's avatar
ilor committed
	void reset_values(); //set all values to default value

	//template<typename T>
	//const map_t& all_variables() const

	//clone

	/** 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);
	}

ilor's avatar
ilor committed
	/** 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>
ilor's avatar
ilor committed
	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);
			}
		}
ilor's avatar
ilor committed
		return VariableAccessor<T>(s);
ilor's avatar
ilor committed
	/** Get a variable using a "fast" accessor.
ilor's avatar
ilor committed
	 * 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
ilor's avatar
ilor committed
	 * variables removed (adding new variables should not invalidate accessors).
ilor's avatar
ilor committed
	 * 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>
ilor's avatar
ilor committed
	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).
	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);
};

/* 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);
			throw VariableTypeMismatch(s);
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);
	}
}