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