diff --git a/libwccl/CMakeLists.txt b/libwccl/CMakeLists.txt index e96ecc2edcff0c282bc917ed8924861803d72e83..db4268a506339368100a546997efeda2c5e6588e 100644 --- a/libwccl/CMakeLists.txt +++ b/libwccl/CMakeLists.txt @@ -18,7 +18,7 @@ SET(libwccl_STAT_SRC ops/and.cpp ops/or.cpp ops/logicalpredicate.cpp - ops/nor.cpp + ops/nor.cpp ops/predicate.cpp parser/Parser.cpp parser/ParserException.cpp diff --git a/libwccl/variables.h b/libwccl/variables.h index 902cf1e10f0dedcdbb61da7b850b8eba8afd7d9e..c48997400656ec2555c847adcf6a833c7330cfd1 100644 --- a/libwccl/variables.h +++ b/libwccl/variables.h @@ -63,6 +63,71 @@ 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 its copies, 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". + * Bad touch is not yet fully defined, removing a variable certainly is one. + */ +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 +189,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, + * or a copy (either way), and the Variables object must have not had + * variables removed (adding new variables should not invalidate accesors). + * + * 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 065b15f4864788b07b3d1466ead482695a130e33..58d9f29202424e079da5495c35ea93dfc07a45c9 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 0000000000000000000000000000000000000000..b10e781f599af0fbd22b55a7a006a0f2fa71e2d3 --- /dev/null +++ b/tests/varaccess.cpp @@ -0,0 +1,54 @@ +#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() { + Variables v2; + v2.put("a", new Bool(true)); + v2.put("b", new Bool(true)); + v2.put("c", new Bool(true)); + v2.put("bb", new Bool(true)); + v2.put("aa", new Position(1)); + v2.put("aaa", new Position(2)); + v = v2; + } +}; + + +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() +