#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/positionref.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 { 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: 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: inherit from another Vmap, add to typelist. */ class Variables : detail::Vmap<Value> , detail::Vmap<Bool> , detail::Vmap<Position> , detail::Vmap<PositionRef> , detail::Vmap<StrSet> , detail::Vmap<TSet> { 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, PositionRef, 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(); } /** 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 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); }; /* 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