From 7dc9c634901e196e1fcbd1633cc0e9f644d3263b Mon Sep 17 00:00:00 2001
From: ilor <kailoran@gmail.com>
Date: Mon, 8 Nov 2010 13:00:40 +0100
Subject: [PATCH] add a VariableAccesor class in Variables and corresponding
 create_accesor and get_fast functions in Variables, plus tests

---
 libwccl/variables.h  | 102 +++++++++++++++++++++++++++++++++++++++++++
 tests/CMakeLists.txt |   1 +
 tests/varaccess.cpp  |  52 ++++++++++++++++++++++
 3 files changed, 155 insertions(+)
 create mode 100644 tests/varaccess.cpp

diff --git a/libwccl/variables.h b/libwccl/variables.h
index 902cf1e..107d36b 100644
--- a/libwccl/variables.h
+++ b/libwccl/variables.h
@@ -63,6 +63,70 @@ public:
 	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 accesors.
+ */
+class BaseAccesor
+{
+public:
+	/// Variable name getter
+	const std::string get_name() const {
+		return varname_;
+	}
+protected:
+	/// Protected ctor, only constructed by derived classes
+	BaseAccesor(const std::string& varname)
+		: varname_(varname)
+	{
+	}
+
+	std::string varname_;
+};
+
+/**
+ * A "fast" accesor class for getting a variable off a Variables object.
+ *
+ * It should be faster to create an accesor 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 accesor class can only be created by the Variables object,
+ * are valid only for that Variables object and might stop working if the
+ * Variables object is touched in the wrong way in between the creation and use
+ * of a VariableAccesor. UB is meant by "stop working".
+ */
+template<typename T>
+class VariableAccesor : public BaseAccesor
+{
+public:
+
+protected:
+	/// Protected ctor, only created by Variables::create_accesor
+	VariableAccesor(const std::string& varname)
+		: BaseAccesor(varname)
+	{
+	}
+
+	friend class Variables;
+};
+
 /**
  * A class for holding variables.
  *
@@ -124,6 +188,44 @@ public:
 		return detail::Vmap<T>::get(s);
 	}
 
+	/** Create a "fast" accesor 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>
+	VariableAccesor<T> create_accesor(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 VariableAccesor<T>(s);
+	}
+
+	/** Get a variable using a "fast" accesor.
+	 *
+	 * The accesor is required to have been created by this particular object,
+	 * and the Variables object must have not had variables removed, however
+	 * adding new variables should not invalidate VariableAccesor objects.
+	 *
+	 * If the VariableAccesor 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 VariableAccesor<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,
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 167604e..f124748 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -11,6 +11,7 @@ add_executable(tests
 	main.cpp
 	position.cpp
 	values.cpp
+	varaccess.cpp
 	variables.cpp
 )
 
diff --git a/tests/varaccess.cpp b/tests/varaccess.cpp
new file mode 100644
index 0000000..663c60d
--- /dev/null
+++ b/tests/varaccess.cpp
@@ -0,0 +1,52 @@
+#include <boost/test/unit_test.hpp>
+#include <boost/bind.hpp>
+
+#include <libwccl/variables.h>
+#include <libpwrutils/foreach.h>
+
+#include <iostream>
+
+using namespace Wccl;
+
+BOOST_AUTO_TEST_SUITE(varaccess);
+
+struct VAfx
+{
+	Variables v;
+	VAfx() {
+		v.put("a", new Bool(true));
+		v.put("b", new Bool(true));
+		v.put("c", new Bool(true));
+		v.put("bb", new Bool(true));
+		v.put("aa", new Position(1));
+		v.put("aaa", new Position(2));
+	}
+};
+
+
+BOOST_FIXTURE_TEST_CASE(access, VAfx)
+{
+	std::vector<std::string> vnames;
+	vnames.push_back("a");
+	vnames.push_back("b");
+	vnames.push_back("c");
+	vnames.push_back("bb");
+	foreach (const std::string vn, vnames) {
+		VariableAccesor<Bool> a1 = v.create_accesor<Bool>(vn);
+		BOOST_CHECK(v.get_fast(a1) == v.get<Bool>(vn));
+		v.set("a", Bool(false));
+		BOOST_CHECK(v.get_fast(a1) == v.get<Bool>(vn));
+		v.put("a", Bool(true));
+		BOOST_CHECK(v.get_fast(a1) == v.get<Bool>(vn));
+	}
+}
+
+BOOST_FIXTURE_TEST_CASE(badaccess, VAfx)
+{
+	BOOST_CHECK_THROW(v.create_accesor<Bool>("asd"), InvalidVariableName);
+	BOOST_CHECK_THROW(v.create_accesor<Bool>("aaa"), VariableTypeMismatch);
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
+
-- 
GitLab