#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; /** * A helper utility that provides the right implementation of construct() * and get_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 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." ); } }; /** * Specialization for when ValueT is: * - A pointer; but * - does not derive from PropertyClass * * This should construct to a nullptr, and throw an exception * 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) { // The default value of pointers is null return nullptr; } 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." ); } }; /** * Specialization for when ValueT is: * - A pointer; and * - does derive from PropertyClass * * This should construct to a nullptr, 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) { // The default value of pointers is null return nullptr; } 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); } }; /** * 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 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); } }; /** * 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, prop.m_value); } static void read(StaticProperty &prop, BitStream &stream) { prop.get_type().read_from(stream, Value(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 > { static void write(const StaticProperty &prop, BitStream &stream) { prop.get_type().write_to(stream, *prop.m_value); } static void read(StaticProperty &prop, BitStream &stream) { prop.get_type().read_from(stream, Value(*prop.m_value)); } }; /** * 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 const PropertyClass *get_object(const StaticProperty &prop) { return value_object_helper::get_object(prop); } 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); } }; /** * TODO: Documentation */ template class StaticProperty : public PropertyBase { // Allow helper utilities access to m_value friend value_object_helper; friend value_rw_helper; public: StaticProperty(PropertyClass &object, const std::string &name, const Type &type) : PropertyBase(object, name, type) , m_value(value_helper::construct(type)) {} StaticProperty(PropertyClass &object, const std::string &name, const Type &type, ValueT value) : PropertyBase(object, name, type) { m_value = value; } constexpr bool is_dynamic() const override { return false; } constexpr bool is_pointer() const override { return std::is_pointer::value; } 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); } const PropertyClass *get_object() const override { return value_helper::get_object(*this); } ValueT &get() { return m_value; } Value get_value() const override { return m_value; } operator ValueT &() const { return m_value; } ValueT *operator&() const { return &m_value; } void operator=(const ValueT &value) { m_value = value; } protected: ValueT m_value; }; } }