mirror of https://github.com/SeanOMik/libki.git
pclass: Implement string primitives and class types
This commit is contained in:
parent
25d371b49f
commit
48aba1f0df
|
@ -25,11 +25,10 @@ namespace ki
|
|||
virtual hash_t calculate_type_hash(const std::string &name) const = 0;
|
||||
|
||||
/**
|
||||
* Calculate a property hash from the property's name and type.
|
||||
* Calculate a property hash from the property's name.
|
||||
* @param name The name of the property.
|
||||
* @param type_hash The hash of the property's type.
|
||||
*/
|
||||
virtual hash_t calculate_property_hash(const std::string& name, const hash_t type_hash) const = 0;
|
||||
virtual hash_t calculate_property_hash(const std::string& name) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -40,7 +39,7 @@ namespace ki
|
|||
{
|
||||
public:
|
||||
hash_t calculate_type_hash(const std::string& name) const override;
|
||||
hash_t calculate_property_hash(const std::string& name, const hash_t type_hash) const override;
|
||||
hash_t calculate_property_hash(const std::string& name) const override;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
#pragma once
|
||||
#include "ki/pclass/types/Type.h"
|
||||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
{
|
||||
class PropertyClass;
|
||||
|
||||
/**
|
||||
* TODO: Documentation
|
||||
*/
|
||||
class PropertyBase
|
||||
{
|
||||
public:
|
||||
PropertyBase(PropertyClass &object,
|
||||
const std::string &name, const Type &type);
|
||||
virtual ~PropertyBase() { }
|
||||
|
||||
std::string get_name() const;
|
||||
hash_t get_name_hash() const;
|
||||
hash_t get_full_hash() const;
|
||||
const Type &get_type() const;
|
||||
|
||||
virtual bool is_pointer() const;
|
||||
virtual bool is_dynamic() const;
|
||||
|
||||
virtual Value get_value() const = 0;
|
||||
virtual const PropertyClass *get_object() const = 0;
|
||||
|
||||
virtual void write_value_to(BitStream &stream) const = 0;
|
||||
virtual void read_value_from(BitStream &stream) = 0;
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
hash_t m_name_hash;
|
||||
hash_t m_full_hash;
|
||||
const Type *m_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO: Documentation
|
||||
*/
|
||||
class DynamicPropertyBase : public PropertyBase
|
||||
{
|
||||
public:
|
||||
DynamicPropertyBase(PropertyClass &object,
|
||||
const std::string &name, const Type &type);
|
||||
virtual ~DynamicPropertyBase() {}
|
||||
|
||||
bool is_dynamic() const override;
|
||||
virtual std::size_t get_element_count() const = 0;
|
||||
|
||||
Value get_value() const final override;
|
||||
const PropertyClass *get_object() const final override;
|
||||
void write_value_to(BitStream &stream) const final override;
|
||||
void read_value_from(BitStream &stream) final override;
|
||||
|
||||
virtual Value get_value(int index) const = 0;
|
||||
virtual const PropertyClass *get_object(int index) const = 0;
|
||||
virtual void write_value_to(BitStream &stream, int index) const = 0;
|
||||
virtual void read_value_from(BitStream &stream, int index) = 0;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,24 +1,60 @@
|
|||
#pragma once
|
||||
#include "ki/pclass/types/Type.h"
|
||||
#include "ki/pclass/PropertyList.h"
|
||||
|
||||
#define _KI_TYPE ki::pclass::Type
|
||||
#define _KI_TYPE_SYSTEM ki::pclass::TypeSystem
|
||||
#define _KI_PCLASS ki::pclass::PropertyClass
|
||||
|
||||
#define _KI_PCLASS_CONSTRUCTOR(derived) \
|
||||
explicit derived(const _KI_TYPE &type, const _KI_TYPE_SYSTEM &type_system)
|
||||
|
||||
#define _KI_PCLASS_CONSTRUCT_BASE(base) \
|
||||
: base(type, type_system)
|
||||
|
||||
#define DERIVED_PCLASS(derived, base) class derived : public base
|
||||
#define PCLASS(n) DERIVED_PCLASS(n, _KI_PCLASS)
|
||||
|
||||
#define PCLASS_CONSTRUCTOR(derived) \
|
||||
_KI_PCLASS_CONSTRUCTOR(derived) \
|
||||
_KI_PCLASS_CONSTRUCT_BASE(_KI_PCLASS)
|
||||
|
||||
#define DERIVED_PCLASS_CONSTRUCTOR(derived, base) \
|
||||
_KI_PCLASS_CONSTRUCTOR(derived) \
|
||||
_KI_PCLASS_CONSTRUCT_BASE(base)
|
||||
|
||||
#define TYPE(n) type_system.get_type(n)
|
||||
|
||||
#define INIT_PROPERTY(identifier, type) \
|
||||
, identifier(*this, #identifier, TYPE(type))
|
||||
|
||||
#define INIT_PROPERTY_VALUE(identifier, type, value) \
|
||||
, identifier(*this, #identifier, TYPE(type), value)
|
||||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
{
|
||||
class Type;
|
||||
template <typename ValueT>
|
||||
class ClassType;
|
||||
|
||||
/**
|
||||
* TODO: Documentation
|
||||
*/
|
||||
class PropertyClass
|
||||
{
|
||||
explicit PropertyClass(const Type &type) : m_type(type) {}
|
||||
virtual ~PropertyClass() {};
|
||||
public:
|
||||
explicit PropertyClass(const Type &type, const TypeSystem &type_system);
|
||||
virtual ~PropertyClass();
|
||||
|
||||
const Type &get_type();
|
||||
const Type &get_type() const;
|
||||
|
||||
PropertyList &get_properties();
|
||||
const PropertyList &get_properties() const;
|
||||
|
||||
virtual void on_created();
|
||||
private:
|
||||
const Type &m_type;
|
||||
const Type *m_type;
|
||||
PropertyList *m_properties;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "ki/pclass/HashCalculator.h"
|
||||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
{
|
||||
class PropertyBase;
|
||||
|
||||
/**
|
||||
* TODO: Documentation
|
||||
*/
|
||||
class PropertyList
|
||||
{
|
||||
friend PropertyBase;
|
||||
|
||||
public:
|
||||
using const_iterator = std::vector<PropertyBase *>::const_iterator;
|
||||
|
||||
std::size_t get_property_count() const;
|
||||
|
||||
bool has_property(const std::string &name) const;
|
||||
bool has_property(hash_t hash) const;
|
||||
|
||||
const PropertyBase &get_property(int index) const;
|
||||
const PropertyBase &get_property(const std::string &name) const;
|
||||
const PropertyBase &get_property(hash_t hash) const;
|
||||
|
||||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
|
||||
protected:
|
||||
void add_property(PropertyBase *prop);
|
||||
|
||||
private:
|
||||
std::vector<PropertyBase *> m_properties;
|
||||
std::map<std::string, PropertyBase *> m_property_name_lookup;
|
||||
std::map<hash_t, PropertyBase *> m_property_hash_lookup;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,307 @@
|
|||
#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;
|
||||
|
||||
/**
|
||||
* 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<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."
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 <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 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."
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 <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 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);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 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);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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, prop.m_value);
|
||||
}
|
||||
|
||||
static void read(StaticProperty<ValueT> &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 <typename ValueT>
|
||||
struct value_rw_helper<
|
||||
ValueT,
|
||||
typename std::enable_if<
|
||||
std::is_pointer<ValueT>::value
|
||||
>::type
|
||||
>
|
||||
{
|
||||
static void write(const StaticProperty<ValueT> &prop, BitStream &stream)
|
||||
{
|
||||
prop.get_type().write_to(stream, *prop.m_value);
|
||||
}
|
||||
|
||||
static void read(StaticProperty<ValueT> &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 <typename ValueT>
|
||||
struct value_helper
|
||||
{
|
||||
static ValueT construct(const Type &type)
|
||||
{
|
||||
return value_object_helper<ValueT>::construct(type);
|
||||
}
|
||||
|
||||
static const PropertyClass *get_object(const StaticProperty<ValueT> &prop)
|
||||
{
|
||||
return value_object_helper<ValueT>::get_object(prop);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO: Documentation
|
||||
*/
|
||||
template <typename ValueT>
|
||||
class StaticProperty : public PropertyBase
|
||||
{
|
||||
// Allow helper utilities access to m_value
|
||||
friend value_object_helper<ValueT>;
|
||||
friend value_rw_helper<ValueT>;
|
||||
|
||||
public:
|
||||
StaticProperty(PropertyClass &object,
|
||||
const std::string &name, const Type &type)
|
||||
: PropertyBase(object, name, type)
|
||||
, m_value(value_helper<ValueT>::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<ValueT>::value;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const PropertyClass *get_object() const override
|
||||
{
|
||||
return value_helper<ValueT>::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;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
#pragma once
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "Type.h"
|
||||
#include "PrimitiveType.h"
|
||||
#include "ki/pclass/HashCalculator.h"
|
||||
#include "ki/pclass/types/Type.h"
|
||||
#include "ki/pclass/types/PrimitiveType.h"
|
||||
#include "ki/pclass/types/ClassType.h"
|
||||
#include "ki/pclass/types/EnumType.h"
|
||||
|
||||
namespace ki
|
||||
{
|
||||
|
@ -14,52 +17,61 @@ namespace pclass
|
|||
class TypeSystem
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @return A singleton instance of TypeSystem with all C++ primitives defined.
|
||||
*/
|
||||
static TypeSystem &get_singleton();
|
||||
|
||||
explicit TypeSystem(HashCalculator *hash_calculator);
|
||||
~TypeSystem();
|
||||
|
||||
const HashCalculator &get_hash_calculator() const;
|
||||
void set_hash_calculator(HashCalculator *hash_calculator);
|
||||
|
||||
Type &get_type(const std::string &name) const;
|
||||
Type &get_type(hash_t hash) const;
|
||||
bool has_type(const std::string &name) const;
|
||||
bool has_type(hash_t hash) const;
|
||||
|
||||
const Type &get_type(const std::string &name) const;
|
||||
const Type &get_type(hash_t hash) const;
|
||||
|
||||
template <typename ValueT>
|
||||
Type &define_primitive(const std::string &name)
|
||||
PrimitiveType<ValueT> &define_primitive(const std::string &name)
|
||||
{
|
||||
auto hash = m_hash_calculator->calculate_type_hash(name);
|
||||
auto *type = new PrimitiveType<ValueT>(name, hash);
|
||||
auto *type = new PrimitiveType<ValueT>(name, *this);
|
||||
define_type(type);
|
||||
return *type;
|
||||
}
|
||||
|
||||
template <class ClassT>
|
||||
Type &define_class(const std::string &name)
|
||||
ClassType<ClassT> &define_class(
|
||||
const std::string &name, const Type *base_class = nullptr)
|
||||
{
|
||||
// Ensure that ClassT inherits PropertyClass
|
||||
static_assert(std::is_base_of<PropertyClass, ClassT>::value, "ClassT must inherit PropertyClass!");
|
||||
// If the caller does not specify a base class, automatically make
|
||||
// ki::pclass::PropertyClass the base class (if it has been defined)
|
||||
if (base_class == nullptr && has_type("class PropertyClass"))
|
||||
base_class = &get_type("class PropertyClass");
|
||||
|
||||
// TODO: Create class types
|
||||
auto *type = new ClassType<ClassT>(name, base_class, *this);
|
||||
define_type(type);
|
||||
return *type;
|
||||
}
|
||||
|
||||
template <typename EnumT>
|
||||
Type &define_enum(const std::string &name)
|
||||
EnumType<EnumT> *define_enum(const std::string &name)
|
||||
{
|
||||
// Ensure that EnumT is an enum
|
||||
static_assert(std::is_enum<EnumT>::value, "EnumT must be an enum!");
|
||||
/*
|
||||
auto *type = new EnumType<EnumT>(name, this);
|
||||
define_type(type);
|
||||
return type;
|
||||
*/
|
||||
}
|
||||
|
||||
// TODO: Create enum types
|
||||
template <typename ClassT>
|
||||
ClassT *instantiate(const std::string &name) const
|
||||
{
|
||||
const auto &type = get_type(name);
|
||||
return dynamic_cast<ClassT *>(type.instantiate());
|
||||
}
|
||||
|
||||
protected:
|
||||
void define_type(Type *type);
|
||||
|
||||
private:
|
||||
static TypeSystem *s_instance;
|
||||
|
||||
TypeList m_types;
|
||||
TypeNameMap m_type_name_lookup;
|
||||
TypeHashMap m_type_hash_lookup;
|
||||
|
|
|
@ -16,7 +16,14 @@ namespace pclass
|
|||
template <typename T>
|
||||
Value(T &value)
|
||||
{
|
||||
m_value_ptr = value;
|
||||
m_value_ptr = static_cast<void *>(&value);
|
||||
m_type_hash = typeid(value).hash_code();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -44,7 +51,7 @@ namespace pclass
|
|||
// Make sure that this is allowed
|
||||
if (!is<T>())
|
||||
throw std::runtime_error("Type mismatch in Value::get<T>() call.");
|
||||
return *(T *)m_value_ptr;
|
||||
return *static_cast<T *>(m_value_ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -56,7 +63,7 @@ namespace pclass
|
|||
// Make sure that this is allowed
|
||||
if (!is<T>())
|
||||
throw std::runtime_error("Type mismatch in Value::get<T>() call.");
|
||||
return *(T *)m_value_ptr;
|
||||
return *static_cast<T *>(m_value_ptr);
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
#pragma once
|
||||
#include <vector>
|
||||
#include "ki/pclass/Property.h"
|
||||
#include "ki/util/exception.h"
|
||||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
{
|
||||
template <typename ValueT>
|
||||
class VectorPropertyBase : public std::vector<ValueT>, public DynamicPropertyBase
|
||||
{
|
||||
public:
|
||||
VectorPropertyBase(PropertyClass &object,
|
||||
const std::string &name, const Type &type)
|
||||
: DynamicPropertyBase(object, name, type) { }
|
||||
|
||||
std::size_t get_element_count() const override
|
||||
{
|
||||
return this->size();
|
||||
}
|
||||
|
||||
Value get_value(int index) const override
|
||||
{
|
||||
if (index < 0 || index >= this->size())
|
||||
throw runtime_error("Index out of bounds.");
|
||||
return this->at(index);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ValueT, typename Enable = void>
|
||||
class VectorPropertyBase2 : public VectorPropertyBase<ValueT>
|
||||
{
|
||||
VectorPropertyBase2(PropertyClass &object,
|
||||
const std::string &name, const Type &type)
|
||||
: VectorPropertyBase<ValueT>(object, name, type) { }
|
||||
|
||||
constexpr bool is_pointer() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void write_value_to(BitStream &stream, const int index) const override
|
||||
{
|
||||
if (index < 0 || index >= this->size())
|
||||
throw runtime_error("Index out of bounds.");
|
||||
this->get_type().write_to(stream, this->at(index));
|
||||
}
|
||||
|
||||
void read_value_from(BitStream &stream, const int index) override
|
||||
{
|
||||
if (index < 0 || index >= this->size())
|
||||
throw runtime_error("Index out of bounds.");
|
||||
this->get_type().read_from(stream, this->at(index));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ValueT>
|
||||
class VectorPropertyBase2<
|
||||
ValueT,
|
||||
typename std::enable_if<
|
||||
std::is_pointer<ValueT>::value
|
||||
>::type
|
||||
> : public VectorPropertyBase<ValueT>
|
||||
{
|
||||
public:
|
||||
VectorPropertyBase2(PropertyClass &object,
|
||||
const std::string &name, const Type &type)
|
||||
: VectorPropertyBase<ValueT>(object, name, type) { }
|
||||
|
||||
constexpr bool is_pointer() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void write_value_to(BitStream &stream, const int index) const override
|
||||
{
|
||||
if (index < 0 || index >= this->size())
|
||||
throw runtime_error("Index out of bounds.");
|
||||
this->get_type().write_to(stream, *this->at(index));
|
||||
}
|
||||
|
||||
void read_value_from(BitStream &stream, const int index) override
|
||||
{
|
||||
if (index < 0 || index >= this->size())
|
||||
throw runtime_error("Index out of bounds.");
|
||||
this->get_type().read_from(stream, Value(*this->at(index)));
|
||||
}
|
||||
};
|
||||
|
||||
template <
|
||||
typename ValueT,
|
||||
typename _IsBaseEnable = void,
|
||||
typename _IsPointerEnable = void
|
||||
>
|
||||
class VectorProperty : public VectorPropertyBase2<ValueT>
|
||||
{
|
||||
public:
|
||||
VectorProperty(PropertyClass &object,
|
||||
const std::string &name, const Type &type)
|
||||
: VectorPropertyBase2<ValueT>(object, name, type) { }
|
||||
|
||||
const PropertyClass *get_object(const int index) const override
|
||||
{
|
||||
// We aren't holding an object at all, whoever called this is mistaken.
|
||||
throw runtime_error(
|
||||
"Tried calling get_object(index) on a property that does not store an object."
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ValueT>
|
||||
class VectorProperty<
|
||||
ValueT,
|
||||
typename std::enable_if<
|
||||
std::is_base_of<
|
||||
PropertyClass,
|
||||
typename std::remove_pointer<ValueT>::type
|
||||
>::value
|
||||
>::type,
|
||||
typename std::enable_if<
|
||||
!std::is_pointer<ValueT>::value
|
||||
>::type
|
||||
> : public VectorPropertyBase2<ValueT>
|
||||
{
|
||||
public:
|
||||
VectorProperty(PropertyClass &object,
|
||||
const std::string &name, const Type &type)
|
||||
: VectorPropertyBase2<ValueT>(object, name, type) { }
|
||||
|
||||
const PropertyClass *get_object(const int index) const override
|
||||
{
|
||||
if (index < 0 || index >= this->size())
|
||||
throw runtime_error("Index out of bounds.");
|
||||
return dynamic_cast<PropertyClass *>(&this->at(index));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ValueT>
|
||||
class VectorProperty<
|
||||
ValueT,
|
||||
typename std::enable_if<
|
||||
std::is_base_of<
|
||||
PropertyClass,
|
||||
typename std::remove_pointer<ValueT>::type
|
||||
>::value
|
||||
>::type,
|
||||
typename std::enable_if<
|
||||
std::is_pointer<ValueT>::value
|
||||
>::type
|
||||
> : public VectorPropertyBase2<ValueT>
|
||||
{
|
||||
public:
|
||||
VectorProperty(PropertyClass &object,
|
||||
const std::string &name, const Type &type)
|
||||
: VectorPropertyBase2<ValueT>(object, name, type) { }
|
||||
|
||||
const PropertyClass *get_object(const int index) const override
|
||||
{
|
||||
if (index < 0 || index >= this->size())
|
||||
throw runtime_error("Index out of bounds.");
|
||||
return dynamic_cast<PropertyClass *>(this->at(index));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
#pragma once
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
#include "ki/pclass/Property.h"
|
||||
#include "ki/pclass/types/Type.h"
|
||||
#include "ki/pclass/PropertyClass.h"
|
||||
#include "ki/util/exception.h"
|
||||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
{
|
||||
/**
|
||||
* TODO: Documentation
|
||||
*/
|
||||
class ClassTypeBase : public Type
|
||||
{
|
||||
public:
|
||||
ClassTypeBase(const std::string &name,
|
||||
const Type *base_class, const TypeSystem &type_system);
|
||||
virtual ~ClassTypeBase() {}
|
||||
|
||||
bool inherits(const Type &type) const;
|
||||
|
||||
private:
|
||||
const ClassTypeBase *m_base_class;
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO: Documentation
|
||||
*/
|
||||
template <class ClassT>
|
||||
class ClassType : public ClassTypeBase
|
||||
{
|
||||
// Ensure that ClassT inherits PropertyClass
|
||||
static_assert(std::is_base_of<PropertyClass, ClassT>::value, "ClassT must inherit PropertyClass!");
|
||||
|
||||
public:
|
||||
ClassType(const std::string &name,
|
||||
const Type *base_class, const TypeSystem &type_system)
|
||||
: ClassTypeBase(name, base_class, type_system) {}
|
||||
|
||||
PropertyClass *instantiate() const override
|
||||
{
|
||||
return new ClassT(*this, get_type_system());
|
||||
}
|
||||
|
||||
void write_to(BitStream &stream, const Value &value) const override
|
||||
{
|
||||
const auto &object = dynamic_cast<const PropertyClass &>(value.get<ClassT>());
|
||||
const auto &properties = object.get_properties();
|
||||
for (auto it = properties.begin(); it != properties.end(); ++it)
|
||||
(*it)->write_value_to(stream);
|
||||
}
|
||||
|
||||
void read_from(BitStream &stream, Value &value) const override
|
||||
{
|
||||
auto &object = dynamic_cast<PropertyClass &>(value.get<ClassT>());
|
||||
auto &properties = object.get_properties();
|
||||
for (auto it = properties.begin(); it != properties.end(); ++it)
|
||||
(*it)->read_value_from(stream);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
#include <type_traits>
|
||||
#include "ki/pclass/types/Type.h"
|
||||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
{
|
||||
/**
|
||||
* TODO: Documentation
|
||||
*/
|
||||
class EnumOption
|
||||
{
|
||||
private:
|
||||
std::string m_name;
|
||||
uint32_t m_value;
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO: Documentation
|
||||
*/
|
||||
template <typename EnumT>
|
||||
class EnumType : public Type
|
||||
{
|
||||
// Ensure that EnumT is an enum
|
||||
static_assert(std::is_enum<EnumT>::value, "EnumT must be an enum!");
|
||||
|
||||
public:
|
||||
EnumType(std::string name, hash_t hash, TypeSystem *type_system);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -4,48 +4,52 @@
|
|||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
namespace pclass
|
||||
{
|
||||
template <typename ValueT>
|
||||
struct PrimitiveTypeWriter<
|
||||
ValueT,
|
||||
typename std::enable_if<std::is_floating_point<ValueT>::value>::type
|
||||
>
|
||||
{
|
||||
template <typename ValueT>
|
||||
struct PrimitiveTypeWriter<
|
||||
ValueT,
|
||||
typename std::enable_if<std::is_floating_point<ValueT>::value>::type
|
||||
>
|
||||
static void write_to(BitStream &stream, const ValueT &value)
|
||||
{
|
||||
static void write_to(BitStream &stream, const ValueT &value)
|
||||
{
|
||||
// Reinterpret the reference as a reference to an integer
|
||||
const uint_type &v = *((const uint_type *)&value);
|
||||
stream.write<uint_type>(v, bitsizeof<ValueT>::value);
|
||||
}
|
||||
// Reinterpret the reference as a reference to an integer
|
||||
const uint_type &v = *(
|
||||
reinterpret_cast<const uint_type *>(&value)
|
||||
);
|
||||
stream.write<uint_type>(v, bitsizeof<ValueT>::value);
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* An unsigned integer type with the same size as the floating point type
|
||||
* ValueT.
|
||||
*/
|
||||
using uint_type = typename bits<bitsizeof<ValueT>::value>::uint_type;
|
||||
};
|
||||
private:
|
||||
/**
|
||||
* An unsigned integer type with the same size as the floating point type
|
||||
* ValueT.
|
||||
*/
|
||||
using uint_type = typename bits<bitsizeof<ValueT>::value>::uint_type;
|
||||
};
|
||||
|
||||
template <typename ValueT>
|
||||
struct PrimitiveTypeReader<
|
||||
ValueT,
|
||||
typename std::enable_if<std::is_floating_point<ValueT>::value>::type
|
||||
>
|
||||
template <typename ValueT>
|
||||
struct PrimitiveTypeReader<
|
||||
ValueT,
|
||||
typename std::enable_if<std::is_floating_point<ValueT>::value>::type
|
||||
>
|
||||
{
|
||||
static void read_from(BitStream &stream, ValueT &value)
|
||||
{
|
||||
static void read_from(BitStream &stream, ValueT &value)
|
||||
{
|
||||
// Reinterpret the reference as a reference to an integer
|
||||
uint_type &v = *((uint_type *)&value);
|
||||
v = stream.read<uint_type>(bitsizeof<ValueT>::value);
|
||||
}
|
||||
// Reinterpret the reference as a reference to an integer
|
||||
uint_type &v = *(
|
||||
reinterpret_cast<uint_type *>(&value)
|
||||
);
|
||||
v = stream.read<uint_type>(bitsizeof<ValueT>::value);
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* An unsigned integer type with the same size as the floating point type
|
||||
* ValueT.
|
||||
*/
|
||||
using uint_type = typename bits<bitsizeof<ValueT>::value>::uint_type;
|
||||
};
|
||||
}
|
||||
private:
|
||||
/**
|
||||
* An unsigned integer type with the same size as the floating point type
|
||||
* ValueT.
|
||||
*/
|
||||
using uint_type = typename bits<bitsizeof<ValueT>::value>::uint_type;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ namespace pclass
|
|||
{
|
||||
static void write_to(BitStream &stream, const ValueT &value)
|
||||
{
|
||||
stream.write<ValueT>(value, bitsizeof<ValueT>::value);
|
||||
stream.write<ValueT>(value);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -26,7 +26,7 @@ namespace pclass
|
|||
{
|
||||
static void read_from(BitStream &stream, ValueT &value)
|
||||
{
|
||||
value = stream.read<ValueT>(bitsizeof<ValueT>::value);
|
||||
value = stream.read<ValueT>();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#pragma once
|
||||
#include "ki/pclass/Type.h"
|
||||
#include "ki/pclass/types/Type.h"
|
||||
|
||||
namespace ki
|
||||
{
|
||||
|
@ -44,8 +44,8 @@ namespace pclass
|
|||
class PrimitiveType : public Type
|
||||
{
|
||||
public:
|
||||
PrimitiveType(const std::string name, const hash_t hash)
|
||||
: Type(name, hash)
|
||||
PrimitiveType(const std::string name, const TypeSystem &type_system)
|
||||
: Type(name, type_system)
|
||||
{
|
||||
m_kind = kind::PRIMITIVE;
|
||||
}
|
||||
|
@ -70,3 +70,4 @@ namespace pclass
|
|||
// Include all template specializations
|
||||
#include "ki/pclass/types/IntegralPrimitiveType.h"
|
||||
#include "ki/pclass/types/FloatingPointPrimitiveType.h"
|
||||
#include "ki/pclass/types/StringPrimitiveType.h"
|
|
@ -0,0 +1,53 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
{
|
||||
template <
|
||||
typename _Elem,
|
||||
typename _Traits,
|
||||
typename _Alloc
|
||||
>
|
||||
struct PrimitiveTypeWriter<std::basic_string<_Elem, _Traits, _Alloc>>
|
||||
{
|
||||
private:
|
||||
using type = std::basic_string<_Elem, _Traits, _Alloc>;
|
||||
|
||||
public:
|
||||
static void write_to(BitStream &stream, const type &value)
|
||||
{
|
||||
// Write the length as an unsigned short
|
||||
stream.write<uint16_t>(value.length());
|
||||
|
||||
// Write each character as _Elem
|
||||
for (auto it = value.begin(); it != value.end(); ++it)
|
||||
stream.write<_Elem>(*it);
|
||||
}
|
||||
};
|
||||
|
||||
template <
|
||||
typename _Elem,
|
||||
typename _Traits,
|
||||
typename _Alloc
|
||||
>
|
||||
struct PrimitiveTypeReader<std::basic_string<_Elem, _Traits, _Alloc>>
|
||||
{
|
||||
private:
|
||||
using type = std::basic_string<_Elem, _Traits, _Alloc>;
|
||||
|
||||
public:
|
||||
static void read_from(BitStream &stream, type &value)
|
||||
{
|
||||
// Read the length and create a new string with the correct capacity
|
||||
auto length = stream.read<uint16_t>();
|
||||
value = type(length, ' ');
|
||||
|
||||
// Read each character into the string
|
||||
for (auto it = value.begin(); it != value.end(); ++it)
|
||||
*it = stream.read<_Elem>();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -4,42 +4,53 @@
|
|||
#include <map>
|
||||
#include "ki/pclass/HashCalculator.h"
|
||||
#include "ki/pclass/Value.h"
|
||||
#include "ki/pclass/PropertyClass.h"
|
||||
#include "ki/util/BitStream.h"
|
||||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
{
|
||||
class PropertyClass;
|
||||
|
||||
/**
|
||||
* TODO: Documentation
|
||||
* A base class for classes that represent a Type.
|
||||
*/
|
||||
class Type
|
||||
{
|
||||
friend class TypeSystem;
|
||||
|
||||
public:
|
||||
/**
|
||||
* TODO: Documentation
|
||||
*/
|
||||
enum class kind
|
||||
{
|
||||
NONE,
|
||||
|
||||
/**
|
||||
* A Type that contain pure, simple values.
|
||||
*/
|
||||
PRIMITIVE,
|
||||
|
||||
/**
|
||||
* A user-defined Type.
|
||||
*/
|
||||
CLASS,
|
||||
|
||||
/**
|
||||
* A data type consisting of a set of named values.
|
||||
*/
|
||||
ENUM
|
||||
};
|
||||
|
||||
Type(std::string name, hash_t hash);
|
||||
virtual ~Type() {};
|
||||
Type(const std::string &name, const TypeSystem &type_system);
|
||||
virtual ~Type() { }
|
||||
|
||||
std::string get_name() const;
|
||||
hash_t get_hash() const;
|
||||
kind get_kind() const;
|
||||
const TypeSystem &get_type_system() const;
|
||||
|
||||
virtual PropertyClass *instantiate() const;
|
||||
virtual void write_to(BitStream &stream, const Value &value) const = 0;
|
||||
virtual void read_from(BitStream &stream, Value &value) const = 0;
|
||||
virtual void write_to(BitStream &stream, const Value &value) const;
|
||||
virtual void read_from(BitStream &stream, Value &value) const;
|
||||
|
||||
protected:
|
||||
kind m_kind;
|
||||
|
@ -47,8 +58,13 @@ namespace pclass
|
|||
private:
|
||||
std::string m_name;
|
||||
hash_t m_hash;
|
||||
const TypeSystem &m_type_system;
|
||||
|
||||
void set_hash(hash_t hash);
|
||||
/**
|
||||
* Called by a TypeSystem instance when it's HashCalculator
|
||||
* is changed.
|
||||
*/
|
||||
virtual void update_hash();
|
||||
};
|
||||
|
||||
typedef std::vector<Type *> TypeList;
|
|
@ -0,0 +1,68 @@
|
|||
#pragma once
|
||||
#include <type_traits>
|
||||
|
||||
#define MAKE_FLAGS_ENUM(n) \
|
||||
template <> \
|
||||
struct ki::is_flags_enum<n> : std::true_type {}
|
||||
|
||||
#define SET_FLAG(v, f) v |= f
|
||||
#define UNSET_FLAG(v, f) v &= ~f
|
||||
#define FLAG_IS_SET(v, f) (v & f) == f
|
||||
|
||||
namespace ki
|
||||
{
|
||||
template <typename EnumT>
|
||||
struct is_flags_enum : std::false_type {};
|
||||
|
||||
template <
|
||||
typename EnumT,
|
||||
typename = typename std::enable_if<is_flags_enum<EnumT>::value>::type
|
||||
>
|
||||
constexpr EnumT operator|(EnumT lhs, EnumT rhs)
|
||||
{
|
||||
using type = typename std::underlying_type<EnumT>::type;
|
||||
return static_cast<EnumT>(
|
||||
static_cast<type>(lhs) | static_cast<type>(rhs)
|
||||
);
|
||||
}
|
||||
|
||||
template <
|
||||
typename EnumT,
|
||||
typename = typename std::enable_if<is_flags_enum<EnumT>::value>::type
|
||||
>
|
||||
constexpr EnumT operator&(EnumT lhs, EnumT rhs)
|
||||
{
|
||||
using type = typename std::underlying_type<EnumT>::type;
|
||||
return static_cast<EnumT>(
|
||||
static_cast<type>(lhs) & static_cast<type>(rhs)
|
||||
);
|
||||
}
|
||||
|
||||
template <
|
||||
typename EnumT,
|
||||
typename = typename std::enable_if<is_flags_enum<EnumT>::value>::type
|
||||
>
|
||||
constexpr EnumT operator~(EnumT lhs)
|
||||
{
|
||||
using type = typename std::underlying_type<EnumT>::type;
|
||||
return static_cast<EnumT>(~static_cast<type>(lhs));
|
||||
}
|
||||
|
||||
template <
|
||||
typename EnumT,
|
||||
typename = typename std::enable_if<is_flags_enum<EnumT>::value>::type
|
||||
>
|
||||
EnumT &operator|=(EnumT &lhs, EnumT rhs)
|
||||
{
|
||||
return lhs = lhs | rhs;
|
||||
}
|
||||
|
||||
template <
|
||||
typename EnumT,
|
||||
typename = typename std::enable_if<is_flags_enum<EnumT>::value>::type
|
||||
>
|
||||
EnumT &operator&=(EnumT &lhs, EnumT rhs)
|
||||
{
|
||||
return lhs = lhs & rhs;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
#include <stdexcept>
|
||||
|
||||
namespace ki
|
||||
{
|
||||
class runtime_error : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit runtime_error(const std::string &message)
|
||||
: std::runtime_error(message) {}
|
||||
};
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
target_sources(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
${PROJECT_SOURCE_DIR}/src/pclass/ClassType.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
|
||||
)
|
|
@ -0,0 +1,44 @@
|
|||
#include "ki/pclass/types/ClassType.h"
|
||||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
{
|
||||
ClassTypeBase::ClassTypeBase(const std::string& name,
|
||||
const Type* base_class, const TypeSystem& type_system)
|
||||
: Type(name, type_system)
|
||||
{
|
||||
m_kind = kind::CLASS;
|
||||
|
||||
// Have we been given a base class?
|
||||
if (base_class)
|
||||
{
|
||||
// Make sure the base class is a class type
|
||||
if (base_class->get_kind() != kind::CLASS)
|
||||
throw runtime_error("base_class must be a class type!");
|
||||
|
||||
// Cast the base class up to a ClassTypeBase pointer
|
||||
m_base_class = dynamic_cast<const ClassTypeBase *>(base_class);
|
||||
if (!m_base_class)
|
||||
throw runtime_error("base_class must inherit ClassTypeBase!");
|
||||
}
|
||||
}
|
||||
|
||||
bool ClassTypeBase::inherits(const Type &type) const
|
||||
{
|
||||
// Types do not technically inherit from themselves, but it is more useful
|
||||
// if they report that they do since these checks are to make sure that objects
|
||||
// have a common interface
|
||||
if (&type == this)
|
||||
return true;
|
||||
|
||||
// If we have a base class, go down the inheritance tree and see if our
|
||||
// ancestors inherit from the requested type
|
||||
if (m_base_class)
|
||||
return m_base_class->inherits(type);
|
||||
|
||||
// We've reached the bottom of the inheritance tree; there is no inheritance.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
#include "ki/pclass/HashCalculator.h"
|
||||
#include <string>
|
||||
|
||||
namespace ki
|
||||
{
|
||||
|
@ -35,16 +36,13 @@ namespace pclass
|
|||
return result;
|
||||
}
|
||||
|
||||
hash_t WizardHashCalculator::calculate_property_hash(const std::string& name, const hash_t type_hash) const
|
||||
hash_t WizardHashCalculator::calculate_property_hash(const std::string& name) const
|
||||
{
|
||||
// Find the hash of the property name
|
||||
hash_t result = 0x1505;
|
||||
for (auto it = name.begin(); it != name.end(); ++it)
|
||||
result = (0x21 * result) + *it;
|
||||
result &= 0x7FFFFFFF;
|
||||
|
||||
// Add the type hash onto it
|
||||
return result + type_hash;
|
||||
return result & 0x7FFFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
#include "ki/pclass/Property.h"
|
||||
#include "ki/pclass/PropertyClass.h"
|
||||
#include "ki/pclass/TypeSystem.h"
|
||||
#include "ki/util/exception.h"
|
||||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
{
|
||||
PropertyBase::PropertyBase(PropertyClass &object,
|
||||
const std::string &name, const Type &type)
|
||||
{
|
||||
m_name = name;
|
||||
m_name_hash = type
|
||||
.get_type_system()
|
||||
.get_hash_calculator()
|
||||
.calculate_property_hash(name);
|
||||
m_full_hash = m_name_hash + type.get_hash();
|
||||
m_type = &type;
|
||||
|
||||
// Add this property to the object's property list
|
||||
object.get_properties().add_property(this);
|
||||
}
|
||||
|
||||
std::string PropertyBase::get_name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
hash_t PropertyBase::get_name_hash() const
|
||||
{
|
||||
return m_name_hash;
|
||||
}
|
||||
|
||||
hash_t PropertyBase::get_full_hash() const
|
||||
{
|
||||
return m_full_hash;
|
||||
}
|
||||
|
||||
const Type &PropertyBase::get_type() const
|
||||
{
|
||||
return *m_type;
|
||||
}
|
||||
|
||||
bool PropertyBase::is_pointer() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PropertyBase::is_dynamic() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DynamicPropertyBase::DynamicPropertyBase(PropertyClass &object,
|
||||
const std::string& name, const Type& type)
|
||||
: PropertyBase(object, name, type) { }
|
||||
|
||||
bool DynamicPropertyBase::is_dynamic() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Value DynamicPropertyBase::get_value() const
|
||||
{
|
||||
// The caller must specify an index
|
||||
throw runtime_error("Called get_value() on a dynamic property. Use get_value(index) instead.");
|
||||
}
|
||||
|
||||
const PropertyClass *DynamicPropertyBase::get_object() const
|
||||
{
|
||||
// The caller must specify an index
|
||||
throw runtime_error("Called get_object() on a dynamic property. Use get_object(index) instead.");
|
||||
}
|
||||
|
||||
void DynamicPropertyBase::write_value_to(BitStream &stream) const
|
||||
{
|
||||
// The caller must specify an index
|
||||
throw runtime_error("Called write_value_to() on a dynamic property. Use write_value_to(index) instead.");
|
||||
}
|
||||
|
||||
void DynamicPropertyBase::read_value_from(BitStream &stream)
|
||||
{
|
||||
// The caller must specify an index
|
||||
throw runtime_error("Called read_value_from() on a dynamic property. Use read_value_from(index) instead.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#include "ki/pclass/PropertyClass.h"
|
||||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
{
|
||||
PropertyClass::PropertyClass(const Type &type, const TypeSystem &type_system)
|
||||
{
|
||||
m_type = &type;
|
||||
m_properties = new PropertyList();
|
||||
}
|
||||
|
||||
PropertyClass::~PropertyClass()
|
||||
{
|
||||
// Delete the list of properties
|
||||
delete m_properties;
|
||||
}
|
||||
|
||||
const Type& PropertyClass::get_type() const
|
||||
{
|
||||
return *m_type;
|
||||
}
|
||||
|
||||
PropertyList &PropertyClass::get_properties()
|
||||
{
|
||||
return *m_properties;
|
||||
}
|
||||
|
||||
const PropertyList& PropertyClass::get_properties() const
|
||||
{
|
||||
return *m_properties;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
#include "ki/pclass/PropertyList.h"
|
||||
#include "ki/pclass/Property.h"
|
||||
#include "ki/util/exception.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace ki
|
||||
{
|
||||
namespace pclass
|
||||
{
|
||||
std::size_t PropertyList::get_property_count() const
|
||||
{
|
||||
return m_properties.size();
|
||||
}
|
||||
|
||||
bool PropertyList::has_property(const std::string& name) const
|
||||
{
|
||||
return m_property_name_lookup.find(name)
|
||||
!= m_property_name_lookup.end();
|
||||
}
|
||||
|
||||
bool PropertyList::has_property(const hash_t hash) const
|
||||
{
|
||||
return m_property_hash_lookup.find(hash)
|
||||
!= m_property_hash_lookup.end();
|
||||
}
|
||||
|
||||
const PropertyBase& PropertyList::get_property(const int index) const
|
||||
{
|
||||
if (index >= 0 && index < m_properties.size())
|
||||
return *m_properties[index];
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "Property index out of range. (index=" << index
|
||||
<< ", size=" << m_properties.size() << ")";
|
||||
throw runtime_error(oss.str());
|
||||
}
|
||||
|
||||
const PropertyBase& PropertyList::get_property(const std::string& name) const
|
||||
{
|
||||
const auto it = m_property_name_lookup.find(name);
|
||||
if (it != m_property_name_lookup.end())
|
||||
return *it->second;
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "Could not find property with name: '" << name << "'.";
|
||||
throw runtime_error(oss.str());
|
||||
}
|
||||
|
||||
const PropertyBase& PropertyList::get_property(const hash_t hash) const
|
||||
{
|
||||
const auto it = m_property_hash_lookup.find(hash);
|
||||
if (it != m_property_hash_lookup.end())
|
||||
return *it->second;
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "Could not find property with hash: 0x"
|
||||
<< std::hex << std::setw(8) << std::setfill('0')
|
||||
<< std::uppercase << hash << ".";
|
||||
throw runtime_error(oss.str());
|
||||
}
|
||||
|
||||
PropertyList::const_iterator PropertyList::begin() const
|
||||
{
|
||||
return m_properties.cbegin();
|
||||
}
|
||||
|
||||
PropertyList::const_iterator PropertyList::end() const
|
||||
{
|
||||
return m_properties.cend();
|
||||
}
|
||||
|
||||
void PropertyList::add_property(PropertyBase *prop)
|
||||
{
|
||||
// Make sure a property with the same name as another isn't being added
|
||||
if (has_property(prop->get_name()))
|
||||
{
|
||||
// This pointer is going to be lost, so delete it now
|
||||
delete prop;
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "A property has already been added with name: '"
|
||||
<< prop->get_name() << "'.";
|
||||
throw runtime_error(oss.str());
|
||||
}
|
||||
|
||||
// Check for hash collisions
|
||||
if (has_property(prop->get_full_hash()))
|
||||
{
|
||||
const auto &other = get_property(prop->get_full_hash());
|
||||
|
||||
// This pointer is going to be lost, so delete it now
|
||||
delete prop;
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "Cannot add property '" << prop->get_name() << "'. "
|
||||
<< "Hash collision with property '" << other.get_name() << "'.";
|
||||
throw runtime_error(oss.str());
|
||||
}
|
||||
|
||||
// Add the property to lookups
|
||||
m_properties.push_back(prop);
|
||||
m_property_name_lookup[prop->get_name()] = prop;
|
||||
m_property_hash_lookup[prop->get_full_hash()] = prop;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
#include "ki/pclass/Type.h"
|
||||
#include "ki/pclass/types/Type.h"
|
||||
#include "ki/pclass/TypeSystem.h"
|
||||
#include "ki/util/exception.h"
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
|
@ -6,10 +8,13 @@ namespace ki
|
|||
{
|
||||
namespace pclass
|
||||
{
|
||||
Type::Type(const std::string name, const hash_t hash)
|
||||
Type::Type(const std::string &name, const TypeSystem &type_system)
|
||||
: m_type_system(type_system)
|
||||
{
|
||||
m_name = name;
|
||||
m_hash = hash;
|
||||
m_hash = m_type_system
|
||||
.get_hash_calculator()
|
||||
.calculate_type_hash(name);
|
||||
m_kind = kind::NONE;
|
||||
}
|
||||
|
||||
|
@ -18,11 +23,6 @@ namespace pclass
|
|||
return m_name;
|
||||
}
|
||||
|
||||
void Type::set_hash(const hash_t hash)
|
||||
{
|
||||
m_hash = hash;
|
||||
}
|
||||
|
||||
hash_t Type::get_hash() const
|
||||
{
|
||||
return m_hash;
|
||||
|
@ -33,11 +33,37 @@ namespace pclass
|
|||
return m_kind;
|
||||
}
|
||||
|
||||
const TypeSystem &Type::get_type_system() const
|
||||
{
|
||||
return m_type_system;
|
||||
}
|
||||
|
||||
void Type::write_to(BitStream& stream, const Value& value) const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "Type '" << m_name << "' does not implement Type::write_to.";
|
||||
throw runtime_error(oss.str());
|
||||
}
|
||||
|
||||
void Type::read_from(BitStream& stream, Value& value) const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "Type '" << m_name << "' does not implement Type::read_from.";
|
||||
throw runtime_error(oss.str());
|
||||
}
|
||||
|
||||
PropertyClass *Type::instantiate() const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "Type '" << m_name << "' cannot be instantiated.";
|
||||
throw std::runtime_error(oss.str());
|
||||
oss << "Type '" << m_name << "' does not implement Type::instantiate.";
|
||||
throw runtime_error(oss.str());
|
||||
}
|
||||
|
||||
void Type::update_hash()
|
||||
{
|
||||
m_hash = m_type_system
|
||||
.get_hash_calculator()
|
||||
.calculate_type_hash(m_name);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
#include "ki/pclass/TypeSystem.h"
|
||||
#include "ki/util/BitTypes.h"
|
||||
#include "ki/util/exception.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
|
@ -21,15 +23,7 @@ namespace ki
|
|||
{
|
||||
namespace pclass
|
||||
{
|
||||
TypeSystem& TypeSystem::get_singleton()
|
||||
{
|
||||
if (s_instance == nullptr)
|
||||
// Create the static instance with the default hash calculator
|
||||
s_instance = new TypeSystem(new WizardHashCalculator());
|
||||
return *s_instance;
|
||||
}
|
||||
|
||||
TypeSystem::TypeSystem(HashCalculator* hash_calculator)
|
||||
TypeSystem::TypeSystem(HashCalculator *hash_calculator)
|
||||
{
|
||||
m_hash_calculator = hash_calculator;
|
||||
|
||||
|
@ -69,8 +63,12 @@ namespace pclass
|
|||
define_primitive<float>("float");
|
||||
define_primitive<double>("double");
|
||||
|
||||
// TODO: Define bit floating point types
|
||||
// TODO: Define string types
|
||||
// Define string types
|
||||
define_primitive<std::string>("std::string");
|
||||
define_primitive<std::wstring>("std::wstring");
|
||||
|
||||
// Define the base class for all classes
|
||||
define_class<PropertyClass>("class PropertyClass");
|
||||
}
|
||||
|
||||
TypeSystem::~TypeSystem()
|
||||
|
@ -87,6 +85,15 @@ namespace pclass
|
|||
delete m_hash_calculator;
|
||||
}
|
||||
|
||||
const HashCalculator &TypeSystem::get_hash_calculator() const
|
||||
{
|
||||
// Make sure the hash calculator isn't null
|
||||
if (m_hash_calculator == nullptr)
|
||||
throw runtime_error("TypeSystem::get_hash_calculator() called but hash calculator is null.");
|
||||
|
||||
return *m_hash_calculator;
|
||||
}
|
||||
|
||||
void TypeSystem::set_hash_calculator(HashCalculator* hash_calculator)
|
||||
{
|
||||
// Update the hash calculator
|
||||
|
@ -96,46 +103,47 @@ namespace pclass
|
|||
m_type_hash_lookup.clear();
|
||||
for (auto it = m_types.begin(); it != m_types.end(); ++it)
|
||||
{
|
||||
// Calculate the new hash and update the type
|
||||
auto *type = *it;
|
||||
const auto new_hash = m_hash_calculator->calculate_type_hash(type->get_name());
|
||||
type->set_hash(new_hash);
|
||||
|
||||
// Add the new hash to lookup
|
||||
m_type_hash_lookup[new_hash] = type;
|
||||
|
||||
// Is this type a class?
|
||||
if (type->get_kind() == Type::kind::CLASS)
|
||||
{
|
||||
// TODO: Recalculate property hashes
|
||||
}
|
||||
auto *type = *it;
|
||||
type->update_hash();
|
||||
m_type_hash_lookup[type->get_hash()] = type;
|
||||
}
|
||||
}
|
||||
|
||||
Type& TypeSystem::get_type(const std::string &name) const
|
||||
bool TypeSystem::has_type(const std::string &name) const
|
||||
{
|
||||
return m_type_name_lookup.find(name) != m_type_name_lookup.end();
|
||||
}
|
||||
|
||||
bool TypeSystem::has_type(const hash_t hash) const
|
||||
{
|
||||
return m_type_hash_lookup.find(hash) != m_type_hash_lookup.end();
|
||||
}
|
||||
|
||||
const Type &TypeSystem::get_type(const std::string &name) const
|
||||
{
|
||||
const auto it = m_type_name_lookup.find(name);
|
||||
if (it == m_type_name_lookup.end())
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "Could not find type with name '" << name << "'.";
|
||||
throw std::runtime_error(oss.str());
|
||||
throw runtime_error(oss.str());
|
||||
}
|
||||
return *(it->second);
|
||||
return *it->second;
|
||||
}
|
||||
|
||||
Type& TypeSystem::get_type(const hash_t hash) const
|
||||
const Type &TypeSystem::get_type(const hash_t hash) const
|
||||
{
|
||||
const auto it = m_type_hash_lookup.find(hash);
|
||||
if (it == m_type_hash_lookup.end())
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "Could not find type with hash: " <<
|
||||
oss << "Could not find type with hash: 0x" <<
|
||||
std::hex << std::setw(8) << std::setfill('0') <<
|
||||
std::uppercase << hash << ".";
|
||||
throw std::runtime_error(oss.str());
|
||||
throw runtime_error(oss.str());
|
||||
}
|
||||
return *(it->second);
|
||||
return *it->second;
|
||||
}
|
||||
|
||||
void TypeSystem::define_type(Type *type)
|
||||
|
@ -149,7 +157,7 @@ namespace pclass
|
|||
// Throw an error
|
||||
std::ostringstream oss;
|
||||
oss << "Type '" << type->get_name() << "' is already defined.";
|
||||
throw std::runtime_error(oss.str());
|
||||
throw runtime_error(oss.str());
|
||||
}
|
||||
|
||||
// Does a type with this hash already exist?
|
||||
|
@ -163,7 +171,7 @@ namespace pclass
|
|||
std::ostringstream oss;
|
||||
oss << "Type hash collision between '" << type->get_name()
|
||||
<< "' and '" << other_type.get_name() << "'.";
|
||||
throw std::runtime_error(oss.str());
|
||||
throw runtime_error(oss.str());
|
||||
}
|
||||
|
||||
// This type is safe to add to our lookups
|
||||
|
|
Loading…
Reference in New Issue