summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorAndreas Stöckel <astoecke@techfak.uni-bielefeld.de>2015-01-13 23:44:40 +0100
committerAndreas Stöckel <astoecke@techfak.uni-bielefeld.de>2015-01-13 23:44:40 +0100
commitc5345f13b3d33630d3e3d0021f10945627ae6e2d (patch)
treee2464f0c638818034ca9fc937dfdc044d3d70cf3 /src/core
parentdf82b147b98cd09cb94f017910e400e148886fbe (diff)
Added new Property class for defining getters and setters
Diffstat (limited to 'src/core')
-rw-r--r--src/core/common/Property.cpp96
-rw-r--r--src/core/common/Property.hpp512
2 files changed, 608 insertions, 0 deletions
diff --git a/src/core/common/Property.cpp b/src/core/common/Property.cpp
new file mode 100644
index 0000000..ea40182
--- /dev/null
+++ b/src/core/common/Property.cpp
@@ -0,0 +1,96 @@
+/*
+ Ousía
+ Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Exceptions.hpp"
+#include "Logger.hpp"
+#include "Property.hpp"
+#include "VariantConverter.hpp"
+
+namespace ousia {
+
+/* Class PropertyType */
+
+const PropertyType PropertyType::None;
+
+/* Class GetterFunction */
+
+void GetterFunction::validateArguments(Variant::arrayType &args) const
+{
+ if (!args.empty()) {
+ throw PropertyException(
+ std::string("Getter function has no arguments, but got ") +
+ std::to_string(args.size()));
+ }
+}
+
+void GetterFunction::validateResult(Variant &res) const
+{
+ ExceptionLogger logger;
+ VariantConverter::convert(res, propertyType->type, propertyType->innerType,
+ logger);
+}
+
+Variant GetterFunction::get(void *obj)
+{
+ return call(Variant::arrayType{}, obj);
+}
+
+/* Class SetterFunction */
+
+void SetterFunction::validateArguments(Variant::arrayType &args) const
+{
+ // Make sure exactly one argument is given
+ if (args.size() != 1U) {
+ throw PropertyException(
+ std::string(
+ "Expected exactly one argument to be passed to the property "
+ "setter, but got ") +
+ std::to_string(args.size()));
+ }
+
+ // Convert the one argument to the requested type, throw an exception if
+ // this fails.
+ ExceptionLogger logger;
+ VariantConverter::convert(args[0], propertyType->type,
+ propertyType->innerType, logger);
+}
+
+void SetterFunction::set(const Variant &value, void *obj)
+{
+ call(Variant::arrayType{value}, obj);
+}
+
+/* Class PropertyDescriptor */
+
+PropertyDescriptor::PropertyDescriptor(const PropertyType &type,
+ std::unique_ptr<GetterFunction> getter,
+ std::unique_ptr<SetterFunction> setter)
+ : type(type), getter(std::move(getter)), setter(std::move(setter))
+{
+ if (!this->getter->isValid()) {
+ throw PropertyException(
+ "Getter must be valid, writeonly properties are not "
+ "supported!");
+ }
+
+ // Assign the property type reference to the getter and setter
+ this->getter->propertyType = &this->type;
+ this->setter->propertyType = &this->type;
+}
+}
+
diff --git a/src/core/common/Property.hpp b/src/core/common/Property.hpp
new file mode 100644
index 0000000..5957e40
--- /dev/null
+++ b/src/core/common/Property.hpp
@@ -0,0 +1,512 @@
+/*
+ Ousía
+ Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file Property.hpp
+ *
+ * Contains classes for describing properties, which allow to generically access
+ * object members via Getter and Setter functions. This functionality is needed
+ * for building scripting language interfaces.
+ *
+ * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
+ */
+
+#ifndef _OUSIA_PROPERTY_HPP_
+#define _OUSIA_PROPERTY_HPP_
+
+#include <memory>
+
+#include "Exceptions.hpp"
+#include "Function.hpp"
+#include "Variant.hpp"
+
+namespace ousia {
+
+/**
+ * Exception type used for signaling exceptions in the context of Properties,
+ * such as calls to not set Getters or Setters.
+ */
+class PropertyException : public LoggableException {
+public:
+ using LoggableException::LoggableException;
+};
+
+// Forward declaration
+class RttiType;
+namespace RttiTypes {
+extern const RttiType None;
+}
+
+/**
+ * Structure describing the type of a property -- which consists of a "outer"
+ * type (which may either be a primitive variant type such as e.g.
+ * RttiTypes::Int or any other RttiType instance) and an inner type, which
+ * describes the type contained within a container type such as RttiTypes::Array
+ * or RttiTypes::Map.
+ */
+struct PropertyType {
+ // Variable declaring the empty property type.
+ static const PropertyType None;
+
+ /**
+ * Outer type, may be any RttiType instance. If set to RttiType::None, any
+ * outer type is acceptable.
+ */
+ const RttiType &type;
+
+ /**
+ * Describes the inner type of the property used when the outer type is a
+ * container type such as RttiType::Array or RttiType::Map. If set to
+ * RttiType::None any inner type is acceptable.
+ */
+ const RttiType &innerType;
+
+ /**
+ * Creates a new instance of the PropertyType class with both inner and
+ * outer type being set to RttiTypes::None and thus allowing any type to be
+ * represented by this property.
+ */
+ PropertyType() : type(RttiTypes::None), innerType(RttiTypes::None){};
+
+ /**
+ * Creates a new instance of the PropertyType class with the given outer
+ * type.
+ *
+ * @param type is the "outer" type of the PropertyType instance which may
+ * be any RttiType instances or RttiType::None, in which case all types are
+ * allowed.
+ */
+ PropertyType(const RttiType &type)
+ : type(type), innerType(RttiTypes::None){};
+
+ /**
+ * Creates a new instance of the PropertyType class with the given outer and
+ * inner type.
+ *
+ * @param type is the "outer" type of the PropertyType instance which may
+ * be any RttiType instances or RttiType::None, in which case all types are
+ * allowed.
+ * @param innerType is the inner type of the PropertyType instance, which is
+ * relevant if the outer type is set to a basic container type, namely
+ * RttiTypes::Array or RttiTypes::Map.
+ */
+ PropertyType(const RttiType &type, const RttiType &innerType)
+ : type(type), innerType(innerType){};
+};
+
+/**
+ * Represents an abstract Function with a property type attached to it. This is
+ * used as base class for Getter and Setter classes.
+ */
+class PropertyFunction : public Function {
+private:
+ /**
+ * Boolean field indicating whether the function is valid or not (a callback
+ * function was provided or not
+ */
+ bool valid;
+
+protected:
+ /**
+ * Constructor of the PropertyFunction class with a preset propertyType
+ * reference.
+ *
+ * @param valid specifies whether a callback function was given or not.
+ */
+ PropertyFunction(bool valid)
+ : valid(valid), propertyType(&PropertyType::None){};
+
+public:
+ /**
+ * Returns the type associated with the property function.
+ */
+ PropertyType const *propertyType;
+
+ /**
+ * Returns true if a callback function was given, false otherwise.
+ */
+ bool isValid() { return valid; }
+};
+
+/**
+ * Abstract function type representing a Getter function. Provides validation
+ * functions, yet does not have the ability to actually call a Getter. This
+ * functionality is provided by the Getter template class, which provides an
+ * easy to use callback mechanism.
+ */
+class GetterFunction : public PropertyFunction {
+protected:
+ /**
+ * Makes sure no arguments are given, throws an exception if any arguments
+ * are provided.
+ *
+ * @param args is an array containing the arguments.
+ */
+ void validateArguments(Variant::arrayType &args) const;
+
+ /**
+ * Makes sure the result adhers to the specified property type. Throws an
+ * exception if this is not the case.
+ *
+ * @param res is the result that should be validated.
+ */
+ void validateResult(Variant &res) const;
+
+ using PropertyFunction::PropertyFunction;
+
+public:
+ /**
+ * Returns the value of the property for the given object.
+ *
+ * @param obj is the instance for which the value of the property should be
+ * returned.
+ * @return the value retrieved from the object pointed at by obj.
+ */
+ Variant get(void *obj);
+};
+
+/**
+ * Class representing the getter function of a property.
+ *
+ * @tparam T is the type of the object on which the getter should be executed.
+ */
+template <class T>
+class Getter : public GetterFunction {
+public:
+ /**
+ * Callback function type used to access the getter.
+ *
+ * @param thisRef is a reference to the object from which the value should
+ * be retrieved.
+ * @return the retrieved value. The result is checked to be of the specified
+ * type of the property.
+ */
+ using Callback = Variant (*)(const T *thisRef);
+
+private:
+ /**
+ * Callback function pointer used to access the getter.
+ */
+ Callback callback;
+
+protected:
+ /**
+ * Calls the callback function and validates the given arguments and the
+ * callback result.
+ *
+ * @param args is a list of arguments passed to the getter. Should be empty.
+ * @param thisRef is a reference to the object from which the value should
+ * be retrieved.
+ * @return the retrieved value. The result is checked to be of the specified
+ * type of the property.
+ */
+ Variant doCall(Variant::arrayType &args, void *thisRef) const override
+ {
+ if (!callback) {
+ throw PropertyException("Property is writeonly.");
+ }
+
+ // Make sure the input arguments are valid
+ validateArguments(args);
+
+ // Call the actual callback function and make sure the output
+ // arguments
+ // are valid
+ Variant res = callback(static_cast<T *>(thisRef));
+ validateResult(res);
+
+ // Return the validated result
+ return res;
+ }
+
+public:
+ /**
+ * Creates an invalid getter instance with no callback function.
+ */
+ Getter() : GetterFunction(false), callback(nullptr) {}
+
+ /**
+ * Create a getter with the given callback function.
+ *
+ * @param callback is the underlying callback function to be used to
+ * get the value from an instance of type T.
+ */
+ Getter(Callback callback)
+ : GetterFunction(callback != nullptr), callback(callback)
+ {
+ }
+};
+
+/**
+ * Abstract function type representing a Setter function. Provides validation
+ * functions, yet does not have the ability to actually call a Setter. This
+ * functionality is provided by the Setter template class, which provides an
+ * easy to use callback mechanism.
+ */
+class SetterFunction : public PropertyFunction {
+protected:
+ /**
+ * Makes sure exactly one argument with the specified type is given.
+ *
+ * @param args is an array containing the arguments.
+ */
+ void validateArguments(Variant::arrayType &args) const;
+
+ using PropertyFunction::PropertyFunction;
+
+public:
+ /**
+ * Sets the value of the property for the given object.
+ *
+ * @param value is the new property value that should be set.
+ * @param obj is the instance for which the value of the property should be
+ * returned.
+ */
+ void set(const Variant &value, void *obj);
+};
+
+/**
+ * Class representing the Setter function of a property.
+ *
+ * @tparam T is the type of the object on which the setter should be executed.
+ */
+template <class T>
+class Setter : public SetterFunction {
+public:
+ /**
+ * Callback function type used to access the setter.
+ *
+ * @param value is the value that should be set. The value has been
+ * validated for compliance with the specified property type.
+ * @param thisRef is a reference to the object from which the value should
+ * be retrieved.
+ */
+ using Callback = void (*)(const Variant &value, T *thisRef);
+
+private:
+ /**
+ * Callback function pointer used to access the setter.
+ */
+ Callback callback;
+
+protected:
+ /**
+ * Calls the callback function and validates the given arguments and the
+ * callback result.
+ *
+ * @param args is a list of arguments passed to the setter. Should contain
+ * exactly one argument.
+ * @param thisRef is a reference to the object in which the value should
+ * be set.
+ * @return a variant containing nullptr.
+ */
+ Variant doCall(Variant::arrayType &args, void *thisRef) const override
+ {
+ if (!callback) {
+ throw PropertyException("Property is readonly.");
+ }
+
+ // Make sure the input argument is valid and call the callback function
+ validateArguments(args);
+ callback(args[0], static_cast<T *>(thisRef));
+ return nullptr;
+ }
+
+public:
+ /**
+ * Creates an invalid setter instance with no callback function.
+ */
+ Setter() : SetterFunction(false), callback(nullptr) {}
+
+ /**
+ * Create a setter with the given callback function.
+ *
+ * @param callback is the underlying callback function to be used to
+ * set the value of an instance of type T.
+ */
+ Setter(Callback callback)
+ : SetterFunction(callback != nullptr), callback(callback)
+ {
+ }
+};
+
+/**
+ * Class describing a generic Property of an object of a not-yet specified type.
+ */
+class PropertyDescriptor {
+private:
+ /**
+ * Description of the type of the property, consisting of an inner and an
+ * outer type.
+ */
+ const PropertyType type;
+
+ /**
+ * Object used to read the value of the property.
+ */
+ std::unique_ptr<GetterFunction> getter;
+
+ /**
+ * Object used to write values of the property. The setter may be invalid
+ * in which case the property is read only.
+ */
+ std::unique_ptr<SetterFunction> setter;
+
+protected:
+ /**
+ * Base constructor of the PropertyDescriptor class, called by all other
+ * constructors.
+ *
+ * @param type describes the type of the PropertyDescriptor.
+ * @param getter is the getter function used for reading the property. The
+ * getter function must be valid, writeonly properties are not supported.
+ * @param setter is the setter function used for writing the property. The
+ * setter function may be invalid, in which case the property is readonly.
+ */
+ PropertyDescriptor(const PropertyType &type,
+ std::unique_ptr<GetterFunction> getter,
+ std::unique_ptr<SetterFunction> setter);
+
+public:
+ /**
+ * Returns true if this is a read only property.
+ *
+ * @return true if no (valid) setter was given in the constructor, false
+ * otherwise.
+ */
+ bool isReadonly() const { return !(setter->isValid()); }
+
+ /**
+ * Returns the type described by the property.
+ *
+ * @return the PropertyType instance describing the type of this property.
+ */
+ const PropertyType &getType() const { return type; }
+
+ /**
+ * Returns the value of the property for the given object.
+ *
+ * @param obj is the instance for which the value of the property should be
+ * returned.
+ * @return the value retrieved from the object pointed at by obj.
+ */
+ Variant get(void *obj) const { return getter->get(obj); }
+
+ /**
+ * Sets the value of the property for the given object.
+ *
+ * @param value is the new property value that should be set.
+ * @param obj is the object for which the property should be updated.
+ */
+ void set(const Variant &value, void *obj) const { setter->set(value, obj); }
+};
+
+/**
+ * Class representing a Property of an object of type T. Provides convenient
+ * constructors for the construction of the underlying PropertyDescriptor.
+ *
+ * @tparam T is the type with that field that should be accessed through this
+ * property.
+ */
+template <class T>
+class Property : public PropertyDescriptor {
+public:
+ /**
+ * Constructor of the Property class, creates a property with no type
+ * restrictions.
+ *
+ * @param getter is a Getter for accessing the described property for
+ * objects of type T.
+ * @param setter is a Setter for writing the described property for objects
+ * of type T.
+ */
+ Property(const Getter<T> &getter, const Setter<T> &setter = Setter<T>{})
+ : PropertyDescriptor(
+ PropertyType{},
+ std::unique_ptr<GetterFunction>{new Getter<T>{getter}},
+ std::unique_ptr<SetterFunction>{new Setter<T>{setter}})
+ {
+ }
+
+ /**
+ * Constructor of the Property class.
+ *
+ * @param type is the type of the field that can be accessed by the
+ * property. This may either be a primitive variant type such as e.g.
+ * RttiTypes::Int or any other RttiType instance
+ * @param getter is a Getter for accessing the described property for
+ * objects of type T.
+ * @param setter is a Setter for writing the described property for objects
+ * of type T.
+ */
+ Property(const RttiType &type, const Getter<T> &getter,
+ const Setter<T> &setter = Setter<T>{})
+ : PropertyDescriptor(
+ PropertyType{type},
+ std::unique_ptr<GetterFunction>{new Getter<T>{getter}},
+ std::unique_ptr<SetterFunction>{new Setter<T>{setter}})
+ {
+ }
+
+ /**
+ * Constructor of the Property class.
+ *
+ * @param type is the type of the field that can be accessed by the
+ * property. This may either be a primitive variant type such as e.g.
+ * RttiTypes::Int or any other RttiType instance.
+ * @param innerType is only relevant if type is set to either
+ * RttiTypes::Array or RttiTypes::Map. In this case the innerType describes
+ * the type of the elements stored inside these containers.
+ * @param getter is a Getter for accessing the described property for
+ * objects of type T.
+ * @param setter is a Setter for writing the described property for objects
+ * of type T.
+ */
+ Property(const RttiType &type, const RttiType &innerType,
+ const Getter<T> &getter, const Setter<T> &setter = Setter<T>{})
+ : PropertyDescriptor(
+ PropertyType{type, innerType},
+ std::unique_ptr<GetterFunction>{new Getter<T>{getter}},
+ std::unique_ptr<SetterFunction>{new Setter<T>{setter}})
+ {
+ }
+
+ /**
+ * Returns the value of the property for the given object.
+ *
+ * @param obj is the instance for which the value of the property should be
+ * returned.
+ * @return the value retrieved from the object pointed at by obj.
+ */
+ Variant get(T *obj) { return PropertyDescriptor::get(obj); }
+
+ /**
+ * Sets the value of the property for the given object.
+ *
+ * @param value is the new property value that should be set.
+ * @param obj is the object for which the property should be updated.
+ */
+ void set(const Variant &value, T *obj)
+ {
+ PropertyDescriptor::set(value, obj);
+ }
+};
+}
+
+#endif /* _OUSIA_PROPERTY_HPP_ */
+