diff --git a/libwccl/variables.cpp b/libwccl/variables.cpp index 3b2c9a02368eabad146e36b15e012998863ff7cb..419c44e210c71441339ca54012602a56a11a9b87 100644 --- a/libwccl/variables.cpp +++ b/libwccl/variables.cpp @@ -1,35 +1,69 @@ #include <libwccl/variables.h> +#include <boost/mpl/always.hpp> +#include <boost/mpl/pop_front.hpp> namespace Wccl { Variables::Variables() - : map_() { } -void Variables::put(const std::string &name, Value *value) +namespace { +struct delhelper { - map_[name] = value; -} + Variables& v; + const std::string& s; + bool& r; + delhelper(Variables& v, const std::string& s, bool& r): v(v), s(s), r(r) {} + + template<typename T> + void operator()(const boost::mpl::always<T>&) { + r = v.del<T>(s) || r; + } +}; -Value* Variables::get_or_null(const std::string &name) +struct puthelper { - map_t::iterator i = map_.find(name); - if (i != map_.end()) { - return i->second; - } else { - return NULL; + Variables& v; + const std::string& s; + bool& rv; + const boost::shared_ptr<Value>& p; + puthelper(Variables& v, const std::string& s, bool& rv, + const boost::shared_ptr<Value>& p): v(v), s(s), rv(rv), p(p) {} + + template<typename T> + void operator()(const boost::mpl::always<T>&) { + if (rv) return; + boost::shared_ptr<T> t = boost::dynamic_pointer_cast<T>(p); + if (t) { + rv = true; + v.put(s, t); + } } +}; + +} /* end anon ns */ + +bool Variables::del_any(const std::string &s) +{ + bool rv = false; + typedef boost::mpl::pop_front< types >::type concrete; + // call delhelper::operator()<T> once for each of the allowed + // Value subtypes (but not for Value itself). + boost::mpl::for_each<concrete, boost::mpl::always<boost::mpl::_1> >( + delhelper(*this, s, rv)); + return rv; } -const Value* Variables::get_or_null(const std::string &name) const +void Variables::put_any(const std::string &s, const boost::shared_ptr<Value> &v) { - map_t::const_iterator i = map_.find(name); - if (i != map_.end()) { - return i->second; - } else { - return NULL; - } + bool rv = false; + typedef boost::mpl::pop_front< types >::type concrete; + // call puthelper::operator()<T> once for each of the allowed + // Value subtypes (but not for Value itself). + boost::mpl::for_each<concrete, boost::mpl::always<boost::mpl::_1> >( + puthelper(*this, s, rv, v)); + if (!rv) throw VariableTypeMismatch(s); } } /* end ns Wccl */ diff --git a/libwccl/variables.h b/libwccl/variables.h index 039d11a168daccab0d6a7bacc1c59d1033cf42e5..6f124de3d70a6c8c98dee1e9e626e5e3f179cb79 100644 --- a/libwccl/variables.h +++ b/libwccl/variables.h @@ -1,69 +1,278 @@ #ifndef LIBWCCL_VARIABLES_H #define LIBWCCL_VARIABLES_H -#include <libwccl/value.h> +#include <libwccl/bool.h> +#include <libwccl/exception.h> +#include <libwccl/position.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 { + /** - * A class for holding values by name. + * Helper class template for the Variables class to facilitate + * per-type maps. */ -class Variables +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 { public: - /// Ctor. + VariableTypeMismatch(const std::string& varname) + : WcclError("Variable type mismatch"), varname(varname) + {} + + ~VariableTypeMismatch() throw() {} + + std::string 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 + * places. + */ +class Variables : detail::Vmap<Value> + , detail::Vmap<Bool> + , detail::Vmap<Position> +{ +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> types; + + /// Constructor, creates an empty instance. Variables(); - /** - * Set the variable with the given name to the given value. + /** 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(); + } + + /** 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, 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, the value is replaced. + * + * 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. */ - void put(const std::string& name, Value* value); + template<typename T> + void put(const std::string& s, const boost::shared_ptr<T>& v); - /** - * Get the value of the variable with the given name, or, if it does not - * exist, NULL. + /** 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 */ - Value* get_or_null(const std::string& name); + template<typename T> + bool del(const std::string& s); - /** - * Get the value of the variable with the given name, or, if it does not - * exist, NULL. Const. + /** 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 */ - const Value* get_or_null(const std::string &name) const; + bool del_any(const std::string& s); - /** - * Get the value of the variable with th egiven name, or, if it does not - * exist, create a new empty variable of the type given as the template - * parameter and return that. + /** Put<T> convenience overload for raw pointers that wraps them in a + * shared_ptr (thus taking ownership). */ template<typename T> - Value* get_or_put(const std::string& name) { - map_t::iterator i = map_.find(name); - if (i != map_.end()) { - return i->second; + 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); } else { - return map_.insert(std::make_pair(name, new T()))->second; + throw VariableTypeMismatch(s); } } + return v; +} - /// Typdef for the map type - typedef std::map<std::string, Value*> map_t; - /** - * Const map accesor, for iterating through all variables - */ - const map_t& all_variables() const { - return map_; +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); } +} -private: - /// The variables map - map_t map_; -}; +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