diff --git a/include/ki/pclass/Property.h b/include/ki/pclass/Property.h index b57635c..fc117cd 100644 --- a/include/ki/pclass/Property.h +++ b/include/ki/pclass/Property.h @@ -32,15 +32,18 @@ namespace pclass virtual bool is_pointer() const; virtual bool is_dynamic() const; + virtual bool is_array() const; + virtual std::size_t get_element_count() const; + virtual void set_element_count(std::size_t size) = 0; - virtual Value get_value() const = 0; - virtual void set_value(Value value) = 0; + virtual Value get_value(std::size_t index = 0) const = 0; + virtual void set_value(Value value, std::size_t index = 0) = 0; - virtual const PropertyClass *get_object() const = 0; - virtual void set_object(std::unique_ptr &object) = 0; + virtual const PropertyClass *get_object(std::size_t index = 0) const = 0; + virtual void set_object(std::unique_ptr &object, std::size_t index = 0) = 0; - virtual void write_value_to(BitStream &stream) const = 0; - virtual void read_value_from(BitStream &stream) = 0; + virtual void write_value_to(BitStream &stream, std::size_t index = 0) const = 0; + virtual void read_value_from(BitStream &stream, std::size_t index = 0) = 0; private: const PropertyClass *m_instance; @@ -49,41 +52,5 @@ namespace pclass hash_t m_full_hash; const Type *m_type; }; - - /** - * TODO: Documentation - */ - class IDynamicProperty : public IProperty - { - public: - // Do not allow copy assignment. Once a property has been constructed, - // it shouldn't be able to change. - IDynamicProperty & operator=(const IDynamicProperty &that) = delete; - - IDynamicProperty(PropertyClass &object, - const std::string &name, const Type &type); - IDynamicProperty(PropertyClass &object, - const IDynamicProperty &that); - - virtual ~IDynamicProperty() {} - - bool is_dynamic() const override; - virtual std::size_t get_element_count() const = 0; - virtual void set_element_count(std::size_t size) = 0; - - Value get_value() const final override; - void set_value(Value value) final override; - const PropertyClass *get_object() const final override; - void set_object(std::unique_ptr &object) final override; - void write_value_to(BitStream &stream) const final override; - void read_value_from(BitStream &stream) final override; - - virtual Value get_value(int index) const = 0; - virtual void set_value(Value value, int index) = 0; - virtual const PropertyClass *get_object(int index) const = 0; - virtual void set_object(std::unique_ptr &object, int index) = 0; - virtual void write_value_to(BitStream &stream, int index) const = 0; - virtual void read_value_from(BitStream &stream, int index) = 0; - }; } } diff --git a/include/ki/pclass/PropertyClass.h b/include/ki/pclass/PropertyClass.h index 8b2a1aa..46103ff 100644 --- a/include/ki/pclass/PropertyClass.h +++ b/include/ki/pclass/PropertyClass.h @@ -73,7 +73,6 @@ namespace pclass PropertyList &get_properties(); const PropertyList &get_properties() const; - virtual PropertyClass *copy() const; virtual void on_created() const {} protected: diff --git a/include/ki/pclass/StaticProperty.h b/include/ki/pclass/StaticProperty.h index e572379..08b7c2e 100644 --- a/include/ki/pclass/StaticProperty.h +++ b/include/ki/pclass/StaticProperty.h @@ -11,426 +11,415 @@ namespace pclass template class StaticProperty; - /// @cond DOXYGEN_SKIP - /** - * A helper utility that provides the right implementation of construct(), - * copy(), get_object() and set_object(), based on characteristics of type: ValueT. - */ - template < - typename ValueT, - typename IsPointerEnable = void, - typename IsBaseEnable = void - > - struct value_object_helper + namespace detail { - static ValueT construct(const Type &type) + /** + * Provides default implementations for static_object_helper. + */ + template + struct default_static_object_helper { - // In cases where ValueT is not a pointer, and does not derive from PropertyClass, - // just call the default constructor. - return ValueT(); - } + static ValueT construct(const Type &type) + { + // In cases where ValueT is not a pointer, and does not derive from PropertyClass, + // just call the default constructor. + return ValueT(); + } - static ValueT copy(const StaticProperty &prop) + static ValueT copy(const StaticProperty &prop) + { + // In cases where ValueT is not a pointer, and does not derive from PropertyClass, + // just call the copy constructor. + return ValueT(prop.m_value); + } + + static const PropertyClass *get_object( + const StaticProperty &prop, int index) + { + // By default, assume that ValueT is not a + throw runtime_error( + "Tried calling get_object() on a property that does not store an object." + ); + } + + static void set_object(StaticProperty &prop, + std::unique_ptr &object, int index) + { + throw runtime_error( + "Tried calling set_object() on a property that does not store an object." + ); + } + }; + + /** + * Specialization for when ValueT is an explicitly-sized array. + * Removes construct() and copy() to stop the compiler complaining about + * a function returning an array. + */ + template + struct default_static_object_helper { - // In cases where ValueT is not a pointer, and does not derive from PropertyClass, - // just call the copy constructor. - return ValueT(prop.m_value); - } + static const PropertyClass *get_object( + const StaticProperty &prop, int index) + { + // By default, assume that ValueT is not a + throw runtime_error( + "Tried calling get_object() on a property that does not store an object." + ); + } - static Value get_value(const StaticProperty &prop) + static void set_object(StaticProperty &prop, + std::unique_ptr &object, int index) + { + throw runtime_error( + "Tried calling set_object() on a property that does not store an object." + ); + } + }; + + /** + * Provides default implementations of construct() and copy() + * for static_object_helper where ValueT is a pointer type. + */ + template + struct pointer_static_object_helper { - // Return a reference to the property's value - return Value::make_reference(prop.m_value); - } + static ValueT *construct(const Type &type) + { + // The default value of pointers is null + return nullptr; + } - static void set_value(StaticProperty &prop, Value value) + static ValueT *copy(const StaticProperty &prop) + { + // The copy constructor for all pointers is to copy the pointer + // without creating a new copy of the object it's pointing to. + return prop.m_value; + } + + static const PropertyClass *get_object( + const StaticProperty &prop, int index) + { + // By default, assume that ValueT is not a + throw runtime_error( + "Tried calling get_object() on a property that does not store an object." + ); + } + + static void set_object(StaticProperty &prop, + std::unique_ptr &object, int index) + { + throw runtime_error( + "Tried calling set_object() on a property that does not store an object." + ); + } + }; + + /** + * A helper utility that provides the right implementation of construct(), + * copy(), get_object() and set_object(), based on characteristics of type: ValueT. + */ + template < + typename ValueT, + typename Enable = void + > + struct static_object_helper : default_static_object_helper { - prop.m_value = value - .dereference() - .get(); - } + using default_static_object_helper::construct; + using default_static_object_helper::copy; + using default_static_object_helper::get_object; + using default_static_object_helper::set_object; + }; - static const PropertyClass *get_object(const StaticProperty &prop) + /** + * Specialization for when ValueT is: + * - Not a pointer; but + * - does derive from PropertyClass + */ + template + struct static_object_helper< + ValueT, + typename std::enable_if< + std::is_base_of::value + >::type + > + : default_static_object_helper { - // ValueT does not derive from PropertyClass, and so, this property is not - // storing an object. - throw runtime_error( - "Tried calling get_object() on a property that does not store an object." - ); - } + using default_static_object_helper::copy; - static void set_object(StaticProperty &prop, - std::unique_ptr &object) + static ValueT construct(const Type &type) + { + // Derivitives of PropertyClass cannot have a default constructor since + // they require their Type and TypeSystem, so we need to pass these + // along from what the StaticProperty constructor has been given. + return ValueT(type, type.get_type_system()); + } + + static const PropertyClass *get_object( + const StaticProperty &prop, int index) + { + // ValueT does derive from PropertyClass, and we have an instance of ValueT, + // so we can cast down to a PropertyClass pointer. + return dynamic_cast(&prop.m_value); + } + + static void set_object(StaticProperty &prop, + std::unique_ptr &object, int index) + { + // Ensure that object is not nullptr + if (!object) + throw runtime_error("Value cannot be null."); + + // Ensure that object is exactly the type of the property. + assert_type_match(prop.get_type(), object->get_type()); + + // ValueT does derive from PropertyClass, but we don't store a pointer, + // so we need to copy the value in. + prop.m_value = *dynamic_cast(object.get()); + } + }; + + /** + * Specialization for when ValueT is: + * - A pointer; but + * - does not derive from PropertyClass + */ + template + struct static_object_helper< + ValueT *, + typename std::enable_if< + !std::is_base_of::value + >::type + > + : pointer_static_object_helper { - // ValueT does not derive from PropertyClass, and so, this property is not - // storing an object. - throw runtime_error( - "Tried calling set_object() on a property that does not store an object." - ); - } - }; + using pointer_static_object_helper::construct; + using pointer_static_object_helper::copy; + using pointer_static_object_helper::get_object; + using pointer_static_object_helper::set_object; + }; - /** - * Specialization for when ValueT is: - * - A pointer; but - * - does not derive from PropertyClass - * - * This should: - * - Construct to a nullptr; - * - Copy construct to the same pointer as the original; and - * - Throw an exception when get_object() or set_object() is called. - */ - template - struct value_object_helper< - ValueT, - typename std::enable_if< - std::is_pointer::value - >::type, - typename std::enable_if< - !std::is_base_of< - PropertyClass, - typename std::remove_pointer::type - >::value - >::type - > - { - using nonpointer_type = typename std::remove_pointer::type; - - static ValueT construct(const Type &type) + /** + * Specialization for when ValueT is: + * - A pointer; and + * - does derive from PropertyClass + */ + template + struct static_object_helper< + ValueT *, + typename std::enable_if< + std::is_base_of::value + >::type + > + : pointer_static_object_helper { - // The default value of pointers is null - return nullptr; - } + using pointer_static_object_helper::construct; + using pointer_static_object_helper::copy; - static ValueT copy(const StaticProperty &prop) + static const PropertyClass *get_object( + const StaticProperty &prop, int index) + { + // ValueT does derive from PropertyClass, and we have a pointer to an instance + // of ValueT, so we can cast down to a PropertyClass pointer. + return dynamic_cast(prop.m_value); + } + + static void set_object(StaticProperty &prop, + std::unique_ptr &object, int index) + { + // Ensure that object inherits the type of the property + if (object) + assert_type_match(prop.get_type(), object->get_type(), true); + + // ValueT does derive from PropertyClass, and we have a pointer to an instance + // of PropertyClass, so cast the pointer up to a ValueT. + prop.m_value = dynamic_cast(object.release()); + } + }; + + /** + * Specialization for when ValueT is: + * - An explicitly-sized array; and + * - does not derive from PropertyClass + */ + template + struct static_object_helper< + ValueT[N], + typename std::enable_if< + !std::is_base_of::value + >::type + > + : default_static_object_helper { - // The copy constructor for all pointers is to copy the pointer - // without creating a new copy of the object it's pointing to. - return prop.m_value; - } + using default_static_object_helper::get_object; + using default_static_object_helper::set_object; + }; - static Value get_value(const StaticProperty &prop) + /** + * Specialization for when ValueT is: + * - An explicitly-sized array of non-pointer values; and + * - does derive from PropertyClass + */ + template + struct static_object_helper< + ValueT[N], + typename std::enable_if< + std::is_base_of::value + >::type + > { - return Value::make_reference(*prop.m_value); - } + static const PropertyClass *get_object( + const StaticProperty &prop, const int index) + { + // ValueT does derive from PropertyClass, and we have an instance of ValueT, + // so we can cast down to a PropertyClass pointer. + return dynamic_cast(&prop.m_value[index]); + } - static void set_value(StaticProperty &prop, Value value) + static void set_object(StaticProperty &prop, + std::unique_ptr &object, const int index) + { + // Ensure that object is not nullptr + if (!object) + throw runtime_error("Value cannot be null."); + + // Ensure that object is exactly the type of the property. + assert_type_match(prop.get_type(), object->get_type()); + + // ValueT does derive from PropertyClass, but we don't store a pointer, + // so we need to copy the value in. + prop.m_value[index] = *dynamic_cast(object.get()); + } + }; + + /** + * Specialization for when ValueT is: + * - An explicitly-sized array of pointer values; and + * - does derive from PropertyClass + */ + template + struct static_object_helper< + ValueT *[N], + typename std::enable_if< + std::is_base_of::value + >::type + > { - prop.m_value = value - .dereference() - .take(); - } + static const PropertyClass *get_object( + const StaticProperty &prop, const int index) + { + // ValueT does derive from PropertyClass, and we have an instance of ValueT, + // so we can cast down to a PropertyClass pointer. + return dynamic_cast(prop.m_value[index]); + } - static const PropertyClass *get_object(const StaticProperty &prop) - { - // ValueT does not derive from PropertyClass, and so, this property is not - // storing an object. - throw runtime_error( - "Tried calling get_object() on a property that does not store an object." - ); - } + static void set_object(StaticProperty &prop, + std::unique_ptr &object, const int index) + { + // Ensure that object inherits the type of the property + if (object) + assert_type_match(prop.get_type(), object->get_type(), true); - static void set_object(StaticProperty &prop, - std::unique_ptr &object) - { - // ValueT does not derive from PropertyClass, and so, this property is not - // storing an object. - throw runtime_error( - "Tried calling set_object() on a property that does not store an object." - ); - } - }; - - /** - * Specialization for when ValueT is: - * - A pointer; and - * - does derive from PropertyClass - * - * This should: - * - Construct to a nullptr; - * - Copy construct to the same pointer as the original; - * - Return a pointer to a ValueT instance (as a PropertyClass *) - * when get_object() is called; and - * - Throw an exception when the object supplied to set_object() - * does not inherit from the property's type, but set m_value otherwise. - */ - template - struct value_object_helper< - ValueT, - typename std::enable_if< - std::is_pointer::value - >::type, - typename std::enable_if< - std::is_base_of< - PropertyClass, - typename std::remove_pointer::type - >::value - >::type - > - { - using nonpointer_type = typename std::remove_pointer::type; - - static ValueT construct(const Type &type) - { - // The default value of pointers is null - return nullptr; - } - - static ValueT copy(const StaticProperty &prop) - { - // The copy constructor for all pointers is to copy the pointer - // without creating a new copy of the object it's pointing to. - return prop.m_value; - } - - static Value get_value(const StaticProperty &prop) - { - return Value::make_reference(*prop.m_value); - } - - static void set_value(StaticProperty &prop, Value value) - { - prop.m_value = value - .dereference() - .take(); - } - - static const PropertyClass *get_object(const StaticProperty &prop) - { - // ValueT does derive from PropertyClass, and we have a pointer to an instance - // of ValueT, so we can cast down to a PropertyClass pointer. - return dynamic_cast(prop.m_value); - } - - static void set_object(StaticProperty &prop, - std::unique_ptr &object) - { - // Ensure that object inherits the type of the property - if (object) - assert_type_match(prop.get_type(), object->get_type(), true); - - // ValueT does derive from PropertyClass, and we have a pointer to an instance - // of PropertyClass, so cast the pointer up to a ValueT. - prop.m_value = dynamic_cast(object.release()); - } - }; - - /** - * Specialization for when ValueT is: - * - Not a pointer; and - * - does derive from PropertyClass - * - * This should: - * - Construct an instance of ValueT by passing the property - * type and the type's type system; and - * - Return a pointer to a ValueT instance (as a PropertyClass *) - * when get_object() is called. - */ - template - struct value_object_helper< - ValueT, - typename std::enable_if< - !std::is_pointer::value - >::type, - typename std::enable_if< - std::is_base_of< - PropertyClass, - typename std::remove_pointer::type - >::value - >::type - > - { - static ValueT construct(const Type &type) - { - // Derivitives of PropertyClass cannot have a default constructor since - // they require their Type and TypeSystem, so we need to pass these - // along from what the StaticProperty constructor has been given. - return ValueT(type, type.get_type_system()); - } - - static ValueT copy(const StaticProperty &prop) - { - // Derivitives of PropertyClass implement a clone method that returns - // a pointer to a copy. - ValueT *value_ptr = dynamic_cast(prop.m_value.clone()); - ValueT value = *value_ptr; - delete value_ptr; - return value; - } - - static Value get_value(const StaticProperty &prop) - { - return Value::make_reference(prop.m_value); - } - - static void set_value(StaticProperty &prop, Value value) - { - prop.m_value = value - .dereference() - .get(); - } - - static const PropertyClass *get_object(const StaticProperty &prop) - { - // ValueT does derive from PropertyClass, and we have an instance of ValueT, - // so we can cast down to a PropertyClass pointer. - return dynamic_cast(&prop.m_value); - } - - static void set_object(StaticProperty &prop, - std::unique_ptr &object) - { - // Ensure that object is not nullptr - if (!object) - throw runtime_error("Value cannot be null."); - - // Ensure that object is exactly the type of the property. - assert_type_match(prop.get_type(), object->get_type()); - - // ValueT does derive from PropertyClass, but we don't store a pointer, - // so we need to copy the value in. - prop.m_value = *dynamic_cast(object.get()); - } - }; + // ValueT does derive from PropertyClass, and we have a pointer to an instance + // of PropertyClass, so cast the pointer up to a ValueT. + prop.m_value[index] = dynamic_cast(object.release()); + } + }; - /** - * A helper utility that provides the right implementation of write() and read() - * based on whether ValueT is a pointer. - */ - template < - typename ValueT, - typename IsPointerEnable = void - > - struct value_rw_helper - { - static void write(const StaticProperty &prop, BitStream &stream) + /** + * A helper utility that provides the right implementation of + * get_value() and set_value() based on whether ValueT is a pointer. + */ + template + struct static_value_helper { - prop.get_type().write_to( - stream, - Value::make_reference(prop.m_value) - ); - } + static Value get_value(const StaticProperty &prop, int index) + { + return Value::make_reference(prop.m_value); + } - static void read(StaticProperty &prop, BitStream &stream) + static void set_value(StaticProperty &prop, Value value, int index) + { + prop.m_value = value.as().get(); + } + }; + + /** + * Specialization for when ValueT is a pointer. + * + * Dereference the pointer before creating Value instances. + * This is so that the Value stores a pointer to a ValueT instance, + * rather than storing a pointer to a pointer. + */ + template + struct static_value_helper { - Value value = prop.get_type().read_from(stream); - prop.m_value = value.get(); - } - }; + static Value get_value(const StaticProperty &prop, int index) + { + return Value::make_reference(*prop.m_value); + } - /** - * Specialization for when ValueT is a pointer. - * - * Dereference the pointer before creating Value instances. - * This is so that the Value stores a pointer to a ValueT instance, - * rather than storing a pointer to a pointer. - */ - template - struct value_rw_helper< - ValueT, - typename std::enable_if< - std::is_pointer::value - >::type - > - { - using type = typename std::remove_pointer::type; + static void set_value(StaticProperty &prop, Value value, int index) + { + prop.m_value = value.as().release(); + } + }; - static void write(const StaticProperty &prop, BitStream &stream) + /** + * Specialization for when ValueT is an explicitly-sized array + * of non-pointer values. + */ + template + struct static_value_helper { - prop.get_type().write_to( - stream, - Value::make_reference(*prop.m_value) - ); - } + static Value get_value(const StaticProperty &prop, const int index) + { + return Value::make_reference(prop.m_value[index]); + } - static void read(StaticProperty &prop, BitStream &stream) - { - Value value = prop.get_type().read_from(stream); - prop.m_value = value.take(); - } - }; + static void set_value(StaticProperty &prop, Value value, const int index) + { + prop.m_value[index] = value.as().get(); + } + }; - /** - * A helper utility that combines functions provided by value_object_helper - * and value_rw_helper into a single type. - */ - template - struct value_helper - { - static ValueT construct(const Type &type) + /** + * Specialization for when ValueT is an explicitly-sized array + * of pointer values. + */ + template + struct static_value_helper { - return value_object_helper::construct(type); - } + static Value get_value(const StaticProperty &prop, const int index) + { + return Value::make_reference(*prop.m_value[index]); + } - static ValueT copy(const StaticProperty &prop) - { - return value_object_helper::copy(prop); - } - - static Value get_value(const StaticProperty &prop) - { - return value_object_helper::get_value(prop); - } - - static void set_value(StaticProperty &prop, Value value) - { - return value_object_helper::set_value(prop, value); - } - - static const PropertyClass *get_object(const StaticProperty &prop) - { - return value_object_helper::get_object(prop); - } - - static void set_object(StaticProperty &prop, - std::unique_ptr &object) - { - value_object_helper::set_object(prop, object); - } - - static void write(const StaticProperty &prop, BitStream &stream) - { - value_rw_helper::write(prop, stream); - } - - static void read(StaticProperty &prop, BitStream &stream) - { - value_rw_helper::read(prop, stream); - } - }; - /// @endcond + static void set_value(StaticProperty &prop, Value value, const int index) + { + prop.m_value[index] = value.as().release(); + } + }; + } /** * TODO: Documentation */ template - class StaticProperty : public IProperty + class IStaticProperty : public IProperty { - // Allow helper utilities access to m_value - friend value_object_helper; - friend value_rw_helper; - public: // Do not allow copy assignment. Once a property has been constructed, // it shouldn't be able to change. - StaticProperty &operator=(const StaticProperty &that) = delete; + virtual IStaticProperty &operator=(const IStaticProperty &that) = delete; - StaticProperty(PropertyClass &object, + IStaticProperty(PropertyClass &object, const std::string &name, const Type &type) : IProperty(object, name, type) - , m_value(value_helper::construct(type)) {} - StaticProperty(PropertyClass &object, - const std::string &name, const Type &type, ValueT value) - : IProperty(object, name, type) - { - m_value = value; - } - - StaticProperty(PropertyClass &object, const StaticProperty &that) + IStaticProperty(PropertyClass &object, const StaticProperty &that) : IProperty(object, that) - , m_value(value_helper::copy(that)) {} constexpr bool is_pointer() const override @@ -443,34 +432,92 @@ namespace pclass return false; } - Value get_value() const override + void set_element_count(std::size_t size) override { - return value_helper::get_value(*this); + throw runtime_error("Tried to call set_element_count() on a property that is not dynamic."); } - void set_value(Value value) override + void write_value_to(BitStream &stream, const std::size_t index) const override { - value_helper::set_value(*this, value); + if (index < 0 || index >= get_element_count()) + throw runtime_error("Index out of bounds."); + get_type().write_to(stream, get_value(index)); } - const PropertyClass *get_object() const override + void read_value_from(BitStream &stream, const std::size_t index) override { - return value_helper::get_object(*this); + if (index < 0 || index >= get_element_count()) + throw runtime_error("Index out of bounds."); + set_value(get_type().read_from(stream), index); + } + }; + + /** + * A statically-sized property. + */ + template + class StaticProperty : public IStaticProperty + { + // Allow helper utilities access to m_value + friend detail::default_static_object_helper; + friend detail::pointer_static_object_helper; + friend detail::static_object_helper; + friend detail::static_value_helper; + + public: + // Do not allow copy assignment. Once a property has been constructed, + // it shouldn't be able to change. + StaticProperty &operator=(const StaticProperty &that) = delete; + + StaticProperty(PropertyClass &object, + const std::string &name, const Type &type) + : IStaticProperty(object, name, type) + , m_value(detail::static_object_helper::construct(type)) + {} + + StaticProperty(PropertyClass &object, + const std::string &name, const Type &type, ValueT value) + : IStaticProperty(object, name, type) + { + m_value = value; } - void set_object(std::unique_ptr &object) override + StaticProperty(PropertyClass &object, const StaticProperty &that) + : IStaticProperty(object, that) + , m_value(detail::static_object_helper::copy(that)) + {} + + std::size_t get_element_count() const override { - return value_helper::set_object(*this, object); + return 1; } - void write_value_to(BitStream &stream) const override + Value get_value(std::size_t index) const override { - value_helper::write(*this, stream); + if (index < 0 || index >= get_element_count()) + throw runtime_error("Index out of bounds."); + return detail::static_value_helper::get_value(*this, index); } - void read_value_from(BitStream &stream) override + void set_value(Value value, const std::size_t index) override { - value_helper::read(*this, stream); + if (index < 0 || index >= get_element_count()) + throw runtime_error("Index out of bounds."); + detail::static_value_helper::set_value(*this, value, index); + } + + const PropertyClass *get_object(const std::size_t index) const override + { + if (index < 0 || index >= get_element_count()) + throw runtime_error("Index out of bounds."); + return detail::static_object_helper::get_object(*this, index); + } + + void set_object(std::unique_ptr &object, const std::size_t index) override + { + if (index < 0 || index >= get_element_count()) + throw runtime_error("Index out of bounds."); + return detail::static_object_helper::set_object(*this, object, index); } ValueT &get() @@ -511,5 +558,84 @@ namespace pclass protected: ValueT m_value; }; + + /** + * Specialization of StaticProperty to support explicitly-sized arrays. + */ + template + class StaticProperty : public IStaticProperty + { + // Allow helper utilities access to m_value + friend detail::static_object_helper; + friend detail::static_value_helper; + + public: + // Do not allow copy assignment. Once a property has been constructed, + // it shouldn't be able to change. + StaticProperty &operator=(const StaticProperty &that) = delete; + + StaticProperty(PropertyClass &object, + const std::string &name, const Type &type) + : IStaticProperty(object, name, type) + { + for (auto i = 0; i < N; ++i) + m_value[i] = detail::static_object_helper::construct(type); + } + + StaticProperty(PropertyClass &object, const StaticProperty &that) + : IStaticProperty(object, that) + { + for (auto i = 0; i < N; ++i) + m_value[i] = that.m_value[i]; + } + + bool is_array() const override + { + return true; + } + + std::size_t get_element_count() const override + { + return N; + } + + Value get_value(std::size_t index) const override + { + if (index < 0 || index >= get_element_count()) + throw runtime_error("Index out of bounds."); + return detail::static_value_helper::get_value(*this, index); + } + + void set_value(Value value, const std::size_t index) override + { + if (index < 0 || index >= get_element_count()) + throw runtime_error("Index out of bounds."); + detail::static_value_helper::set_value(*this, value, index); + } + + const PropertyClass *get_object(const std::size_t index) const override + { + if (index < 0 || index >= get_element_count()) + throw runtime_error("Index out of bounds."); + return detail::static_object_helper::get_object(*this, index); + } + + void set_object(std::unique_ptr &object, const std::size_t index) override + { + if (index < 0 || index >= get_element_count()) + throw runtime_error("Index out of bounds."); + return detail::static_object_helper::set_object(*this, object, index); + } + + ValueT &operator[](const int index) + { + if (index < 0 || index >= get_element_count()) + throw runtime_error("Index out of bounds."); + return m_value[index]; + } + + protected: + ValueT m_value[N]; + }; } } diff --git a/include/ki/pclass/Value.h b/include/ki/pclass/Value.h index 8d1f9c7..5f9cac1 100644 --- a/include/ki/pclass/Value.h +++ b/include/ki/pclass/Value.h @@ -16,7 +16,7 @@ namespace pclass { template < typename SrcT, typename DestT, - typename SrcEnable = void, typename DestEnable = void + typename Enable = void > struct value_caster; @@ -53,10 +53,7 @@ namespace pclass /** * TODO: Documentation */ - template < - typename SrcT, typename DestT, - typename SrcEnable, typename DestEnable - > + template struct value_caster : value_caster_impl { DestT cast_value(const SrcT &value) const override @@ -238,16 +235,6 @@ namespace pclass Value &operator=(Value &&that) noexcept; ~Value(); - /** - * @returns Whether or the not the value is holding a reference or a value. - */ - bool is_reference() const - { - // If the pointer isn't owned, then it isn't this Value's responsibility - // to clean it up, so we say it's referencing something. - return !m_ptr_is_owned; - } - /** * @returns Whether or not the value being held is of type T. */ @@ -257,34 +244,40 @@ namespace pclass // Do the type hashes match? return m_type_hash == typeid(T).hash_code(); } + + /** + * @returns Whether or the not the value is holding a reference or a value. + */ + bool is_reference() const + { + // If the pointer isn't owned, then it isn't this Value's responsibility + // to clean it up, so we say it's referencing something. + return !m_ptr_is_owned; + } /** - * @tparam T + * @tparam T The expected type. * @returns A new Value instance that owns it's value. */ template Value dereference() const { - // Do we need to attempt casting? if (!is()) - return m_caster->cast_value(*this); + throw runtime_error("Invalid call to Value::dereference."); return Value::make_value(*static_cast(m_value_ptr)); } /** - * @tparam T The expected type. - * @returns A reference to the value being held. - * @throws ki::runtime_error The expected type and the type of the value being held are not the same. + * @tparam T The type to cast to. + * @returns A new Value instance holding a T value. */ template - const T &get() const + Value as() const { - // Make sure they requested the correct type + // Do we need to cast? if (!is()) - throw runtime_error("Invalid call to Value::get."); - - // Return a reference to the value being held - return *static_cast(m_value_ptr); + return m_caster->cast_value(*this); + return Value::make_value(*static_cast(m_value_ptr)); } /** @@ -303,6 +296,22 @@ namespace pclass return *static_cast(m_value_ptr); } + /** + * @tparam T The expected type. + * @returns A reference to the value being held. + * @throws ki::runtime_error The expected type and the type of the value being held are not the same. + */ + template + const T &get() const + { + // Make sure they requested the correct type + if (!is()) + throw runtime_error("Invalid call to Value::get."); + + // Return a reference to the value being held + return *static_cast(m_value_ptr); + } + /** * @tparam T The expected type. * @returns A pointer to the value being held (that the caller takes ownership of). @@ -310,11 +319,11 @@ namespace pclass * @throws ki::runtime_error If the expected type and the type of the value being held are not the same. */ template - T *take() + T *release() { // Make sure this Value is not a reference if (is_reference()) - throw runtime_error("Cannot take ownership from a reference Value."); + throw runtime_error("Cannot release ownership from a reference Value."); // Make sure they requested the correct type if (!is()) @@ -335,7 +344,7 @@ namespace pclass { auto *ptr = static_cast(new T(value)); auto val = Value(ptr, true); - val.construct(); + val.set_type(); return val; } @@ -349,7 +358,7 @@ namespace pclass { auto *ptr = static_cast(&value); auto val = Value(ptr); - val.construct(); + val.set_type(); return val; } @@ -363,7 +372,7 @@ namespace pclass { auto *ptr = const_cast(static_cast(&value)); auto val = Value(ptr); - val.construct(); + val.set_type(); return val; } @@ -381,7 +390,7 @@ namespace pclass * @tparam T The type that is being held. */ template - void construct() + void set_type() { m_type_hash = typeid(T).hash_code(); m_caster = &ValueCaster::get(); diff --git a/include/ki/pclass/VectorProperty.h b/include/ki/pclass/VectorProperty.h index 58fbb1e..298a359 100644 --- a/include/ki/pclass/VectorProperty.h +++ b/include/ki/pclass/VectorProperty.h @@ -46,7 +46,7 @@ namespace pclass Value value, const int index) { prop.at(index) = value - .dereference() + .as() .get(); } @@ -112,8 +112,8 @@ namespace pclass Value value, const int index) { prop.at(index) = value - .dereference() - .take(); + .as() + .release(); } static const PropertyClass *get_object(const VectorProperty &prop, const int index) @@ -174,8 +174,8 @@ namespace pclass Value value, const int index) { prop.at(index) = value - .dereference() - .take(); + .as() + .release(); } static const PropertyClass *get_object(const VectorProperty &prop, const int index) @@ -249,7 +249,7 @@ namespace pclass Value value, const int index) { prop.at(index) = value - .dereference() + .as() .get(); } @@ -347,7 +347,7 @@ namespace pclass Value value = prop.get_type().read_from(stream); ValueT &value_ref = prop.at(index); - value_ref = value.take(); + value_ref = value.release(); } }; @@ -404,7 +404,7 @@ namespace pclass * */ template - class VectorProperty : public std::vector, public IDynamicProperty + class VectorProperty : public std::vector, public IProperty { public: // Do not allow copy assignment. Once a property has been constructed, @@ -413,12 +413,12 @@ namespace pclass VectorProperty(PropertyClass &object, const std::string &name, const Type &type) - : IDynamicProperty(object, name, type) + : IProperty(object, name, type) {} VectorProperty(PropertyClass &object, const VectorProperty &that) - : IDynamicProperty(object, that) + : IProperty(object, that) { // Copy vector values into this vector for (auto i = 0; i < this->size(); i++) @@ -430,6 +430,16 @@ namespace pclass return std::is_pointer::value; } + bool is_dynamic() const override + { + return true; + } + + bool is_array() const override + { + return true; + } + std::size_t get_element_count() const override { return this->size(); @@ -440,36 +450,36 @@ namespace pclass this->resize(size); } - Value get_value(int index) const override + Value get_value(std::size_t index) const override { if (index < 0 || index >= this->size()) throw runtime_error("Index out of bounds."); return vector_value_helper::get_value(*this, index); } - void set_value(Value value, int index) override + void set_value(Value value, std::size_t index) override { if (index < 0 || index >= this->size()) throw runtime_error("Index out of bounds."); return vector_value_helper::set_value(*this, value, index); } - const PropertyClass *get_object(const int index) const override + const PropertyClass *get_object(const std::size_t index) const override { return vector_value_helper::get_object(*this, index); } - void set_object(std::unique_ptr &object, int index) override + void set_object(std::unique_ptr &object, std::size_t index) override { return vector_value_helper::set_object(*this, object, index); } - void write_value_to(BitStream &stream, const int index) const override + void write_value_to(BitStream &stream, const std::size_t index) const override { vector_value_helper::write_value_to(*this, stream, index); } - void read_value_from(BitStream &stream, const int index) override + void read_value_from(BitStream &stream, const std::size_t index) override { vector_value_helper::read_value_from(*this, stream, index); } diff --git a/include/ki/serialization/BinarySerializer.h b/include/ki/serialization/BinarySerializer.h index 68c24e0..63cc476 100644 --- a/include/ki/serialization/BinarySerializer.h +++ b/include/ki/serialization/BinarySerializer.h @@ -46,50 +46,47 @@ namespace serialization virtual ~BinarySerializer() {} /** - * @param object - * @param stream + * @param object The object to write to the stream. + * @param stream The stream to write the object to. */ void save(const pclass::PropertyClass *object, BitStream &stream); /** - * @param dest - * @param stream - * @param size + * @param dest Where to load the PropertyClass instance into. + * @param stream The stream to read the object from. + * @param size The size of the stream's available data. */ void load(std::unique_ptr &dest, BitStream &stream, std::size_t size); protected: /** - * @param object - * @param stream + * @param object The object that is being saved. + * @param stream The stream to write the object header to. + * @returns Whether or not the object is null. */ virtual bool presave_object(const pclass::PropertyClass *object, BitStream &stream) const; /** * Read an object header, and instantiate the necessary PropertyClass. - * @param dest - * @param stream + * @param dest Where to instantiate the PropertyClass. + * @param stream The stream to read the object header from. */ virtual void preload_object( std::unique_ptr &dest, BitStream &stream) const; private: + const pclass::PropertyClass *m_root_object; const pclass::TypeSystem *m_type_system; bool m_is_file; flags m_flags; - const pclass::PropertyClass *m_root_object; - void save_object(const pclass::PropertyClass *object, BitStream &stream) const; void save_property(const pclass::IProperty &prop, BitStream &stream) const; - void save_dynamic_property( - const pclass::IDynamicProperty &prop, BitStream &stream) const; void load_object( std::unique_ptr &dest, BitStream &stream) const; void load_property(pclass::IProperty &prop, BitStream &stream) const; - void load_dynamic_property(pclass::IDynamicProperty &prop, BitStream &stream) const; }; } } diff --git a/include/ki/serialization/JsonSerializer.h b/include/ki/serialization/JsonSerializer.h index 2b627e1..7af326a 100644 --- a/include/ki/serialization/JsonSerializer.h +++ b/include/ki/serialization/JsonSerializer.h @@ -29,12 +29,10 @@ namespace serialization virtual bool presave_object(nlohmann::json &j, const pclass::PropertyClass *object) const; nlohmann::json save_object(const pclass::PropertyClass *object) const; void save_property(nlohmann::json &j, const pclass::IProperty &prop) const; - void save_dynamic_property(nlohmann::json &j, const pclass::IDynamicProperty &prop) const; virtual bool preload_object(std::unique_ptr &dest, nlohmann::json &j) const; void load_object(std::unique_ptr &dest, nlohmann::json &j) const; void load_property(pclass::IProperty &prop, nlohmann::json &j) const; - void load_dynamic_property(pclass::IDynamicProperty &prop, nlohmann::json &j) const; }; } } diff --git a/src/pclass/Property.cpp b/src/pclass/Property.cpp index 09cec69..163bd46 100644 --- a/src/pclass/Property.cpp +++ b/src/pclass/Property.cpp @@ -71,55 +71,14 @@ namespace pclass return false; } - IDynamicProperty::IDynamicProperty(PropertyClass &object, - const std::string& name, const Type& type) - : IProperty(object, name, type) - {} - - IDynamicProperty::IDynamicProperty(PropertyClass &object, - const IDynamicProperty &that) - : IProperty(object, that) - {} - - bool IDynamicProperty::is_dynamic() const + bool IProperty::is_array() const { - return true; + return false; } - Value IDynamicProperty::get_value() const + std::size_t IProperty::get_element_count() const { - // The caller must specify an index - throw runtime_error("Called get_value() on a dynamic property. Use get_value(index) instead."); - } - - void IDynamicProperty::set_value(Value value) - { - // The caller must specify an index - throw runtime_error("Called set_value() on a dynamic property. Use set_value(index) instead."); - } - - const PropertyClass *IDynamicProperty::get_object() const - { - // The caller must specify an index - throw runtime_error("Called get_object() on a dynamic property. Use get_object(index) instead."); - } - - void IDynamicProperty::set_object(std::unique_ptr &object) - { - // The caller must specify an index - throw runtime_error("Called set_object() on a dynamic property. Use set_object(index) instead."); - } - - void IDynamicProperty::write_value_to(BitStream &stream) const - { - // The caller must specify an index - throw runtime_error("Called write_value_to() on a dynamic property. Use write_value_to(index) instead."); - } - - void IDynamicProperty::read_value_from(BitStream &stream) - { - // The caller must specify an index - throw runtime_error("Called read_value_from() on a dynamic property. Use read_value_from(index) instead."); + return 0; } } } diff --git a/src/pclass/PropertyClass.cpp b/src/pclass/PropertyClass.cpp index 6b99548..a4a25ea 100644 --- a/src/pclass/PropertyClass.cpp +++ b/src/pclass/PropertyClass.cpp @@ -37,11 +37,6 @@ namespace pclass return m_properties; } - PropertyClass *PropertyClass::copy() const - { - return new PropertyClass(*this); - } - void PropertyClass::add_property(IProperty &prop) { m_properties.add_property(prop); diff --git a/src/serialization/BinarySerializer.cpp b/src/serialization/BinarySerializer.cpp index 1570191..2a3313a 100644 --- a/src/serialization/BinarySerializer.cpp +++ b/src/serialization/BinarySerializer.cpp @@ -176,27 +176,21 @@ namespace serialization stream.write(prop.get_full_hash()); } - // Is the property dynamic? (holding more than one value) + // If the property is dynamic, write the element count if (prop.is_dynamic()) + stream.write(prop.get_element_count()); + + for (auto i = 0; i < prop.get_element_count(); ++i) { - // Cast the property to a IDynamicProperty - const auto &dynamic_property = - dynamic_cast(prop); - save_dynamic_property(dynamic_property, stream); - } - else if (prop.is_pointer()) - { - // Does this property hold a pointer to another object? - if (prop.get_type().get_kind() == pclass::Type::kind::CLASS) + if (prop.is_pointer() + && prop.get_type().get_kind() == pclass::Type::kind::CLASS) + { // Write the value as a nested object - save_object(prop.get_object(), stream); + save_object(prop.get_object(i), stream); + } else - // Write the value as normal (let the property deal with dereferencing) - prop.write_value_to(stream); + prop.write_value_to(stream, i); } - else - // If the value isn't a pointer, and it's not dynamic, just write it as a value - prop.write_value_to(stream); // Finish writing the property header by writing the length if (m_is_file) @@ -212,32 +206,6 @@ namespace serialization } } - void BinarySerializer::save_dynamic_property( - const pclass::IDynamicProperty& prop, BitStream& stream) const - { - // Write the number of elements - stream.write(prop.get_element_count()); - - // Iterate through the elements - for (auto i = 0; i < prop.get_element_count(); i++) - { - // Is this a collection of pointers? - if (prop.is_pointer()) - { - // Is the property a collection of pointers to other objects? - if (prop.get_type().get_kind() == pclass::Type::kind::CLASS) - // Write the value as a nested object - save_object(prop.get_object(i), stream); - else - // Write the value as normal (let the property deal with dereferencing) - prop.write_value_to(stream, i); - } - else - // If the value isn't a pointer, and it's not dynamic, just write it as a value - prop.write_value_to(stream, i); - } - } - void BinarySerializer::load( std::unique_ptr &dest, BitStream &stream, const std::size_t size) @@ -380,58 +348,24 @@ namespace serialization void BinarySerializer::load_property(pclass::IProperty &prop, BitStream &stream) const { + // If the property is dynamic, we need to load the element count if (prop.is_dynamic()) { - auto &dynamic_property = - dynamic_cast(prop); - load_dynamic_property(dynamic_property, stream); + const auto element_count = stream.read(); + prop.set_element_count(element_count); } - else if (prop.is_pointer()) + + for (auto i = 0; i < prop.get_element_count(); ++i) { - // Does this property hold a pointer to another object? - if (prop.get_type().get_kind() == pclass::Type::kind::CLASS) + if (prop.is_pointer() && + prop.get_type().get_kind() == pclass::Type::kind::CLASS) { // Read the object as a nested object std::unique_ptr object = nullptr; load_object(object, stream); - prop.set_object(object); + prop.set_object(object, i); } else - // Read the value as normal (let the property deal with dereferencing) - prop.read_value_from(stream); - } - else - // If the value isn't a pointer, and it's not dynamic, just read it as a value - prop.read_value_from(stream); - } - - void BinarySerializer::load_dynamic_property( - pclass::IDynamicProperty& prop, BitStream& stream) const - { - // How many elements are there in this dynamic property? - const auto element_count = stream.read(); - prop.set_element_count(element_count); - - // Load each of the elements - for (uint16_t i = 0; i < element_count; i++) - { - // Is this a collection of pointers? - if (prop.is_pointer()) - { - // Is the property a collection of pointers to other objects? - if (prop.get_type().get_kind() == pclass::Type::kind::CLASS) - { - // Read the object as a nested object - std::unique_ptr object = nullptr; - load_object(object, stream); - prop.set_object(object, i); - } - else - // Read the value as normal (let the property deal with dereferencing) - prop.read_value_from(stream, i); - } - else - // If the value isn't a pointer, and it's not dynamic, just read it as a value prop.read_value_from(stream, i); } } diff --git a/src/serialization/JsonSerializer.cpp b/src/serialization/JsonSerializer.cpp index 51fe460..a6e7316 100644 --- a/src/serialization/JsonSerializer.cpp +++ b/src/serialization/JsonSerializer.cpp @@ -50,48 +50,28 @@ namespace serialization return j; } - void JsonSerializer::save_property(json& j, - const pclass::IProperty &prop) const + void JsonSerializer::save_property( + json& j, const pclass::IProperty &prop) const { - if (prop.is_dynamic()) - { - const auto &dynamic_property = - dynamic_cast(prop); - return save_dynamic_property(j, dynamic_property); - } - - if (prop.get_type().get_kind() == pclass::Type::kind::CLASS) - { - auto *other_object = prop.get_object(); - j[prop.get_name()] = save_object(other_object); - } - else - { - auto value = prop.get_value().dereference(); - j[prop.get_name()] = value.get(); - } - } - - void JsonSerializer::save_dynamic_property(json& j, - const pclass::IDynamicProperty &prop) const - { - json property_value; - - for (auto i = 0; i < prop.get_element_count(); ++i) + for (std::size_t i = 0; i < prop.get_element_count(); ++i) { if (prop.get_type().get_kind() == pclass::Type::kind::CLASS) { auto *other_object = prop.get_object(i); - property_value.push_back(save_object(other_object)); + if (prop.is_array()) + j[prop.get_name()].push_back(save_object(other_object)); + else + j[prop.get_name()] = save_object(other_object); } else { - auto value = prop.get_value(i).dereference(); - property_value.push_back(value.get()); + auto value = prop.get_value(i).as(); + if (prop.is_array()) + j[prop.get_name()].push_back(value.get()); + else + j[prop.get_name()] = value.get(); } } - - j[prop.get_name()] = property_value; } void JsonSerializer::load(std::unique_ptr &dest, @@ -159,42 +139,30 @@ namespace serialization auto &property_j = j[prop.get_name()]; if (prop.is_dynamic()) - { - auto &dynamic_property = - dynamic_cast(prop); - return load_dynamic_property(dynamic_property, j); - } + prop.set_element_count(property_j.size()); - if (prop.get_type().get_kind() == pclass::Type::kind::CLASS) - { - std::unique_ptr other_object = nullptr; - load_object(other_object, property_j); - prop.set_object(other_object); - } - else - prop.set_value( - pclass::Value::make_reference(property_j) - ); - } - - void JsonSerializer::load_dynamic_property( - pclass::IDynamicProperty &prop, json &j) const - { - auto &property_j = j[prop.get_name()]; - prop.set_element_count(property_j.size()); - - for (auto i = 0; i < prop.get_element_count(); ++i) + for (std::size_t i = 0; i < prop.get_element_count(); ++i) { if (prop.get_type().get_kind() == pclass::Type::kind::CLASS) { std::unique_ptr other_object = nullptr; - load_object(other_object, property_j.at(i)); + if (prop.is_array()) + load_object(other_object, property_j.at(i)); + else + load_object(other_object, property_j); prop.set_object(other_object, i); } else - prop.set_value( - pclass::Value::make_reference(property_j.at(i)), i - ); + { + if (prop.is_array()) + prop.set_value( + pclass::Value::make_reference(property_j.at(i)), i + ); + else + prop.set_value( + pclass::Value::make_reference(property_j), i + ); + } } } }