/* 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 . */ /** * @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 #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 Rtti; namespace RttiTypes { extern const Rtti 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 Rtti 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 Rtti instance. If set to RttiTypes::None, any * outer type is acceptable. */ const Rtti &type; /** * Describes the inner type of the property used when the outer type is a * container type such as RttiTypes::Array or RttiTypes::Map. If set to * RttiTypes::None any inner type is acceptable. */ const Rtti &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 Rtti instances or RttiTypes::None, in which case all types are * allowed. */ PropertyType(const Rtti &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 Rtti instances or RttiTypes::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 Rtti &type, const Rtti &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(nullptr){}; public: /** * Returns the type associated with the property function. */ std::shared_ptr 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 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(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 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(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. */ std::shared_ptr type; /** * Object used to read the value of the property. */ std::shared_ptr getter; /** * Object used to write values of the property. The setter may be invalid * in which case the property is read only. */ std::shared_ptr 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::shared_ptr getter, std::shared_ptr 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 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 &getter, const Setter &setter = Setter{}) : PropertyDescriptor( PropertyType{}, std::make_shared>(getter), std::make_shared>(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 Rtti 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 Rtti &type, const Getter &getter, const Setter &setter = Setter{}) : PropertyDescriptor( PropertyType{type}, std::make_shared>(getter), std::make_shared>(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 Rtti 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 Rtti &type, const Rtti &innerType, const Getter &getter, const Setter &setter = Setter{}) : PropertyDescriptor( PropertyType{type, innerType}, std::make_shared>(getter), std::make_shared>(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_ */