pclass: Implement string primitives and class types

This commit is contained in:
Joshua Scott 2018-11-16 15:02:43 +00:00
parent 25d371b49f
commit 48aba1f0df
25 changed files with 1335 additions and 140 deletions

View File

@ -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;
};
}
}

View File

@ -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;
};
}
}

View File

@ -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;
};
}
}

View File

@ -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;
};
}
}

View File

@ -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;
};
}
}

View File

@ -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;

View File

@ -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:

View File

@ -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));
}
};
}
}

View File

@ -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);
}
};
}
}

View File

@ -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);
};
}
}

View File

@ -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;
};
}
}

View File

@ -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,8 +26,8 @@ namespace pclass
{
static void read_from(BitStream &stream, ValueT &value)
{
value = stream.read<ValueT>(bitsizeof<ValueT>::value);
value = stream.read<ValueT>();
}
};
}
}
}

View File

@ -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;
}
@ -69,4 +69,5 @@ namespace pclass
// Include all template specializations
#include "ki/pclass/types/IntegralPrimitiveType.h"
#include "ki/pclass/types/FloatingPointPrimitiveType.h"
#include "ki/pclass/types/FloatingPointPrimitiveType.h"
#include "ki/pclass/types/StringPrimitiveType.h"

View File

@ -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>();
}
};
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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) {}
};
}

View File

@ -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
)

44
src/pclass/ClassType.cpp Normal file
View File

@ -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;
}
}
}

View File

@ -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;
}
}
}
}

88
src/pclass/Property.cpp Normal file
View File

@ -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.");
}
}
}

View File

@ -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;
}
}
}

107
src/pclass/PropertyList.cpp Normal file
View File

@ -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;
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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