From 17420a49f5e20454462b1b1a2e5a0140cc984dcc Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Tue, 13 Jan 2015 23:43:11 +0100 Subject: Fixed doxygen warnings --- src/core/model/Domain.hpp | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/core/model/Domain.hpp b/src/core/model/Domain.hpp index 4b35fd4..23a55f3 100644 --- a/src/core/model/Domain.hpp +++ b/src/core/model/Domain.hpp @@ -233,15 +233,17 @@ class Domain; * As an example consider the "paragraph" StructuredClass, which might allow * the actual text content. Here is the according XML: * - * - * - * - * - * - * - * - * - * + * \code{*.xml} + * + * + * + * + * + * + * + * + * + * \endcode * * Accordingly the primitiveType field of a FieldDescriptor may only be * defined if the type is set to "PRIMITIVE". If the type is something else @@ -372,9 +374,11 @@ public: * explained as the difference between node attributes and node children. * Consider the XML * + * \code{*.xml} * * value * + * \endcode * * key="value" inside the A-node would be an attribute, while value * would be a primitive field. While equivalent in XML the semantics are @@ -451,6 +455,7 @@ typedef RangeSet Cardinality; * defining itself as a viable child in one existing field. Consider the * example of the "heading" domain from the header documentation again: * + * \code{*.xml} * * * @@ -468,6 +473,7 @@ typedef RangeSet Cardinality; * * * + * \endcode * * The "parent" construct allows to "invade" another domain. * @@ -481,19 +487,23 @@ typedef RangeSet Cardinality; * If we go back to our example a user would (without transparency) have to * explicitly declare: * + * \code{*.xml} * *
* Text. *
*
+ * \endcode * * But in our mind the document - + * + * \code{*.xml} * *
* Text. *
*
+ * \endcode * * Is already sufficiently specific. We can infer that a paragraph should be * wrapped around "Text.". Therefore we set the 'transparent' flag of the -- cgit v1.2.3 From b6cfdef98f487fc5ae38c170fa52bcf031e882e2 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Tue, 13 Jan 2015 23:43:57 +0100 Subject: Made functions move and copy constructible. Why? Because I can. --- src/core/common/Function.hpp | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src') diff --git a/src/core/common/Function.hpp b/src/core/common/Function.hpp index 0e8af12..adfc9bb 100644 --- a/src/core/common/Function.hpp +++ b/src/core/common/Function.hpp @@ -62,11 +62,6 @@ protected: 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. -- cgit v1.2.3 From df82b147b98cd09cb94f017910e400e148886fbe Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Tue, 13 Jan 2015 23:44:14 +0100 Subject: Documented missing parameter --- src/core/common/Argument.hpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/core/common/Argument.hpp b/src/core/common/Argument.hpp index 23078b2..78f7ec1 100644 --- a/src/core/common/Argument.hpp +++ b/src/core/common/Argument.hpp @@ -242,6 +242,8 @@ public: * case the arguments are given as a map. * @param type is the RttiType of acceptable objects. All objects where the * "isa" function returns true for the given type are be accepted. + * @param defaultValue must be set to nullptr. Default object instances + * cannot be stored. * @return a new Argument instance. */ static Argument Object(std::string name, const RttiType &type, -- cgit v1.2.3 From c5345f13b3d33630d3e3d0021f10945627ae6e2d Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Tue, 13 Jan 2015 23:44:40 +0100 Subject: Added new Property class for defining getters and setters --- CMakeLists.txt | 2 + src/core/common/Property.cpp | 96 +++++++ src/core/common/Property.hpp | 512 ++++++++++++++++++++++++++++++++++++++ test/core/common/PropertyTest.cpp | 178 +++++++++++++ 4 files changed, 788 insertions(+) create mode 100644 src/core/common/Property.cpp create mode 100644 src/core/common/Property.hpp create mode 100644 test/core/common/PropertyTest.cpp (limited to 'src') diff --git a/CMakeLists.txt b/CMakeLists.txt index 9778b2b..90539bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,6 +117,7 @@ ADD_LIBRARY(ousia_core src/core/common/Function src/core/common/Logger src/core/common/Number + src/core/common/Property src/core/common/Rtti src/core/common/Terminal src/core/common/Utils @@ -201,6 +202,7 @@ IF(TEST) test/core/common/CharReaderTest test/core/common/FunctionTest test/core/common/LoggerTest + test/core/common/PropertyTest test/core/common/RttiTest test/core/common/VariantReaderTest test/core/common/VariantWriterTest 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 . +*/ + +#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 getter, + std::unique_ptr 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 . +*/ + +/** + * @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 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 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. + */ + const PropertyType type; + + /** + * Object used to read the value of the property. + */ + std::unique_ptr 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 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 getter, + std::unique_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::unique_ptr{new Getter{getter}}, + std::unique_ptr{new Setter{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 &getter, + const Setter &setter = Setter{}) + : PropertyDescriptor( + PropertyType{type}, + std::unique_ptr{new Getter{getter}}, + std::unique_ptr{new Setter{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 &getter, const Setter &setter = Setter{}) + : PropertyDescriptor( + PropertyType{type, innerType}, + std::unique_ptr{new Getter{getter}}, + std::unique_ptr{new Setter{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_ */ + diff --git a/test/core/common/PropertyTest.cpp b/test/core/common/PropertyTest.cpp new file mode 100644 index 0000000..c28feeb --- /dev/null +++ b/test/core/common/PropertyTest.cpp @@ -0,0 +1,178 @@ +/* + 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 . +*/ + +#include + +#include +#include + +namespace ousia { + +namespace { +struct TestObject { + TestObject(int a) : a(a) {} + int a; + + static Variant getA(const TestObject *obj) { return Variant{obj->a}; } + + static void setA(const Variant &value, TestObject *obj) + { + if (value.isInt()) { + obj->a = value.asInt(); + } + } +}; +} + +static Variant getString(const TestObject *obj) { return "foo"; } + +TEST(Getter, construction) +{ + { + Getter getter{}; + ASSERT_FALSE(getter.isValid()); + } + + { + Getter getter{nullptr}; + ASSERT_FALSE(getter.isValid()); + } + + { + Getter getter{TestObject::getA}; + ASSERT_TRUE(getter.isValid()); + } +} + +TEST(Getter, validation) +{ + const PropertyType type{RttiTypes::Int}; + TestObject obj{123}; + + { + // No specifiy type set, strings can be returned + Getter getter{getString}; + ASSERT_EQ("foo", getter.get(&obj)); + } + + { + // Int type set, returning strings is an exception + Getter getter{getString}; + getter.propertyType = &type; + ASSERT_THROW(getter.get(&obj), LoggableException); + } + + { + Getter getter{TestObject::getA}; + + // Basic functionality + ASSERT_EQ(123, getter.call(Variant::arrayType{}, &obj)); + + // Exception should be thrown if an argument is explicitly given + ASSERT_THROW(getter.call(Variant::arrayType{1}, &obj), + PropertyException); + } +} + +TEST(Setter, construction) +{ + { + Setter setter{}; + ASSERT_FALSE(setter.isValid()); + } + + { + Setter setter{nullptr}; + ASSERT_FALSE(setter.isValid()); + } + + { + Setter setter{TestObject::setA}; + ASSERT_TRUE(setter.isValid()); + } +} + +TEST(Setter, validation) +{ + const PropertyType type{RttiTypes::Int}; + TestObject obj{123}; + + Setter setter{TestObject::setA}; + + // An exception should be thrown if not exactly one argument is passed to + // the setter + ASSERT_THROW(setter.call(Variant::arrayType{}, &obj), PropertyException); + ASSERT_THROW(setter.call(Variant::arrayType{1, 2}, &obj), + PropertyException); + + setter.call(Variant::arrayType{42}, &obj); // OK + ASSERT_EQ(42, obj.a); + + // No specifiy type set, any value can be given (does not crash because of + // explicity type check in the callback function, see above). + setter.set("foo", &obj); + ASSERT_EQ(42, obj.a); + + setter.propertyType = &type; + ASSERT_THROW(setter.set("foo", &obj), LoggableException); + + setter.set(123, &obj); + ASSERT_EQ(123, obj.a); +} + +TEST(Property, construction) +{ + TestObject obj{123}; + + { + ASSERT_THROW(Property property{nullptr}, PropertyException); + } + + { + Property property{TestObject::getA}; + ASSERT_TRUE(property.isReadonly()); + ASSERT_THROW(property.set(42, &obj), LoggableException); + } + + { + Property property{TestObject::getA, TestObject::setA}; + ASSERT_FALSE(property.isReadonly()); + ASSERT_EQ(123, property.get(&obj).asInt()); + + property.set(42, &obj); + ASSERT_EQ(42, property.get(&obj).asInt()); + + property.set("bla", &obj); + ASSERT_EQ(42, property.get(&obj).asInt()); + } + + { + Property property{RttiTypes::Int, TestObject::getA, + TestObject::setA}; + ASSERT_FALSE(property.isReadonly()); + + ASSERT_EQ(42, property.get(&obj).asInt()); + + property.set(123, &obj); + ASSERT_EQ(123, property.get(&obj).asInt()); + + ASSERT_THROW(property.set("bla", &obj), LoggableException); + } +} +} + -- cgit v1.2.3