An error occurred while loading the file. Please try again.
-
Paweł Kędzia authored9650bf7f
variables.h 12.92 KiB
#ifndef LIBWCCL_VARIABLES_H
#define LIBWCCL_VARIABLES_H
#include <libwccl/exception.h>
#include <libwccl/values/bool.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 {
class Match;
class Position;
class StrSet;
class TSet;
namespace detail {
/**
* Helper class template for the Variables class to facilitate
* per-type maps.
*/
template<typename T>
class Vmap
{
public:
typedef std::map< std::string, boost::shared_ptr<T> > map_t;
protected:
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>
, detail::Vmap<Match>
, 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, Match> 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();
}
/** Set all values to their default value.
*
* Effectively iterates through all variables and assigns them their
* respective type's default-constructed value.
*/
void reset_values();
/** Per-type all variables accesor.
*
* Allows iterating through all variables of a given type (or all variables
* if the type is Value). Values may be modified, the variable names or
* shared pointers themselves cannot, use put etc. for that.
*/
template<typename T>
const typename detail::Vmap<T>::map_t get_all() const {
BOOST_MPL_ASSERT(( boost::mpl::count<types, T> ));
return detail::Vmap<T>::map_;
}
/** Variables cloning.
*
* A clone has the same variable names, with distinct underlying Value
* objects, and the same values in these objects.
*/
Variables* clone() const;
/** 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, throwing version.
*
* Returns a valid pointer to the variable with the given name, as get(),
* or throws if it is not found. Never returns NULL.
*
* May throw either VariableTypeMismatch or InvalidVariableName.
*/
template<typename T>
boost::shared_ptr<T> get_or_throw(const std::string& s) const {
BOOST_MPL_ASSERT(( boost::mpl::count<types, T> ));
boost::shared_ptr<T> r = detail::Vmap<T>::get(s);
if (r) {
return r;
} else {
if (detail::Vmap<Value>::get(s)) {
throw VariableTypeMismatch(s);
} else {
throw InvalidVariableName(s);
}
}
}
/** Convenience function to get the actual underlying Value of a variable.
*
* Will throw on errors like get_or_throw would. Returns whatever the
* Value referenced returns in its get_value.
*/
template<typename T>
const typename T::value_type& get_value(const std::string& s) const {
return get_or_throw<T>(s)->get_value();
}
/** 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_variable(const std::string& s);
/** Remove a variable regardless of it's type.
*
* Prefer del_variable<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);
struct AccessHelper;
friend struct Variables::AccessHelper;
struct AccessHelper
{
template<typename T>
typename detail::Vmap<T>::map_t& access() {
return vars.get_all_nonconst<T>();
}
Variables& vars;
private:
AccessHelper(Variables& v) : vars(v) {}
friend class Variables;
};
private:
template<typename T>
typename detail::Vmap<T>::map_t& get_all_nonconst() {
return detail::Vmap<T>::map_;
}
};
/* 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_variable(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_variable<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