diff --git a/include/ki/pclass/Casters.h b/include/ki/pclass/Casters.h index eb981bd..0cfbaeb 100644 --- a/include/ki/pclass/Casters.h +++ b/include/ki/pclass/Casters.h @@ -4,7 +4,7 @@ #include #include "ki/pclass/Value.h" #include "ki/util/BitTypes.h" -#include "ki/pclass/types/EnumType.h" +#include "ki/pclass/EnumType.h" namespace ki { diff --git a/include/ki/pclass/types/ClassType.h b/include/ki/pclass/ClassType.h similarity index 64% rename from include/ki/pclass/types/ClassType.h rename to include/ki/pclass/ClassType.h index ce78676..22521e6 100644 --- a/include/ki/pclass/types/ClassType.h +++ b/include/ki/pclass/ClassType.h @@ -3,7 +3,7 @@ #include #include #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 + template class ClassType : public IClassType { // Ensure that ClassT inherits PropertyClass static_assert(std::is_base_of::value, "ClassT must inherit PropertyClass!"); public: + // Do not allow copy construction or movement of types + ClassType(const ClassType &that) = delete; + ClassType &operator=(const ClassType &that) = delete; + ClassType(ClassType &&that) noexcept = delete; + ClassType &operator=(ClassType &&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 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(); const auto &properties = object.get_properties(); diff --git a/include/ki/pclass/Enum.h b/include/ki/pclass/Enum.h index 3494384..395f54d 100644 --- a/include/ki/pclass/Enum.h +++ b/include/ki/pclass/Enum.h @@ -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 { diff --git a/include/ki/pclass/types/EnumType.h b/include/ki/pclass/EnumType.h similarity index 80% rename from include/ki/pclass/types/EnumType.h rename to include/ki/pclass/EnumType.h index c29e424..cbd40aa 100644 --- a/include/ki/pclass/types/EnumType.h +++ b/include/ki/pclass/EnumType.h @@ -1,8 +1,8 @@ #pragma once #include #include -#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(); auto &underlying_reference = reinterpret_cast(enum_reference); diff --git a/include/ki/pclass/HashCalculator.h b/include/ki/pclass/HashCalculator.h index 9b07266..02a058d 100644 --- a/include/ki/pclass/HashCalculator.h +++ b/include/ki/pclass/HashCalculator.h @@ -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; diff --git a/include/ki/pclass/PrimitiveType.h b/include/ki/pclass/PrimitiveType.h new file mode 100644 index 0000000..2109a2c --- /dev/null +++ b/include/ki/pclass/PrimitiveType.h @@ -0,0 +1,189 @@ +#pragma once +#include "ki/pclass/Type.h" + +namespace ki +{ +namespace pclass +{ + namespace detail + { + /** + * Provides implementations to PrimitiveType::write_to, + * and PrimitiveType::read_from. + */ + template + 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 + struct primitive_type_helper< + ValueT, + typename std::enable_if::value>::type + > + { + static void write_to(BitStream &stream, const ValueT &value) + { + stream.write(value); + } + + static Value read_from(BitStream &stream) + { + return Value::make_value( + stream.read() + ); + } + }; + + /** + * Specialization of primitive_type_helper for floating point + * types. + */ + template + struct primitive_type_helper< + ValueT, + typename std::enable_if::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(&value) + ); + stream.write(v, bitsizeof::value); + } + + static Value read_from(BitStream &stream) + { + uint_type uint_value = stream.read(bitsizeof::value); + return Value::make_value(*reinterpret_cast(&uint_value)); + } + + private: + /** + * An unsigned integer type with the same size as the floating point type + * ValueT. + */ + using uint_type = typename bits::value>::uint_type; + }; + + /** + * Specialization of primitive_type_helper for string types. + */ + template < + typename _Elem, + typename _Traits, + typename _Alloc + > + struct primitive_type_helper> + { + 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(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(); + 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(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 + class PrimitiveType : public Type + { + public: + // Do not allow copy construction or movement of types + PrimitiveType(const PrimitiveType &that) = delete; + PrimitiveType &operator=(const PrimitiveType &that) = delete; + PrimitiveType(PrimitiveType &&that) noexcept = delete; + PrimitiveType &operator=(PrimitiveType &&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(); + detail::primitive_type_helper::write_to( + stream, + deref_value.get() + ); + } + 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::read_from(stream); + } + catch (runtime_error &e) + { + std::ostringstream oss; + oss << "Invalid call to Type::read_from -- " << e.what(); + throw runtime_error(oss.str()); + } + } + }; +} +} diff --git a/include/ki/pclass/Property.h b/include/ki/pclass/Property.h index 2c89e7d..a1a1954 100644 --- a/include/ki/pclass/Property.h +++ b/include/ki/pclass/Property.h @@ -1,6 +1,6 @@ #pragma once #include -#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 &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: diff --git a/include/ki/pclass/PropertyClass.h b/include/ki/pclass/PropertyClass.h index 46103ff..6472432 100644 --- a/include/ki/pclass/PropertyClass.h +++ b/include/ki/pclass/PropertyClass.h @@ -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 - 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); }; } } diff --git a/include/ki/pclass/PropertyList.h b/include/ki/pclass/PropertyList.h index 79e94d5..ed8c96a 100644 --- a/include/ki/pclass/PropertyList.h +++ b/include/ki/pclass/PropertyList.h @@ -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(); diff --git a/include/ki/pclass/StaticProperty.h b/include/ki/pclass/StaticProperty.h index 06684c6..09d3c6f 100644 --- a/include/ki/pclass/StaticProperty.h +++ b/include/ki/pclass/StaticProperty.h @@ -271,14 +271,14 @@ namespace pclass > { static const PropertyClass *get_object( - const StaticProperty &prop, const int index) + const StaticProperty &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(&prop.m_value[index]); } - static void set_object(StaticProperty &prop, + static void set_object(StaticProperty &prop, std::unique_ptr &object, const int index) { // Ensure that object is not nullptr @@ -308,14 +308,14 @@ namespace pclass > { static const PropertyClass *get_object( - const StaticProperty &prop, const int index) + const StaticProperty &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(prop.m_value[index]); } - static void set_object(StaticProperty &prop, + static void set_object(StaticProperty &prop, std::unique_ptr &object, const int index) { // Ensure that object inherits the type of the property @@ -397,12 +397,12 @@ namespace pclass template struct static_value_helper { - static Value get_value(const StaticProperty &prop, const int index) + static Value get_value(const StaticProperty &prop, const int index) { return Value::make_reference(*prop.m_value[index]); } - static void set_value(StaticProperty &prop, Value value, const int index) + static void set_value(StaticProperty &prop, Value value, const int index) { Value casted_value = value.as(); prop.m_value[index] = casted_value.release(); diff --git a/include/ki/pclass/types/Type.h b/include/ki/pclass/Type.h similarity index 56% rename from include/ki/pclass/types/Type.h rename to include/ki/pclass/Type.h index d9a76f2..7d5a5c1 100644 --- a/include/ki/pclass/types/Type.h +++ b/include/ki/pclass/Type.h @@ -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 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; diff --git a/include/ki/pclass/TypeSystem.h b/include/ki/pclass/TypeSystem.h index 7a464eb..4d15644 100644 --- a/include/ki/pclass/TypeSystem.h +++ b/include/ki/pclass/TypeSystem.h @@ -4,31 +4,62 @@ #include #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 &hash_calculator); + explicit TypeSystem(std::unique_ptr &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. + */ template PrimitiveType &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. + */ template ClassType &define_class(const std::string &name) { return define_class(name, nullptr); } - template + /** + * 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. + */ + template ClassType &define_class( const std::string &name, const Type &base_class) { return define_class(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. + */ template CppEnumType &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 std::unique_ptr instantiate(const std::string &name) const { @@ -87,7 +147,7 @@ namespace pclass std::vector> m_types; std::unordered_map m_type_name_lookup; std::unordered_map m_type_hash_lookup; - std::unique_ptr m_hash_calculator; + std::unique_ptr m_hash_calculator; template ClassType &define_class( diff --git a/include/ki/pclass/Value.h b/include/ki/pclass/Value.h index 5f9cac1..642e621 100644 --- a/include/ki/pclass/Value.h +++ b/include/ki/pclass/Value.h @@ -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 struct value_caster : value_caster_impl @@ -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 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,8 +140,8 @@ namespace pclass } /** - * TODO: Documentation - */ + * Provides a way to perform casting on Value instances. + */ class ValueCaster { // Allow Value to call the default constructor and get() @@ -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 static Value make_reference(T &value) diff --git a/include/ki/pclass/VectorProperty.h b/include/ki/pclass/VectorProperty.h index dd222ce..ea0021b 100644 --- a/include/ki/pclass/VectorProperty.h +++ b/include/ki/pclass/VectorProperty.h @@ -112,14 +112,14 @@ namespace pclass using default_vector_object_helper::copy; static const PropertyClass *get_object( - const VectorProperty &prop, const int index) + const VectorProperty &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(&prop.at(index)); } - static void set_object(VectorProperty &prop, + static void set_object(VectorProperty &prop, std::unique_ptr &object, const int index) { // Ensure that object is not nullptr diff --git a/include/ki/pclass/types/FloatingPointPrimitiveType.h b/include/ki/pclass/types/FloatingPointPrimitiveType.h deleted file mode 100644 index defc58a..0000000 --- a/include/ki/pclass/types/FloatingPointPrimitiveType.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once -#include -#include "ki/util/BitTypes.h" - -namespace ki -{ -namespace pclass -{ -namespace detail -{ - template - struct primitive_type_helper< - ValueT, - typename std::enable_if::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(&value) - ); - stream.write(v, bitsizeof::value); - } - - static Value read_from(BitStream &stream) - { - uint_type uint_value = stream.read(bitsizeof::value); - return Value::make_value(*reinterpret_cast(&uint_value)); - } - - private: - /** - * An unsigned integer type with the same size as the floating point type - * ValueT. - */ - using uint_type = typename bits::value>::uint_type; - }; -} -} -} diff --git a/include/ki/pclass/types/IntegralPrimitiveType.h b/include/ki/pclass/types/IntegralPrimitiveType.h deleted file mode 100644 index 496670c..0000000 --- a/include/ki/pclass/types/IntegralPrimitiveType.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include -#include "ki/util/BitTypes.h" - -namespace ki -{ -namespace pclass -{ -namespace detail -{ - template - struct primitive_type_helper< - ValueT, - typename std::enable_if::value>::type - > - { - static void write_to(BitStream &stream, const ValueT &value) - { - stream.write(value); - } - - static Value read_from(BitStream &stream) - { - return Value::make_value( - stream.read() - ); - } - }; -} -} -} diff --git a/include/ki/pclass/types/PrimitiveType.h b/include/ki/pclass/types/PrimitiveType.h deleted file mode 100644 index 03c6883..0000000 --- a/include/ki/pclass/types/PrimitiveType.h +++ /dev/null @@ -1,97 +0,0 @@ -#pragma once -#include "ki/pclass/types/Type.h" - -namespace ki -{ -namespace pclass -{ - namespace detail - { - /** - * TODO: Documentation - */ - template - 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 - 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(); - detail::primitive_type_helper::write_to( - stream, - deref_value.get() - ); - } - 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::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" diff --git a/include/ki/pclass/types/StringPrimitiveType.h b/include/ki/pclass/types/StringPrimitiveType.h deleted file mode 100644 index 1ff952b..0000000 --- a/include/ki/pclass/types/StringPrimitiveType.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once -#include -#include - -namespace ki -{ -namespace pclass -{ -namespace detail -{ - /** - * primitive_type_helper specialization for string types. - */ - template < - typename _Elem, - typename _Traits, - typename _Alloc - > - struct primitive_type_helper> - { - 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(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(); - 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(value); - } - }; -} -} -} diff --git a/src/pclass/ClassType.cpp b/src/pclass/ClassType.cpp index c857ffb..1880dc4 100644 --- a/src/pclass/ClassType.cpp +++ b/src/pclass/ClassType.cpp @@ -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" @@ -7,16 +7,16 @@ namespace ki namespace pclass { IClassType::IClassType(const std::string& name, - const Type* base_class, const TypeSystem& type_system) + 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 diff --git a/src/pclass/Enum.cpp b/src/pclass/Enum.cpp index ca20a8b..a3fa69f 100644 --- a/src/pclass/Enum.cpp +++ b/src/pclass/Enum.cpp @@ -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(type); @@ -17,14 +17,14 @@ 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(type); set_value(element_name); } - Enum& Enum::operator=(const Enum& that) + Enum &Enum::operator=(const Enum& that) { // Are the types the same? if (&get_type() != &that.get_type()) diff --git a/src/pclass/EnumType.cpp b/src/pclass/EnumType.cpp index c65ca16..3941bbc 100644 --- a/src/pclass/EnumType.cpp +++ b/src/pclass/EnumType.cpp @@ -1,4 +1,4 @@ -#include "ki/pclass/types/EnumType.h" +#include "ki/pclass/EnumType.h" #include "ki/util/exception.h" #include #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().write_to(stream); diff --git a/src/pclass/Property.cpp b/src/pclass/Property.cpp index 3d5857e..6bf8e17 100644 --- a/src/pclass/Property.cpp +++ b/src/pclass/Property.cpp @@ -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) diff --git a/src/pclass/Type.cpp b/src/pclass/Type.cpp index da39192..d17ed95 100644 --- a/src/pclass/Type.cpp +++ b/src/pclass/Type.cpp @@ -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 #include 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(actual); if (actual_class.inherits(expected)) diff --git a/src/pclass/TypeSystem.cpp b/src/pclass/TypeSystem.cpp index 751c122..0ab0f0c 100644 --- a/src/pclass/TypeSystem.cpp +++ b/src/pclass/TypeSystem.cpp @@ -36,7 +36,7 @@ namespace pclass template <> void define_bit_integer_primitive<0>(TypeSystem &type_system) {} - TypeSystem::TypeSystem(std::unique_ptr &hash_calculator) + TypeSystem::TypeSystem(std::unique_ptr &hash_calculator) { m_hash_calculator = std::move(hash_calculator); @@ -48,6 +48,7 @@ namespace pclass define_primitive("int8_t"); define_primitive("uint8_t"); DEFINE_INTEGER_PRIMTIIVE(int16_t, uint16_t, "short"); + define_primitive("wchar_t"); DEFINE_INTEGER_PRIMTIIVE(int16_t, uint16_t, "__int16"); define_primitive("int16_t"); define_primitive("uint16_t"); @@ -59,7 +60,6 @@ namespace pclass DEFINE_INTEGER_PRIMTIIVE(int64_t, uint64_t, "__int64"); define_primitive("int64_t"); define_primitive("uint64_t"); - define_primitive("gid"); // Define bit-integer types define_bit_integer_primitive(*this); @@ -78,11 +78,8 @@ namespace pclass define_class("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()); diff --git a/src/pclass/Value.cpp b/src/pclass/Value.cpp index ecd1ce8..648f385 100644 --- a/src/pclass/Value.cpp +++ b/src/pclass/Value.cpp @@ -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; diff --git a/src/serialization/BinarySerializer.cpp b/src/serialization/BinarySerializer.cpp index 2a3313a..17bbce8 100644 --- a/src/serialization/BinarySerializer.cpp +++ b/src/serialization/BinarySerializer.cpp @@ -120,7 +120,7 @@ namespace serialization if (object) stream.write(object->get_type().get_hash()); else - stream.write(NULL); + stream.write(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 object = nullptr; diff --git a/src/serialization/JsonSerializer.cpp b/src/serialization/JsonSerializer.cpp index a6e7316..454e574 100644 --- a/src/serialization/JsonSerializer.cpp +++ b/src/serialization/JsonSerializer.cpp @@ -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 other_object = nullptr; if (prop.is_array()) diff --git a/test/samples/serialization/file.bin b/test/samples/serialization/file.bin index c67b1ef..81c052e 100644 Binary files a/test/samples/serialization/file.bin and b/test/samples/serialization/file.bin differ diff --git a/test/samples/serialization/file.json b/test/samples/serialization/file.json index a74d7a7..6eac95b 100644 --- a/test/samples/serialization/file.json +++ b/test/samples/serialization/file.json @@ -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 ] } \ No newline at end of file diff --git a/test/samples/serialization/file_compressed.bin b/test/samples/serialization/file_compressed.bin index 2280edc..6651481 100644 Binary files a/test/samples/serialization/file_compressed.bin and b/test/samples/serialization/file_compressed.bin differ diff --git a/test/samples/serialization/regular.bin b/test/samples/serialization/regular.bin index 079bd66..3661715 100644 Binary files a/test/samples/serialization/regular.bin and b/test/samples/serialization/regular.bin differ diff --git a/test/samples/serialization/regular.json b/test/samples/serialization/regular.json index df8ad02..e4654eb 100644 --- a/test/samples/serialization/regular.json +++ b/test/samples/serialization/regular.json @@ -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]} \ No newline at end of file +{"_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]} \ No newline at end of file diff --git a/test/samples/serialization/regular_compressed.bin b/test/samples/serialization/regular_compressed.bin index b2d8ba9..3efc38d 100644 Binary files a/test/samples/serialization/regular_compressed.bin and b/test/samples/serialization/regular_compressed.bin differ diff --git a/test/src/unit-bitstream.cpp b/test/src/unit-bitstream.cpp index 40ff7fe..2b1bd84 100644 --- a/test/src/unit-bitstream.cpp +++ b/test/src/unit-bitstream.cpp @@ -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::value * (strlen(str) + 1); bit_stream.write_copy(reinterpret_cast(str), size); diff --git a/test/src/unit-pclass.cpp b/test/src/unit-pclass.cpp new file mode 100644 index 0000000..c98a75f --- /dev/null +++ b/test/src/unit-pclass.cpp @@ -0,0 +1,149 @@ +#define CATCH_CONFIG_MAIN +#include +#include +#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 hash_calculator + = ki::make_unique(); + auto type_system = ki::make_unique(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(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(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(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( + "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(class_name); + type_system->define_class(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(class_name); + type_system->define_class(class_name); + FAIL(); + } + catch (runtime_error &e) + { + SUCCEED(); + } + } + + SECTION("Define type with the same hash") + { + try + { + type_system->define_class("a"); + type_system->define_class("a "); + FAIL(); + } + catch (runtime_error &e) + { + SUCCEED(); + } + } +} + +TEST_CASE("PropertyClass and PropertyList", "[pclass]") +{ + // TODO: Test PropertyClass instances +} + +TEST_CASE("Enums", "[pclass]") +{ + // TODO: Test Enums +} diff --git a/test/src/unit-serialization.cpp b/test/src/unit-serialization.cpp index 5277472..251aaea 100644 --- a/test/src/unit-serialization.cpp +++ b/test/src/unit-serialization.cpp @@ -1,4 +1,4 @@ -#define CATCH_CONFIG_MAIN +#define CATCH_CONFIG_MAIN #include #include #include @@ -10,6 +10,7 @@ #include #include #include +#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_ptr; - // Test writing a single instance of another object - pclass::StaticProperty value_object; - pclass::StaticProperty not_null_object; - pclass::StaticProperty null_object; + // Test explicitly-sized array of primitives and pointers + pclass::StaticProperty int_array; + pclass::StaticProperty int_ptr_array; + + // Test writing a class type as a value + pclass::StaticProperty object; + + // Test writing a class type as a pointer + pclass::StaticProperty object_ptr; + pclass::StaticProperty null_object_ptr; // Test writing collections of primitives - pclass::VectorProperty collection; - pclass::VectorProperty ptr_collection; + pclass::VectorProperty int_vector; + pclass::VectorProperty int_ptr_vector; // Test writing multiple instances of another object - pclass::VectorProperty objects; + pclass::VectorProperty object_ptr_vector; }; // Setup a global TypeSystem instance -std::unique_ptr g_hash_calculator +std::unique_ptr g_hash_calculator = ki::make_unique(); auto g_type_system = ki::make_unique(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 @@ -372,22 +380,29 @@ void configure_test_object(TestObject &object) SET_EXPECTED(object, float64); 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("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("class NestedTestObject").release(); + object.null_object_ptr = nullptr; + object.object_ptr_vector.push_back( g_type_system->instantiate("class NestedTestObjectA").release() ); - object.objects.push_back( + object.object_ptr_vector.push_back( g_type_system->instantiate("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 );