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;
|
||||
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;
|
||||
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;
|
||||
|
@ -82,13 +82,12 @@ namespace pclass
|
|||
iterator end();
|
||||
const_iterator end() const;
|
||||
|
||||
protected:
|
||||
void add_property(IProperty &prop);
|
||||
|
||||
private:
|
||||
std::vector<IProperty *> m_properties;
|
||||
std::map<std::string, IProperty *> m_property_name_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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
return m_value;
|
||||
return Value::make_reference(m_value);
|
||||
}
|
||||
|
||||
const PropertyClass *get_object() const override
|
||||
|
|
|
@ -50,13 +50,11 @@ namespace pclass
|
|||
}
|
||||
|
||||
template <typename EnumT>
|
||||
EnumType<EnumT> &define_enum(const std::string &name)
|
||||
CppEnumType<EnumT> &define_enum(const std::string &name)
|
||||
{
|
||||
/*
|
||||
auto *type = new EnumType<EnumT>(name, this);
|
||||
auto *type = new CppEnumType<EnumT>(name, *this);
|
||||
define_type(type);
|
||||
return type;
|
||||
*/
|
||||
return *type;
|
||||
}
|
||||
|
||||
template <typename ClassT>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <utility>
|
||||
#include <typeinfo>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#include "ki/util/exception.h"
|
||||
|
||||
namespace ki
|
||||
|
@ -9,90 +10,194 @@ namespace ki
|
|||
namespace pclass
|
||||
{
|
||||
class Value;
|
||||
class PropertyClass;
|
||||
class IEnum;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename SrcT>
|
||||
struct value_caster_helper;
|
||||
template <typename SrcT, typename DestT>
|
||||
struct value_caster;
|
||||
|
||||
/**
|
||||
* TODO: Documentation
|
||||
* A base class for Value casters.
|
||||
* Provides a common interface for cast()
|
||||
*/
|
||||
struct 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
|
||||
* consistent error when casting is not possible.
|
||||
* @param[in] value The value being casted.
|
||||
* @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
|
||||
*/
|
||||
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;
|
||||
|
||||
public:
|
||||
ValueDeallocator(ValueDeallocator &&that) noexcept;
|
||||
ValueDeallocator &operator=(ValueDeallocator &&that) noexcept;
|
||||
~ValueDeallocator();
|
||||
|
||||
/**
|
||||
* @tparam DstT The cast destination type.
|
||||
* @param[in] value The Value that is being casted to the destination type.
|
||||
* @returns A Value with a reference that has been casted to the destination type.
|
||||
* Deallocate a Value-owned pointer.
|
||||
* @param[in] ptr The pointer to deallocate.
|
||||
*/
|
||||
template <typename DstT>
|
||||
Value cast(const Value &value) const;
|
||||
void deallocate(void *ptr) const;
|
||||
|
||||
private:
|
||||
value_caster_base *m_caster;
|
||||
value_deallocator_base *m_deallocator;
|
||||
|
||||
explicit value_caster(value_caster_base *caster = nullptr)
|
||||
{
|
||||
m_caster = caster;
|
||||
}
|
||||
ValueDeallocator();
|
||||
explicit ValueDeallocator(value_deallocator_base *deallocator);
|
||||
|
||||
/**
|
||||
* @tparam SrcT
|
||||
*/
|
||||
template <typename SrcT>
|
||||
static value_caster make()
|
||||
template <typename T>
|
||||
static ValueDeallocator 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.
|
||||
*/
|
||||
class Value
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
Value(T &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))
|
||||
{}
|
||||
Value(Value &&that) noexcept;
|
||||
Value &operator=(Value &&that) noexcept;
|
||||
~Value();
|
||||
|
||||
/**
|
||||
* @return Whether or not the value being held is of type T.
|
||||
|
@ -112,7 +217,7 @@ namespace pclass
|
|||
{
|
||||
// Do we need to attempt casting?
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -124,42 +229,130 @@ namespace pclass
|
|||
{
|
||||
// Do we need to attempt casting?
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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:
|
||||
void *m_value_ptr;
|
||||
bool m_ptr_is_owned;
|
||||
|
||||
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
|
||||
{
|
||||
inline Value value_caster_base::bad_cast(
|
||||
const std::type_info& src_type, const std::type_info& dst_type) const
|
||||
template <typename SrcT, typename DestT>
|
||||
Value value_caster_impl<SrcT, DestT>::bad_cast()
|
||||
{
|
||||
std::ostringstream oss;
|
||||
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));
|
||||
throw cast_error(typeid(SrcT), typeid(DestT));
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Documentation
|
||||
*/
|
||||
template <typename SrcT>
|
||||
struct value_caster_helper : value_caster_base
|
||||
template <typename SrcT, typename DestT>
|
||||
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())
|
||||
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)
|
||||
|
@ -263,7 +263,7 @@ namespace pclass
|
|||
if (index < 0 || index >= prop.size())
|
||||
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())
|
||||
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
|
||||
|
|
|
@ -6,27 +6,80 @@ namespace ki
|
|||
{
|
||||
namespace pclass
|
||||
{
|
||||
typedef uint64_t enum_value_t;
|
||||
|
||||
/**
|
||||
* TODO: Documentation
|
||||
*/
|
||||
class EnumOption
|
||||
class EnumType : public Type
|
||||
{
|
||||
/**
|
||||
* 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;
|
||||
uint32_t m_value;
|
||||
enum_value_t m_value;
|
||||
};
|
||||
|
||||
public:
|
||||
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 EnumType : public Type
|
||||
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:
|
||||
EnumType(const std::string &name, const TypeSystem &type_system);
|
||||
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
|
||||
#include <stdexcept>
|
||||
#include <typeinfo>
|
||||
#include <sstream>
|
||||
|
||||
namespace ki
|
||||
{
|
||||
|
@ -9,4 +11,24 @@ namespace ki
|
|||
explicit runtime_error(const std::string &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}
|
||||
PRIVATE
|
||||
${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/Property.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/pclass/PropertyClass.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/pclass/PropertyList.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/pclass/Type.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