#pragma once #include "ki/pclass/Property.h" #include "ki/pclass/PropertyClass.h" #include "ki/util/exception.h" namespace ki { namespace pclass { // Forward declare StaticProperty for our helpers template class StaticProperty; namespace detail { /** * Provides default implementations for static_object_helper. */ template struct default_static_object_helper { 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) { // 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 { 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." ); } }; /** * Provides default implementations of construct(), copy() * get_object(), and set_object() 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_static_object_helper { 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 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 { 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; }; /** * 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 { using default_static_object_helper::copy; 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 { 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; 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 { using pointer_static_object_helper::construct; using pointer_static_object_helper::copy; 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 { using default_static_object_helper::get_object; using default_static_object_helper::set_object; }; /** * 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 > { 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_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 > { 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_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); // 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 * get_value() and set_value() based on whether ValueT is a pointer. */ template struct static_value_helper { static Value get_value(const StaticProperty &prop, int index) { return Value::make_reference(prop.m_value); } static void set_value(StaticProperty &prop, Value value, int index) { Value casted_value = value.as(); prop.m_value = 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 static_value_helper { static Value get_value(const StaticProperty &prop, int index) { if (prop.m_value == nullptr) throw runtime_error("Called get_value() but value is nullptr."); return Value::make_reference(*prop.m_value); } static void set_value(StaticProperty &prop, Value value, int index) { Value casted_value = value.as(); prop.m_value = casted_value.release(); } }; /** * Specialization for when ValueT is an explicitly-sized array * of non-pointer values. */ template struct static_value_helper { static Value get_value(const StaticProperty &prop, const int index) { return Value::make_reference(prop.m_value[index]); } static void set_value(StaticProperty &prop, Value value, const int index) { Value casted_value = value.as(); prop.m_value[index] = casted_value.get(); } }; /** * Specialization for when ValueT is an explicitly-sized array * of pointer values. */ template struct static_value_helper { static Value get_value(const StaticProperty &prop, const int index) { return Value::make_reference(*prop.m_value[index]); } static void set_value(StaticProperty &prop, Value value, const int index) { Value casted_value = value.as(); prop.m_value[index] = casted_value.release(); } }; } /** * Base type for StaticProperty. * This is used to remove the amount of repeated code in specializations. */ template class IStaticProperty : public IProperty { public: using IProperty::IProperty; // Do not allow copy assignment. Once a property has been constructed, // it shouldn't be able to change. virtual IStaticProperty &operator=(const IStaticProperty &that) = delete; constexpr bool is_pointer() const override { return std::is_pointer::value; } constexpr bool is_dynamic() const override { return false; } void set_element_count(std::size_t size) override { throw runtime_error("Tried to call set_element_count() on a property that is not dynamic."); } }; /** * 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, IProperty::flags flags) : IStaticProperty(object, name, type, flags) , m_value(detail::static_object_helper::construct(type)) {} StaticProperty(PropertyClass &object, const std::string &name, const Type &type, IProperty::flags flags, ValueT value) : IStaticProperty(object, name, type, flags) { m_value = value; } 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 1; } 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 &get() { return m_value; } const ValueT &get() const { return m_value; } operator ValueT &() { return m_value; } operator const ValueT &() const { return m_value; } ValueT *operator&() { return &m_value; } const ValueT *operator&() const { return &m_value; } void operator=(const ValueT &value) { m_value = value; } 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, IProperty::flags flags) : IStaticProperty(object, name, type, flags) { 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]; }; } }