/* 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 Rtti.hpp * * Classes used for storing runtime type information (RTTI). RTTI is used to * resolve objects of a certain type in the object graph and to attach * information that should be accessible to the script engine. * * Why is this needed? C++ provides the typeid operator to * retrieve a reference at an internal table associated with type information * for the given class. However, there is no native way for attaching additonal * information to this type information table. Additional information we need to * store is the inheritance graph (which cannot easily be extracted from C++) * and information relevant for script engines (such as a list of methods and * properties). One could of course store information about the type within each * instance of this type, however when managing thousands of objects * this would create a significant overhead. * * How to use: The Rtti class allows to attach information to a certain * C++ class. To do so, create a global constant of the type Rtti in the * source file associated with the type declaration, where T is the type you * want to register. As the type must only be registered once, you must not * declare the variable as "static" in the header file (this would register it * whever the header is included). If you want to access the global constant * from other Rtti definitions (as parent), create a forward declaration * in the header file. If you want to access the RTTI of a certain object or * type, use the global typeOf() function (however, don't use it * within global variable initializations). * * Example: * In the header file: * \code{.hpp} * // Only needed if the type needs to be accessed * // from other compilation units! * namespace RttiTypes { * extern const Rtti MyT; * } * \endcode * In the source file: * \code{.cpp} * #include * * // [...] * * namespace RttiTypes { * const Rtti MyT = RttiBuilder("MyT"); * } * \endcode * * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de) */ #ifndef _OUSIA_RTTI_HPP_ #define _OUSIA_RTTI_HPP_ #include #include #include #include #include #include namespace ousia { class Rtti; class Function; class PropertyDescriptor; /** * Type describing a set of Rtti pointers. */ using RttiSet = std::unordered_set; /** * Type describing a map containing methods and their name. */ using RttiMethodMap = std::unordered_map>; /** * Type describing a map containing properties and their name. */ using RttiPropertyMap = std::unordered_map>; /** * Helper class used to globally store and access the runtime type information. */ class RttiStore { private: /** * Function used internally to access the static map storing all registered * native types and their corresponding type information. */ static std::unordered_map &table(); public: /** * Registers the given pointer to the Rtti class in the RTTI table. Does * not override information for already registered types. * * @param native is a reference at the native type information provided * by the compiler. * @param rtti is a pointer pointing at the type information that should be * stored for this type. */ static void store(const std::type_info &native, const Rtti *rtti); /** * Looks up the type information stored for the given native type * information. */ static const Rtti *lookup(const std::type_info &native); }; /** * The RttiBuilderBase class is used to build new instances of the Rtti or the * Rtti class. It follows the "Builder" pattern and allows to create * the properties of the Rtti class by chaining method calls. The RttiType * class can be constructed from the RttiBuilderBase instance. Use the * RttiBuilder class for a more convenient, templated version that does not * require the native C++ type in the constructor and allows for more convenient * definition of methods and properties. */ class RttiBuilderBase { public: /** * Reference at the native type for which the Rtti information is currently * being built by the RttiBuilder. */ const std::type_info &native; /** * Contains the human readable name of the type for which the type * information is being built. */ std::string currentName; /** * Set containing references to all parent types. */ RttiSet parentTypes; /** * Set containing references to all composite types. */ RttiSet compositeTypes; /** * Map containing all methods. */ RttiMethodMap methods; /** * Map containing all properties. */ RttiPropertyMap properties; /** * Default constructor, initializes the name of the type described by the * RttiSet with "unknown". * * @param native is the native C++ type information for which the type * information is being built. */ RttiBuilderBase(const std::type_info &native) : native(native), currentName("unknown"){}; /** * Default constructor, initializes the name of the type described by the * RttiSet with the given name. * * @param native is the native C++ type information for which the type * information is being built. * @param name is the initial name of the type described by the type * builder. */ RttiBuilderBase(const std::type_info &native, std::string name) : native(native), currentName(std::move(name)){}; /** * 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 RttiBuilderBase instance to allow * method chaining. */ RttiBuilderBase &name(const std::string &s) { currentName = s; return *this; } /** * Adds the given type descriptor as "parent" of the type information that * is being built by this RttiBuilderBase instance. * * @param p is the pointer to the type descriptor that should be added. * @return a reference to the current RttiBuilderBase instance to allow * method chaining. */ RttiBuilderBase &parent(const Rtti *p) { parentTypes.insert(p); return *this; } /** * Adds the given type descriptors as "parent" of the type information that * is being built by this RttiBuilderBase instance. * * @param p is the pointer to the type descriptor that should be added. * @return a reference to the current RttiBuilderBase instance to allow * method chaining. */ RttiBuilderBase &parent(const RttiSet &p) { parentTypes.insert(p.begin(), p.end()); return *this; } /** * Marks the current type being built by this RttiBuilderBase 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 RttiBuilderBase instance to allow * method chaining. */ RttiBuilderBase &composedOf(const Rtti *p) { compositeTypes.insert(p); return *this; } /** * Marks the current type being built by this RttiBuilderBase 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 RttiBuilderBase instance to allow * method chaining. */ RttiBuilderBase &composedOf(const RttiSet &p) { compositeTypes.insert(p.begin(), p.end()); return *this; } /** * Registers a generic (no particular C++ type given) method for this RTTI * type descriptor. * * @param name is the name of the method. Names must be unique for one * Rtti 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 RttiBuilderBase instance to allow * method chaining. */ RttiBuilderBase &genericMethod(const std::string &name, std::shared_ptr 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 * Rtti 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 RttiBuilderBase instance to allow * method chaining. */ RttiBuilderBase &genericProperty( const std::string &name, std::shared_ptr property); }; /** * The Rtti class allows for attaching data to native types that can be * accessed at runtime. This type information can e.g. be retrieved using the * "type" method of the Managed class. This system is used for attaching human * readable names, parent types and script engine functionality. Use the * Rtti class for convenient registration of type information. */ class Rtti { private: /** * Set to true if once the parents and the composite types list have been * completed (by including the parents of the original parent elements and * the composite types of the original composite types). */ mutable bool initialized; /** * Set containing references to all parent types, including their parents. */ mutable RttiSet parents; /** * Set containing references to all types this type is a composition of, * including all composite types of the original composite types. */ mutable RttiSet compositeTypes; /** * Map used for storing all registered methods. */ mutable RttiMethodMap methods; /** * Map used for storing all registered properties. */ mutable RttiPropertyMap properties; /** * Adds the parent types of the original parents and the composite types of * the original composite types to the internal sets for faster lookup. */ void initialize() const; public: /** * Human readable name associated with the type. */ const std::string name; /** * Creates a new Rtti instance and registers it in the global type * table. Use the Rtti class for more convenient registration of type * information. * * @param builder is the builder instance containing the Rtti data. */ Rtti(const RttiBuilderBase &builder) : initialized(false), 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(builder.native, this); } /** * Default constructor. Creates a Rtti instance with name "unknown" * and no parents. */ Rtti() : name("unknown") {} /** * Constructor for an empty Rtti with the given name. */ Rtti(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. * * @param other is the other type for which the relation to this type * should be checked. * @return true if this type (directly or indirectly) has the given other * type as parent or equals the other type. */ bool isa(const Rtti *other) const; /** * Returns true if this Rtti instance is one of the given types. * * @param others is a set of other types to be checked. * @return true if this type (directly or indirectly) has once of the given * other types as parent or equals one of the other types. */ bool isOneOf(const RttiSet &others) const; /** * Checks whether any type in the first set is one type in the second set. * * @param s1 is the first set. For each type in this set we check whether * it is one of the types in s2. * @param s2 is the second set. * @return true if the above condition is fulfilled, false otherwise. */ static bool setIsOneOf(const RttiSet &s1, const RttiSet &s2); /** * Calculates the intersection of two RttiSets. Only the elements of s1 * which are at least one element in s2 are returned. * * @param s1 is the first set. For each type in this set we check whether * it is one of the types in s2, only those elements are returned. * @param s2 is the second set. * @return s1 restricted to the types in s2. */ static RttiSet setIntersection(const RttiSet &s1, const RttiSet &s2); /** * Returns true if an instance of this type may have references to the other * given type. This mechanism is used to prune impossible paths when * resolving objects of a certain type by name in an object graph. * * @param other is the other type for which should be checked whether this * type is directly or indirectly composed of it. */ bool composedOf(const Rtti *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 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 getProperty( const std::string &name) const; /** * Returns true if a method with the given name is registered for this type. * * @param name is the name of the method that should be looked up. * @return true if a method with this name exists, false otherwise. */ bool hasMethod(const std::string &name) const; /** * Returns true if a property with the given name is registered for this * type. * * @param name is the name of the property that should be looked up. * @return true if a property with this name exists, false otherwise. */ bool hasProperty(const std::string &name) const; }; /** * Function that can be used to retrieve the RTTI information of a Managed * object. Do not use this function in the initialization of global Rtti * variables, use pointers at the other global variable instead (as the * initialization order is not well defined). * * @tparam T is the C++ type for which the type information should be returned. */ template inline const Rtti *typeOf() { return RttiStore::lookup(typeid(T)); } /** * Function that can be used to retrieve the RTTI information of a Managed * object. Do not use this function in the initialization of global Rtti * variables, use pointers at the other global variable instead (as the * initialization order is not well defined). * * @tparam T is the C++ type for which the type information should be returned. * @param obj is a dummy object for which the type information should be * returned. */ template inline const Rtti *typeOf(const T &obj) { return RttiStore::lookup(typeid(obj)); } namespace RttiTypes { /** * Type of no particular type. */ extern const Rtti None; /** * Nullptr type for use by the Variant::getRtti method. */ extern const Rtti Nullptr; /** * Bool type for use by the Variant::getRtti method. */ extern const Rtti Bool; /** * Integer type for use by the Variant::getRtti method. */ extern const Rtti Int; /** * Double type for use by the Variant::getRtti method. */ extern const Rtti Double; /** * String type for use by the Variant::getRtti method. */ extern const Rtti String; /** * Array type for use by the Variant::getRtti method. */ extern const Rtti Array; /** * Map type for use by the Variant::getRtti method. */ extern const Rtti Map; /** * Cardinality type for use by the Variant::getRtti method. */ extern const Rtti Cardinality; /** * Function type for use by the Variant::getRtti method. */ extern const Rtti Function; } } #endif /* _OUSIA_RTTI_HPP_ */