-
ilor authored9dcc1f05
variables.h 7.91 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/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