diff --git a/CMakeLists.txt b/CMakeLists.txt index 2293844..10ba4dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ target_include_directories(${PROJECT_NAME} target_link_libraries(${PROJECT_NAME} RapidXML) add_subdirectory("src/dml") +add_subdirectory("src/pclass") add_subdirectory("src/protocol") add_subdirectory("src/util") diff --git a/include/ki/pclass/PrimitiveType.h b/include/ki/pclass/PrimitiveType.h new file mode 100644 index 0000000..306c1b1 --- /dev/null +++ b/include/ki/pclass/PrimitiveType.h @@ -0,0 +1,71 @@ +#pragma once +#include "ki/pclass/Type.h" + +namespace ki +{ +namespace pclass +{ + /** + * TODO: Documentation + */ + template + struct PrimitiveTypeWriter + { + 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 PrimitiveTypeWriter::write_to" + ); + } + }; + + /** + * TODO: Documentation + */ + template + struct PrimitiveTypeReader + { + static void read_from(BitStream &stream, ValueT &value) + { + // Provide a compiler error if this is not specialized + static_assert( + sizeof(ValueT) == 0, + "Missing specialization of PrimitiveTypeReader::read_from" + ); + } + }; + + /** + * TODO: Documentation + */ + template + class PrimitiveType : public Type + { + public: + PrimitiveType(const std::string name, const hash_t hash) + : Type(name, hash) + { + m_kind = kind::PRIMITIVE; + } + + void write_to(BitStream &stream, const Value &value) const override + { + if (!value.is()) + throw std::runtime_error("Invalid call to Type::write_to -- value type does not match ValueT."); + PrimitiveTypeWriter::write_to(stream, value.get()); + } + + void read_from(BitStream &stream, Value &value) const override + { + if (!value.is()) + throw std::runtime_error("Invalid call to Type::read_from -- value type does not match ValueT."); + PrimitiveTypeReader::read_from(stream, value.get()); + } + }; +} +} + +// Include all template specializations +#include "ki/pclass/types/IntegralPrimitiveType.h" \ No newline at end of file diff --git a/include/ki/pclass/Type.h b/include/ki/pclass/Type.h index bc58549..d77e8d7 100644 --- a/include/ki/pclass/Type.h +++ b/include/ki/pclass/Type.h @@ -5,11 +5,10 @@ #include "ki/pclass/HashCalculator.h" #include "ki/pclass/Value.h" #include "ki/pclass/PropertyClass.h" +#include "ki/util/BitStream.h" namespace ki { - class BitStream; - namespace pclass { /** diff --git a/include/ki/pclass/TypeSystem.h b/include/ki/pclass/TypeSystem.h index 4135ab2..b2960ca 100644 --- a/include/ki/pclass/TypeSystem.h +++ b/include/ki/pclass/TypeSystem.h @@ -30,14 +30,17 @@ namespace pclass template Type &define_primitive(const std::string &name) { - // TODO: Create primitive types + auto hash = m_hash_calculator->calculate_type_hash(name); + auto *type = new PrimitiveType(name, hash); + define_type(type); + return *type; } template Type &define_class(const std::string &name) { // Ensure that ClassT inherits PropertyClass - static_assert(std::is_base_of::value, "ClassT must inherit PropertyClass!"); + static_assert(std::is_base_of::value, "ClassT must inherit PropertyClass!"); // TODO: Create class types } @@ -51,6 +54,9 @@ namespace pclass // TODO: Create enum types } + protected: + void define_type(Type *type); + private: static TypeSystem *s_instance; diff --git a/include/ki/pclass/types/IntegralPrimitiveType.h b/include/ki/pclass/types/IntegralPrimitiveType.h new file mode 100644 index 0000000..682dc41 --- /dev/null +++ b/include/ki/pclass/types/IntegralPrimitiveType.h @@ -0,0 +1,32 @@ +#pragma once +#include + +namespace ki +{ +namespace pclass +{ + template + struct PrimitiveTypeWriter< + ValueT, + typename std::enable_if::value>::type + > + { + static void write_to(BitStream &stream, const ValueT &value) + { + stream.write(value, sizeof(ValueT) * 8); + } + }; + + template + struct PrimitiveTypeReader< + ValueT, + typename std::enable_if::value>::type + > + { + static void read_from(BitStream &stream, ValueT &value) + { + value = stream.read(sizeof(ValueT) * 8); + } + }; +} +} \ No newline at end of file diff --git a/src/pclass/Type.cpp b/src/pclass/Type.cpp index e90c8ce..b81672a 100644 --- a/src/pclass/Type.cpp +++ b/src/pclass/Type.cpp @@ -33,7 +33,7 @@ namespace pclass return m_kind; } - PropertyClass* Type::instantiate() const + PropertyClass *Type::instantiate() const { std::ostringstream oss; oss << "Type '" << m_name << "' cannot be instantiated."; diff --git a/src/pclass/TypeSystem.cpp b/src/pclass/TypeSystem.cpp index be657aa..1e1aaaa 100644 --- a/src/pclass/TypeSystem.cpp +++ b/src/pclass/TypeSystem.cpp @@ -3,9 +3,9 @@ #include #define DEFINE_INTEGER_PRIMTIIVE(st, ut, n) \ - s_instance->define_primitive(n); \ - s_instance->define_primitive("signed " n); \ - s_instance->define_primitive("unsigned " n) + define_primitive(n); \ + define_primitive("signed " n); \ + define_primitive("unsigned " n) namespace ki { @@ -14,54 +14,41 @@ namespace pclass TypeSystem& TypeSystem::get_singleton() { if (s_instance == nullptr) - { // Create the static instance with the default hash calculator s_instance = new TypeSystem(new WizardHashCalculator()); - - // Define integer types - s_instance->define_primitive("bool"); - DEFINE_INTEGER_PRIMTIIVE(int8_t, uint8_t, "char"); - DEFINE_INTEGER_PRIMTIIVE(int8_t, uint8_t, "__int8"); - s_instance->define_primitive("int8_t"); - s_instance->define_primitive("uint8_t"); - - DEFINE_INTEGER_PRIMTIIVE(int16_t, uint16_t, "short"); - DEFINE_INTEGER_PRIMTIIVE(int16_t, uint16_t, "__int16"); - s_instance->define_primitive("int16_t"); - s_instance->define_primitive("uint16_t"); - - DEFINE_INTEGER_PRIMTIIVE(int32_t, uint32_t, "int"); - DEFINE_INTEGER_PRIMTIIVE(int32_t, uint32_t, "__int32"); - s_instance->define_primitive("int32_t"); - s_instance->define_primitive("uint32_t"); - - DEFINE_INTEGER_PRIMTIIVE(int64_t, uint64_t, "long"); - DEFINE_INTEGER_PRIMTIIVE(int64_t, uint64_t, "__int64"); - s_instance->define_primitive("int64_t"); - s_instance->define_primitive("uint64_t"); - s_instance->define_primitive("gid"); - - // TODO: Define bit integer types - - // Define floating point types - s_instance->define_primitive("float"); - s_instance->define_primitive("double"); - - // TODO: Define bit floating point types - - // Define string types - s_instance->define_primitive("std::string"); - s_instance->define_primitive("std::wstring"); - } - return *s_instance; } TypeSystem::TypeSystem(HashCalculator* hash_calculator) { m_hash_calculator = hash_calculator; - } + // Pre-define C++ primitive types + // Define integer types + define_primitive("bool"); + DEFINE_INTEGER_PRIMTIIVE(int8_t, uint8_t, "char"); + DEFINE_INTEGER_PRIMTIIVE(int8_t, uint8_t, "__int8"); + define_primitive("int8_t"); + define_primitive("uint8_t"); + DEFINE_INTEGER_PRIMTIIVE(int16_t, uint16_t, "short"); + DEFINE_INTEGER_PRIMTIIVE(int16_t, uint16_t, "__int16"); + define_primitive("int16_t"); + define_primitive("uint16_t"); + DEFINE_INTEGER_PRIMTIIVE(int32_t, uint32_t, "int"); + DEFINE_INTEGER_PRIMTIIVE(int32_t, uint32_t, "__int32"); + define_primitive("int32_t"); + define_primitive("uint32_t"); + DEFINE_INTEGER_PRIMTIIVE(int64_t, uint64_t, "long"); + DEFINE_INTEGER_PRIMTIIVE(int64_t, uint64_t, "__int64"); + define_primitive("int64_t"); + define_primitive("uint64_t"); + define_primitive("gid"); + + // TODO: Define bit integer types + // TODO: Define floating point types + // TODO: Define bit floating point types + // TODO: Define string types + } TypeSystem::~TypeSystem() { @@ -127,5 +114,39 @@ namespace pclass } return *(it->second); } + + void TypeSystem::define_type(Type *type) + { + // Does a type with this name already exist? + if (m_type_name_lookup.find(type->get_name()) != m_type_name_lookup.end()) + { + // This pointer will become lost since it isn't being added to the lookups. + delete type; + + // Throw an error + std::ostringstream oss; + oss << "Type '" << type->get_name() << "' is already defined."; + throw std::runtime_error(oss.str()); + } + + // Does a type with this hash already exist? + if (m_type_name_lookup.find(type->get_name()) != m_type_name_lookup.end()) + { + // This pointer will become lost since it isn't being added to the lookups. + delete type; + + // Throw an error + auto &other_type = get_type(type->get_hash()); + std::ostringstream oss; + oss << "Type hash collision between '" << type->get_name() + << "' and '" << other_type.get_name() << "'."; + throw std::runtime_error(oss.str()); + } + + // This type is safe to add to our lookups + m_types.push_back(type); + m_type_name_lookup[type->get_name()] = type; + m_type_hash_lookup[type->get_hash()] = type; + } } }