#pragma once #include #include #include "ki/pclass/Property.h" #include "ki/util/exception.h" namespace ki { namespace pclass { // Forward declare for our helpers template class VectorProperty; namespace detail { /** * Provides default implementations for vector_object_helper. */ template struct default_vector_object_helper { static ValueT copy(VectorProperty &prop, const int index) { // In cases where ValueT is not a pointer, and does not derive from PropertyClass, // just call the copy constructor. return ValueT(prop.at(index)); } static const PropertyClass *get_object(const VectorProperty &prop, const int index) { // 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(VectorProperty &prop, std::unique_ptr &object, const int index) { // 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." ); } }; /** * Provides default implementations of copy() * for static_object_helper where ValueT is a pointer type. * @tparam ValueT The type of the value (as a non-pointer type). */ template struct pointer_vector_object_helper { static ValueT *copy(const VectorProperty &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 VectorProperty &prop, const 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(VectorProperty &prop, std::unique_ptr &object, const 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 copy(), * get_object() and set_object(), based on characteristics of type: ValueT. */ template < typename ValueT, typename Enable = void > struct vector_object_helper : default_vector_object_helper { using default_vector_object_helper::copy; using default_vector_object_helper::get_object; using default_vector_object_helper::set_object; }; /** * Specialization for when ValueT is: * - Not a pointer; but * - does derive from PropertyClass */ template struct vector_object_helper< ValueT, typename std::enable_if< std::is_base_of::value >::type > : default_vector_object_helper { using default_vector_object_helper::copy; static const PropertyClass *get_object( const VectorProperty &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.at(index)); } static void set_object(VectorProperty &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.at(index) = *dynamic_cast(object.get()); } }; /** * Specialization for when ValueT is: * - A pointer; but * - does not derive from PropertyClass */ template struct vector_object_helper< ValueT *, typename std::enable_if< !std::is_base_of::value >::type > : pointer_vector_object_helper { using pointer_vector_object_helper::copy; using pointer_vector_object_helper::get_object; using pointer_vector_object_helper::set_object; }; /** * Specialization for when ValueT is: * - A pointer; and * - does derive from PropertyClass */ template struct vector_object_helper< ValueT *, typename std::enable_if< std::is_base_of::value >::type > : pointer_vector_object_helper { using pointer_vector_object_helper::copy; static const PropertyClass *get_object( const VectorProperty &prop, const 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.at(index)); } static void set_object(VectorProperty &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); // ValueT does derive from PropertyClass, and we have a pointer to an instance // of PropertyClass, so cast the pointer up to a ValueT. prop.at(index) = dynamic_cast(object.release()); } }; /** * A helper utility that provides the right implementation of * get_value() and set_value() based on whether ValueT is a pointer * for vector properties. */ template struct vector_value_helper { static Value get_value(const VectorProperty &prop, const int index) { return Value::make_reference(prop.at(index)); } static void set_value(VectorProperty &prop, Value value, const int index) { Value casted_value = value.as(); prop.at(index) = casted_value.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 vector_value_helper { static Value get_value(const VectorProperty &prop, const int index) { ValueT *value_ptr = prop.at(index); if (value_ptr == nullptr) throw runtime_error("Called get_value() but value is nullptr."); return Value::make_reference(*value_ptr); } static void set_value(VectorProperty &prop, Value value, const int index) { Value casted_value = value.as(); prop.at(index) = casted_value.release(); } }; } /** * A dynamically-sized array property. */ template class VectorProperty : public std::vector, public IProperty { public: // Do not allow copy assignment. Once a property has been constructed, // it shouldn't be able to change. VectorProperty &operator=(const VectorProperty &that) = delete; VectorProperty(PropertyClass &object, const std::string &name, const Type &type) : IProperty(object, name, type) {} VectorProperty(PropertyClass &object, const VectorProperty &that) : IProperty(object, that) { // Copy vector values into this vector for (auto i = 0; i < this->size(); i++) this->push_back(detail::vector_value_helper::copy(*this, i)); } constexpr bool is_pointer() const override { 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(); } void set_element_count(const std::size_t size) override { this->resize(size); } Value get_value(std::size_t index) const override { if (index < 0 || index >= this->size()) throw runtime_error("Index out of bounds."); return detail::vector_value_helper::get_value(*this, index); } void set_value(Value value, std::size_t index) override { if (index < 0 || index >= this->size()) throw runtime_error("Index out of bounds."); return detail::vector_value_helper::set_value(*this, value, index); } const PropertyClass *get_object(const std::size_t index) const override { if (index < 0 || index >= this->size()) throw runtime_error("Index out of bounds."); return detail::vector_object_helper::get_object(*this, index); } void set_object(std::unique_ptr &object, std::size_t index) override { if (index < 0 || index >= this->size()) throw runtime_error("Index out of bounds."); return detail::vector_object_helper::set_object(*this, object, index); } }; } }