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