etc: Refactoring, documentation, and more tests

This commit is contained in:
Joshua Scott 2019-01-01 20:54:57 +00:00
parent bab80d20c7
commit 4833f7fb76
36 changed files with 891 additions and 528 deletions

View File

@ -4,7 +4,7 @@
#include <json.hpp>
#include "ki/pclass/Value.h"
#include "ki/util/BitTypes.h"
#include "ki/pclass/types/EnumType.h"
#include "ki/pclass/EnumType.h"
namespace ki
{

View File

@ -3,7 +3,7 @@
#include <type_traits>
#include <string>
#include "ki/pclass/Property.h"
#include "ki/pclass/types/Type.h"
#include "ki/pclass/Type.h"
#include "ki/pclass/PropertyClass.h"
namespace ki
@ -11,18 +11,22 @@ namespace ki
namespace pclass
{
/**
* TODO: Documentation
* Base type for classes. Adds inheritance to Type.
*/
class IClassType : public Type
{
public:
IClassType(const std::string &name,
const Type *base_class, const TypeSystem &type_system);
virtual ~IClassType() {}
virtual ~IClassType() = default;
/**
* @param[in] type The ancestor type to check against.
* @returns True if this Type is a descendant of the given type. False otherwise.
*/
bool inherits(const Type &type) const;
void write_to(BitStream &stream, Value value) const override = 0;
void write_to(BitStream &stream, Value &value) const override = 0;
Value read_from(BitStream &stream) const override = 0;
private:
@ -30,19 +34,27 @@ namespace pclass
};
/**
* TODO: Documentation
* A user-defined structure.
* @tparam ClassT The compile-time user-defined class that the class represents.
*/
template <class ClassT>
template <typename ClassT>
class ClassType : public IClassType
{
// Ensure that ClassT inherits PropertyClass
static_assert(std::is_base_of<PropertyClass, ClassT>::value, "ClassT must inherit PropertyClass!");
public:
// Do not allow copy construction or movement of types
ClassType(const ClassType<ClassT> &that) = delete;
ClassType &operator=(const ClassType<ClassT> &that) = delete;
ClassType(ClassType<ClassT> &&that) noexcept = delete;
ClassType &operator=(ClassType<ClassT> &&that) noexcept = delete;
ClassType(const std::string &name,
const Type *base_class, const TypeSystem &type_system)
: IClassType(name, base_class, type_system)
{}
~ClassType() = default;
std::unique_ptr<PropertyClass> instantiate() const override
{
@ -52,7 +64,7 @@ namespace pclass
);
}
void write_to(BitStream &stream, Value value) const override
void write_to(BitStream &stream, Value &value) const override
{
const auto &object = value.get<ClassT>();
const auto &properties = object.get_properties();

View File

@ -1,6 +1,6 @@
#pragma once
#include "ki/pclass/types/EnumType.h"
#include "ki/pclass/types/PrimitiveType.h"
#include "ki/pclass/EnumType.h"
#include "ki/pclass/PrimitiveType.h"
namespace ki
{

View File

@ -1,8 +1,8 @@
#pragma once
#include <type_traits>
#include <unordered_map>
#include "ki/pclass/types/Type.h"
#include "ki/pclass/types/PrimitiveType.h"
#include "ki/pclass/Type.h"
#include "ki/pclass/PrimitiveType.h"
namespace ki
{
@ -32,6 +32,12 @@ namespace pclass
};
public:
// Do not allow copy construction or movement of types
EnumType(const EnumType &that) = delete;
EnumType &operator=(const EnumType &that) = delete;
EnumType(EnumType &&that) noexcept = delete;
EnumType &operator=(EnumType &&that) noexcept = delete;
EnumType(const std::string &name, const TypeSystem &type_system);
~EnumType();
@ -43,7 +49,7 @@ namespace pclass
EnumType &add_element(const std::string &name, enum_value_t value);
void write_to(BitStream &stream, Value value) const override;
void write_to(BitStream &stream, Value &value) const override;
Value read_from(BitStream &stream) const override;
private:
@ -66,10 +72,10 @@ namespace pclass
CppEnumType(const std::string &name, const TypeSystem &type_system)
: Type(name, type_system)
{
m_kind = kind::ENUM;
m_kind = Kind::ENUM;
}
void write_to(BitStream &stream, const Value value) const override
void write_to(BitStream &stream, Value &value) const override
{
auto &enum_reference = value.get<EnumT>();
auto &underlying_reference = reinterpret_cast<const underlying_type &>(enum_reference);

View File

@ -13,10 +13,10 @@ namespace ki
/**
* A base class for type/property hash calculators.
*/
class HashCalculator
class IHashCalculator
{
public:
virtual ~HashCalculator() {};
virtual ~IHashCalculator() {};
/**
* Calculate a type hash from the type's name.
@ -35,7 +35,7 @@ namespace ki
* A hash calculator that uses the algorithms found and used in
* Wizard101.
*/
class WizardHashCalculator : public HashCalculator
class WizardHashCalculator : public IHashCalculator
{
public:
hash_t calculate_type_hash(const std::string& name) const override;

View File

@ -0,0 +1,189 @@
#pragma once
#include "ki/pclass/Type.h"
namespace ki
{
namespace pclass
{
namespace detail
{
/**
* Provides implementations to PrimitiveType<ValueT>::write_to,
* and PrimitiveType<ValueT>::read_from.
*/
template <typename ValueT, typename Enable = void>
struct primitive_type_helper
{
static void write_to(BitStream &stream, const ValueT &value)
{
// Provide a compiler error if this is not specialized
static_assert(
sizeof(ValueT) == 0,
"Missing specialization of primitive_type_writer::write_to"
);
}
static Value read_from(BitStream &stream)
{
// Provide a compiler error if this is not specialized
static_assert(
sizeof(ValueT) == 0,
"Missing specialization of PrimitiveTypeReader::read_from"
);
// This should be impossible to reach.
throw runtime_error("Missing specialization of PrimitiveTypeReader::read_from");
}
};
/**
* Specialization of primitive_type_helper for integer types.
* This includes instantiations of ki::BitInteger<>.
*/
template <typename ValueT>
struct primitive_type_helper<
ValueT,
typename std::enable_if<is_integral<ValueT>::value>::type
>
{
static void write_to(BitStream &stream, const ValueT &value)
{
stream.write<ValueT>(value);
}
static Value read_from(BitStream &stream)
{
return Value::make_value<ValueT>(
stream.read<ValueT>()
);
}
};
/**
* Specialization of primitive_type_helper for floating point
* types.
*/
template <typename ValueT>
struct primitive_type_helper<
ValueT,
typename std::enable_if<std::is_floating_point<ValueT>::value>::type
>
{
static void write_to(BitStream &stream, const 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);
}
static Value read_from(BitStream &stream)
{
uint_type uint_value = stream.read<uint_type>(bitsizeof<ValueT>::value);
return Value::make_value<ValueT>(*reinterpret_cast<ValueT *>(&uint_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;
};
/**
* Specialization of primitive_type_helper for string types.
*/
template <
typename _Elem,
typename _Traits,
typename _Alloc
>
struct primitive_type_helper<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);
}
static Value read_from(BitStream &stream)
{
// Read the length and create a new string with the correct capacity
auto length = stream.read<uint16_t>();
auto value = type(length, ' ');;
// Read each character into the string
for (auto it = value.begin(); it != value.end(); ++it)
*it = stream.read<_Elem>();
// Copy string value into the return value
return Value::make_value<type>(value);
}
};
}
/**
* A basic data type that can be used to build more complex structures.
* @tparam ValueT The compile-time primitive type that the class represents.
*/
template <typename ValueT>
class PrimitiveType : public Type
{
public:
// Do not allow copy construction or movement of types
PrimitiveType(const PrimitiveType<ValueT> &that) = delete;
PrimitiveType &operator=(const PrimitiveType<ValueT> &that) = delete;
PrimitiveType(PrimitiveType<ValueT> &&that) noexcept = delete;
PrimitiveType &operator=(PrimitiveType<ValueT> &&that) noexcept = delete;
PrimitiveType(const std::string name, const TypeSystem &type_system)
: Type(name, type_system)
{
m_kind = Kind::PRIMITIVE;
}
~PrimitiveType() = default;
void write_to(BitStream &stream, Value &value) const override
{
try
{
Value deref_value = value.dereference<ValueT>();
detail::primitive_type_helper<ValueT>::write_to(
stream,
deref_value.get<ValueT>()
);
}
catch (runtime_error &e)
{
std::ostringstream oss;
oss << "Invalid call to Type::write_to -- " << e.what();
throw runtime_error(oss.str());
}
}
Value read_from(BitStream &stream) const override
{
try
{
return detail::primitive_type_helper<ValueT>::read_from(stream);
}
catch (runtime_error &e)
{
std::ostringstream oss;
oss << "Invalid call to Type::read_from -- " << e.what();
throw runtime_error(oss.str());
}
}
};
}
}

View File

@ -1,6 +1,6 @@
#pragma once
#include <string>
#include "ki/pclass/types/Type.h"
#include "ki/pclass/Type.h"
#include "ki/pclass/HashCalculator.h"
#include "ki/pclass/Value.h"
#include "ki/util/BitStream.h"
@ -12,7 +12,9 @@ namespace pclass
class PropertyClass;
/**
* TODO: Documentation
* A base class for properties.
* Provides access to meta information such as name, and type
* of instance members, and a mechanism to get/set their values.
*/
class IProperty
{
@ -26,27 +28,86 @@ namespace pclass
IProperty(PropertyClass &object,
const IProperty &that);
virtual ~IProperty() {}
virtual ~IProperty() = default;
const PropertyClass &get_instance() const;
std::string get_name() const;
hash_t get_name_hash() const;
hash_t get_full_hash() const;
const Type &get_type() const;
/**
* @returns A reference to the instance of PropertyClass that this property
* belongs to.
*/
const PropertyClass &get_instance() const;
/**
* @returns Whether or not the property's value type is a pointer.
*/
virtual bool is_pointer() const;
/**
* @returns Whether or not the property's size is dynamic.
*/
virtual bool is_dynamic() const;
/**
* @returns Whether or not the property's value type is capable of
* holding more than one value.
*/
virtual bool is_array() const;
/**
* @returns The number of values that the property is holding.
*/
virtual std::size_t get_element_count() const;
/**
* @param[in] size The new number of elements.
* @throws ki::runtime_error If the property is not dynamically sized.
*/
virtual void set_element_count(std::size_t size) = 0;
/**
* @param[in] index The index of the element to retrieve the value from.
* @returns A reference to the value at the specified index, as a Value instance.
*/
virtual Value get_value(std::size_t index = 0) const = 0;
/**
* @param[in] value The new value.
* @param[in] index The index of the element to modify.
*/
virtual void set_value(Value value, std::size_t index = 0) = 0;
/**
* @param[in] index The index of the object element to retrieve.
* @returns A pointer to the instance of PropertyClass at the specified index.
* @throws ki::runtime_error If the property's value type is not an object,
* as in, it does not inherit PropertyClass.
*/
virtual const PropertyClass *get_object(std::size_t index = 0) const = 0;
/**
* @param[in] object A pointer to the new object value.
* @param[in] index The index of the object element to modify.
* @throws ki::runtime_error If the property's value type is not an object,
* as in, it does not inherit PropertyClass.
*/
virtual void set_object(std::unique_ptr<PropertyClass> &object, std::size_t index = 0) = 0;
/**
* Write the value of this property's specified element to a BitStream.
* @param[in] stream The stream to write to.
* @param[in] index The index of the element to retrieve the value from
*/
virtual void write_value_to(BitStream &stream, std::size_t index = 0) const;
/**
* Read a value from a BitStream into the specified element of this property.
* @param[in] stream The stream to read from.
* @param[in] index The index of the element to read a value into.
*/
virtual void read_value_from(BitStream &stream, std::size_t index = 0);
private:

View File

@ -1,5 +1,5 @@
#pragma once
#include "ki/pclass/types/Type.h"
#include "ki/pclass/Type.h"
#include "ki/pclass/PropertyList.h"
#define _KI_TYPE ki::pclass::Type
@ -52,11 +52,10 @@ namespace ki
{
namespace pclass
{
template <typename ValueT>
class ClassType;
/**
* TODO: Documentation
* A base class for all objects defined as ClassTypes by a
* TypeSystem instance. Provides a way to access a list of
* instance properties, as well as their values, at runtime.
*/
class PropertyClass
{
@ -64,7 +63,7 @@ namespace pclass
public:
explicit PropertyClass(const Type &type, const TypeSystem &type_system);
virtual ~PropertyClass() {}
virtual ~PropertyClass() = default;
PropertyClass(const PropertyClass &that);
PropertyClass &operator=(const PropertyClass &that);
@ -75,12 +74,11 @@ namespace pclass
virtual void on_created() const {}
protected:
void add_property(IProperty &prop);
private:
const Type *m_type;
PropertyList m_properties;
void add_property(IProperty &prop);
};
}
}

View File

@ -11,16 +11,13 @@ namespace pclass
class IProperty;
/**
* TODO: Documentation
* Manages property lookup on a PropertyClass instance.
*/
class PropertyList
{
friend PropertyClass;
public:
/**
* TODO: Documentation
*/
class iterator
{
friend PropertyList;
@ -40,9 +37,6 @@ namespace pclass
explicit iterator(PropertyList &list, int index = 0);
};
/**
* TODO: Documentation
*/
class const_iterator
{
friend PropertyList;
@ -62,18 +56,63 @@ namespace pclass
explicit const_iterator(const PropertyList &list, int index = 0);
};
/**
* @returns The number of properties in this list.
*/
std::size_t get_property_count() const;
/**
* @param[in] name The name of the property to search for.
* @returns Whether or not a property with the given name exists.
*/
bool has_property(const std::string &name) const;
/**
* @param[in] hash The full hash of the property to search for.
* @returns Whether or not a property with the given hash exists.
*/
bool has_property(hash_t hash) const;
/**
* @param[in] index The index of the property to return.
* @returns The property at the specified index.
* @throw ki::runtime_error If the index is out of bounds.
*/
IProperty &get_property(int index);
/**
* @param[in] index The index of the property to return.
* @returns The property at the specified index.
* @throw ki::runtime_error If the specified index is out of bounds.
*/
const IProperty &get_property(int index) const;
/**
* @param[in] name The name of the property to search for.
* @returns The property found with the specified name.
* @throw ki::runtime_error If no property exists with the specified name.
*/
IProperty &get_property(const std::string &name);
/**
* @param[in] name The name of the property to search for.
* @returns The property found with the specified name.
* @throw ki::runtime_error If no property exists with the specified name.
*/
const IProperty &get_property(const std::string &name) const;
/**
* @param[in] hash The full hash of the property to search for.
* @returns The property found with the specified hash.
* @throw ki::runtime_error If no property exists with the specified hash.
*/
IProperty &get_property(hash_t hash);
/**
* @param[in] hash The full hash of the property to search for.
* @returns The property found with the specified hash.
* @throw ki::runtime_error If no property exists with the specified hash.
*/
const IProperty &get_property(hash_t hash) const;
iterator begin();

View File

@ -271,14 +271,14 @@ namespace pclass
>
{
static const PropertyClass *get_object(
const StaticProperty<ValueT> &prop, const int index)
const StaticProperty<ValueT[N]> &prop, const int index)
{
// 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[index]);
}
static void set_object(StaticProperty<ValueT> &prop,
static void set_object(StaticProperty<ValueT[N]> &prop,
std::unique_ptr<PropertyClass> &object, const int index)
{
// Ensure that object is not nullptr
@ -308,14 +308,14 @@ namespace pclass
>
{
static const PropertyClass *get_object(
const StaticProperty<ValueT> &prop, const int index)
const StaticProperty<ValueT *[N]> &prop, const int index)
{
// 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[index]);
}
static void set_object(StaticProperty<ValueT> &prop,
static void set_object(StaticProperty<ValueT *[N]> &prop,
std::unique_ptr<PropertyClass> &object, const int index)
{
// Ensure that object inherits the type of the property
@ -397,12 +397,12 @@ namespace pclass
template <typename ValueT, int N>
struct static_value_helper<ValueT *[N]>
{
static Value get_value(const StaticProperty<ValueT[N]> &prop, const int index)
static Value get_value(const StaticProperty<ValueT *[N]> &prop, const int index)
{
return Value::make_reference<ValueT>(*prop.m_value[index]);
}
static void set_value(StaticProperty<ValueT[N]> &prop, Value value, const int index)
static void set_value(StaticProperty<ValueT *[N]> &prop, Value value, const int index)
{
Value casted_value = value.as<ValueT>();
prop.m_value[index] = casted_value.release<ValueT>();

View File

@ -19,12 +19,21 @@ namespace pclass
class Type
{
public:
enum class kind
// Do not allow copy construction or movement of types
Type(const Type &that) = delete;
Type &operator=(const Type &that) = delete;
Type(Type &&that) noexcept = delete;
Type &operator=(Type &&that) noexcept = delete;
/**
* An enum of Type kinds.
*/
enum class Kind
{
NONE,
/**
* A Type that contain pure, simple values.
* A Type that contains pure, simple values.
*/
PRIMITIVE,
@ -40,19 +49,39 @@ namespace pclass
};
Type(const std::string &name, const TypeSystem &type_system);
virtual ~Type() {}
virtual ~Type() = default;
std::string get_name() const;
const std::string &get_name() const;
hash_t get_hash() const;
kind get_kind() const;
Kind get_kind() const;
/**
* The TypeSystem used to define this Type instance.
*/
const TypeSystem &get_type_system() const;
/**
* Create an instance of the type being represented.
* @returns A pointer to a new PropertyClass instance.
*/
virtual std::unique_ptr<PropertyClass> instantiate() const;
virtual void write_to(BitStream &stream, Value value) const;
/**
* Write a value of this type to a BitStream.
* @param[in] stream The stream to write to.
* @param[in] value The value to write to the stream.
*/
virtual void write_to(BitStream &stream, Value &value) const;
/**
* Read a value of this type from a BitStream.
* @param stream[in] The stream to read from.
* @returns The value read from the stream.
*/
virtual Value read_from(BitStream &stream) const;
protected:
kind m_kind;
Kind m_kind;
private:
std::string m_name;

View File

@ -4,31 +4,62 @@
#include <json.hpp>
#include "ki/pclass/HashCalculator.h"
#include "ki/pclass/Casters.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"
#include "ki/pclass/Type.h"
#include "ki/pclass/PrimitiveType.h"
#include "ki/pclass/ClassType.h"
#include "ki/pclass/EnumType.h"
#include "ki/util/unique.h"
namespace ki
{
namespace pclass
{
/**
* TODO: Documentation
* A class that provides run-time type definition and lookup.
*/
class TypeSystem
{
public:
explicit TypeSystem(std::unique_ptr<HashCalculator> &hash_calculator);
explicit TypeSystem(std::unique_ptr<IHashCalculator> &hash_calculator);
const HashCalculator &get_hash_calculator() const;
/**
* @returns The IHashCalculator instance this TypeSystem uses to calculate
* type and property hashes.
*/
const IHashCalculator &get_hash_calculator() const;
/**
* @param[in] name The name of the type to search for.
* @returns Whether a type has been defined with the specified name.
*/
bool has_type(const std::string &name) const;
/**
* @param[in] hash The hash of the type to search for.
* @returns Whether a type exists with the specified hash.
*/
bool has_type(hash_t hash) const;
/**
* @param[in] name The name of the type to search for.
* @returns The Type instance defined with the specified name.
* @throws ki::runtime_error If a Type with the specified name could not be found.
*/
const Type &get_type(const std::string &name) const;
/**
* @param[in] hash The hash of the type to search for.
* @returns The Type instance with the specified type hash.
* @throws ki::runtime_error If a Type with the specified hash could not be found.
*/
const Type &get_type(hash_t hash) const;
/**
* Define a new primitive type.
* @tparam ValueT the compile-time primitive type to represent.
* @param[in] name The name of the primitive to define.
* @returns A reference to the newly defined PrimitiveType<ValueT>.
*/
template <typename ValueT>
PrimitiveType<ValueT> &define_primitive(const std::string &name)
{
@ -42,21 +73,45 @@ namespace pclass
return *type;
}
/**
* Define a new class type.
* @tparam ClassT The compile-time class type to represent. Must inherit PropertyClass.
* @param[in] name The name of the class to define.
* @returns A reference to the newly defined ClassType<ClassT>.
*/
template <class ClassT>
ClassType<ClassT> &define_class(const std::string &name)
{
return define_class<ClassT>(name, nullptr);
}
template <class ClassT>
/**
* Define a new derived class type.
* @tparam ClassT The compile-time class type to represent.
* @param[in] name The name of the class to define.
* @param[in] base_class A reference to the Type instance of the base class. Must be an IClassType.
* @returns A reference to the newly defined ClassType<ClassT>.
*/
template <typename ClassT>
ClassType<ClassT> &define_class(
const std::string &name, const Type &base_class)
{
return define_class<ClassT>(name, &base_class);
}
/**
* Define a new dynamic enum type.
* @param[in] name The name of the enum to define.
* @returns A reference to the newly defined EnumType.
*/
EnumType &define_enum(const std::string &name);
/**
* Define a new static enum type.
* @tparam EnumT the compile-time enum type to represent.
* @param[in] name The name of the enum to define.
* @returns A reference to the newly defined CppEnumType<EnumT>.
*/
template <typename EnumT>
CppEnumType<EnumT> &define_enum(const std::string &name)
{
@ -70,6 +125,11 @@ namespace pclass
return *type;
}
/**
* Create a new instance of a PropertyClass-derived class.
* @tparam ClassT The expected compile-time class.
* @param[in] name The name of the class type to instantiate.
*/
template <typename ClassT>
std::unique_ptr<ClassT> instantiate(const std::string &name) const
{
@ -87,7 +147,7 @@ namespace pclass
std::vector<std::unique_ptr<Type>> m_types;
std::unordered_map<std::string, Type *> m_type_name_lookup;
std::unordered_map<hash_t, Type *> m_type_hash_lookup;
std::unique_ptr<HashCalculator> m_hash_calculator;
std::unique_ptr<IHashCalculator> m_hash_calculator;
template <class ClassT>
ClassType<ClassT> &define_class(

View File

@ -26,7 +26,7 @@ namespace pclass
*/
struct value_caster_base
{
virtual ~value_caster_base() {}
virtual ~value_caster_base() = default;
/**
* @param[in] value The value being casted.
@ -51,7 +51,7 @@ namespace pclass
};
/**
* TODO: Documentation
* Provides casting implementations to ValueCaster instances.
*/
template <typename SrcT, typename DestT, typename Enable>
struct value_caster : value_caster_impl<SrcT, DestT>
@ -64,15 +64,15 @@ namespace pclass
};
/**
* A base class for Value deallocators.
* A base class for Value de-allocators.
* Provides a common interface for deallocate()
*/
struct value_deallocator_base
{
virtual ~value_deallocator_base() {}
virtual ~value_deallocator_base() = default;
/**
* Deallocate a Value-owned pointer.
* De-allocate a Value-owned pointer.
* @param[in] ptr The pointer to deallocate.
*/
virtual void deallocate(void *ptr) const = 0;
@ -84,7 +84,9 @@ namespace pclass
};
/**
* TODO: Documentation
* Provides a deallocate() implementation to ValueDeallocator
* based on the type being de-allocated.
* @tparam T The type being de-allocated.
*/
template <typename T>
struct value_deallocator : value_deallocator_base
@ -102,7 +104,8 @@ namespace pclass
};
/**
* TODO: Documentation
* Provides Value with a way to safely delete the void pointer
* that has had it's type information "erased".
*/
class ValueDeallocator
{
@ -110,9 +113,9 @@ namespace pclass
friend Value;
public:
ValueDeallocator(ValueDeallocator &that);
ValueDeallocator(const ValueDeallocator &that);
ValueDeallocator(ValueDeallocator &&that) noexcept;
ValueDeallocator &operator=(ValueDeallocator &that);
ValueDeallocator &operator=(const ValueDeallocator &that);
ValueDeallocator &operator=(ValueDeallocator &&that) noexcept;
~ValueDeallocator();
@ -137,7 +140,7 @@ namespace pclass
}
/**
* TODO: Documentation
* Provides a way to perform casting on Value instances.
*/
class ValueCaster
{
@ -229,9 +232,9 @@ namespace pclass
class Value
{
public:
Value(Value &that);
Value(const Value &that);
Value(Value &&that) noexcept;
Value &operator=(Value &that);
Value &operator=(const Value &that);
Value &operator=(Value &&that) noexcept;
~Value();
@ -351,7 +354,7 @@ namespace pclass
/**
* @tparam T The type of value to hold.
* @param[in] value The initial value.
* @returns A new Value instance that refers to a value it doesn't own.
* @returns A new Value instance that refers to a value it does not own.
*/
template <typename T>
static Value make_reference(T &value)

View File

@ -112,14 +112,14 @@ namespace pclass
using default_vector_object_helper<ValueT>::copy;
static const PropertyClass *get_object(
const VectorProperty<ValueT *> &prop, const int index)
const VectorProperty<ValueT> &prop, const int index)
{
// 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.at(index));
}
static void set_object(VectorProperty<ValueT *> &prop,
static void set_object(VectorProperty<ValueT> &prop,
std::unique_ptr<PropertyClass> &object, const int index)
{
// Ensure that object is not nullptr

View File

@ -1,41 +0,0 @@
#pragma once
#include <type_traits>
#include "ki/util/BitTypes.h"
namespace ki
{
namespace pclass
{
namespace detail
{
template <typename ValueT>
struct primitive_type_helper<
ValueT,
typename std::enable_if<std::is_floating_point<ValueT>::value>::type
>
{
static void write_to(BitStream &stream, const 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);
}
static Value read_from(BitStream &stream)
{
uint_type uint_value = stream.read<uint_type>(bitsizeof<ValueT>::value);
return Value::make_value<ValueT>(*reinterpret_cast<ValueT *>(&uint_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;
};
}
}
}

View File

@ -1,31 +0,0 @@
#pragma once
#include <type_traits>
#include "ki/util/BitTypes.h"
namespace ki
{
namespace pclass
{
namespace detail
{
template <typename ValueT>
struct primitive_type_helper<
ValueT,
typename std::enable_if<is_integral<ValueT>::value>::type
>
{
static void write_to(BitStream &stream, const ValueT &value)
{
stream.write<ValueT>(value);
}
static Value read_from(BitStream &stream)
{
return Value::make_value<ValueT>(
stream.read<ValueT>()
);
}
};
}
}
}

View File

@ -1,97 +0,0 @@
#pragma once
#include "ki/pclass/types/Type.h"
namespace ki
{
namespace pclass
{
namespace detail
{
/**
* TODO: Documentation
*/
template <typename ValueT, typename Enable = void>
struct primitive_type_helper
{
/**
* TODO: Documentation
*/
static void write_to(BitStream &stream, const ValueT &value)
{
// Provide a compiler error if this is not specialized
static_assert(
sizeof(ValueT) == 0,
"Missing specialization of primitive_type_writer::write_to"
);
}
/**
* TODO: Documentation
*/
static Value read_from(BitStream &stream)
{
// Provide a compiler error if this is not specialized
static_assert(
sizeof(ValueT) == 0,
"Missing specialization of PrimitiveTypeReader::read_from"
);
// This should be impossible to reach.
throw runtime_error("Missing specialization of PrimitiveTypeReader::read_from");
}
};
}
/**
* TODO: Documentation
*/
template <typename ValueT>
class PrimitiveType : public Type
{
public:
PrimitiveType(const std::string name, const TypeSystem &type_system)
: Type(name, type_system)
{
m_kind = kind::PRIMITIVE;
}
void write_to(BitStream &stream, const Value value) const override
{
try
{
// Dereference the value to the correct type
Value deref_value = value.dereference<ValueT>();
detail::primitive_type_helper<ValueT>::write_to(
stream,
deref_value.get<ValueT>()
);
}
catch (runtime_error &e)
{
std::ostringstream oss;
oss << "Invalid call to Type::write_to -- " << e.what();
throw runtime_error(oss.str());
}
}
Value read_from(BitStream &stream) const override
{
try
{
return detail::primitive_type_helper<ValueT>::read_from(stream);
}
catch (runtime_error &e)
{
std::ostringstream oss;
oss << "Invalid call to Type::read_from -- " << e.what();
throw runtime_error(oss.str());
}
}
};
}
}
// Include all template specializations
#include "ki/pclass/types/IntegralPrimitiveType.h"
#include "ki/pclass/types/FloatingPointPrimitiveType.h"
#include "ki/pclass/types/StringPrimitiveType.h"

View File

@ -1,51 +0,0 @@
#pragma once
#include <cstdint>
#include <string>
namespace ki
{
namespace pclass
{
namespace detail
{
/**
* primitive_type_helper specialization for string types.
*/
template <
typename _Elem,
typename _Traits,
typename _Alloc
>
struct primitive_type_helper<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);
}
static Value read_from(BitStream &stream)
{
// Read the length and create a new string with the correct capacity
auto length = stream.read<uint16_t>();
auto value = type(length, ' ');;
// Read each character into the string
for (auto it = value.begin(); it != value.end(); ++it)
*it = stream.read<_Elem>();
// Copy string value into the return value
return Value::make_value<type>(value);
}
};
}
}
}

View File

@ -1,4 +1,4 @@
#include "ki/pclass/types/ClassType.h"
#include "ki/pclass/ClassType.h"
#include "ki/pclass/TypeSystem.h"
#include "ki/util/exception.h"
@ -10,13 +10,13 @@ namespace pclass
const Type *base_class, const TypeSystem& type_system)
: Type(name, type_system)
{
m_kind = kind::CLASS;
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)
if (base_class->get_kind() != Kind::CLASS)
throw runtime_error("base_class must be a class type!");
// Cast the base class up to a IClassType pointer

View File

@ -7,7 +7,7 @@ namespace pclass
Enum::Enum(const Type &type, const enum_value_t value)
{
// Make sure the type we've been given is an enum type
if (type.get_kind() != Type::kind::ENUM)
if (type.get_kind() != Type::Kind::ENUM)
throw runtime_error("Enum constructor was supplied with a non-enum type.");
m_type = &dynamic_cast<const EnumType &>(type);
@ -17,7 +17,7 @@ namespace pclass
Enum::Enum(const Type &type, const std::string &element_name)
{
// Make sure the type we've been given is an enum type
if (type.get_kind() != Type::kind::ENUM)
if (type.get_kind() != Type::Kind::ENUM)
throw runtime_error("Enum constructor was supplied with a non-enum type.");
m_type = &dynamic_cast<const EnumType &>(type);

View File

@ -1,4 +1,4 @@
#include "ki/pclass/types/EnumType.h"
#include "ki/pclass/EnumType.h"
#include "ki/util/exception.h"
#include <sstream>
#include "ki/pclass/Enum.h"
@ -7,7 +7,6 @@ namespace ki
{
namespace pclass
{
EnumType::Element::Element(const std::string &name,
const enum_value_t value)
{
@ -29,7 +28,7 @@ namespace pclass
const TypeSystem &type_system)
: Type(name, type_system)
{
m_kind = kind::ENUM;
m_kind = Kind::ENUM;
}
EnumType::~EnumType()
@ -110,7 +109,7 @@ namespace pclass
return *this;
}
void EnumType::write_to(BitStream &stream, const Value value) const
void EnumType::write_to(BitStream &stream, Value &value) const
{
// Get an Enum reference and use it to write to the stream
value.get<Enum>().write_to(stream);

View File

@ -85,7 +85,8 @@ namespace pclass
{
if (index < 0 || index >= get_element_count())
throw runtime_error("Index out of bounds.");
get_type().write_to(stream, get_value(index));
auto ref_value = get_value(index);
get_type().write_to(stream, ref_value);
}
void IProperty::read_value_from(BitStream& stream, const std::size_t index)

View File

@ -1,8 +1,7 @@
#include "ki/pclass/types/Type.h"
#include "ki/pclass/types/ClassType.h"
#include "ki/pclass/Type.h"
#include "ki/pclass/ClassType.h"
#include "ki/pclass/TypeSystem.h"
#include "ki/util/exception.h"
#include <stdexcept>
#include <sstream>
namespace ki
@ -16,10 +15,10 @@ namespace pclass
m_hash = m_type_system
.get_hash_calculator()
.calculate_type_hash(name);
m_kind = kind::NONE;
m_kind = Kind::NONE;
}
std::string Type::get_name() const
const std::string &Type::get_name() const
{
return m_name;
}
@ -29,7 +28,7 @@ namespace pclass
return m_hash;
}
Type::kind Type::get_kind() const
Type::Kind Type::get_kind() const
{
return m_kind;
}
@ -39,7 +38,7 @@ namespace pclass
return m_type_system;
}
void Type::write_to(BitStream &stream, Value value) const
void Type::write_to(BitStream &stream, Value &value) const
{
std::ostringstream oss;
oss << "Type '" << m_name << "' does not implement Type::write_to.";
@ -68,7 +67,7 @@ namespace pclass
{
// Do the types match via inheritance?
if (allow_inheritance &&
expected.get_kind() == Type::kind::CLASS)
expected.get_kind() == Type::Kind::CLASS)
{
const auto &actual_class = dynamic_cast<const IClassType &>(actual);
if (actual_class.inherits(expected))

View File

@ -36,7 +36,7 @@ namespace pclass
template <>
void define_bit_integer_primitive<0>(TypeSystem &type_system) {}
TypeSystem::TypeSystem(std::unique_ptr<HashCalculator> &hash_calculator)
TypeSystem::TypeSystem(std::unique_ptr<IHashCalculator> &hash_calculator)
{
m_hash_calculator = std::move(hash_calculator);
@ -48,6 +48,7 @@ namespace pclass
define_primitive<int8_t>("int8_t");
define_primitive<uint8_t>("uint8_t");
DEFINE_INTEGER_PRIMTIIVE(int16_t, uint16_t, "short");
define_primitive<char16_t>("wchar_t");
DEFINE_INTEGER_PRIMTIIVE(int16_t, uint16_t, "__int16");
define_primitive<int16_t>("int16_t");
define_primitive<uint16_t>("uint16_t");
@ -59,7 +60,6 @@ namespace pclass
DEFINE_INTEGER_PRIMTIIVE(int64_t, uint64_t, "__int64");
define_primitive<int64_t>("int64_t");
define_primitive<uint64_t>("uint64_t");
define_primitive<uint64_t>("gid");
// Define bit-integer types
define_bit_integer_primitive(*this);
@ -78,11 +78,8 @@ namespace pclass
define_class<PropertyClass>("class PropertyClass");
}
const HashCalculator &TypeSystem::get_hash_calculator() const
const IHashCalculator &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;
}
@ -143,7 +140,7 @@ namespace pclass
}
// Does a type with this hash already exist?
if (m_type_name_lookup.find(type->get_name()) != m_type_name_lookup.end())
if (m_type_hash_lookup.find(type->get_hash()) != m_type_hash_lookup.end())
{
// Throw an error
auto &other_type = get_type(type->get_hash());

View File

@ -9,7 +9,7 @@ namespace pclass
namespace detail
{
Value value_caster_base::cast(const Value &v) const
Value value_caster_base::cast(const Value &value) const
{
throw runtime_error("Unimplemented cast.");
}
@ -24,7 +24,7 @@ namespace pclass
m_deallocator = deallocator;
}
ValueDeallocator::ValueDeallocator(ValueDeallocator &that)
ValueDeallocator::ValueDeallocator(const ValueDeallocator &that)
{
m_deallocator = that.m_deallocator->copy();
}
@ -35,7 +35,7 @@ namespace pclass
that.m_deallocator = nullptr;
}
ValueDeallocator &ValueDeallocator::operator=(ValueDeallocator &that)
ValueDeallocator &ValueDeallocator::operator=(const ValueDeallocator &that)
{
m_deallocator = that.m_deallocator->copy();
return *this;
@ -87,7 +87,7 @@ namespace pclass
m_deallocator = detail::ValueDeallocator();
}
Value::Value(Value& that)
Value::Value(const Value &that)
{
m_value_ptr = that.m_value_ptr;
m_ptr_is_owned = false;
@ -108,7 +108,7 @@ namespace pclass
m_deallocator = std::move(that.m_deallocator);
}
Value &Value::operator=(Value &that)
Value &Value::operator=(const Value &that)
{
m_value_ptr = that.m_value_ptr;
m_ptr_is_owned = false;

View File

@ -120,7 +120,7 @@ namespace serialization
if (object)
stream.write<uint32_t>(object->get_type().get_hash());
else
stream.write<uint32_t>(NULL);
stream.write<uint32_t>(0);
return object != nullptr;
}
@ -183,7 +183,7 @@ namespace serialization
for (auto i = 0; i < prop.get_element_count(); ++i)
{
if (prop.is_pointer()
&& prop.get_type().get_kind() == pclass::Type::kind::CLASS)
&& prop.get_type().get_kind() == pclass::Type::Kind::CLASS)
{
// Write the value as a nested object
save_object(prop.get_object(i), stream);
@ -358,7 +358,7 @@ namespace serialization
for (auto i = 0; i < prop.get_element_count(); ++i)
{
if (prop.is_pointer() &&
prop.get_type().get_kind() == pclass::Type::kind::CLASS)
prop.get_type().get_kind() == pclass::Type::Kind::CLASS)
{
// Read the object as a nested object
std::unique_ptr<pclass::PropertyClass> object = nullptr;

View File

@ -27,7 +27,7 @@ namespace serialization
{
// Add the object's meta information
j["_pclass_meta"] = {
{ "type_hash", object ? object->get_type().get_hash() : NULL }
{ "type_hash", object ? object->get_type().get_hash() : 0 }
};
return object != nullptr;
}
@ -38,7 +38,6 @@ namespace serialization
if (!presave_object(j, object))
return j;
// Add the object's properties
auto &property_list = object->get_properties();
for (auto it = property_list.begin();
it != property_list.end(); ++it)
@ -55,7 +54,7 @@ namespace serialization
{
for (std::size_t i = 0; i < prop.get_element_count(); ++i)
{
if (prop.get_type().get_kind() == pclass::Type::kind::CLASS)
if (prop.get_type().get_kind() == pclass::Type::Kind::CLASS)
{
auto *other_object = prop.get_object(i);
if (prop.is_array())
@ -124,6 +123,10 @@ namespace serialization
auto &prop = *it;
load_property(prop, j);
}
// All properties on this object have been set, let the new
// instance react to this change
dest->on_created();
}
void JsonSerializer::load_property(
@ -143,7 +146,7 @@ namespace serialization
for (std::size_t i = 0; i < prop.get_element_count(); ++i)
{
if (prop.get_type().get_kind() == pclass::Type::kind::CLASS)
if (prop.get_type().get_kind() == pclass::Type::Kind::CLASS)
{
std::unique_ptr<pclass::PropertyClass> other_object = nullptr;
if (prop.is_array())

Binary file not shown.

View File

@ -2,7 +2,30 @@
"_pclass_meta": {
"type_hash": 2069042008
},
"collection": [
"float32": 3.1415927410125732,
"float64": 3.141592653589793,
"int16": 515,
"int24": 263430,
"int32": 117967114,
"int4": -6,
"int64": 796025588171149586,
"int8": 1,
"int_array": [
0,
1,
2,
3,
4
],
"int_ptr": 52,
"int_ptr_array": [
0,
1,
2,
3,
4
],
"int_ptr_vector": [
0,
1,
2,
@ -104,27 +127,127 @@
98,
99
],
"float32": 3.1415927410125732,
"float64": 3.141592653589793,
"int16": 515,
"int24": 263430,
"int32": 117967114,
"int4": -6,
"int64": 796025588171149586,
"int8": 1,
"int_ptr": 52,
"not_null_object": {
"int_vector": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37,
38,
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52,
53,
54,
55,
56,
57,
58,
59,
60,
61,
62,
63,
64,
65,
66,
67,
68,
69,
70,
71,
72,
73,
74,
75,
76,
77,
78,
79,
80,
81,
82,
83,
84,
85,
86,
87,
88,
89,
90,
91,
92,
93,
94,
95,
96,
97,
98,
99
],
"null_object_ptr": {
"_pclass_meta": {
"type_hash": 0
}
},
"object": {
"_pclass_meta": {
"type_hash": 1180894394
},
"extra_value": 20,
"m_kind": 2
},
"object_ptr": {
"_pclass_meta": {
"type_hash": 1181435066
},
"m_kind": 1
},
"null_object": {
"_pclass_meta": {
"type_hash": 0
}
},
"objects": [
"object_ptr_vector": [
{
"_pclass_meta": {
"type_hash": 1180894394
@ -139,108 +262,6 @@
"m_kind": 3
}
],
"ptr_collection": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37,
38,
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52,
53,
54,
55,
56,
57,
58,
59,
60,
61,
62,
63,
64,
65,
66,
67,
68,
69,
70,
71,
72,
73,
74,
75,
76,
77,
78,
79,
80,
81,
82,
83,
84,
85,
86,
87,
88,
89,
90,
91,
92,
93,
94,
95,
96,
97,
98,
99
],
"string": "This is a test value",
"uint16": 515,
"uint24": 263430,
@ -248,38 +269,31 @@
"uint4": 5,
"uint64": 796025588171149586,
"uint8": 1,
"value_object": {
"_pclass_meta": {
"type_hash": 1180894394
},
"extra_value": 20,
"m_kind": 2
},
"vector3d": {
"x": 24.0,
"y": 61.0,
"z": 3.619999885559082
},
"wstring": [
84,
104,
105,
115,
7511,
688,
8305,
738,
32,
105,
115,
8305,
738,
32,
97,
7491,
32,
116,
101,
115,
116,
7511,
7497,
738,
7511,
32,
118,
97,
108,
117,
101
7515,
7491,
737,
7512,
7497
]
}

View File

@ -1 +1 @@
{"_pclass_meta":{"type_hash":2069042008},"collection":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99],"float32":3.1415927410125732,"float64":3.141592653589793,"int16":515,"int24":263430,"int32":117967114,"int4":-6,"int64":796025588171149586,"int8":1,"int_ptr":52,"not_null_object":{"_pclass_meta":{"type_hash":1181435066},"m_kind":1},"null_object":{"_pclass_meta":{"type_hash":0}},"objects":[{"_pclass_meta":{"type_hash":1180894394},"extra_value":10,"m_kind":2},{"_pclass_meta":{"type_hash":1180943546},"m_kind":3}],"ptr_collection":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99],"string":"This is a test value","uint16":515,"uint24":263430,"uint32":117967114,"uint4":5,"uint64":796025588171149586,"uint8":1,"value_object":{"_pclass_meta":{"type_hash":1180894394},"extra_value":20,"m_kind":2},"vector3d":{"x":24.0,"y":61.0,"z":3.619999885559082},"wstring":[84,104,105,115,32,105,115,32,97,32,116,101,115,116,32,118,97,108,117,101]}
{"_pclass_meta":{"type_hash":2069042008},"float32":3.1415927410125732,"float64":3.141592653589793,"int16":515,"int24":263430,"int32":117967114,"int4":-6,"int64":796025588171149586,"int8":1,"int_array":[0,1,2,3,4],"int_ptr":52,"int_ptr_array":[0,1,2,3,4],"int_ptr_vector":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99],"int_vector":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99],"null_object_ptr":{"_pclass_meta":{"type_hash":0}},"object":{"_pclass_meta":{"type_hash":1180894394},"extra_value":20,"m_kind":2},"object_ptr":{"_pclass_meta":{"type_hash":1181435066},"m_kind":1},"object_ptr_vector":[{"_pclass_meta":{"type_hash":1180894394},"extra_value":10,"m_kind":2},{"_pclass_meta":{"type_hash":1180943546},"m_kind":3}],"string":"This is a test value","uint16":515,"uint24":263430,"uint32":117967114,"uint4":5,"uint64":796025588171149586,"uint8":1,"vector3d":{"x":24.0,"y":61.0,"z":3.619999885559082},"wstring":[7511,688,8305,738,32,8305,738,32,7491,32,7511,7497,738,7511,32,7515,7491,737,7512,7497]}

View File

@ -330,7 +330,7 @@ TEST_CASE("BitStream Functionality", "[bit-stream]")
SECTION("Copying memory in/out of the stream")
{
// Copy the string into the stream
char *str = "Hello, world!";
char str[] = "Hello, world!";
const auto size = bitsizeof<char>::value * (strlen(str) + 1);
bit_stream.write_copy(reinterpret_cast<uint8_t *>(str), size);

149
test/src/unit-pclass.cpp Normal file
View File

@ -0,0 +1,149 @@
#define CATCH_CONFIG_MAIN
#include <catch.hpp>
#include <memory>
#include "ki/pclass/TypeSystem.h"
#include "ki/pclass/HashCalculator.h"
using namespace ki;
PCLASS(TestClassTypeA)
{
public:
using PropertyClass::PropertyClass;
};
PCLASS(TestClassTypeB)
{
public:
using PropertyClass::PropertyClass;
};
TEST_CASE("TypeSystem and Type definition", "[pclass]")
{
std::unique_ptr<pclass::IHashCalculator> hash_calculator
= ki::make_unique<pclass::WizardHashCalculator>();
auto type_system = ki::make_unique<pclass::TypeSystem>(hash_calculator);
SECTION("Define class")
{
const auto class_name = "class TestClassTypeA";
const auto class_hash = type_system->get_hash_calculator()
.calculate_type_hash(class_name);
auto &defined_class = type_system->define_class<TestClassTypeA>(class_name);
REQUIRE(type_system->has_type(class_name));
REQUIRE(type_system->has_type(class_hash));
auto &retrieved_class_name = type_system->get_type(class_name);
auto &retrieved_class_hash = type_system->get_type(class_hash);
REQUIRE(&retrieved_class_name == &defined_class);
REQUIRE(&retrieved_class_hash == &defined_class);
}
SECTION("Define derived class")
{
const auto base_class_name = "class TestClassTypeA";
auto &base_class = type_system->define_class<TestClassTypeA>(base_class_name);
REQUIRE(type_system->has_type(base_class_name));
const auto derived_class_name = "class TestClassTypeB";
auto &derived_class = type_system->define_class<TestClassTypeB>(derived_class_name, base_class);
REQUIRE(type_system->has_type(derived_class_name));
}
SECTION("Define class with non-class ancestor")
{
try
{
// Attempt to define a type that inherits "int".
type_system->define_class<TestClassTypeA>(
"class TestClassTypeA", type_system->get_type("int")
);
FAIL();
}
catch (runtime_error &e)
{
SUCCEED();
}
}
SECTION("Get a type that does not exist by name")
{
try
{
auto &made_up_struct = type_system->get_type("struct MadeUp");
FAIL();
}
catch (runtime_error &e)
{
SUCCEED();
}
}
SECTION("Get a type that does not exist by hash")
{
try
{
auto &made_up_struct = type_system->get_type(0xDEADA55);
FAIL();
}
catch (runtime_error &e)
{
SUCCEED();
}
}
SECTION("Define type with the same name")
{
try
{
const auto class_name = "class TestClassTypeA";
type_system->define_class<TestClassTypeA>(class_name);
type_system->define_class<TestClassTypeA>(class_name);
FAIL();
}
catch (runtime_error &e)
{
SUCCEED();
}
}
SECTION("Define type with the same name")
{
try
{
const auto class_name = "class TestClassTypeA";
type_system->define_class<TestClassTypeA>(class_name);
type_system->define_class<TestClassTypeA>(class_name);
FAIL();
}
catch (runtime_error &e)
{
SUCCEED();
}
}
SECTION("Define type with the same hash")
{
try
{
type_system->define_class<TestClassTypeA>("a");
type_system->define_class<TestClassTypeA>("a ");
FAIL();
}
catch (runtime_error &e)
{
SUCCEED();
}
}
}
TEST_CASE("PropertyClass and PropertyList", "[pclass]")
{
// TODO: Test PropertyClass instances
}
TEST_CASE("Enums", "[pclass]")
{
// TODO: Test Enums
}

View File

@ -1,4 +1,4 @@
#define CATCH_CONFIG_MAIN
#define CATCH_CONFIG_MAIN
#include <cstdio>
#include <cstdint>
#include <string>
@ -10,6 +10,7 @@
#include <ki/pclass/VectorProperty.h>
#include <ki/serialization/BinarySerializer.h>
#include <ki/serialization/JsonSerializer.h>
#include "ki/pclass/Enum.h"
using namespace ki;
@ -42,11 +43,9 @@ struct Vector3D
bool operator==(const Vector3D &that) const
{
return (
m_x == that.m_x,
m_y == that.m_y,
m_z == that.m_z
);
return m_x == that.m_x &&
m_y == that.m_y &&
m_z == that.m_z;
}
void write_to(BitStream &stream) const
@ -244,12 +243,14 @@ public:
INIT_PROPERTY(float64, "double")
INIT_PROPERTY(vector3d, "struct Vector3D")
INIT_PROPERTY(int_ptr, "int")
INIT_PROPERTY(value_object, "class NestedTestObjectA")
INIT_PROPERTY(not_null_object, "class NestedTestObject")
INIT_PROPERTY(null_object, "class NestedTestObject")
INIT_PROPERTY(collection, "int")
INIT_PROPERTY(ptr_collection, "int")
INIT_PROPERTY(objects, "class NestedTestObject")
INIT_PROPERTY(int_array, "int")
INIT_PROPERTY(int_ptr_array, "int")
INIT_PROPERTY(object, "class NestedTestObjectA")
INIT_PROPERTY(object_ptr, "class NestedTestObject")
INIT_PROPERTY(null_object_ptr, "class NestedTestObject")
INIT_PROPERTY(int_vector, "int")
INIT_PROPERTY(int_ptr_vector, "int")
INIT_PROPERTY(object_ptr_vector, "class NestedTestObject")
{}
// Test signed and unsigned integers with a bit length less than 8
@ -282,21 +283,27 @@ public:
// Test dereferencing when writing pointers to primitives
pclass::StaticProperty<int *> int_ptr;
// Test writing a single instance of another object
pclass::StaticProperty<NestedTestObjectA> value_object;
pclass::StaticProperty<NestedTestObject *> not_null_object;
pclass::StaticProperty<NestedTestObject *> null_object;
// Test explicitly-sized array of primitives and pointers
pclass::StaticProperty<int[5]> int_array;
pclass::StaticProperty<int *[5]> int_ptr_array;
// Test writing a class type as a value
pclass::StaticProperty<NestedTestObjectA> object;
// Test writing a class type as a pointer
pclass::StaticProperty<NestedTestObject *> object_ptr;
pclass::StaticProperty<NestedTestObject *> null_object_ptr;
// Test writing collections of primitives
pclass::VectorProperty<int> collection;
pclass::VectorProperty<int *> ptr_collection;
pclass::VectorProperty<int> int_vector;
pclass::VectorProperty<int *> int_ptr_vector;
// Test writing multiple instances of another object
pclass::VectorProperty<NestedTestObject *> objects;
pclass::VectorProperty<NestedTestObject *> object_ptr_vector;
};
// Setup a global TypeSystem instance
std::unique_ptr<pclass::HashCalculator> g_hash_calculator
std::unique_ptr<pclass::IHashCalculator> g_hash_calculator
= ki::make_unique<pclass::WizardHashCalculator>();
auto g_type_system = ki::make_unique<pclass::TypeSystem>(g_hash_calculator);
bool g_types_defined = false;
@ -338,13 +345,14 @@ void define_types()
#define EXPECTED_uint32 0x0708090A
#define EXPECTED_uint64 0x0B0C0D0E0F101112
#define EXPECTED_string "This is a test value"
#define EXPECTED_wstring u"This is a test value"
#define EXPECTED_wstring u"\u1d57\u02b0\u2071\u02e2\u0020\u2071\u02e2\u0020\u1d43\u0020\u1d57\u1d49\u02e2\u1d57\u0020\u1d5b\u1d43\u02e1\u1d58\u1d49"
#define EXPECTED_float32 3.1415927410125732421875f
#define EXPECTED_float64 3.141592653589793115997963468544185161590576171875
#define EXPECTED_vector3d Vector3D(24.0f, 61.0f, 3.62f)
#define EXPECTED_int_ptr 52
#define EXPECTED_value_object_extra_value 20
#define EXPECTED_collection_size 100
#define EXPECTED_int_array_size 5
#define EXPECTED_int_vector_size 100
#define SET_EXPECTED(object, identifier) object.identifier = EXPECTED_##identifier
#define IS_EXPECTED(object, identifier) object.identifier.get() == EXPECTED_##identifier
@ -373,21 +381,28 @@ void configure_test_object(TestObject &object)
SET_EXPECTED(object, vector3d);
object.int_ptr = new int(EXPECTED_int_ptr);
// Configure the collection of integers
for (auto i = 0; i < EXPECTED_collection_size; ++i)
// Configure the int array
for (auto i = 0; i < EXPECTED_int_array_size; ++i)
{
object.collection.push_back(i);
object.ptr_collection.push_back(new int(i));
object.int_array[i] = i;
object.int_ptr_array[i] = new int(i);
}
// Configure the int vector
for (auto i = 0; i < EXPECTED_int_vector_size; ++i)
{
object.int_vector.push_back(i);
object.int_ptr_vector.push_back(new int(i));
}
// Configure nested objects
object.value_object.get().extra_value = EXPECTED_value_object_extra_value;
object.not_null_object = g_type_system->instantiate<NestedTestObject>("class NestedTestObject").release();
object.null_object = nullptr;
object.objects.push_back(
object.object.get().extra_value = EXPECTED_value_object_extra_value;
object.object_ptr = g_type_system->instantiate<NestedTestObject>("class NestedTestObject").release();
object.null_object_ptr = nullptr;
object.object_ptr_vector.push_back(
g_type_system->instantiate<NestedTestObjectA>("class NestedTestObjectA").release()
);
object.objects.push_back(
object.object_ptr_vector.push_back(
g_type_system->instantiate<NestedTestObjectB>("class NestedTestObjectB").release()
);
}
@ -417,23 +432,32 @@ void validate_test_object(TestObject &object)
REQUIRE(IS_EXPECTED(object, vector3d));
REQUIRE(*object.int_ptr == EXPECTED_int_ptr);
// Validate both collections
REQUIRE(object.collection.size() == EXPECTED_collection_size);
REQUIRE(object.ptr_collection.size() == EXPECTED_collection_size);
for (auto i = 0; i < EXPECTED_collection_size; i++)
// Validate the int array
REQUIRE(object.int_array.get_element_count() == EXPECTED_int_array_size);
REQUIRE(object.int_ptr_array.get_element_count() == EXPECTED_int_array_size);
for (auto i = 0; i < EXPECTED_int_array_size; i++)
{
REQUIRE(object.collection[i] == i);
REQUIRE(*object.ptr_collection[i] == i);
REQUIRE(object.int_array[i] == i);
REQUIRE(*object.int_ptr_array[i] == i);
}
// Validate the int vector
REQUIRE(object.int_vector.size() == EXPECTED_int_vector_size);
REQUIRE(object.int_ptr_vector.size() == EXPECTED_int_vector_size);
for (auto i = 0; i < EXPECTED_int_vector_size; i++)
{
REQUIRE(object.int_vector[i] == i);
REQUIRE(*object.int_ptr_vector[i] == i);
}
// Validate nested objects
REQUIRE(object.value_object.get().extra_value == EXPECTED_value_object_extra_value);
REQUIRE(object.not_null_object.get() != nullptr);
REQUIRE(object.not_null_object.get()->get_kind() == NestedObjectKind::OBJECT);
REQUIRE(object.null_object.get() == nullptr);
REQUIRE(object.objects.size() == 2);
REQUIRE(object.objects[0]->get_kind() == NestedObjectKind::OBJECT_A);
REQUIRE(object.objects[1]->get_kind() == NestedObjectKind::OBJECT_B);
REQUIRE(object.object.get().extra_value == EXPECTED_value_object_extra_value);
REQUIRE(object.object_ptr.get() != nullptr);
REQUIRE(object.object_ptr.get()->get_kind() == NestedObjectKind::OBJECT);
REQUIRE(object.null_object_ptr.get() == nullptr);
REQUIRE(object.object_ptr_vector.size() == 2);
REQUIRE(object.object_ptr_vector[0]->get_kind() == NestedObjectKind::OBJECT_A);
REQUIRE(object.object_ptr_vector[1]->get_kind() == NestedObjectKind::OBJECT_B);
}
/**
@ -580,7 +604,7 @@ TEST_CASE("Serialization tests", "[serialization]")
SECTION("Regular format without compression")
{
serialization::BinarySerializer serializer(
*g_type_system.get(), false,
*g_type_system, false,
serialization::BinarySerializer::flags::NONE
);
test_serializer(test_object, serializer, "regular");
@ -588,7 +612,7 @@ TEST_CASE("Serialization tests", "[serialization]")
SECTION("File format without compression")
{
serialization::BinarySerializer serializer(
*g_type_system.get(), true,
*g_type_system, true,
serialization::BinarySerializer::flags::WRITE_SERIALIZER_FLAGS
);
test_serializer(test_object, serializer, "file");
@ -596,7 +620,7 @@ TEST_CASE("Serialization tests", "[serialization]")
SECTION("Regular format with compression")
{
serialization::BinarySerializer serializer(
*g_type_system.get(), false,
*g_type_system, false,
serialization::BinarySerializer::flags::COMPRESSED
);
test_serializer(test_object, serializer, "regular_compressed");
@ -604,7 +628,7 @@ TEST_CASE("Serialization tests", "[serialization]")
SECTION("File format with compression")
{
serialization::BinarySerializer serializer(
*g_type_system.get(), true,
*g_type_system, true,
serialization::BinarySerializer::flags::WRITE_SERIALIZER_FLAGS |
serialization::BinarySerializer::flags::COMPRESSED
);