Newer
Older
ilor
committed
#ifndef LIBWCCL_VARIABLES_H
#define LIBWCCL_VARIABLES_H
#include <libwccl/exception.h>
#include <libwccl/values/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>
ilor
committed
namespace Wccl {
ilor
committed
/**
* Helper class template for the Variables class to facilitate
* per-type maps.
ilor
committed
*/
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
ilor
committed
{
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;
};
/**
*/
{
public:
/// Variable name getter
const std::string get_name() const {
return varname_;
}
protected:
/// Protected ctor, only constructed by derived classes
: 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>
{
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>
{
public:
/// Valid value types, should match the inheritance.
/// the type Value must be first, order of other items is not important
Bool, Position, StrSet, TSet> types;
/// Constructor, creates an empty instance.
ilor
committed
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);
}
*
* 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>
BOOST_MPL_ASSERT(( boost::mpl::count<types, T> ));
if (!get<T>(s)) {
if (get<Value>(s)) {
throw VariableTypeMismatch(s);
} else {
throw InvalidVariableName(s);
}
}
}
*
* 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.
ilor
committed
*
* 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.
ilor
committed
*/
template<typename T>
void put(const std::string& s, const boost::shared_ptr<T>& v);
ilor
committed
/** 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
ilor
committed
*/
template<typename T>
bool del(const std::string& s);
ilor
committed
/** 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
ilor
committed
*/
bool del_any(const std::string& s);
ilor
committed
/** Put<T> convenience overload for raw pointers that wraps them in a
* shared_ptr (thus taking ownership).
ilor
committed
*/
template<typename T>
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
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);
ilor
committed
} else {
throw VariableTypeMismatch(s);
ilor
committed
}
}
ilor
committed
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);
ilor
committed
}
ilor
committed
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);
}
ilor
committed
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);
}
}
ilor
committed
} /* end ns Wccl */
#endif // LIBWCCL_VARIABLES_H