#pragma once #include "ki/pclass/Property.h" #include "ki/util/exception.h" namespace ki { namespace pclass { // Forward declare StaticProperty for our helpers 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 { 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) { // 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) { // 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; 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 > { 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) { // 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) { // 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 > { 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) { // 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 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()); } }; /** * 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) { prop.get_type().write_to( stream, Value::make_reference(prop.m_value) ); } static void read(StaticProperty &prop, BitStream &stream) { Value value = prop.get_type().read_from(stream); prop.m_value = 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 value_rw_helper< ValueT, typename std::enable_if< std::is_pointer::value >::type > { using type = typename std::remove_pointer::type; static void write(const StaticProperty &prop, BitStream &stream) { prop.get_type().write_to( stream, Value::make_reference(*prop.m_value) ); } static void read(StaticProperty &prop, BitStream &stream) { Value value = prop.get_type().read_from(stream); prop.m_value = value.take(); } }; /** * 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) { return value_object_helper::construct(type); } static ValueT copy(const StaticProperty &prop) { return value_object_helper::copy(prop); } 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 /** * TODO: Documentation */ template class StaticProperty : 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; StaticProperty(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) : IProperty(object, that) , m_value(value_helper::copy(that)) {} constexpr bool is_pointer() const override { return std::is_pointer::value; } constexpr bool is_dynamic() const override { return false; } Value get_value() const override { return Value::make_reference(m_value); } const PropertyClass *get_object() const override { return value_helper::get_object(*this); } void set_object(std::unique_ptr &object) override { return value_helper::set_object(*this, object); } void write_value_to(BitStream &stream) const override { value_helper::write(*this, stream); } void read_value_from(BitStream &stream) override { value_helper::read(*this, stream); } 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; }; } }