mirror of https://github.com/SeanOMik/libki.git
pclass: Improve Value casting and implement basic enum types
This commit is contained in:
parent
595b6167c0
commit
4872d1a2a4
|
@ -0,0 +1,134 @@
|
||||||
|
#pragma once
|
||||||
|
#include "ki/pclass/types/EnumType.h"
|
||||||
|
#include "ki/pclass/types/PrimitiveType.h"
|
||||||
|
#include "ki/util/BitTypes.h"
|
||||||
|
|
||||||
|
namespace ki
|
||||||
|
{
|
||||||
|
namespace pclass
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* TODO: Documentation
|
||||||
|
*/
|
||||||
|
class IEnum
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit IEnum(const Type &type);
|
||||||
|
virtual ~IEnum() {}
|
||||||
|
|
||||||
|
const EnumType &get_type() const;
|
||||||
|
|
||||||
|
virtual void write_to(BitStream &stream) const = 0;
|
||||||
|
virtual void read_from(BitStream &stream) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
EnumType *m_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Documentation
|
||||||
|
*/
|
||||||
|
template <typename UnderlyingT>
|
||||||
|
class Enum : public IEnum
|
||||||
|
{
|
||||||
|
// Make sure UnderlyingT is integral
|
||||||
|
static_assert(
|
||||||
|
is_integral<UnderlyingT>::value,
|
||||||
|
"The underlying type of an enum must be integral."
|
||||||
|
);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Enum(const Type &type, UnderlyingT value = 0)
|
||||||
|
: IEnum(type)
|
||||||
|
{
|
||||||
|
set_value(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit Enum(const Type &type, const std::string &element_name)
|
||||||
|
: IEnum(type)
|
||||||
|
{
|
||||||
|
set_value(element_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
UnderlyingT get_value() const
|
||||||
|
{
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void set_value(UnderlyingT value)
|
||||||
|
{
|
||||||
|
if (value != 0 && !get_type().has_element(value))
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Enum '" << get_type().get_name()
|
||||||
|
<< "' has no element with value: " << value << ".";
|
||||||
|
throw runtime_error(oss.str());
|
||||||
|
}
|
||||||
|
m_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_value(const std::string &element_name)
|
||||||
|
{
|
||||||
|
m_value = get_type().get_element(element_name).get_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_to(BitStream& stream) const override
|
||||||
|
{
|
||||||
|
PrimitiveTypeWriter<UnderlyingT>::write_to(stream, m_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_from(BitStream& stream) override
|
||||||
|
{
|
||||||
|
UnderlyingT value;
|
||||||
|
PrimitiveTypeReader<UnderlyingT>::read_from(stream, value);
|
||||||
|
set_value(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
operator UnderlyingT() const
|
||||||
|
{
|
||||||
|
return get_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator=(UnderlyingT value)
|
||||||
|
{
|
||||||
|
set_value(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const UnderlyingT &rhs) const
|
||||||
|
{
|
||||||
|
return m_value == rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator std::string() const
|
||||||
|
{
|
||||||
|
return get_type().get_element(m_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator=(const std::string &element_name)
|
||||||
|
{
|
||||||
|
set_value(element_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const std::string &rhs) const
|
||||||
|
{
|
||||||
|
if (!get_type().has_element(rhs))
|
||||||
|
return false;
|
||||||
|
return m_value == get_type().get_element(rhs).get_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Enum<UnderlyingT> &rhs) const
|
||||||
|
{
|
||||||
|
return get_type() == &rhs.get_type() &&
|
||||||
|
m_value == rhs.m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Enum<UnderlyingT> &rhs) const
|
||||||
|
{
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
UnderlyingT m_value;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,7 +37,7 @@ namespace pclass
|
||||||
PropertyList *m_list;
|
PropertyList *m_list;
|
||||||
int m_index;
|
int m_index;
|
||||||
|
|
||||||
explicit iterator(PropertyList &list, const int index = 0);
|
explicit iterator(PropertyList &list, int index = 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,7 +59,7 @@ namespace pclass
|
||||||
const PropertyList *m_list;
|
const PropertyList *m_list;
|
||||||
int m_index;
|
int m_index;
|
||||||
|
|
||||||
explicit const_iterator(const PropertyList &list, const int index = 0);
|
explicit const_iterator(const PropertyList &list, int index = 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::size_t get_property_count() const;
|
std::size_t get_property_count() const;
|
||||||
|
@ -82,13 +82,12 @@ namespace pclass
|
||||||
iterator end();
|
iterator end();
|
||||||
const_iterator end() const;
|
const_iterator end() const;
|
||||||
|
|
||||||
protected:
|
|
||||||
void add_property(IProperty &prop);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<IProperty *> m_properties;
|
std::vector<IProperty *> m_properties;
|
||||||
std::map<std::string, IProperty *> m_property_name_lookup;
|
std::map<std::string, IProperty *> m_property_name_lookup;
|
||||||
std::map<hash_t, IProperty *> m_property_hash_lookup;
|
std::map<hash_t, IProperty *> m_property_hash_lookup;
|
||||||
|
|
||||||
|
void add_property(IProperty &prop);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -248,12 +248,12 @@ namespace pclass
|
||||||
{
|
{
|
||||||
static void write(const StaticProperty<ValueT> &prop, BitStream &stream)
|
static void write(const StaticProperty<ValueT> &prop, BitStream &stream)
|
||||||
{
|
{
|
||||||
prop.get_type().write_to(stream, prop.m_value);
|
prop.get_type().write_to(stream, Value::make_reference(prop.m_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void read(StaticProperty<ValueT> &prop, BitStream &stream)
|
static void read(StaticProperty<ValueT> &prop, BitStream &stream)
|
||||||
{
|
{
|
||||||
prop.get_type().read_from(stream, Value(prop.m_value));
|
prop.get_type().read_from(stream, Value::make_reference(prop.m_value));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -274,12 +274,12 @@ namespace pclass
|
||||||
{
|
{
|
||||||
static void write(const StaticProperty<ValueT> &prop, BitStream &stream)
|
static void write(const StaticProperty<ValueT> &prop, BitStream &stream)
|
||||||
{
|
{
|
||||||
prop.get_type().write_to(stream, *prop.m_value);
|
prop.get_type().write_to(stream, Value::make_reference(*prop.m_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void read(StaticProperty<ValueT> &prop, BitStream &stream)
|
static void read(StaticProperty<ValueT> &prop, BitStream &stream)
|
||||||
{
|
{
|
||||||
prop.get_type().read_from(stream, Value(*prop.m_value));
|
prop.get_type().read_from(stream, Value::make_reference(*prop.m_value));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -367,7 +367,7 @@ namespace pclass
|
||||||
|
|
||||||
Value get_value() const override
|
Value get_value() const override
|
||||||
{
|
{
|
||||||
return m_value;
|
return Value::make_reference(m_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const PropertyClass *get_object() const override
|
const PropertyClass *get_object() const override
|
||||||
|
|
|
@ -50,13 +50,11 @@ namespace pclass
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename EnumT>
|
template <typename EnumT>
|
||||||
EnumType<EnumT> &define_enum(const std::string &name)
|
CppEnumType<EnumT> &define_enum(const std::string &name)
|
||||||
{
|
{
|
||||||
/*
|
auto *type = new CppEnumType<EnumT>(name, *this);
|
||||||
auto *type = new EnumType<EnumT>(name, this);
|
|
||||||
define_type(type);
|
define_type(type);
|
||||||
return type;
|
return *type;
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ClassT>
|
template <typename ClassT>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <typeinfo>
|
#include <typeinfo>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <map>
|
||||||
#include "ki/util/exception.h"
|
#include "ki/util/exception.h"
|
||||||
|
|
||||||
namespace ki
|
namespace ki
|
||||||
|
@ -9,90 +10,194 @@ namespace ki
|
||||||
namespace pclass
|
namespace pclass
|
||||||
{
|
{
|
||||||
class Value;
|
class Value;
|
||||||
|
class PropertyClass;
|
||||||
|
class IEnum;
|
||||||
|
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
template <typename SrcT>
|
template <typename SrcT, typename DestT>
|
||||||
struct value_caster_helper;
|
struct value_caster;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Documentation
|
* A base class for Value casters.
|
||||||
|
* Provides a common interface for cast()
|
||||||
*/
|
*/
|
||||||
struct value_caster_base
|
struct value_caster_base
|
||||||
{
|
{
|
||||||
virtual ~value_caster_base() {}
|
virtual ~value_caster_base() {}
|
||||||
virtual Value cast(const std::type_info &dst_type, Value &v) const = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/**
|
/**
|
||||||
* Provides a nice way for specialized casters to throw with a
|
* @param[in] value The value being casted.
|
||||||
* consistent error when casting is not possible.
|
* @returns A new Value holding a casted value.
|
||||||
|
* @throws ki::cast_error If the cast was unsuccessful.
|
||||||
*/
|
*/
|
||||||
Value bad_cast(const std::type_info &src_type, const std::type_info &dst_type) const;
|
virtual Value cast(const Value &value) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A base class for Value caster implementations.
|
||||||
|
* Provides a shortcut for throwing a cast_error.
|
||||||
|
*/
|
||||||
|
template <typename SrcT, typename DestT>
|
||||||
|
struct value_caster_impl : value_caster_base
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
static Value bad_cast();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A base class for Value deallocators.
|
||||||
|
* Provides a common interface for deallocate()
|
||||||
|
*/
|
||||||
|
struct value_deallocator_base
|
||||||
|
{
|
||||||
|
virtual ~value_deallocator_base() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deallocate a Value-owned pointer.
|
||||||
|
* @param[in] ptr The pointer to deallocate.
|
||||||
|
*/
|
||||||
|
virtual void deallocate(void *ptr) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Documentation
|
* TODO: Documentation
|
||||||
*/
|
*/
|
||||||
struct value_caster
|
template <typename T>
|
||||||
|
struct value_deallocator : value_deallocator_base
|
||||||
{
|
{
|
||||||
// Allow Value to call the default constructor and make
|
void deallocate(void *ptr) const override
|
||||||
|
{
|
||||||
|
// By default, now that we have the proper type, just delete it.
|
||||||
|
delete static_cast<T *>(ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Documentation
|
||||||
|
*/
|
||||||
|
class ValueDeallocator
|
||||||
|
{
|
||||||
|
// Allow Value to call make<T>() and the default constructor
|
||||||
friend Value;
|
friend Value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ValueDeallocator(ValueDeallocator &&that) noexcept;
|
||||||
|
ValueDeallocator &operator=(ValueDeallocator &&that) noexcept;
|
||||||
|
~ValueDeallocator();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @tparam DstT The cast destination type.
|
* Deallocate a Value-owned pointer.
|
||||||
* @param[in] value The Value that is being casted to the destination type.
|
* @param[in] ptr The pointer to deallocate.
|
||||||
* @returns A Value with a reference that has been casted to the destination type.
|
|
||||||
*/
|
*/
|
||||||
template <typename DstT>
|
void deallocate(void *ptr) const;
|
||||||
Value cast(const Value &value) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
value_caster_base *m_caster;
|
value_deallocator_base *m_deallocator;
|
||||||
|
|
||||||
explicit value_caster(value_caster_base *caster = nullptr)
|
ValueDeallocator();
|
||||||
{
|
explicit ValueDeallocator(value_deallocator_base *deallocator);
|
||||||
m_caster = caster;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
template <typename T>
|
||||||
* @tparam SrcT
|
static ValueDeallocator make()
|
||||||
*/
|
|
||||||
template <typename SrcT>
|
|
||||||
static value_caster make()
|
|
||||||
{
|
{
|
||||||
return value_caster(new value_caster_helper<SrcT>());
|
return ValueDeallocator(new value_deallocator<T>());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Documentation
|
||||||
|
*/
|
||||||
|
class ValueCaster
|
||||||
|
{
|
||||||
|
// Allow Value to call the default constructor and get<SrcT>()
|
||||||
|
friend Value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~ValueCaster();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declare a run-time casting interface from SrcT to DestT.
|
||||||
|
* @tparam SrcT The cast source type.
|
||||||
|
* @tparam DestT the cast destination type.
|
||||||
|
*/
|
||||||
|
template <typename SrcT, typename DestT>
|
||||||
|
static void declare()
|
||||||
|
{
|
||||||
|
ValueCaster::get<SrcT>().add_caster<SrcT, DestT>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tparam SrcT The cast source type.
|
||||||
|
* @tparam DestT The cast destination type.
|
||||||
|
* @param[in] value The Value that is being casted to the destination type.
|
||||||
|
* @returns A Value with a reference to a DestT value/instance.
|
||||||
|
*/
|
||||||
|
template <typename SrcT, typename DestT>
|
||||||
|
static Value cast(const Value &value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tparam DestT The cast destination type.
|
||||||
|
* @param[in] src_type The std::type_info object of the source type.
|
||||||
|
* @param[in] value The Value that is being casted to the destination type.
|
||||||
|
* @returns A Value with a reference to a DestT value/instance.
|
||||||
|
*/
|
||||||
|
template <typename DestT>
|
||||||
|
static Value cast(const std::type_info &src_type, const Value &value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tparam DestT The cast destination type.
|
||||||
|
* @param[in] value The Value that is being casted to the destination type.
|
||||||
|
* @returns A Value with a reference to a DestT value/instance.
|
||||||
|
*/
|
||||||
|
template <typename DestT>
|
||||||
|
Value cast_value(const Value &value) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* A static lookup used to find appropriate casters at runtime.
|
||||||
|
* Contains SrcT -> Caster elements.
|
||||||
|
*/
|
||||||
|
static std::map<std::size_t, ValueCaster *> s_caster_lookup;
|
||||||
|
|
||||||
|
const std::type_info *m_src_type;
|
||||||
|
std::map<std::size_t, detail::value_caster_base *> m_casts;
|
||||||
|
|
||||||
|
ValueCaster();
|
||||||
|
explicit ValueCaster(const std::type_info &src_type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tparam SrcT The cast source type.
|
||||||
|
* @returns The ValueCaster instance responsible for casting values of type SrcT.
|
||||||
|
*/
|
||||||
|
template <typename SrcT>
|
||||||
|
static ValueCaster &get()
|
||||||
|
{
|
||||||
|
const auto &src_type = typeid(SrcT);
|
||||||
|
const auto src_type_hash = src_type.hash_code();
|
||||||
|
if (s_caster_lookup.find(src_type_hash) == s_caster_lookup.end())
|
||||||
|
s_caster_lookup[src_type_hash] = new ValueCaster(src_type);
|
||||||
|
return *s_caster_lookup[src_type_hash];
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename SrcT, typename DestT>
|
||||||
|
void add_caster()
|
||||||
|
{
|
||||||
|
const auto dest_type_hash = typeid(DestT).hash_code();
|
||||||
|
m_casts[dest_type_hash] = new detail::value_caster<SrcT, DestT>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A wrapper around a void pointer that ensures type safety.
|
* A wrapper around a void pointer that ensures type safety.
|
||||||
*/
|
*/
|
||||||
class Value
|
class Value
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
template <typename T>
|
Value(Value &&that) noexcept;
|
||||||
Value(T &value)
|
Value &operator=(Value &&that) noexcept;
|
||||||
{
|
~Value();
|
||||||
m_value_ptr = static_cast<void *>(&value);
|
|
||||||
m_type_hash = typeid(value).hash_code();
|
|
||||||
m_caster = detail::value_caster::make<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
Value(const T &value)
|
|
||||||
{
|
|
||||||
m_value_ptr = const_cast<void *>(static_cast<const void *>(&value));
|
|
||||||
m_type_hash = typeid(value).hash_code();
|
|
||||||
m_caster = detail::value_caster::make<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
Value(Value &&o) noexcept
|
|
||||||
: m_value_ptr(std::move(o.m_value_ptr))
|
|
||||||
, m_type_hash(std::move(o.m_type_hash))
|
|
||||||
, m_caster(std::move(o.m_caster))
|
|
||||||
{}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Whether or not the value being held is of type T.
|
* @return Whether or not the value being held is of type T.
|
||||||
|
@ -112,7 +217,7 @@ namespace pclass
|
||||||
{
|
{
|
||||||
// Do we need to attempt casting?
|
// Do we need to attempt casting?
|
||||||
if (!is<T>())
|
if (!is<T>())
|
||||||
return m_caster.cast<T>(*this).get<T>();
|
return m_caster->cast_value<T>(*this).get<T>();
|
||||||
return *static_cast<T *>(m_value_ptr);
|
return *static_cast<T *>(m_value_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,42 +229,130 @@ namespace pclass
|
||||||
{
|
{
|
||||||
// Do we need to attempt casting?
|
// Do we need to attempt casting?
|
||||||
if (!is<T>())
|
if (!is<T>())
|
||||||
return m_caster.cast<T>(*this).get<T>();
|
return m_caster->cast_value<T>(*this).get<T>();
|
||||||
return *static_cast<T *>(m_value_ptr);
|
return *static_cast<T *>(m_value_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tparam T The type of value to hold.
|
||||||
|
* @param[in] value The initial value.
|
||||||
|
* @returns A new Value instance that owns it's value.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
static Value make_value(T value)
|
||||||
|
{
|
||||||
|
auto *ptr = static_cast<void *>(new T(value));
|
||||||
|
auto val = Value(ptr, true);
|
||||||
|
val.construct<T>();
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tparam T The type of value to hold.
|
||||||
|
* @param[in] value The initial value.
|
||||||
|
* @returns A new Value instance that refers to a value it doesn't own.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
static Value make_reference(T &value)
|
||||||
|
{
|
||||||
|
auto *ptr = static_cast<void *>(&value);
|
||||||
|
auto val = Value(ptr);
|
||||||
|
val.construct<T>();
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @tparam T The type of value to hold.
|
||||||
|
* @param[in] value The initial value.
|
||||||
|
* @returns A new Value instance that refers to a value it doesn't own.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
static Value make_reference(const T &value)
|
||||||
|
{
|
||||||
|
auto *ptr = const_cast<void *>(static_cast<const void *>(&value));
|
||||||
|
auto val = Value(ptr);
|
||||||
|
val.construct<T>();
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void *m_value_ptr;
|
void *m_value_ptr;
|
||||||
|
bool m_ptr_is_owned;
|
||||||
|
|
||||||
std::size_t m_type_hash;
|
std::size_t m_type_hash;
|
||||||
detail::value_caster m_caster;
|
bool m_value_is_object;
|
||||||
|
bool m_value_is_enum;
|
||||||
|
|
||||||
|
ValueCaster *m_caster;
|
||||||
|
detail::ValueDeallocator m_deallocator;
|
||||||
|
|
||||||
|
explicit Value(void *value_ptr, bool owned = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set values that depend on knowing T.
|
||||||
|
* @tparam T The type that is being held.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
void construct()
|
||||||
|
{
|
||||||
|
m_value_is_object = std::is_base_of<
|
||||||
|
PropertyClass,
|
||||||
|
typename std::decay<T>::type
|
||||||
|
>::value;
|
||||||
|
m_value_is_enum = std::is_base_of<
|
||||||
|
IEnum,
|
||||||
|
typename std::decay<T>::type
|
||||||
|
>::value;
|
||||||
|
|
||||||
|
m_type_hash = typeid(T).hash_code();
|
||||||
|
m_caster = &ValueCaster::get<T>();
|
||||||
|
m_deallocator = detail::ValueDeallocator::make<T>();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename DestT>
|
||||||
|
Value ValueCaster::cast(const std::type_info &src_type, const Value& value)
|
||||||
|
{
|
||||||
|
const auto it = s_caster_lookup.find(src_type.hash_code());
|
||||||
|
if (it != s_caster_lookup.end())
|
||||||
|
return it->second.cast<DestT>(value);
|
||||||
|
throw cast_error(src_type, typeid(DestT));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename SrcT, typename DestT>
|
||||||
|
Value ValueCaster::cast(const Value& value)
|
||||||
|
{
|
||||||
|
ValueCaster::declare<SrcT, DestT>();
|
||||||
|
return ValueCaster::get<SrcT>().cast<DestT>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename DestT>
|
||||||
|
Value ValueCaster::cast_value(const Value& value) const
|
||||||
|
{
|
||||||
|
const auto it = m_casts.find(typeid(DestT).hash_code());
|
||||||
|
if (it != m_casts.end())
|
||||||
|
return it->second->cast(value);
|
||||||
|
throw cast_error(*m_src_type, typeid(DestT));
|
||||||
|
}
|
||||||
|
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
inline Value value_caster_base::bad_cast(
|
template <typename SrcT, typename DestT>
|
||||||
const std::type_info& src_type, const std::type_info& dst_type) const
|
Value value_caster_impl<SrcT, DestT>::bad_cast()
|
||||||
{
|
{
|
||||||
std::ostringstream oss;
|
throw cast_error(typeid(SrcT), typeid(DestT));
|
||||||
oss << "Cannot cast Value from " << src_type.name()
|
|
||||||
<< " to " << dst_type.name() << ".";
|
|
||||||
throw runtime_error(oss.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename DstT>
|
|
||||||
Value value_caster::cast(const Value &value) const
|
|
||||||
{
|
|
||||||
return m_caster->cast(typeid(DstT), const_cast<Value &>(value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Documentation
|
* TODO: Documentation
|
||||||
*/
|
*/
|
||||||
template <typename SrcT>
|
template <typename SrcT, typename DestT>
|
||||||
struct value_caster_helper : value_caster_base
|
struct value_caster : value_caster_impl<SrcT, DestT>
|
||||||
{
|
{
|
||||||
Value cast(const std::type_info &dst_type, Value& value) const override
|
Value cast(const Value &value) const override
|
||||||
{
|
{
|
||||||
return bad_cast(typeid(SrcT), dst_type);
|
// By default, just attempt to static_cast from SrcT to DestT.
|
||||||
|
return static_cast<DestT>(value.get<SrcT>());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -254,7 +254,7 @@ namespace pclass
|
||||||
if (index < 0 || index >= prop.size())
|
if (index < 0 || index >= prop.size())
|
||||||
throw runtime_error("Index out of bounds.");
|
throw runtime_error("Index out of bounds.");
|
||||||
|
|
||||||
prop.get_type().write_to(stream, *prop.at(index));
|
prop.get_type().write_to(stream, Value::make_reference(*prop.at(index)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void read_value_from(VectorProperty<ValueT> &prop, BitStream &stream, const int index)
|
static void read_value_from(VectorProperty<ValueT> &prop, BitStream &stream, const int index)
|
||||||
|
@ -263,7 +263,7 @@ namespace pclass
|
||||||
if (index < 0 || index >= prop.size())
|
if (index < 0 || index >= prop.size())
|
||||||
throw runtime_error("Index out of bounds.");
|
throw runtime_error("Index out of bounds.");
|
||||||
|
|
||||||
prop.get_type().read_from(stream, Value(*prop.at(index)));
|
prop.get_type().read_from(stream, Value::make_reference(*prop.at(index)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -344,7 +344,7 @@ namespace pclass
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= this->size())
|
if (index < 0 || index >= this->size())
|
||||||
throw runtime_error("Index out of bounds.");
|
throw runtime_error("Index out of bounds.");
|
||||||
return this->at(index);
|
return Value::make_reference(this->at(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
const PropertyClass *get_object(const int index) const override
|
const PropertyClass *get_object(const int index) const override
|
||||||
|
|
|
@ -6,27 +6,80 @@ namespace ki
|
||||||
{
|
{
|
||||||
namespace pclass
|
namespace pclass
|
||||||
{
|
{
|
||||||
|
typedef uint64_t enum_value_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Documentation
|
* TODO: Documentation
|
||||||
*/
|
*/
|
||||||
class EnumOption
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
std::string m_name;
|
|
||||||
uint32_t m_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: Documentation
|
|
||||||
*/
|
|
||||||
template <typename EnumT>
|
|
||||||
class EnumType : public Type
|
class EnumType : public Type
|
||||||
{
|
{
|
||||||
// Ensure that EnumT is an enum
|
/**
|
||||||
static_assert(std::is_enum<EnumT>::value, "EnumT must be an enum!");
|
* TODO: Documentation
|
||||||
|
*/
|
||||||
|
class Element
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Element(const std::string &name, enum_value_t value);
|
||||||
|
|
||||||
|
std::string get_name() const;
|
||||||
|
enum_value_t get_value() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_name;
|
||||||
|
enum_value_t m_value;
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
EnumType(const std::string &name, const TypeSystem &type_system);
|
EnumType(const std::string &name, const TypeSystem &type_system);
|
||||||
|
~EnumType();
|
||||||
|
|
||||||
|
bool has_element(const std::string &name) const;
|
||||||
|
bool has_element(enum_value_t value) const;
|
||||||
|
|
||||||
|
const Element &get_element(const std::string &name) const;
|
||||||
|
const Element &get_element(enum_value_t value) const;
|
||||||
|
|
||||||
|
EnumType &add_element(const std::string &name, enum_value_t value);
|
||||||
|
|
||||||
|
void write_to(BitStream& stream, const Value& value) const override;
|
||||||
|
void read_from(BitStream& stream, Value& value) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Element *> m_elements;
|
||||||
|
std::map<std::string, Element *> m_element_name_lookup;
|
||||||
|
std::map<enum_value_t, Element *> m_element_value_lookup;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Documentation
|
||||||
|
*/
|
||||||
|
template <typename EnumT>
|
||||||
|
class CppEnumType : public Type
|
||||||
|
{
|
||||||
|
// Ensure that EnumT is an enum
|
||||||
|
static_assert(std::is_enum<EnumT>::value, "EnumT must be an enum!");
|
||||||
|
using underlying_type = typename std::underlying_type<EnumT>::type;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CppEnumType(const std::string &name, const TypeSystem &type_system)
|
||||||
|
: Type(name, type_system)
|
||||||
|
{
|
||||||
|
m_kind = kind::ENUM;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_to(BitStream& stream, const Value &value) const override
|
||||||
|
{
|
||||||
|
PrimitiveTypeWriter<underlying_type>::write_to(
|
||||||
|
stream, reinterpret_cast<const underlying_type &>(value.get<EnumT>())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_from(BitStream& stream, Value &value) const override
|
||||||
|
{
|
||||||
|
PrimitiveTypeReader<underlying_type>::read_from(
|
||||||
|
stream, reinterpret_cast<underlying_type &>(value.get<EnumT>())
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <typeinfo>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
namespace ki
|
namespace ki
|
||||||
{
|
{
|
||||||
|
@ -9,4 +11,24 @@ namespace ki
|
||||||
explicit runtime_error(const std::string &message)
|
explicit runtime_error(const std::string &message)
|
||||||
: std::runtime_error(message) {}
|
: std::runtime_error(message) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class cast_error : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
cast_error(const std::type_info &src_type, const std::type_info &dest_type)
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Cannot cast from " << src_type.name()
|
||||||
|
<< " to " << dest_type.name() << ".";
|
||||||
|
m_what = oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
char const* what() const override
|
||||||
|
{
|
||||||
|
return m_what.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_what;
|
||||||
|
};
|
||||||
}
|
}
|
|
@ -1,10 +1,13 @@
|
||||||
target_sources(${PROJECT_NAME}
|
target_sources(${PROJECT_NAME}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
${PROJECT_SOURCE_DIR}/src/pclass/ClassType.cpp
|
${PROJECT_SOURCE_DIR}/src/pclass/ClassType.cpp
|
||||||
|
${PROJECT_SOURCE_DIR}/src/pclass/Enum.cpp
|
||||||
|
${PROJECT_SOURCE_DIR}/src/pclass/EnumType.cpp
|
||||||
${PROJECT_SOURCE_DIR}/src/pclass/HashCalculator.cpp
|
${PROJECT_SOURCE_DIR}/src/pclass/HashCalculator.cpp
|
||||||
${PROJECT_SOURCE_DIR}/src/pclass/Property.cpp
|
${PROJECT_SOURCE_DIR}/src/pclass/Property.cpp
|
||||||
${PROJECT_SOURCE_DIR}/src/pclass/PropertyClass.cpp
|
${PROJECT_SOURCE_DIR}/src/pclass/PropertyClass.cpp
|
||||||
${PROJECT_SOURCE_DIR}/src/pclass/PropertyList.cpp
|
${PROJECT_SOURCE_DIR}/src/pclass/PropertyList.cpp
|
||||||
${PROJECT_SOURCE_DIR}/src/pclass/Type.cpp
|
${PROJECT_SOURCE_DIR}/src/pclass/Type.cpp
|
||||||
${PROJECT_SOURCE_DIR}/src/pclass/TypeSystem.cpp
|
${PROJECT_SOURCE_DIR}/src/pclass/TypeSystem.cpp
|
||||||
|
${PROJECT_SOURCE_DIR}/src/pclass/Value.cpp
|
||||||
)
|
)
|
|
@ -0,0 +1,9 @@
|
||||||
|
#include "ki/pclass/Enum.h"
|
||||||
|
|
||||||
|
namespace ki
|
||||||
|
{
|
||||||
|
namespace pclass
|
||||||
|
{
|
||||||
|
// TODO: Runtime Enum implementation
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
#include "ki/pclass/types/EnumType.h"
|
||||||
|
#include "ki/util/exception.h"
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace ki
|
||||||
|
{
|
||||||
|
namespace pclass
|
||||||
|
{
|
||||||
|
|
||||||
|
EnumType::Element::Element(const std::string &name,
|
||||||
|
const enum_value_t value)
|
||||||
|
{
|
||||||
|
m_name = name;
|
||||||
|
m_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string EnumType::Element::get_name() const
|
||||||
|
{
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum_value_t EnumType::Element::get_value() const
|
||||||
|
{
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnumType::EnumType(const std::string &name,
|
||||||
|
const TypeSystem &type_system)
|
||||||
|
: Type(name, type_system)
|
||||||
|
{
|
||||||
|
m_kind = kind::ENUM;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnumType::~EnumType()
|
||||||
|
{
|
||||||
|
for (auto it = m_elements.begin();
|
||||||
|
it != m_elements.end(); ++it)
|
||||||
|
{
|
||||||
|
delete *it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EnumType::has_element(const std::string& name) const
|
||||||
|
{
|
||||||
|
return m_element_name_lookup.find(name) != m_element_name_lookup.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EnumType::has_element(const enum_value_t value) const
|
||||||
|
{
|
||||||
|
return m_element_value_lookup.find(value) != m_element_value_lookup.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
const EnumType::Element& EnumType::get_element(const std::string& name) const
|
||||||
|
{
|
||||||
|
const auto &it = m_element_name_lookup.find(name);
|
||||||
|
if (it == m_element_name_lookup.end())
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "No element called '" << name
|
||||||
|
<< "' exists in enum: '" << get_name() << "'.";
|
||||||
|
throw runtime_error(oss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return *it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EnumType::Element& EnumType::get_element(const enum_value_t value) const
|
||||||
|
{
|
||||||
|
const auto &it = m_element_value_lookup.find(value);
|
||||||
|
if (it == m_element_value_lookup.end())
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "No element with value " << value
|
||||||
|
<< " exists in enum: '" << get_name() << "'.";
|
||||||
|
throw runtime_error(oss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return *it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnumType& EnumType::add_element(const std::string &name,
|
||||||
|
const enum_value_t value)
|
||||||
|
{
|
||||||
|
// Has an element already been added with this name?
|
||||||
|
if (has_element(name))
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Enum '" << get_name()
|
||||||
|
<< "' already has an element called '"
|
||||||
|
<< name << "'.";
|
||||||
|
throw runtime_error(oss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has an element already been added with this value?
|
||||||
|
if (has_element(value))
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Enum '" << get_name()
|
||||||
|
<< "' already has an element with the value '"
|
||||||
|
<< value << "'.";
|
||||||
|
throw runtime_error(oss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the element and add it to our lookups
|
||||||
|
auto *element = new Element(name, value);
|
||||||
|
m_elements.push_back(element);
|
||||||
|
m_element_name_lookup[name] = element;
|
||||||
|
m_element_value_lookup[value] = element;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnumType::write_to(BitStream& stream, const Value &value) const
|
||||||
|
{
|
||||||
|
// TODO: Extend Value to get IEnum values
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnumType::read_from(BitStream& stream, Value &value) const
|
||||||
|
{
|
||||||
|
// TODO: Extend Value to get IEnum values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
#include "ki/pclass/Value.h"
|
||||||
|
|
||||||
|
namespace ki
|
||||||
|
{
|
||||||
|
namespace pclass
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
Value value_caster_base::cast(const Value& v) const
|
||||||
|
{
|
||||||
|
throw runtime_error("Unimplemented cast.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueDeallocator::ValueDeallocator()
|
||||||
|
{
|
||||||
|
m_deallocator = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueDeallocator::ValueDeallocator(value_deallocator_base *deallocator)
|
||||||
|
{
|
||||||
|
m_deallocator = deallocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueDeallocator::ValueDeallocator(ValueDeallocator &&that) noexcept
|
||||||
|
{
|
||||||
|
m_deallocator = that.m_deallocator;
|
||||||
|
that.m_deallocator = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueDeallocator& ValueDeallocator::operator=(ValueDeallocator &&that) noexcept
|
||||||
|
{
|
||||||
|
m_deallocator = that.m_deallocator;
|
||||||
|
that.m_deallocator = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueDeallocator::~ValueDeallocator()
|
||||||
|
{
|
||||||
|
delete m_deallocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValueDeallocator::deallocate(void* ptr) const
|
||||||
|
{
|
||||||
|
m_deallocator->deallocate(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueCaster::ValueCaster()
|
||||||
|
{
|
||||||
|
m_src_type = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueCaster::~ValueCaster()
|
||||||
|
{
|
||||||
|
for (auto it = m_casts.begin();
|
||||||
|
it != m_casts.end(); ++it)
|
||||||
|
{
|
||||||
|
delete it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueCaster::ValueCaster(const std::type_info& src_type)
|
||||||
|
{
|
||||||
|
m_src_type = &src_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value::Value(void *value_ptr, const bool owned)
|
||||||
|
{
|
||||||
|
m_value_ptr = value_ptr;
|
||||||
|
m_ptr_is_owned = owned;
|
||||||
|
m_type_hash = 0;
|
||||||
|
m_value_is_object = false;
|
||||||
|
m_value_is_enum = false;
|
||||||
|
m_caster = nullptr;
|
||||||
|
m_deallocator = detail::ValueDeallocator();
|
||||||
|
}
|
||||||
|
|
||||||
|
Value::Value(Value&& that) noexcept
|
||||||
|
{
|
||||||
|
// Move pointer to this Value object, and take ownership if
|
||||||
|
// the other Value previously owned it.
|
||||||
|
m_value_ptr = that.m_value_ptr;
|
||||||
|
m_ptr_is_owned = that.m_ptr_is_owned;
|
||||||
|
that.m_ptr_is_owned = false;
|
||||||
|
|
||||||
|
m_value_is_object = that.m_value_is_object;
|
||||||
|
m_value_is_enum = that.m_value_is_enum;
|
||||||
|
m_type_hash = that.m_type_hash;
|
||||||
|
m_caster = that.m_caster;
|
||||||
|
m_deallocator = std::move(that.m_deallocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value &Value::operator=(Value&& that) noexcept
|
||||||
|
{
|
||||||
|
// If the current pointer is owned, deallocate it
|
||||||
|
if (m_ptr_is_owned)
|
||||||
|
m_deallocator.deallocate(m_value_ptr);
|
||||||
|
|
||||||
|
// Move pointer to this Value object, and take ownership if
|
||||||
|
// the other Value previously owned it.
|
||||||
|
m_value_ptr = that.m_value_ptr;
|
||||||
|
m_ptr_is_owned = that.m_ptr_is_owned;
|
||||||
|
that.m_ptr_is_owned = false;
|
||||||
|
|
||||||
|
m_value_is_object = that.m_value_is_object;
|
||||||
|
m_value_is_enum = that.m_value_is_enum;
|
||||||
|
m_type_hash = that.m_type_hash;
|
||||||
|
m_caster = that.m_caster;
|
||||||
|
m_deallocator = std::move(that.m_deallocator);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value::~Value()
|
||||||
|
{
|
||||||
|
if (m_value_ptr && m_ptr_is_owned)
|
||||||
|
m_deallocator.deallocate(m_value_ptr);
|
||||||
|
m_value_ptr = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the static lookup map
|
||||||
|
std::map<std::size_t, ValueCaster *> ValueCaster::s_caster_lookup = {};
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue