diff options
author | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-01-13 02:01:30 +0100 |
---|---|---|
committer | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-01-13 02:01:30 +0100 |
commit | dcf154aaf037ac67260abcec0b0ed3db32bc65ac (patch) | |
tree | 94dd32865be508d70617c351c609f398c9ec6f06 | |
parent | 742b76b006daf27ea19b2834e56696cb3c5a0e18 (diff) |
allowing validated methods
-rw-r--r-- | src/core/common/Function.cpp | 12 | ||||
-rw-r--r-- | src/core/common/Function.hpp | 147 | ||||
-rw-r--r-- | test/core/common/ArgumentTest.cpp | 23 | ||||
-rw-r--r-- | test/core/common/FunctionTest.cpp | 17 |
4 files changed, 162 insertions, 37 deletions
diff --git a/src/core/common/Function.cpp b/src/core/common/Function.cpp index eeabbc3..6aedb7b 100644 --- a/src/core/common/Function.cpp +++ b/src/core/common/Function.cpp @@ -17,9 +17,19 @@ */ #include "Function.hpp" +#include "Logger.hpp" namespace ousia { - +Variant::arrayType &ValidatingFunction::validate(Variant::arrayType &args) const +{ + // If an argument descriptor was given, use it to validate the arguments. + // Throw any violation as exception. + if (checkArguments) { + ExceptionLogger logger; + arguments.validateArray(args, logger); + } + return args; +} } diff --git a/src/core/common/Function.hpp b/src/core/common/Function.hpp index 79ee6b9..0e8af12 100644 --- a/src/core/common/Function.hpp +++ b/src/core/common/Function.hpp @@ -30,6 +30,7 @@ #include <cassert> +#include "Argument.hpp" #include "Variant.hpp" namespace ousia { @@ -38,17 +39,18 @@ namespace ousia { * The Function interface defines all the methods needed to represent a * generic function. Function objects can be called using the "call" function in * which an array of Variant is supplied to the function and a Variant is - * returned to the caller. + * returned to the caller. The actual function that is being represented by an + * instance of the Function class may either be a C++ function or a function + * residing in some script. */ class Function { protected: + /** + * Protecte default constructor -- prevents the Function class from being + * created. Use one of the child classes instead. + */ Function(){}; -public: - Function(const Function &) = delete; - Function(Function &&) = delete; - virtual ~Function(){}; - /** * Abstract function which is meant to call the underlying function (be it * a host or a script function) with the given arguments. @@ -57,8 +59,50 @@ public: * the function. * @return a Variant containing the return value. */ - virtual Variant call(const Variant::arrayType &args = Variant::arrayType{}, - void *thisRef = nullptr) const = 0; + virtual Variant doCall(Variant::arrayType &args, void *thisRef) const = 0; + +public: + // No copy constructor + Function(const Function &) = delete; + + // No move constructor + Function(Function &&) = delete; + + /** + * Virtual destructor of the Function class. + */ + virtual ~Function(){}; + + /** + * Calls the function. + * + * @param args is an array of variants that should be passed to the + * function. Note that the arguments might be modified, e.g. by a validation + * process or the called function itself. + * @param thisRef is a user-defined reference which may be pointing at the + * object the function should be working on. + * @return a Variant containing the result of the function call. + */ + Variant call(Variant::arrayType &args, void *thisRef = nullptr) const + { + return doCall(args, thisRef); + } + + /** + * Calls the function. + * + * @param args is an array of variants that should be passed to the + * function. + * @param thisRef is a user-defined reference which may be pointing at the + * object the function should be working on. + * @return a Variant containing the result of the function call. + */ + Variant call(const Variant::arrayType &args = Variant::arrayType{}, + void *thisRef = nullptr) const + { + Variant::arrayType argsCopy = args; + return doCall(argsCopy, thisRef); + } }; /** @@ -66,16 +110,55 @@ public: * for instances of the Function class. */ class FunctionStub : public Function { +protected: + Variant doCall(Variant::arrayType &, void *) const override + { + return nullptr; + } + public: /** * Constructor of the FunctionStub class. */ FunctionStub() {} +}; - Variant call(const Variant::arrayType &, void *) const override - { - return nullptr; - } +/** + * Function class providing factilities for the validation of arguments. + */ +class ValidatingFunction : public Function { +private: + /** + * List describing a valid set to arguments. + */ + Arguments arguments; + + /** + * Set to true if any arguments for checking were given in the constructor. + * If set to false, no argument checks are performed. + */ + bool checkArguments; + +protected: + /** + * Default constructor. Disables validation, all arguments are allowed. + */ + ValidatingFunction() : checkArguments(false){}; + + /** + * Default constructor. Disables validation, all arguments are allowed. + */ + ValidatingFunction(Arguments arguments) + : arguments(std::move(arguments)), checkArguments(true){}; + + /** + * Function which cares about validating a set of arguments. + * + * @param args is an array containing the arguments that should be + * validated. + * @return the reference to the array. + */ + Variant::arrayType &validate(Variant::arrayType &args) const; }; /** @@ -85,7 +168,7 @@ public: * @tparam T is the type of the method that should be called. */ template <class T> -class Method : public Function { +class Method : public ValidatingFunction { public: /** * Type of the Callback function that is being called by the "call" @@ -96,7 +179,7 @@ public: * @param thisRef is a pointer pointing at an instance of type T. * @return the return value of the function as Variant instance. */ - using Callback = Variant (*)(const Variant::arrayType &args, T *thisRef); + using Callback = Variant (*)(Variant::arrayType &args, T *thisRef); private: /** @@ -104,14 +187,7 @@ private: */ const Callback method; -public: - /** - * Constructor of the Method class. - * - * @param method is a pointer at the C++ function that should be called. - */ - Method(Callback method) : method(method){}; - +protected: /** * Calls the underlying method. * @@ -119,12 +195,31 @@ public: * to the method. * @return a Variant containing the return value. */ - Variant call(const Variant::arrayType &args = Variant::arrayType{}, - void *thisRef = nullptr) const override + Variant doCall(Variant::arrayType &args, void *thisRef) const override { - // Call the method - return method(args, static_cast<T *>(thisRef)); + return method(validate(args), static_cast<T *>(thisRef)); } + +public: + /** + * Constructor of the Method class with a description of the arguments that + * are to be passed to the callback method. + * + * @param arguments is a type description restricting the arguments that are + * being passed to the callback function. + * @param method is the actual callback function that is being called once + * the method is executed. The arguments passed to the method are validated + * using the given argument descriptor. + */ + Method(Arguments arguments, Callback method) + : ValidatingFunction(arguments), method(method){}; + + /** + * Constructor of the Method class. + * + * @param method is a pointer at the C++ function that should be called. + */ + Method(Callback method) : method(method){}; }; } diff --git a/test/core/common/ArgumentTest.cpp b/test/core/common/ArgumentTest.cpp index d58f71b..0dec809 100644 --- a/test/core/common/ArgumentTest.cpp +++ b/test/core/common/ArgumentTest.cpp @@ -471,11 +471,11 @@ TEST(Argument, validateObjectDefault) } } -static std::shared_ptr<Function> helloWorldFun{new Method<void>{[]( - const Variant::arrayType &arr, void *) { return Variant{"Hello World"}; }}}; +static std::shared_ptr<Function> helloWorldFun{new Method<void>{ + [](Variant::arrayType &arr, void *) { return Variant{"Hello World"}; }}}; static std::shared_ptr<Function> goodbyeWorldFun{ - new Method<void>{[](const Variant::arrayType &arr, + new Method<void>{[](Variant::arrayType &arr, void *) { return Variant{"Goodbye Cruel World"}; }}}; TEST(Argument, validateFunction) @@ -835,31 +835,38 @@ TEST(Arguments, validateMap) { Variant::mapType map{{"a", 2}, {"c", false}}; ASSERT_TRUE(args.validateMap(map, logger, false)); - ASSERT_EQ(Variant::mapType({{"a", 2}, {"b", "test"}, {"c", false}}), map); + ASSERT_EQ(Variant::mapType({{"a", 2}, {"b", "test"}, {"c", false}}), + map); } { Variant::mapType map{{"a", 2}}; ASSERT_TRUE(args.validateMap(map, logger, false)); - ASSERT_EQ(Variant::mapType({{"a", 2}, {"b", "test"}, {"c", true}}), map); + ASSERT_EQ(Variant::mapType({{"a", 2}, {"b", "test"}, {"c", true}}), + map); } { Variant::mapType map{}; ASSERT_FALSE(args.validateMap(map, logger, false)); - ASSERT_EQ(Variant::mapType({{"a", 0}, {"b", "test"}, {"c", true}}), map); + ASSERT_EQ(Variant::mapType({{"a", 0}, {"b", "test"}, {"c", true}}), + map); } { Variant::mapType map{{"a", 2}, {"d", nullptr}}; ASSERT_FALSE(args.validateMap(map, logger, false)); - ASSERT_EQ(Variant::mapType({{"a", 2}, {"b", "test"}, {"c", true}, {"d", nullptr}}), map); + ASSERT_EQ(Variant::mapType( + {{"a", 2}, {"b", "test"}, {"c", true}, {"d", nullptr}}), + map); } { Variant::mapType map{{"a", 2}, {"d", nullptr}}; ASSERT_TRUE(args.validateMap(map, logger, true)); - ASSERT_EQ(Variant::mapType({{"a", 2}, {"b", "test"}, {"c", true}, {"d", nullptr}}), map); + ASSERT_EQ(Variant::mapType( + {{"a", 2}, {"b", "test"}, {"c", true}, {"d", nullptr}}), + map); } } } diff --git a/test/core/common/FunctionTest.cpp b/test/core/common/FunctionTest.cpp index 7225f9c..ed20695 100644 --- a/test/core/common/FunctionTest.cpp +++ b/test/core/common/FunctionTest.cpp @@ -29,10 +29,10 @@ public: void visit() { visited = true; } }; -TEST(Method, simpleTest) +TEST(Method, simple) { Method<MethodTestClass> m{ - [](const Variant::arrayType &args, MethodTestClass *thisRef) { + [](Variant::arrayType &args, MethodTestClass *thisRef) { thisRef->visit(); return Variant{}; }}; @@ -40,5 +40,18 @@ TEST(Method, simpleTest) MethodTestClass inst; m.call({}, &inst); } + +TEST(Method, validation) +{ + Method<void> m{{Argument::Int("a"), Argument::Int("b")}, + [](Variant::arrayType &args, void *thisRef) { + return Variant{args[0].asInt() + args[1].asInt()}; + }}; + + MethodTestClass inst; + ASSERT_EQ(3, m.call({1, 2}, &inst).asInt()); + ASSERT_THROW(m.call({1}, &inst), LoggableException); + ASSERT_THROW(m.call({1, "bla"}, &inst), LoggableException); +} } |