From 75ef7daa3ce44abc7b7a716a6570e9e44420a442 Mon Sep 17 00:00:00 2001 From: Adam Wardynski <award@.(B-4.4.46a)> Date: Thu, 25 Nov 2010 21:13:58 +0100 Subject: [PATCH] ParsedExpression, an Expression with Variables. Intended as a base class for library-user-level operators and rules. Allows manipulation of Variables via get, set. Intoduces cloning support. --- libwccl/ops/parsedexpression.h | 251 +++++++++++++++++++++++++++++++++ 1 file changed, 251 insertions(+) create mode 100644 libwccl/ops/parsedexpression.h diff --git a/libwccl/ops/parsedexpression.h b/libwccl/ops/parsedexpression.h new file mode 100644 index 0000000..532d342 --- /dev/null +++ b/libwccl/ops/parsedexpression.h @@ -0,0 +1,251 @@ +#ifndef LIBWCCL_OPS_PARSEDEXPRESSION_H +#define LIBWCCL_OPS_PARSEDEXPRESSION_H + +#include <libwccl/ops/expression.h> +#include <libwccl/variables.h> + +namespace Wccl { + +namespace detail { + +template<class T> +class PtrCloneable +{ +public: + /** + * @returns Shared pointer to a copy of the object. + */ + boost::shared_ptr<T> clone_ptr() const; +}; + +template<class T> +class PtrCleanCloneable : public PtrCloneable<T> +{ +public: + /** + * @returns Shared pointer to a copy of the object. + * The copy has "clean" method called for convenience. + */ + boost::shared_ptr<T> clone_clean_ptr() const; +}; + +} + +/** + * Abstract base class for WCCL expressions coming from parser and thus + * having a set of variables. + * @attention Usage of the class is not thread safe, but you can + * create a copy and the copy can run concurrently, beacuse it has its + * own set of variables. + * @note See below for various usage of the class. + * \code + * // op is a ParsedExpression and op_ptr a (shared) pointer to it. + * std::string s; + * s = op["Foo"].to_raw_string(); + * s = (*op_ptr)["Foo"].to_raw_string(); + * // versus + * s = op.get<Wccl::Bool>("Foo").to_raw_string(); + * s = op_ptr->get<Wccl::Bool>("Foo").to_raw_string(); + * // but! + * Wccl::Bool b; + * op["Foo"] = b; // NOT OK! compile error! + * op.get<Wccl::Bool>("Foo") = b; // OK + * op.set("Foo", b); // alternative for setting the value + * \endcode + */ +class ParsedExpression : public Expression +{ +public: + + /** + * operator[] that gets value of a variable with given name. + * @param var_name Variable name. + * @returns Const reference to value of the variable. + * @throws InvalidVariableName if the expression doesn't have variable + * of given name. + * @note This allows only read-only (const) access to value, using the base + * type Value. There would be no way to guarantee proper type if assignment + * to returned reference had been allowed. + * @see \link get(const std::string& var_name) const get<T> const \endlink - + * a version that returns concrete type of Value. + * @see \link get(const std::string& var_name) get<T> \endlink - a version + * that returns concrete type of Value and also allows assignment of the value. + * @see set<T> - a convenience function if all you want is to assign + * value to a variable. + */ + const Value& operator[](const std::string& var_name) const; + + /** + * Gets value of a variable with given name and type. + * @returns Const reference to value of the variable of type T. + * @param var_name Variable name. + * @throws InvalidVariableName if the expression doesn't have variable + * of given name. + * @throws VariableTypeMismatch if the expression has variable of the given + * name, but its type is different than the supplied T. + * @see \link operator[]() operator[] \endlink - a version that returns + * the value using base type Value, which may be more convenient + * in some situations. + * @see \link get(const std::string& var_name) get<T> \endlink - non-const + * version that allows assignment of a value to the result + * @see set<T> - a convenience function if all you want is to assign + * value to a variable. + */ + template<class T> + const T& get(const std::string& var_name) const; + + /** + * Gets value of a variable with given name and type, allowing for + * assignment. + * @returns Reference to Value of the variable of type T. You can assign + * values to the result and that changes value of the variable accordingly. + * @param var_name Variable name. + * @throws InvalidVariableName if the expression doesn't have variable + * of given name. + * @throws VariableTypeMismatch if the expression has variable of the given + * name, but its type is different than the supplied T. + * @see \link operator[]() operator[] \endlink - a version that returns + * the value using base type Value, which may be more convenient + * in some situations (but doesn't allow assignment to the result) + * @see \link get(const std::string& var_name) const get<T> const \endlink - + * the const version (which doesn't allow assignment to the result) + * @see set<T> - a convenience function if all you want is to assign + * a value to a variable. + */ + template<class T> + T& get(const std::string& var_name); + + /** + * Assigns value to a variable with given name and type. + * @param var_name Variable name. + * @param value Value to assign. + * @throws InvalidVariableName if the expression doesn't have variable + * of given name. + * @throws VariableTypeMismatch if the expression has variable of the given + * name, but its type is different than the supplied T. + * @note This may be more convenient than assiging a value via get, because + * the type T is inferred from the parameter value: + * \code + * Bool b; + * op.set("Foo", b) //notice that set<Bool> isn't required + * //versus + * op.get<Bool>("Foo") = b; // specification of type is required + * \endcode + * @see get<T> - allows assignment of the value with assignment operator, + * but requires explicit specification of T. + */ + template<class T> + void set(const std::string& var_name, const T& value); + + /** + * Sets all variables to their default values. + * @note Values of variables are preserved between applications + * of the expression. If the expression depends on some variables + * that it isn't setting by itself prior to the usage, you probably + * want to reset those variables yourself before each run, or you may + * call clean() depending on the situation. + * Also, derived classes that have clone() method copy the values + * as well, but a class may provide convenience method clone_clean() + * in case you want to get a clean state. + * @todo It needs implementing, currently it is a no-op, because + * Variables object doesn't really provide means to do it yet + */ + void clean(); + + /** + * @returns A copy of the expression, with values of the variables + * copied as well. + * @see clone_clean_ptr - convenience version that returns clean copy. + */ + boost::shared_ptr<ParsedExpression> clone_ptr() const; + + /** + * @returns A copy of the expression, with values of the variables + * set to their defaults. + * @see clone_ptr - a version that keeps values of the variables. + */ + boost::shared_ptr<ParsedExpression> clone_clean_ptr() const; + +protected: + explicit ParsedExpression(const Variables& variables); + + const boost::shared_ptr<Variables> variables_; + + virtual ParsedExpression* clone_internal() const = 0; +}; + + + +//--- implementation details --- + +inline +ParsedExpression::ParsedExpression(const Variables &variables) + : variables_(boost::make_shared<Variables>(variables)) +{ +} + +inline +const Value& ParsedExpression::operator[](const std::string& var_name) const { + boost::shared_ptr<Value> value = variables_->get<Value>(var_name); + if (!value) { + throw InvalidVariableName(var_name); + } + return *value; +} + +template <class T> inline +const T& ParsedExpression::get(const std::string &var_name) const { + BOOST_MPL_ASSERT_NOT(( boost::is_same<T, Value> )); + boost::shared_ptr<T> value = variables_->get<T>(var_name); + if (!value) { + if (!variables_->get<Value>(var_name)) { + throw InvalidVariableName(var_name); + } else { + throw VariableTypeMismatch(var_name); + } + } + return *value; +} + +template <class T> inline +T& ParsedExpression::get(const std::string &var_name) { + BOOST_MPL_ASSERT_NOT(( boost::is_same<T, Value> )); + boost::shared_ptr<T> value = variables_->get<T>(var_name); + if (!value) { + if (!variables_->get<Value>(var_name)) { + throw InvalidVariableName(var_name); + } else { + throw VariableTypeMismatch(var_name); + } + } + return *value; +} + +template<class T> inline +void ParsedExpression::set(const std::string& var_name, const T& value) { + get<T>(var_name) = value; +} + +inline +void clean() +{ + //TODO - imlement this. The Variables object doesn't really + //provide a way to do that atm so it should be changed +} + +inline +boost::shared_ptr<ParsedExpression> ParsedExpression::clone_ptr() const { + return boost::shared_ptr<ParsedExpression>(clone_internal()); +} + +inline +boost::shared_ptr<ParsedExpression> ParsedExpression::clone_clean_ptr() const { + boost::shared_ptr<ParsedExpression> copy(clone_internal()); + BOOST_ASSERT(copy); + copy->clean(); + return copy; +} + +} /* end ns Wccl */ + +#endif // LIBWCCL_OPS_PARSEDEXPRESSION_H -- GitLab