diff options
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/core/common/Property.cpp | 22 | ||||
-rw-r--r-- | src/core/common/Property.hpp | 28 | ||||
-rw-r--r-- | src/core/common/Rtti.cpp | 70 | ||||
-rw-r--r-- | src/core/common/Rtti.hpp | 196 | ||||
-rw-r--r-- | src/core/common/TypedRttiBuilder.cpp | 24 | ||||
-rw-r--r-- | src/core/common/TypedRttiBuilder.hpp | 171 | ||||
-rw-r--r-- | test/core/common/PropertyTest.cpp | 10 | ||||
-rw-r--r-- | test/core/common/RttiTest.cpp | 164 | ||||
-rw-r--r-- | test/core/managed/ManagedTest.cpp | 9 |
10 files changed, 584 insertions, 111 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 90539bc..95934ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,6 +120,7 @@ ADD_LIBRARY(ousia_core src/core/common/Property src/core/common/Rtti src/core/common/Terminal + src/core/common/TypedRttiBuilder src/core/common/Utils src/core/common/Variant src/core/common/VariantConverter diff --git a/src/core/common/Property.cpp b/src/core/common/Property.cpp index ea40182..8248058 100644 --- a/src/core/common/Property.cpp +++ b/src/core/common/Property.cpp @@ -41,8 +41,10 @@ void GetterFunction::validateArguments(Variant::arrayType &args) const void GetterFunction::validateResult(Variant &res) const { ExceptionLogger logger; - VariantConverter::convert(res, propertyType->type, propertyType->innerType, - logger); + if (propertyType != nullptr) { + VariantConverter::convert(res, propertyType->type, + propertyType->innerType, logger); + } } Variant GetterFunction::get(void *obj) @@ -66,8 +68,10 @@ void SetterFunction::validateArguments(Variant::arrayType &args) const // 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); + if (propertyType != nullptr) { + VariantConverter::convert(args[0], propertyType->type, + propertyType->innerType, logger); + } } void SetterFunction::set(const Variant &value, void *obj) @@ -78,9 +82,9 @@ void SetterFunction::set(const Variant &value, void *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)) + std::shared_ptr<GetterFunction> getter, + std::shared_ptr<SetterFunction> setter) + : type(std::make_shared<PropertyType>(type)), getter(getter), setter(setter) { if (!this->getter->isValid()) { throw PropertyException( @@ -89,8 +93,8 @@ PropertyDescriptor::PropertyDescriptor(const PropertyType &type, } // Assign the property type reference to the getter and setter - this->getter->propertyType = &this->type; - this->setter->propertyType = &this->type; + this->getter->propertyType = this->type; + this->setter->propertyType = this->type; } } diff --git a/src/core/common/Property.hpp b/src/core/common/Property.hpp index 5957e40..72dff71 100644 --- a/src/core/common/Property.hpp +++ b/src/core/common/Property.hpp @@ -129,13 +129,13 @@ protected: * @param valid specifies whether a callback function was given or not. */ PropertyFunction(bool valid) - : valid(valid), propertyType(&PropertyType::None){}; + : valid(valid), propertyType(nullptr){}; public: /** * Returns the type associated with the property function. */ - PropertyType const *propertyType; + std::shared_ptr<PropertyType> propertyType; /** * Returns true if a callback function was given, false otherwise. @@ -354,18 +354,18 @@ private: * Description of the type of the property, consisting of an inner and an * outer type. */ - const PropertyType type; + std::shared_ptr<PropertyType> type; /** * Object used to read the value of the property. */ - std::unique_ptr<GetterFunction> getter; + std::shared_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; + std::shared_ptr<SetterFunction> setter; protected: /** @@ -379,8 +379,8 @@ protected: * 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); + std::shared_ptr<GetterFunction> getter, + std::shared_ptr<SetterFunction> setter); public: /** @@ -396,7 +396,7 @@ public: * * @return the PropertyType instance describing the type of this property. */ - const PropertyType &getType() const { return type; } + const PropertyType &getType() const { return *type; } /** * Returns the value of the property for the given object. @@ -438,8 +438,8 @@ public: 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}}) + std::make_shared<Getter<T>>(getter), + std::make_shared<Setter<T>>(setter)) { } @@ -458,8 +458,8 @@ public: 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}}) + std::make_shared<Getter<T>>(getter), + std::make_shared<Setter<T>>(setter)) { } @@ -481,8 +481,8 @@ public: 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}}) + std::make_shared<Getter<T>>(getter), + std::make_shared<Setter<T>>(setter)) { } diff --git a/src/core/common/Rtti.cpp b/src/core/common/Rtti.cpp index a8343ef..239f2b4 100644 --- a/src/core/common/Rtti.cpp +++ b/src/core/common/Rtti.cpp @@ -16,6 +16,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "Exceptions.hpp" #include "Rtti.hpp" namespace ousia { @@ -44,6 +45,30 @@ const RttiType &RttiStore::lookup(const std::type_info &native) } } +/* Class RttiBuilder */ + +RttiBuilder &RttiBuilder::genericMethod(const std::string name, + std::shared_ptr<Function> function) +{ + if (!methods.emplace(name, function).second) { + throw OusiaException(std::string("Method with name \"") + name + + std::string("\" for type \"") + currentName + + std::string("\" already registered!")); + } + return *this; +} + +RttiBuilder &RttiBuilder::genericProperty( + const std::string name, std::shared_ptr<PropertyDescriptor> property) +{ + if (!properties.emplace(name, property).second) { + throw OusiaException(std::string("Property with name \"") + name + + std::string("\" for type \"") + currentName + + std::string("\" already registered!")); + } + return *this; +} + /* Class RttiType */ void RttiType::initialize() const @@ -53,6 +78,15 @@ void RttiType::initialize() const if (!initialized) { initialized = true; + // Register the parent properties and methods + { + for (const RttiType *parent: parents) { + parent->initialize(); + methods.insert(parent->methods.begin(), parent->methods.end()); + properties.insert(parent->properties.begin(), parent->properties.end()); + } + } + // Insert the parent types of the parent types and the composite types // of the parents { @@ -64,7 +98,7 @@ void RttiType::initialize() const for (const RttiType *parent : parents) { parent->initialize(); compositeTypes.insert(parent->compositeTypes.begin(), - parent->compositeTypes.end()); + parent->compositeTypes.end()); } parents.insert(this); } @@ -77,9 +111,9 @@ void RttiType::initialize() const for (const RttiType *compositeType : origCompositeTypes) { compositeType->initialize(); compositeTypes.insert(compositeType->compositeTypes.begin(), - compositeType->compositeTypes.end()); + compositeType->compositeTypes.end()); compositeTypes.insert(compositeType->parents.begin(), - compositeType->parents.end()); + compositeType->parents.end()); } } } @@ -97,6 +131,36 @@ bool RttiType::composedOf(const RttiType &other) const return compositeTypes.count(&other) > 0; } +const RttiMethodMap &RttiType::getMethods() const { + initialize(); + return methods; +} + +const RttiPropertyMap &RttiType::getProperties() const { + initialize(); + return properties; +} + +std::shared_ptr<Function> RttiType::getMethod(const std::string &name) const +{ + initialize(); + auto it = methods.find(name); + if (it == methods.end()) { + return nullptr; + } + return it->second; +} + +std::shared_ptr<PropertyDescriptor> RttiType::getProperty(const std::string &name) const +{ + initialize(); + auto it = properties.find(name); + if (it == properties.end()) { + return nullptr; + } + return it->second; +} + /* Constant initialization */ namespace RttiTypes { diff --git a/src/core/common/Rtti.hpp b/src/core/common/Rtti.hpp index 39a61d3..6449c75 100644 --- a/src/core/common/Rtti.hpp +++ b/src/core/common/Rtti.hpp @@ -64,6 +64,7 @@ #ifndef _OUSIA_RTTI_HPP_ #define _OUSIA_RTTI_HPP_ +#include <memory> #include <typeinfo> #include <typeindex> #include <unordered_map> @@ -73,6 +74,25 @@ namespace ousia { class RttiType; +class Function; +class PropertyDescriptor; + +/** + * Type describing a set of RttiType pointers. + */ +using RttiTypeSet = std::unordered_set<const RttiType *>; + +/** + * Type describing a map containing methods and their name. + */ +using RttiMethodMap = + std::unordered_map<std::string, std::shared_ptr<Function>>; + +/** + * Type describing a map containing properties and their name. + */ +using RttiPropertyMap = + std::unordered_map<std::string, std::shared_ptr<PropertyDescriptor>>; /** * Helper class used to globally store and access the runtime type information. @@ -113,11 +133,6 @@ public: class RttiBuilder { public: /** - * Type describing a set of RttiType pointers. - */ - using RttiTypeSet = std::unordered_set<const RttiType *>; - - /** * Contains the human readable name of the type for which the type * information is being built. */ @@ -134,6 +149,16 @@ public: RttiTypeSet compositeTypes; /** + * Map containing all methods. + */ + RttiMethodMap methods; + + /** + * Map containing all properties. + */ + RttiPropertyMap properties; + + /** * Default constructor, initializes the name of the type described by the * RttiTypeSet with "unknown". */ @@ -177,20 +202,6 @@ public: } /** - * Adds the given type descriptor as "parent" of the type information that - * is being built by this RttiBuilder instance. - * - * @param p is the pointer to the type descriptor that should be added. - * @return a reference to the current RttiBuilder reference to allow method - * chaining. - */ - RttiBuilder &parent(const RttiType &p) - { - parentTypes.insert(&p); - return *this; - } - - /** * Adds the given type descriptors as "parent" of the type information that * is being built by this RttiBuilder instance. * @@ -221,33 +232,44 @@ public: /** * Marks the current type being built by this RttiBuilder instance as being - * a composition of the given other type. + * a composition of the given other types. * * @param p is the pointer to the type descriptor that should be added as * composition type. * @return a reference to the current RttiBuilder reference to allow method * chaining. */ - RttiBuilder &composedOf(const RttiType &p) + RttiBuilder &composedOf(const RttiTypeSet &p) { - compositeTypes.insert(&p); + compositeTypes.insert(p.begin(), p.end()); return *this; } /** - * Marks the current type being built by this RttiBuilder instance as being - * a composition of the given other types. + * Registers a generic (no particular C++ type given) method for this RTTI + * type descriptor. * - * @param p is the pointer to the type descriptor that should be added as - * composition type. + * @param name is the name of the method. Names must be unique for one + * RttiType instance. If the name is not unique, an exception is thrown. + * @param function is the function that should be registered. * @return a reference to the current RttiBuilder reference to allow method * chaining. */ - RttiBuilder &composedOf(const RttiTypeSet &p) - { - compositeTypes.insert(p.begin(), p.end()); - return *this; - } + RttiBuilder &genericMethod(const std::string name, + std::shared_ptr<Function> function); + + /** + * Registers a generic (no particular C++ type given) property descriptor + * for this RTTI type descriptor. + * + * @param name is the name of the property. Names must be unique for one + * RttiType instance. If the property is not unique, an exception is thrown. + * @param property is the property that should be registered. + * @return a reference to the current RttiBuilder reference to allow method + * chaining. + */ + RttiBuilder &genericProperty(const std::string name, + std::shared_ptr<PropertyDescriptor> property); }; /** @@ -269,37 +291,31 @@ private: /** * Set containing references to all parent types, including their parents. */ - mutable std::unordered_set<const RttiType *> parents; + mutable RttiTypeSet parents; /** * Set containing references to all types this type is a composition of, * including all composite types of the original composite types. */ - mutable std::unordered_set<const RttiType *> compositeTypes; + mutable RttiTypeSet compositeTypes; /** - * Adds the parent types of the original parents and the composite types of - * the original composite types to the internal sets for faster lookup. + * Map used for storing all registered methods. */ - void initialize() const; + mutable RttiMethodMap methods; -public: /** - * Human readable name associated with the type. + * Map used for storing all registered properties. */ - const std::string name; + mutable RttiPropertyMap properties; /** - * Default constructor. Creates a Rtti instance with name "unknown" - * and no parents. - */ - RttiType() : name("unknown") {} - - /** - * Constructor for an empty RttiType with the given name. + * Adds the parent types of the original parents and the composite types of + * the original composite types to the internal sets for faster lookup. */ - RttiType(std::string name) : name(std::move(name)) {} + void initialize() const; +protected: /** * Creates a new RttiType instance and registers it in the global type * table. Use the Rtti and the RttiBuilder class for more convenient @@ -313,13 +329,15 @@ public: * are composited (consist of). */ RttiType(std::string name, const std::type_info &native, - std::unordered_set<const RttiType *> parents = - std::unordered_set<const RttiType *>{}, - std::unordered_set<const RttiType *> compositeTypes = - std::unordered_set<const RttiType *>{}) + RttiTypeSet parents = RttiTypeSet{}, + RttiTypeSet compositeTypes = RttiTypeSet{}, + RttiMethodMap methods = RttiMethodMap{}, + RttiPropertyMap properties = RttiPropertyMap{}) : initialized(false), parents(std::move(parents)), compositeTypes(compositeTypes), + methods(std::move(methods)), + properties(std::move(properties)), name(std::move(name)) { RttiStore::store(native, this); @@ -334,13 +352,32 @@ public: */ RttiType(const std::type_info &native, const RttiBuilder &builder) : initialized(false), - parents(builder.parentTypes), - compositeTypes(builder.compositeTypes), - name(builder.currentName) + parents(std::move(builder.parentTypes)), + compositeTypes(std::move(builder.compositeTypes)), + methods(std::move(builder.methods)), + properties(std::move(builder.properties)), + name(std::move(builder.currentName)) { RttiStore::store(native, this); } +public: + /** + * Human readable name associated with the type. + */ + const std::string name; + + /** + * Default constructor. Creates a Rtti instance with name "unknown" + * and no parents. + */ + RttiType() : name("unknown") {} + + /** + * Constructor for an empty RttiType with the given name. + */ + RttiType(std::string name) : name(std::move(name)) {} + /** * Returns true if this Rtti instance is the given type or has the * given type as one of its parents. @@ -359,6 +396,45 @@ public: * type is directly or indirectly composed of it. */ bool composedOf(const RttiType &other) const; + + /** + * Returns all methods that are registered for this type (and the parent + * types, where methods with the same name as those in the parent type + * shadow the parent name methods). + * + * @return a mapping between method name and shared pointers of the + * registered function. + */ + const RttiMethodMap& getMethods() const; + + /** + * Returns all properties that are registered for this type (and the parent + * types, where properties with the same name as those in the parent type + * shadow the parent name properties). + * + * @return a mapping between property name and the shared pointers of the + * registered properties. + */ + const RttiPropertyMap& getProperties() const; + + /** + * Searches for a method with the given name. Returns a shared pointer to + * that method if found or nullptr otherwise. + * + * @param name is the name of the method that should be looked up. + * @return a shared pointer pointing at the method with the given name + */ + std::shared_ptr<Function> getMethod(const std::string &name) const; + + /** + * Searches for a property with the given name. Returns a shared pointer to + * that property if found or nullptr otherwise. + * + * @param name is the name of the property that should be looked up. + * @return a shared pointer pointing at the property with the given name + */ + std::shared_ptr<PropertyDescriptor> getProperty(const std::string &name) const; + }; /** @@ -376,18 +452,8 @@ public: * Creates a new Rtti instance and registers it in the global type table. * * @param name is the name of the type. - * @param parents is a list of parent types. - * @param compositeTypes is a list of types of which instances of this type - * are composited (consist of). */ - Rtti(std::string name, const std::unordered_set<const RttiType *> &parents = - std::unordered_set<const RttiType *>{}, - std::unordered_set<const RttiType *> compositeTypes = - std::unordered_set<const RttiType *>{}) - : RttiType(name, typeid(T), std::move(parents), - std::move(compositeTypes)) - { - } + Rtti(std::string name) : RttiType(name, typeid(T)) {} /** * Creates a new Rtti instance from the data stored in the given builder diff --git a/src/core/common/TypedRttiBuilder.cpp b/src/core/common/TypedRttiBuilder.cpp new file mode 100644 index 0000000..ea836e0 --- /dev/null +++ b/src/core/common/TypedRttiBuilder.cpp @@ -0,0 +1,24 @@ +/* + 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 "TypedRttiBuilder.hpp" + +namespace ousia { + +} + diff --git a/src/core/common/TypedRttiBuilder.hpp b/src/core/common/TypedRttiBuilder.hpp new file mode 100644 index 0000000..e390b38 --- /dev/null +++ b/src/core/common/TypedRttiBuilder.hpp @@ -0,0 +1,171 @@ +/* + 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 TypedRttiBuilder.hpp + * + * Defines a more convenient version of the RttiBuilder. + * + * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de) + */ + +#ifndef _OUSIA_TYPED_RTTI_BUILDER_HPP_ +#define _OUSIA_TYPED_RTTI_BUILDER_HPP_ + +#include "Argument.hpp" +#include "Rtti.hpp" +#include "Function.hpp" +#include "Property.hpp" + +namespace ousia { + +/** + * The TypedRttiBuilder class is a more convenient version of the RttiBuilder + * class which allows simple definition of new methods and properties. + * + * @tparam T is the C++ class for which the type is being built. + */ +template <class T> +class TypedRttiBuilder : public RttiBuilder { +public: + using RttiBuilder::RttiBuilder; + + /** + * Sets the human readable name of the type information being built to the + * given string. + * + * @param s is the name to which the name should be set. + * @return a reference to the current TypedRttiBuilder reference to allow + * method chaining. + */ + TypedRttiBuilder<T> &name(const std::string &s) + { + RttiBuilder::name(s); + return *this; + } + + /** + * Adds the given type descriptor as "parent" of the type information that + * is being built by this RttiBuilder instance. + * + * @param p is the pointer to the type descriptor that should be added. + * @return a reference to the current TypedRttiBuilder reference to allow + * method chaining. + */ + TypedRttiBuilder<T> &parent(const RttiType *p) + { + RttiBuilder::parent(p); + return *this; + } + + /** + * Adds the given type descriptors as "parent" of the type information that + * is being built by this RttiBuilder instance. + * + * @param p is a + * @return a reference to the current TypedRttiBuilder reference to allow + * method chaining. + */ + TypedRttiBuilder<T> &parent(const RttiTypeSet &p) + { + RttiBuilder::parent(p); + return *this; + } + + /** + * Marks the current type being built by this RttiBuilder instance as being + * a composition of the given other type. + * + * @param p is the pointer to the type descriptor that should be added as + * composition type. + * @return a reference to the current TypedRttiBuilder reference to allow + * method chaining. + */ + TypedRttiBuilder<T> &composedOf(const RttiType *p) + { + RttiBuilder::composedOf(p); + return *this; + } + + /** + * Marks the current type being built by this RttiBuilder instance as being + * a composition of the given other types. + * + * @param p is the pointer to the type descriptor that should be added as + * composition type. + * @return a reference to the current TypedRttiBuilder reference to allow + * method chaining. + */ + TypedRttiBuilder<T> &composedOf(const RttiTypeSet &p) + { + RttiBuilder::composedOf(p); + return *this; + } + + /** + * Registers a method for this RTTI type descriptor. + * + * @param name is the name of the method. Names must be unique for one + * RttiType instance. If the name is not unique, an exception is thrown. + * @param method is the function that should be registered. + * @return a reference to the current TypedRttiBuilder reference to allow + * method chaining. + */ + TypedRttiBuilder<T> &method(const std::string name, const Method<T> &method) + { + RttiBuilder::genericMethod(name, std::make_shared<Method<T>>(method)); + return *this; + } + + /** + * Registers a method for this RTTI type descriptor. + * + * @param name is the name of the method. Names must be unique for one + * RttiType instance. If the name is not unique, an exception is thrown. + * @param method is the function that should be registered. + * @return a reference to the current TypedRttiBuilder reference to allow + * method chaining. + */ + TypedRttiBuilder<T> &method(const std::string name, + const typename Method<T>::Callback &method) + { + RttiBuilder::genericMethod(name, std::make_shared<Method<T>>(method)); + return *this; + } + + /** + * Registers a property for this RTTI type descriptor. + * + * @param name is the name of the property. Names must be unique for one + * RttiType instance. If the property is not unique, an exception is thrown. + * @param property is the property that should be registered. + * @return a reference to the current TypedRttiBuilder reference to allow + * method chaining. + */ + TypedRttiBuilder<T> &property(const std::string name, + const Property<T> &property) + { + RttiBuilder::genericProperty(name, + std::make_shared<Property<T>>(property)); + return *this; + } +}; +} + +#endif /* _OUSIA_TYPED_RTTI_BUILDER_HPP_ */ + diff --git a/test/core/common/PropertyTest.cpp b/test/core/common/PropertyTest.cpp index c28feeb..0f3d74e 100644 --- a/test/core/common/PropertyTest.cpp +++ b/test/core/common/PropertyTest.cpp @@ -61,7 +61,8 @@ TEST(Getter, construction) TEST(Getter, validation) { - const PropertyType type{RttiTypes::Int}; + std::shared_ptr<PropertyType> type = + std::make_shared<PropertyType>(RttiTypes::Int); TestObject obj{123}; { @@ -73,7 +74,7 @@ TEST(Getter, validation) { // Int type set, returning strings is an exception Getter<TestObject> getter{getString}; - getter.propertyType = &type; + getter.propertyType = type; ASSERT_THROW(getter.get(&obj), LoggableException); } @@ -109,7 +110,8 @@ TEST(Setter, construction) TEST(Setter, validation) { - const PropertyType type{RttiTypes::Int}; + std::shared_ptr<PropertyType> type = + std::make_shared<PropertyType>(RttiTypes::Int); TestObject obj{123}; Setter<TestObject> setter{TestObject::setA}; @@ -128,7 +130,7 @@ TEST(Setter, validation) setter.set("foo", &obj); ASSERT_EQ(42, obj.a); - setter.propertyType = &type; + setter.propertyType = type; ASSERT_THROW(setter.set("foo", &obj), LoggableException); setter.set(123, &obj); diff --git a/test/core/common/RttiTest.cpp b/test/core/common/RttiTest.cpp index 36bf48f..5d02553 100644 --- a/test/core/common/RttiTest.cpp +++ b/test/core/common/RttiTest.cpp @@ -22,7 +22,10 @@ #include <gtest/gtest.h> +#include <core/common/Function.hpp> #include <core/common/Rtti.hpp> +#include <core/common/TypedRttiBuilder.hpp> +#include <core/common/Variant.hpp> namespace ousia { namespace { @@ -45,18 +48,15 @@ class RttiTestClass7 { extern const Rtti<RttiTestClass6> Type6; extern const Rtti<RttiTestClass7> Type7; -const Rtti<RttiTestClass1> Type1("Type1"); -const Rtti<RttiTestClass2> Type2("Type2"); -const Rtti<RttiTestClass3> Type3("Type3", {&Type1}); -const Rtti<RttiTestClass4> Type4("Type4", {&Type3, &Type2}); -const Rtti<RttiTestClass5> Type5("Type5", - std::unordered_set<const RttiType *>{}, - {&Type6, &Type7}); -const Rtti<RttiTestClass6> Type6("Type6", - std::unordered_set<const RttiType *>{}, - {&Type1}); -const Rtti<RttiTestClass7> Type7("Type7", {&Type6}, - std::unordered_set<const RttiType *>{}); +const Rtti<RttiTestClass1> Type1 = RttiBuilder{"Type1"}; +const Rtti<RttiTestClass2> Type2 = RttiBuilder{"Type2"}; +const Rtti<RttiTestClass3> Type3 = RttiBuilder{"Type3"}.parent(&Type1); +const Rtti<RttiTestClass4> Type4 = + RttiBuilder{"Type4"}.parent({&Type3, &Type2}); +const Rtti<RttiTestClass5> Type5 = + RttiBuilder{"Type5"}.composedOf({&Type6, &Type7}); +const Rtti<RttiTestClass6> Type6 = RttiBuilder{"Type6"}.composedOf(&Type1); +const Rtti<RttiTestClass7> Type7 = RttiBuilder{"Type7"}.parent(&Type6); TEST(Rtti, isa) { @@ -118,6 +118,146 @@ TEST(Rtti, composedOf) ASSERT_FALSE(Type7.composedOf(Type6)); ASSERT_FALSE(Type7.composedOf(Type7)); } + +class RttiMethodTestClass1 { +}; +class RttiMethodTestClass2 { +}; + +static const Rtti<RttiMethodTestClass1> MType1 = + RttiBuilder{"MType1"} + .genericMethod( + "a", std::make_shared<Method<RttiMethodTestClass1>>([]( + Variant::arrayType &args, + RttiMethodTestClass1 *thisPtr) { return Variant{"a"}; })) + .genericMethod( + "b", std::make_shared<Method<RttiMethodTestClass1>>([]( + Variant::arrayType &args, + RttiMethodTestClass1 *thisPtr) { return Variant{"b"}; })) + .genericMethod( + "c", std::make_shared<Method<RttiMethodTestClass1>>([]( + Variant::arrayType &args, + RttiMethodTestClass1 *thisPtr) { return Variant{"c"}; })); + +static const Rtti<RttiMethodTestClass2> MType2 = + TypedRttiBuilder<RttiMethodTestClass2>{"MType2"} + .parent(&MType1) + .method("c", + [](Variant::arrayType &args, + RttiMethodTestClass2 *thisPtr) { return Variant{"c2"}; }) + .method("d", [](Variant::arrayType &args, + RttiMethodTestClass2 *thisPtr) { return Variant{"d"}; }) + .method("e", + {{Argument::Int("a"), Argument::Int("b")}, + [](Variant::arrayType &args, RttiMethodTestClass2 *thisPtr) { + return Variant{args[0].asInt() * args[1].asInt()}; + }}); + +TEST(Rtti, methods) +{ + auto methods = MType1.getMethods(); + ASSERT_TRUE(methods.count("a") > 0); + ASSERT_TRUE(methods.count("b") > 0); + ASSERT_TRUE(methods.count("c") > 0); + + ASSERT_FALSE(MType1.getMethod("a") == nullptr); + ASSERT_FALSE(MType1.getMethod("b") == nullptr); + ASSERT_FALSE(MType1.getMethod("c") == nullptr); + ASSERT_TRUE(MType1.getMethod("d") == nullptr); + + ASSERT_EQ("a", MType1.getMethod("a")->call().asString()); + ASSERT_EQ("b", MType1.getMethod("b")->call().asString()); + ASSERT_EQ("c", MType1.getMethod("c")->call().asString()); + + methods = MType2.getMethods(); + ASSERT_TRUE(methods.count("a") > 0); + ASSERT_TRUE(methods.count("b") > 0); + ASSERT_TRUE(methods.count("c") > 0); + ASSERT_TRUE(methods.count("d") > 0); + + ASSERT_FALSE(MType2.getMethod("a") == nullptr); + ASSERT_FALSE(MType2.getMethod("b") == nullptr); + ASSERT_FALSE(MType2.getMethod("c") == nullptr); + ASSERT_FALSE(MType2.getMethod("d") == nullptr); + + ASSERT_EQ("a", MType2.getMethod("a")->call().asString()); + ASSERT_EQ("b", MType2.getMethod("b")->call().asString()); + ASSERT_EQ("c2", MType2.getMethod("c")->call().asString()); + ASSERT_EQ("d", MType2.getMethod("d")->call().asString()); + ASSERT_EQ(42, + MType2.getMethod("e")->call(Variant::arrayType{6, 7}).asInt()); + ASSERT_THROW(MType2.getMethod("e")->call(Variant::arrayType{6, "7"}), + LoggableException); +} + +class RttiPropertyTestClass1 { +public: + int a; + + RttiPropertyTestClass1() : a(0) {} + + static Variant getA(const RttiPropertyTestClass1 *obj) { return obj->a; } + + static void setA(const Variant &value, RttiPropertyTestClass1 *obj) + { + obj->a = value.asInt(); + } +}; + +class RttiPropertyTestClass2 : public RttiPropertyTestClass1 { +public: + int b; + + RttiPropertyTestClass2() : b(0) {} + + static Variant getB(const RttiPropertyTestClass2 *obj) { return obj->b; } + + static void setB(const Variant &value, RttiPropertyTestClass2 *obj) + { + obj->b = value.asInt(); + } +}; + +static const Rtti<RttiPropertyTestClass1> PType1 = + TypedRttiBuilder<RttiPropertyTestClass1>{"PType1"}.property( + "a", {RttiTypes::Int, RttiPropertyTestClass1::getA, + RttiPropertyTestClass1::setA}); + +static const Rtti<RttiMethodTestClass2> PType2 = + TypedRttiBuilder<RttiPropertyTestClass2>{"PType2"}.parent(&PType1).property( + "b", {RttiTypes::Int, RttiPropertyTestClass2::getB, + RttiPropertyTestClass2::setB}); + +TEST(Rtti, properties) +{ + RttiPropertyTestClass2 obj; + + auto properties = PType1.getProperties(); + ASSERT_TRUE(properties.count("a") > 0); + ASSERT_FALSE(properties.count("b") > 0); + + ASSERT_FALSE(PType1.getProperty("a") == nullptr); + ASSERT_TRUE(PType1.getProperty("b") == nullptr); + + ASSERT_EQ(0, PType1.getProperty("a")->get(&obj).asInt()); + PType1.getProperty("a")->set(4, &obj); + ASSERT_EQ(4, PType1.getProperty("a")->get(&obj).asInt()); + + properties = PType2.getProperties(); + ASSERT_TRUE(properties.count("a") > 0); + ASSERT_TRUE(properties.count("b") > 0); + + ASSERT_FALSE(PType2.getProperty("a") == nullptr); + ASSERT_FALSE(PType2.getProperty("b") == nullptr); + + ASSERT_EQ(4, PType2.getProperty("a")->get(&obj).asInt()); + PType2.getProperty("a")->set(8, &obj); + ASSERT_EQ(8, PType2.getProperty("a")->get(&obj).asInt()); + + ASSERT_EQ(0, PType2.getProperty("b")->get(&obj).asInt()); + PType2.getProperty("b")->set(5, &obj); + ASSERT_EQ(5, PType2.getProperty("b")->get(&obj).asInt()); +} } } diff --git a/test/core/managed/ManagedTest.cpp b/test/core/managed/ManagedTest.cpp index a943f5d..0391738 100644 --- a/test/core/managed/ManagedTest.cpp +++ b/test/core/managed/ManagedTest.cpp @@ -76,10 +76,11 @@ class TypeTestManaged5 : public Managed { using Managed::Managed; }; -static const Rtti<TypeTestManaged1> Type1("Type1"); -static const Rtti<TypeTestManaged2> Type2("Type2"); -static const Rtti<TypeTestManaged3> Type3("Type3", {&Type1}); -static const Rtti<TypeTestManaged4> Type4("Type4", {&Type3, &Type2}); +static const Rtti<TypeTestManaged1> Type1 = RttiBuilder{"Type1"}; +static const Rtti<TypeTestManaged2> Type2 = RttiBuilder{"Type2"}; +static const Rtti<TypeTestManaged3> Type3 = RttiBuilder{"Type3"}.parent(&Type1); +static const Rtti<TypeTestManaged4> Type4 = + RttiBuilder{"Type4"}.parent({&Type3, &Type2}); TEST(Managed, type) { |