libki/include/ki/pclass/StaticProperty.h

443 lines
11 KiB
C++

#pragma once
#include "ki/pclass/Property.h"
#include "ki/util/exception.h"
namespace ki
{
namespace pclass
{
// Forward declare StaticProperty<ValueT> for our helpers
template <typename ValueT>
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<ValueT> &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<ValueT> &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<ValueT> &prop, PropertyClass *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 <typename ValueT>
struct value_object_helper<
ValueT,
typename std::enable_if<
std::is_pointer<ValueT>::value
>::type,
typename std::enable_if<
!std::is_base_of<
PropertyClass,
typename std::remove_pointer<ValueT>::type
>::value
>::type
>
{
static ValueT construct(const Type &type)
{
// The default value of pointers is null
return nullptr;
}
static ValueT copy(const StaticProperty<ValueT> &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<ValueT> &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<ValueT> &prop, PropertyClass *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 <typename ValueT>
struct value_object_helper<
ValueT,
typename std::enable_if<
std::is_pointer<ValueT>::value
>::type,
typename std::enable_if<
std::is_base_of<
PropertyClass,
typename std::remove_pointer<ValueT>::type
>::value
>::type
>
{
static ValueT construct(const Type &type)
{
// The default value of pointers is null
return nullptr;
}
static ValueT copy(const StaticProperty<ValueT> &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<ValueT> &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<const PropertyClass *>(prop.m_value);
}
static void set_object(StaticProperty<ValueT> &prop, PropertyClass *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<ValueT>(object);
}
};
/**
* 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 <typename ValueT>
struct value_object_helper<
ValueT,
typename std::enable_if<
!std::is_pointer<ValueT>::value
>::type,
typename std::enable_if<
std::is_base_of<
PropertyClass,
typename std::remove_pointer<ValueT>::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<ValueT> &prop)
{
// Derivitives of PropertyClass implement a clone method that returns
// a pointer to a copy.
ValueT *value_ptr = dynamic_cast<ValueT *>(prop.m_value.clone());
ValueT value = *value_ptr;
delete value_ptr;
return value;
}
static const PropertyClass *get_object(const StaticProperty<ValueT> &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<const PropertyClass *>(&prop.m_value);
}
static void set_object(StaticProperty<ValueT> &prop, PropertyClass *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<ValueT *>(object);
delete object;
}
};
/**
* 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<ValueT> &prop, BitStream &stream)
{
prop.get_type().write_to(
stream,
Value::make_reference<ValueT>(prop.m_value)
);
}
static void read(StaticProperty<ValueT> &prop, BitStream &stream)
{
Value value = prop.get_type().read_from(stream);
prop.m_value = value.get<ValueT>();
}
};
/**
* 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 <typename ValueT>
struct value_rw_helper<
ValueT,
typename std::enable_if<
std::is_pointer<ValueT>::value
>::type
>
{
using type = typename std::remove_pointer<ValueT>::type;
static void write(const StaticProperty<ValueT> &prop, BitStream &stream)
{
prop.get_type().write_to(
stream,
Value::make_reference<type>(*prop.m_value)
);
}
static void read(StaticProperty<ValueT> &prop, BitStream &stream)
{
Value value = prop.get_type().read_from(stream);
prop.m_value = value.take<type>();
}
};
/**
* A helper utility that combines functions provided by value_object_helper
* and value_rw_helper into a single type.
*/
template <typename ValueT>
struct value_helper
{
static ValueT construct(const Type &type)
{
return value_object_helper<ValueT>::construct(type);
}
static ValueT copy(const StaticProperty<ValueT> &prop)
{
return value_object_helper<ValueT>::copy(prop);
}
static const PropertyClass *get_object(const StaticProperty<ValueT> &prop)
{
return value_object_helper<ValueT>::get_object(prop);
}
static void set_object(StaticProperty<ValueT> &prop, PropertyClass *object)
{
value_object_helper<ValueT>::set_object(prop, object);
}
static void write(const StaticProperty<ValueT> &prop, BitStream &stream)
{
value_rw_helper<ValueT>::write(prop, stream);
}
static void read(StaticProperty<ValueT> &prop, BitStream &stream)
{
value_rw_helper<ValueT>::read(prop, stream);
}
};
/// @endcond
/**
* TODO: Documentation
*/
template <typename ValueT>
class StaticProperty : public IProperty
{
// Allow helper utilities access to m_value
friend value_object_helper<ValueT>;
friend value_rw_helper<ValueT>;
public:
// Do not allow copy assignment. Once a property has been constructed,
// it shouldn't be able to change.
StaticProperty<ValueT> &operator=(const StaticProperty<ValueT> &that) = delete;
StaticProperty(PropertyClass &object,
const std::string &name, const Type &type)
: IProperty(object, name, type)
, m_value(value_helper<ValueT>::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<ValueT> &that)
: IProperty(object, that)
, m_value(value_helper<ValueT>::copy(that))
{}
constexpr bool is_pointer() const override
{
return std::is_pointer<ValueT>::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<ValueT>::get_object(*this);
}
void set_object(PropertyClass *object) override
{
return value_helper<ValueT>::set_object(*this, object);
}
void write_value_to(BitStream &stream) const override
{
value_helper<ValueT>::write(*this, stream);
}
void read_value_from(BitStream &stream) override
{
value_helper<ValueT>::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;
};
}
}