ソースを参照

Reduced the amount of memory allocated by global constructors (#146)

* Factory::sInstance is now a pointer so the application determines when it gets created and destroyed
* Base and attribute arrays of RTTI are now fixed size
* Added Clear function to the factory to remove all registered classes
* Fixed bug with multiple inheritance where multiple bases have a size
* RegisterTypes will now register all types in 1 call
Jorrit Rouwe 3 年 前
コミット
3ca62973da
34 ファイル変更465 行追加390 行削除
  1. 8 0
      HelloWorld/HelloWorld.cpp
  2. 17 2
      Jolt/Core/Factory.cpp
  3. 7 1
      Jolt/Core/Factory.h
  4. 5 18
      Jolt/Core/RTTI.cpp
  5. 5 38
      Jolt/Core/RTTI.h
  6. 10 10
      Jolt/ObjectStream/GetPrimitiveTypeOfType.h
  7. 2 2
      Jolt/ObjectStream/ObjectStream.cpp
  8. 7 26
      Jolt/ObjectStream/ObjectStream.h
  9. 2 2
      Jolt/ObjectStream/ObjectStreamBinaryIn.cpp
  10. 1 1
      Jolt/ObjectStream/ObjectStreamBinaryIn.h
  11. 1 1
      Jolt/ObjectStream/ObjectStreamBinaryOut.cpp
  12. 1 1
      Jolt/ObjectStream/ObjectStreamBinaryOut.h
  13. 33 34
      Jolt/ObjectStream/ObjectStreamIn.cpp
  14. 3 3
      Jolt/ObjectStream/ObjectStreamIn.h
  15. 9 15
      Jolt/ObjectStream/ObjectStreamOut.cpp
  16. 4 4
      Jolt/ObjectStream/ObjectStreamOut.h
  17. 19 19
      Jolt/ObjectStream/ObjectStreamTextIn.cpp
  18. 1 1
      Jolt/ObjectStream/ObjectStreamTextIn.h
  19. 20 20
      Jolt/ObjectStream/ObjectStreamTextOut.cpp
  20. 1 1
      Jolt/ObjectStream/ObjectStreamTextOut.h
  21. 86 12
      Jolt/ObjectStream/SerializableAttribute.h
  22. 35 52
      Jolt/ObjectStream/SerializableAttributeEnum.h
  23. 27 48
      Jolt/ObjectStream/SerializableAttributeTyped.h
  24. 10 9
      Jolt/ObjectStream/SerializableObject.h
  25. 1 1
      Jolt/Physics/Collision/GroupFilter.cpp
  26. 1 1
      Jolt/Physics/Collision/PhysicsMaterial.cpp
  27. 1 1
      Jolt/Physics/Constraints/Constraint.cpp
  28. 1 1
      Jolt/Physics/Constraints/PathConstraintPath.cpp
  29. 1 1
      Jolt/Physics/Vehicle/VehicleConstraint.cpp
  30. 45 37
      Jolt/RegisterTypes.cpp
  31. 8 0
      PerformanceTest/PerformanceTest.cpp
  32. 6 0
      TestFramework/Application/Application.cpp
  33. 59 26
      UnitTests/ObjectStream/ObjectStreamTest.cpp
  34. 28 2
      UnitTests/UnitTestFramework.cpp

+ 8 - 0
HelloWorld/HelloWorld.cpp

@@ -7,6 +7,7 @@
 
 // Jolt includes
 #include <Jolt/RegisterTypes.h>
+#include <Jolt/Core/Factory.h>
 #include <Jolt/Core/TempAllocator.h>
 #include <Jolt/Core/JobSystemThreadPool.h>
 #include <Jolt/Physics/PhysicsSettings.h>
@@ -200,6 +201,9 @@ int main(int argc, char** argv)
 	Trace = TraceImpl;
 	JPH_IF_ENABLE_ASSERTS(AssertFailed = AssertFailedImpl;)
 
+	// Create a factory
+	Factory::sInstance = new Factory();
+
 	// Register all Jolt physics types
 	RegisterTypes();
 
@@ -324,5 +328,9 @@ int main(int argc, char** argv)
 	body_interface.RemoveBody(floor->GetID());
 	body_interface.DestroyBody(floor->GetID());
 
+	// Destroy the factory
+	delete Factory::sInstance;
+	Factory::sInstance = nullptr;
+
 	return 0;
 }

+ 17 - 2
Jolt/Core/Factory.cpp

@@ -7,7 +7,7 @@
 
 JPH_NAMESPACE_BEGIN
 
-Factory Factory::sInstance;
+Factory *Factory::sInstance = nullptr;
 
 void *Factory::CreateObject(const char *inName)
 { 
@@ -51,7 +51,7 @@ bool Factory::Register(const RTTI *inRTTI)
 	// Register attribute classes
 	for (int i = 0; i < inRTTI->GetAttributeCount(); ++i)
 	{
-		const RTTI *rtti = inRTTI->GetAttribute(i)->GetMemberPrimitiveType();
+		const RTTI *rtti = inRTTI->GetAttribute(i).GetMemberPrimitiveType();
 		if (rtti != nullptr && !Register(rtti))
 			return false;
 	}
@@ -59,6 +59,21 @@ bool Factory::Register(const RTTI *inRTTI)
 	return true;
 }
 
+bool Factory::Register(const RTTI **inRTTIs, uint inNumber)
+{
+	for (const RTTI **rtti = inRTTIs; rtti < inRTTIs + inNumber; ++rtti)
+		if (!Register(*rtti))
+			return false;
+
+	return true;
+}
+
+void Factory::Clear()
+{
+	mClassNameMap.clear();
+	mClassHashMap.clear();
+}
+
 vector<const RTTI *> Factory::GetAllClasses() const
 {
 	vector<const RTTI *> all_classes;

+ 7 - 1
Jolt/Core/Factory.h

@@ -27,11 +27,17 @@ public:
 	/// Register an object with the factory. Returns false on failure.
 	bool						Register(const RTTI *inRTTI);
 
+	/// Register a list of objects with the factory. Returns false on failure.
+	bool						Register(const RTTI **inRTTIs, uint inNumber);
+
+	/// Unregisters all types
+	void						Clear();
+
 	/// Get all registered classes
 	vector<const RTTI *>		GetAllClasses() const;
 
 	/// Singleton factory instance
-	static Factory 				sInstance;
+	static Factory *			sInstance;
 
 private:
 	using ClassNameMap = unordered_map<string_view, const RTTI *>;

+ 5 - 18
Jolt/Core/RTTI.cpp

@@ -8,10 +8,6 @@
 
 JPH_NAMESPACE_BEGIN
 
-JPH_IMPLEMENT_RTTI_VIRTUAL_BASE(RTTIAttribute)
-{
-}
-
 //////////////////////////////////////////////////////////////////////////////////////////
 // RTTI
 //////////////////////////////////////////////////////////////////////////////////////////
@@ -74,11 +70,11 @@ void RTTI::AddBaseClass(const RTTI *inRTTI, int inOffset)
 	mBaseClasses.push_back(base); 
 
 	// Add attributes of base class
-	for (AttributeRefC a : inRTTI->mAttributes)
-		mAttributes.push_back(a);
+	for (const SerializableAttribute &a : inRTTI->mAttributes)
+		mAttributes.push_back(SerializableAttribute(a, inOffset));
 }
 
-bool RTTI::operator == (const RTTI &inRHS) const							
+bool RTTI::operator == (const RTTI &inRHS) const
 { 
 	// Compare addresses 
 	if (this == &inRHS) 
@@ -128,7 +124,7 @@ const void *RTTI::CastTo(const void *inObject, const RTTI *inRTTI) const
 	return nullptr;
 }
 
-void RTTI::AddAttribute(const RTTIAttribute *inAttribute)
+void RTTI::AddAttribute(const SerializableAttribute &inAttribute)
 { 
 	mAttributes.push_back(inAttribute); 
 }
@@ -138,18 +134,9 @@ int RTTI::GetAttributeCount() const
 	return (int)mAttributes.size(); 
 }
 
-const RTTIAttribute *RTTI::GetAttribute(int inIdx) const
+const SerializableAttribute &RTTI::GetAttribute(int inIdx) const
 { 
 	return mAttributes[inIdx]; 
 }
 
-const RTTIAttribute *RTTI::GetAttribute(const RTTI *inRTTI, const char *inName) const
-{
-	for (AttributeRefC a : mAttributes)
-		if (::JPH::IsKindOf(a, inRTTI) && strcmp(inName, a->GetName()) == 0) 
-			return a;
-
-	return nullptr;
-}
-
 JPH_NAMESPACE_END

+ 5 - 38
Jolt/Core/RTTI.h

@@ -5,6 +5,7 @@
 
 #include <Jolt/Core/Reference.h>
 #include <Jolt/Core/StaticArray.h>
+#include <Jolt/ObjectStream/SerializableAttribute.h>
 
 JPH_NAMESPACE_BEGIN
 
@@ -12,8 +13,6 @@ JPH_NAMESPACE_BEGIN
 // RTTI
 //////////////////////////////////////////////////////////////////////////////////////////
 
-class RTTIAttribute;
-
 /// Light weight runtime type information system. This way we don't need to turn
 /// on the default RTTI system of the compiler (introducing a possible overhead for every
 /// class)
@@ -163,10 +162,9 @@ public:
 	const void *				CastTo(const void *inObject, const RTTI *inRTTI) const;
 
 	/// Attribute access
-	void						AddAttribute(const RTTIAttribute *inAttribute);
+	void						AddAttribute(const SerializableAttribute &inAttribute);
 	int							GetAttributeCount() const;
-	const RTTIAttribute *		GetAttribute(int inIdx) const;
-	const RTTIAttribute *		GetAttribute(const RTTI *inRTTI, const char *inName) const;
+	const SerializableAttribute & GetAttribute(int inIdx) const;
 
 protected:
 	/// Base class information
@@ -176,14 +174,12 @@ protected:
 		int						mOffset;
 	};
 
-	using AttributeRefC = RefConst<RTTIAttribute>;
-
 	const char *				mName;														///< Class name
 	int							mSize;														///< Class size
-	vector<BaseClass>			mBaseClasses;												///< Names of base classes
+	StaticArray<BaseClass, 4>	mBaseClasses;												///< Names of base classes
 	pCreateObjectFunction		mCreate;													///< Pointer to a function that will create a new instance of this class
 	pDestructObjectFunction		mDestruct;													///< Pointer to a function that will destruct an object of this class
-	vector<AttributeRefC>		mAttributes;												///< All attributes of this class
+	StaticArray<SerializableAttribute, 32> mAttributes;										///< All attributes of this class
 };
 
 //////////////////////////////////////////////////////////////////////////////////////////
@@ -434,33 +430,4 @@ inline Ref<DstType> DynamicCast(Ref<SrcType> &inObject)
 	return inObject != nullptr? const_cast<DstType *>(reinterpret_cast<const DstType *>(inObject->CastTo(JPH_RTTI(DstType)))) : nullptr;
 }
 
-//////////////////////////////////////////////////////////////////////////////////////////
-// RTTIAttribute
-//////////////////////////////////////////////////////////////////////////////////////////
-
-/// Represents a member of a class.
-class RTTIAttribute : public RefTarget<RTTIAttribute>
-{
-	JPH_DECLARE_RTTI_VIRTUAL_BASE(RTTIAttribute)
-
-public:
-	/// Constructor
-								RTTIAttribute()												: mName("") { }
-	explicit					RTTIAttribute(const char *inName)							: mName(inName) { }
-	virtual						~RTTIAttribute() = default;
-
-	/// Name of the attribute
-	void						SetName(const char *inName)									{ mName = inName; }
-	const char *				GetName() const												{ return mName; }
-
-	/// In case this attribute contains an RTTI type, return it (note that a vector<sometype> will return the rtti of sometype)
-	virtual const RTTI *		GetMemberPrimitiveType() const								{ return nullptr; }
-
-	/// In case this attribute references an object, return data pointer here (note that if the attribute is vector<sometype> it will return the address of the vector)
-	virtual const void *		GetMemberPointer(const void *inObject) const				{ return nullptr; }
-
-private:
-	const char *				mName;
-};
-
 JPH_NAMESPACE_END

+ 10 - 10
Jolt/ObjectStream/GetPrimitiveTypeOfType.h

@@ -9,43 +9,43 @@ JPH_NAMESPACE_BEGIN
 
 /// Helper functions to get the underlying RTTI type of a type (so e.g. vector<sometype> will return sometype)
 template <class T>				
-RTTI *GetPrimitiveTypeOfType(T *)
+const RTTI *GetPrimitiveTypeOfType(T *)
 { 
 	return GetRTTIOfType((T *)nullptr);
 }
 
 template <class T>				
-RTTI *GetPrimitiveTypeOfType(T **)
+const RTTI *GetPrimitiveTypeOfType(T **)
 { 
-	return GetPrimitiveTypeOfType((T *)nullptr);
+	return GetRTTIOfType((T *)nullptr);
 }
 
 template <class T>				
-RTTI *GetPrimitiveTypeOfType(Ref<T> *)
+const RTTI *GetPrimitiveTypeOfType(Ref<T> *)
 { 
-	return GetPrimitiveTypeOfType((T *)nullptr);
+	return GetRTTIOfType((T *)nullptr);
 }
 
 template <class T>				
-RTTI *GetPrimitiveTypeOfType(RefConst<T> *)
+const RTTI *GetPrimitiveTypeOfType(RefConst<T> *)
 { 
-	return GetPrimitiveTypeOfType((T *)nullptr);
+	return GetRTTIOfType((T *)nullptr);
 }
 
 template <class T>				
-RTTI *GetPrimitiveTypeOfType(vector<T> *)
+const RTTI *GetPrimitiveTypeOfType(vector<T> *)
 { 
 	return GetPrimitiveTypeOfType((T *)nullptr);
 }
 
 template <class T, uint N>				
-RTTI *GetPrimitiveTypeOfType(StaticArray<T, N> *)
+const RTTI *GetPrimitiveTypeOfType(StaticArray<T, N> *)
 { 
 	return GetPrimitiveTypeOfType((T *)nullptr);
 }
 
 template <class T, uint N>				
-RTTI *GetPrimitiveTypeOfType(T (*)[N])
+const RTTI *GetPrimitiveTypeOfType(T (*)[N])
 { 
 	return GetPrimitiveTypeOfType((T *)nullptr);
 }

+ 2 - 2
Jolt/ObjectStream/ObjectStream.cpp

@@ -9,9 +9,9 @@ JPH_NAMESPACE_BEGIN
 
 // Define macro to declare functions for a specific primitive type
 #define JPH_DECLARE_PRIMITIVE(name)														\
-	bool				OSIsType(name *, int inArrayDepth, ObjectStream::EDataType inDataType, const char *inClassName) \
+	bool				OSIsType(name *, int inArrayDepth, EOSDataType inDataType, const char *inClassName) \
 	{																					\
-		return inArrayDepth == 0 && inDataType == ObjectStream::EDataType::T_##name;	\
+		return inArrayDepth == 0 && inDataType == EOSDataType::T_##name;				\
 	}
 
 // This file uses the JPH_DECLARE_PRIMITIVE macro to define all types

+ 7 - 26
Jolt/ObjectStream/ObjectStream.h

@@ -5,6 +5,7 @@
 
 #include <Jolt/Core/StaticArray.h>
 #include <Jolt/Core/Reference.h>
+#include <Jolt/ObjectStream/SerializableAttribute.h>
 
 JPH_NAMESPACE_BEGIN
 
@@ -19,26 +20,6 @@ public:
 		Binary,
 	};
 
-	/// Data type
-	enum class EDataType
-	{
-		/// Control codes
-		Declare,																		///< Used to declare the attributes of a new object type
-		Object,																			///< Start of a new object
-		Instance,																		///< Used in attribute declaration, indicates that an object is an instanced attribute (no pointer)
-		Pointer,																		///< Used in attribute declaration, indicates that an object is a pointer attribute
-		Array,																			///< Used in attribute declaration, indicates that this is an array of objects
-		
-		// Basic types (primitives)
-		#define JPH_DECLARE_PRIMITIVE(name)	T_##name,
-
-		// This file uses the JPH_DECLARE_PRIMITIVE macro to define all types
-		#include <Jolt/ObjectStream/ObjectStreamTypes.h>
-
-		// Error values for read functions
-		Invalid,																		///< Next token on the stream was not a valid data type
-	};
-
 protected:
 	/// Constructor
 	virtual							~ObjectStream() = default;
@@ -53,38 +34,38 @@ protected:
 
 // Define macro to declare functions for a specific primitive type
 #define JPH_DECLARE_PRIMITIVE(name)													\
-	bool						OSIsType(name *, int inArrayDepth, ObjectStream::EDataType inDataType, const char *inClassName);
+	bool						OSIsType(name *, int inArrayDepth, EOSDataType inDataType, const char *inClassName);
 
 // This file uses the JPH_DECLARE_PRIMITIVE macro to define all types
 #include <Jolt/ObjectStream/ObjectStreamTypes.h>
 
 // Define serialization templates
 template <class T>
-bool OSIsType(vector<T> *, int inArrayDepth, ObjectStream::EDataType inDataType, const char *inClassName)	
+bool OSIsType(vector<T> *, int inArrayDepth, EOSDataType inDataType, const char *inClassName)	
 { 
 	return (inArrayDepth > 0 && OSIsType((T *)nullptr, inArrayDepth - 1, inDataType, inClassName)); 
 }
 
 template <class T, uint N>
-bool OSIsType(StaticArray<T, N> *, int inArrayDepth, ObjectStream::EDataType inDataType, const char *inClassName)	
+bool OSIsType(StaticArray<T, N> *, int inArrayDepth, EOSDataType inDataType, const char *inClassName)	
 { 
 	return (inArrayDepth > 0 && OSIsType((T *)nullptr, inArrayDepth - 1, inDataType, inClassName)); 
 }
 
 template <class T, uint N>
-bool OSIsType(T (*)[N], int inArrayDepth, ObjectStream::EDataType inDataType, const char *inClassName)	
+bool OSIsType(T (*)[N], int inArrayDepth, EOSDataType inDataType, const char *inClassName)	
 { 
 	return (inArrayDepth > 0 && OSIsType((T *)nullptr, inArrayDepth - 1, inDataType, inClassName)); 
 }
 
 template <class T>
-bool OSIsType(Ref<T> *, int inArrayDepth, ObjectStream::EDataType inDataType, const char *inClassName)
+bool OSIsType(Ref<T> *, int inArrayDepth, EOSDataType inDataType, const char *inClassName)
 {
 	return OSIsType((T *)nullptr, inArrayDepth, inDataType, inClassName);
 }
 
 template <class T>
-bool OSIsType(RefConst<T> *, int inArrayDepth, ObjectStream::EDataType inDataType, const char *inClassName)
+bool OSIsType(RefConst<T> *, int inArrayDepth, EOSDataType inDataType, const char *inClassName)
 {
 	return OSIsType((T *)nullptr, inArrayDepth, inDataType, inClassName);
 }

+ 2 - 2
Jolt/ObjectStream/ObjectStreamBinaryIn.cpp

@@ -12,12 +12,12 @@ ObjectStreamBinaryIn::ObjectStreamBinaryIn(istream &inStream) :
 {
 }
 
-bool ObjectStreamBinaryIn::ReadDataType(EDataType &outType)
+bool ObjectStreamBinaryIn::ReadDataType(EOSDataType &outType)
 {
 	uint32 type;
 	mStream.read((char *)&type, sizeof(type));
 	if (mStream.fail()) return false;
-	outType = (EDataType)type;
+	outType = (EOSDataType)type;
 	return true;
 }
 

+ 1 - 1
Jolt/ObjectStream/ObjectStreamBinaryIn.h

@@ -15,7 +15,7 @@ public:
 	explicit 					ObjectStreamBinaryIn(istream &inStream);
 
 	///@name Input type specific operations
-	virtual bool				ReadDataType(EDataType &outType) override;
+	virtual bool				ReadDataType(EOSDataType &outType) override;
 	virtual bool				ReadName(string &outName) override;
 	virtual bool				ReadIdentifier(Identifier &outIdentifier) override;
 	virtual bool				ReadCount(uint32 &outCount) override;

+ 1 - 1
Jolt/ObjectStream/ObjectStreamBinaryOut.cpp

@@ -16,7 +16,7 @@ ObjectStreamBinaryOut::ObjectStreamBinaryOut(ostream &inStream) :
 	mStream.write(header.c_str(), header.size());
 }
 
-void ObjectStreamBinaryOut::WriteDataType(EDataType inType)
+void ObjectStreamBinaryOut::WriteDataType(EOSDataType inType)
 {
 	mStream.write((const char *)&inType, sizeof(inType));
 }

+ 1 - 1
Jolt/ObjectStream/ObjectStreamBinaryOut.h

@@ -15,7 +15,7 @@ public:
 	explicit 					ObjectStreamBinaryOut(ostream &inStream);
 
 	///@name Output type specific operations
-	virtual void				WriteDataType(EDataType inType) override;
+	virtual void				WriteDataType(EOSDataType inType) override;
 	virtual void				WriteName(const char *inName) override;
 	virtual void				WriteIdentifier(Identifier inIdentifier) override;
 	virtual void				WriteCount(uint32 inCount) override;

+ 33 - 34
Jolt/ObjectStream/ObjectStreamIn.cpp

@@ -4,7 +4,6 @@
 #include <Jolt/Jolt.h>
 
 #include <Jolt/Core/Factory.h>
-#include <Jolt/ObjectStream/SerializableAttribute.h>
 #include <Jolt/ObjectStream/ObjectStreamIn.h>
 #include <Jolt/ObjectStream/ObjectStreamTextIn.h>
 #include <Jolt/ObjectStream/ObjectStreamBinaryIn.h>
@@ -88,11 +87,11 @@ void *ObjectStreamIn::Read(const RTTI *inRTTI)
 	for (;;)
 	{
 		// Get type of next operation
-		EDataType data_type;
+		EOSDataType data_type;
 		if (!ReadDataType(data_type)) 
 			break;
 
-		if (data_type == EDataType::Declare)
+		if (data_type == EOSDataType::Declare)
 		{	
 			// Read type declaration
 			if (!ReadRTTI()) 
@@ -102,7 +101,7 @@ void *ObjectStreamIn::Read(const RTTI *inRTTI)
 				break;
 			}
 		}
-		else if (data_type == EDataType::Object)
+		else if (data_type == EOSDataType::Object)
 		{	
 			const RTTI *rtti;
 			void *object = ReadObject(rtti);
@@ -259,7 +258,7 @@ bool ObjectStreamIn::ReadRTTI()
 		return false;
 
 	// Find class 
-	const RTTI *rtti = Factory::sInstance.Find(class_name.c_str());
+	const RTTI *rtti = Factory::sInstance->Find(class_name.c_str());
 	if (rtti == nullptr)
 		Trace("ObjectStreamIn: Unknown class: \"%s\".", class_name.c_str());
 
@@ -286,7 +285,7 @@ bool ObjectStreamIn::ReadRTTI()
 			return false;
 
 		// Read array depth
-		while (attribute.mDataType == EDataType::Array) 
+		while (attribute.mDataType == EOSDataType::Array) 
 		{
 			++attribute.mArrayDepth;
 			if (!ReadDataType(attribute.mDataType)) 
@@ -294,7 +293,7 @@ bool ObjectStreamIn::ReadRTTI()
 		}
 
 		// Read instance/pointer class name
-		if ((attribute.mDataType == EDataType::Instance || attribute.mDataType == EDataType::Pointer) 
+		if ((attribute.mDataType == EOSDataType::Instance || attribute.mDataType == EOSDataType::Pointer) 
 			&& !ReadName(attribute.mClassName)) 
 			return false;
 
@@ -304,8 +303,8 @@ bool ObjectStreamIn::ReadRTTI()
 			// Find attribute index
 			for (int idx = 0; idx < rtti->GetAttributeCount(); ++idx)
 			{
-				const SerializableAttribute *attr = DynamicCast<SerializableAttribute, RTTIAttribute>(rtti->GetAttribute(idx));
-				if (attr != nullptr && strcmp(attr->GetName(), attribute_name.c_str()) == 0) 
+				const SerializableAttribute &attr = rtti->GetAttribute(idx);
+				if (strcmp(attr.GetName(), attribute_name.c_str()) == 0) 
 				{
 					attribute.mIndex = idx;
 					break;
@@ -315,8 +314,8 @@ bool ObjectStreamIn::ReadRTTI()
 			// Check if attribute is of expected type
 			if (attribute.mIndex >= 0)
 			{
-				const SerializableAttribute *attr = (const SerializableAttribute *)rtti->GetAttribute(attribute.mIndex);
-				if (!attr->IsType(attribute.mArrayDepth, attribute.mDataType, attribute.mClassName.c_str()))
+				const SerializableAttribute &attr = rtti->GetAttribute(attribute.mIndex);
+				if (!attr.IsType(attribute.mArrayDepth, attribute.mDataType, attribute.mClassName.c_str()))
 					attribute.mIndex = -1;
 			}
 		} 
@@ -348,8 +347,8 @@ bool ObjectStreamIn::ReadClassData(const ClassDescription &inClassDesc, void *in
 		// Read or skip the attribute data
 		if (attr_desc.mIndex >= 0 && inInstance)
 		{
-			const SerializableAttribute *attr = (const SerializableAttribute *)inClassDesc.mRTTI->GetAttribute(attr_desc.mIndex);
-			continue_reading = attr->ReadData(*this, inInstance);
+			const SerializableAttribute &attr = inClassDesc.mRTTI->GetAttribute(attr_desc.mIndex);
+			continue_reading = attr.ReadData(*this, inInstance);
 		}
 		else
 			continue_reading = SkipAttributeData(attr_desc.mArrayDepth, attr_desc.mDataType, attr_desc.mClassName.c_str());
@@ -387,7 +386,7 @@ bool ObjectStreamIn::ReadPointerData(const RTTI *inRTTI, void **inPointer, int i
 	return false;
 }
 
-bool ObjectStreamIn::SkipAttributeData(int inArrayDepth, EDataType inDataType, const char *inClassName)
+bool ObjectStreamIn::SkipAttributeData(int inArrayDepth, EOSDataType inDataType, const char *inClassName)
 {
 	bool continue_reading = true;
 
@@ -412,7 +411,7 @@ bool ObjectStreamIn::SkipAttributeData(int inArrayDepth, EDataType inDataType, c
 	// Read data for all items
 	if (continue_reading) 
 	{
-		if (inDataType == EDataType::Instance) 
+		if (inDataType == EOSDataType::Instance) 
 		{
 			// Get the class description
 			ClassDescriptionMap::iterator i = mClassDescriptionMap.find(inClassName);
@@ -433,109 +432,109 @@ bool ObjectStreamIn::SkipAttributeData(int inArrayDepth, EDataType inDataType, c
 			{
 				switch (inDataType) 
 				{
-				case EDataType::Pointer:
+				case EOSDataType::Pointer:
 					{	
 						Identifier temporary;
 						continue_reading = ReadIdentifier(temporary);
 						break;
 					}
 
-				case EDataType::T_uint8:
+				case EOSDataType::T_uint8:
 					{	
 						uint8 temporary;
 						continue_reading = ReadPrimitiveData(temporary);
 						break;
 					}
 				
-				case EDataType::T_uint16:
+				case EOSDataType::T_uint16:
 					{	
 						uint16 temporary;
 						continue_reading = ReadPrimitiveData(temporary);
 						break;
 					}
 				
-				case EDataType::T_int:
+				case EOSDataType::T_int:
 					{	
 						int temporary;
 						continue_reading = ReadPrimitiveData(temporary);
 						break;
 					}
 				
-				case EDataType::T_uint32:
+				case EOSDataType::T_uint32:
 					{	
 						uint32 temporary;
 						continue_reading = ReadPrimitiveData(temporary);
 						break;
 					}
 				
-				case EDataType::T_uint64:
+				case EOSDataType::T_uint64:
 					{	
 						uint64 temporary;
 						continue_reading = ReadPrimitiveData(temporary);
 						break;
 					}
 				
-				case EDataType::T_float:
+				case EOSDataType::T_float:
 					{	
 						float temporary;
 						continue_reading = ReadPrimitiveData(temporary);
 						break;
 					}
 				
-				case EDataType::T_bool:
+				case EOSDataType::T_bool:
 					{	
 						bool temporary;
 						continue_reading = ReadPrimitiveData(temporary);
 						break;
 					}
 				
-				case EDataType::T_string:
+				case EOSDataType::T_string:
 					{	
 						string temporary;
 						continue_reading = ReadPrimitiveData(temporary);
 						break;
 					}
 
-				case EDataType::T_Float3:
+				case EOSDataType::T_Float3:
 					{	
 						Float3 temporary;
 						continue_reading = ReadPrimitiveData(temporary);
 						break;
 					}
 
-				case EDataType::T_Vec3:
+				case EOSDataType::T_Vec3:
 					{	
 						Vec3 temporary;
 						continue_reading = ReadPrimitiveData(temporary);
 						break;
 					}
 
-				case EDataType::T_Vec4:
+				case EOSDataType::T_Vec4:
 					{	
 						Vec4 temporary;
 						continue_reading = ReadPrimitiveData(temporary);
 						break;
 					}
 
-				case EDataType::T_Quat:
+				case EOSDataType::T_Quat:
 					{	
 						Quat temporary;
 						continue_reading = ReadPrimitiveData(temporary);
 						break;
 					}
 
-				case EDataType::T_Mat44:
+				case EOSDataType::T_Mat44:
 					{	
 						Mat44 temporary;
 						continue_reading = ReadPrimitiveData(temporary);
 						break;
 					}
 
-				case EDataType::Array:
-				case EDataType::Object:
-				case EDataType::Declare:
-				case EDataType::Instance:
-				case EDataType::Invalid:
+				case EOSDataType::Array:
+				case EOSDataType::Object:
+				case EOSDataType::Declare:
+				case EOSDataType::Instance:
+				case EOSDataType::Invalid:
 				default:
 					continue_reading = false;
 					break;

+ 3 - 3
Jolt/ObjectStream/ObjectStreamIn.h

@@ -81,10 +81,10 @@ public:
 	bool						ReadClassData(const char *inClassName, void *inInstance);
 	bool						ReadClassData(const ClassDescription &inClassDesc, void *inInstance);
 	bool						ReadPointerData(const RTTI *inRTTI, void **inPointer, int inRefCountOffset = -1);
-	bool						SkipAttributeData(int inArrayDepth, EDataType inDataType, const char *inClassName);
+	bool						SkipAttributeData(int inArrayDepth, EOSDataType inDataType, const char *inClassName);
 
 	///@name Input type specific operations
-	virtual bool				ReadDataType(EDataType &outType) = 0;
+	virtual bool				ReadDataType(EOSDataType &outType) = 0;
 	virtual bool				ReadName(string &outName) = 0;
 	virtual bool				ReadIdentifier(Identifier &outIdentifier) = 0;
 	virtual bool				ReadCount(uint32 &outCount) = 0;
@@ -120,7 +120,7 @@ private:
 	struct AttributeDescription
 	{
 		int						mArrayDepth = 0;
-		EDataType				mDataType = EDataType::Invalid;
+		EOSDataType				mDataType = EOSDataType::Invalid;
 		string					mClassName;
 		int						mIndex = -1;
 	};

+ 9 - 15
Jolt/ObjectStream/ObjectStreamOut.cpp

@@ -6,7 +6,6 @@
 #include <Jolt/ObjectStream/ObjectStreamOut.h>
 #include <Jolt/ObjectStream/ObjectStreamTextOut.h>
 #include <Jolt/ObjectStream/ObjectStreamBinaryOut.h>
-#include <Jolt/ObjectStream/SerializableAttribute.h>
 #include <Jolt/ObjectStream/TypeDeclarations.h>
 
 JPH_NAMESPACE_BEGIN
@@ -65,7 +64,7 @@ void ObjectStreamOut::WriteObject(const void *inObject)
 	HintNextItem();
 
 	// Write object header.
-	WriteDataType(EDataType::Object);
+	WriteDataType(EOSDataType::Object);
 	WriteName(i->second.mRTTI->GetName());
 	WriteIdentifier(i->second.mIdentifier);
 
@@ -89,7 +88,7 @@ void ObjectStreamOut::WriteRTTI(const RTTI *inRTTI)
 	HintNextItem();
 
 	// Write class header. E.g. in text mode: "class <name> <attr-count>"
-	WriteDataType(EDataType::Declare);
+	WriteDataType(EOSDataType::Declare);
 	WriteName(inRTTI->GetName());
 	WriteCount(inRTTI->GetAttributeCount());
 
@@ -98,20 +97,18 @@ void ObjectStreamOut::WriteRTTI(const RTTI *inRTTI)
 	for (int attr_index = 0; attr_index < inRTTI->GetAttributeCount(); ++attr_index) 
 	{
 		// Get attribute
-		const SerializableAttribute *attr = DynamicCast<SerializableAttribute, RTTIAttribute>(inRTTI->GetAttribute(attr_index));
-		if (attr == nullptr)
-			continue;
+		const SerializableAttribute &attr = inRTTI->GetAttribute(attr_index);
 
 		// Write definition of attribute class if undefined
-		const RTTI *rtti = attr->GetMemberPrimitiveType();
+		const RTTI *rtti = attr.GetMemberPrimitiveType();
 		if (rtti != nullptr)
 			QueueRTTI(rtti);
 
 		HintNextItem();
 
 		// Write attribute information.
-		WriteName(attr->GetName());
-		attr->WriteDataType(*this);
+		WriteName(attr.GetName());
+		attr.WriteDataType(*this);
 	}
 	HintIndentDown();
 }
@@ -125,11 +122,8 @@ void ObjectStreamOut::WriteClassData(const RTTI *inRTTI, const void *inInstance)
 	for (int attr_index = 0; attr_index < inRTTI->GetAttributeCount(); ++attr_index) 
 	{
 		// Get attribute
-		const SerializableAttribute *attr = DynamicCast<SerializableAttribute, RTTIAttribute>(inRTTI->GetAttribute(attr_index));
-		if (attr == nullptr)
-			continue;
-
-		attr->WriteData(*this, inInstance);
+		const SerializableAttribute &attr = inRTTI->GetAttribute(attr_index);
+		attr.WriteData(*this, inInstance);
 	}
 	HintIndentDown();
 }
@@ -170,7 +164,7 @@ void ObjectStreamOut::WritePointerData(const RTTI *inRTTI, const void *inPointer
 #define JPH_DECLARE_PRIMITIVE(name)															\
 	void	OSWriteDataType(ObjectStreamOut &ioStream, name *)								\
 	{																						\
-		ioStream.WriteDataType(ObjectStream::EDataType::T_##name);							\
+		ioStream.WriteDataType(EOSDataType::T_##name);										\
 	}																						\
 	void	OSWriteData(ObjectStreamOut &ioStream, const name &inPrimitive)					\
 	{																						\

+ 4 - 4
Jolt/ObjectStream/ObjectStreamOut.h

@@ -64,7 +64,7 @@ public:
 	void						WritePointerData(const RTTI *inRTTI, const void *inPointer);
 
 	///@name Output type specific operations
-	virtual void				WriteDataType(EDataType inType) = 0;
+	virtual void				WriteDataType(EOSDataType inType) = 0;
 	virtual void				WriteName(const char *inName) = 0;
 	virtual void				WriteIdentifier(Identifier inIdentifier) = 0;
 	virtual void				WriteCount(uint32 inCount) = 0;
@@ -131,7 +131,7 @@ private:
 template <class T>
 void OSWriteDataType(ObjectStreamOut &ioStream, vector<T> *)		
 { 
-	ioStream.WriteDataType(ObjectStream::EDataType::Array); 
+	ioStream.WriteDataType(EOSDataType::Array); 
 	OSWriteDataType(ioStream, (T *)nullptr); 
 }
 
@@ -153,7 +153,7 @@ void OSWriteData(ObjectStreamOut &ioStream, const vector<T> &inArray)
 template <class T, uint N>
 void OSWriteDataType(ObjectStreamOut &ioStream, StaticArray<T, N> *)		
 { 
-	ioStream.WriteDataType(ObjectStream::EDataType::Array); 
+	ioStream.WriteDataType(EOSDataType::Array); 
 	OSWriteDataType(ioStream, (T *)nullptr); 
 }
 
@@ -175,7 +175,7 @@ void OSWriteData(ObjectStreamOut &ioStream, const StaticArray<T, N> &inArray)
 template <class T, uint N>
 void OSWriteDataType(ObjectStreamOut &ioStream, T (*)[N])		
 { 
-	ioStream.WriteDataType(ObjectStream::EDataType::Array); 
+	ioStream.WriteDataType(EOSDataType::Array); 
 	OSWriteDataType(ioStream, (T *)nullptr); 
 }
 

+ 19 - 19
Jolt/ObjectStream/ObjectStreamTextIn.cpp

@@ -12,48 +12,48 @@ ObjectStreamTextIn::ObjectStreamTextIn(istream &inStream) :
 {
 }
 
-bool ObjectStreamTextIn::ReadDataType(EDataType &outType)
+bool ObjectStreamTextIn::ReadDataType(EOSDataType &outType)
 {
 	string token;
 	if (ReadWord(token)) 
 	{
 		transform(token.begin(), token.end(), token.begin(), [](char inValue) { return (char)tolower(inValue); });
 		if (token == "declare")
-			outType = EDataType::Declare;
+			outType = EOSDataType::Declare;
 		else if (token == "object")
-			outType = EDataType::Object;		
+			outType = EOSDataType::Object;		
 		else if (token == "instance")
-			outType = EDataType::Instance;
+			outType = EOSDataType::Instance;
 		else if (token == "pointer")
-			outType = EDataType::Pointer;
+			outType = EOSDataType::Pointer;
 		else if (token == "array")
-			outType  = EDataType::Array;
+			outType  = EOSDataType::Array;
 		else if (token == "uint8")
-			outType  = EDataType::T_uint8;
+			outType  = EOSDataType::T_uint8;
 		else if (token == "uint16")
-			outType  = EDataType::T_uint16;
+			outType  = EOSDataType::T_uint16;
 		else if (token == "int")
-			outType  = EDataType::T_int;
+			outType  = EOSDataType::T_int;
 		else if (token == "uint32")
-			outType  = EDataType::T_uint32;
+			outType  = EOSDataType::T_uint32;
 		else if (token == "uint64")
-			outType  = EDataType::T_uint64;
+			outType  = EOSDataType::T_uint64;
 		else if (token == "float")
-			outType  = EDataType::T_float;
+			outType  = EOSDataType::T_float;
 		else if (token == "bool")
-			outType  = EDataType::T_bool;
+			outType  = EOSDataType::T_bool;
 		else if (token == "string")
-			outType  = EDataType::T_string;
+			outType  = EOSDataType::T_string;
 		else if (token == "float3")
-			outType  = EDataType::T_Float3;
+			outType  = EOSDataType::T_Float3;
 		else if (token == "vec3")
-			outType  = EDataType::T_Vec3;
+			outType  = EOSDataType::T_Vec3;
 		else if (token == "vec4")
-			outType  = EDataType::T_Vec4;
+			outType  = EOSDataType::T_Vec4;
 		else if (token == "quat")
-			outType  = EDataType::T_Quat;
+			outType  = EOSDataType::T_Quat;
 		else if (token == "mat44")
-			outType  = EDataType::T_Mat44;
+			outType  = EOSDataType::T_Mat44;
 		else
 		{
 			Trace("ObjectStreamTextIn: Found unknown data type.");

+ 1 - 1
Jolt/ObjectStream/ObjectStreamTextIn.h

@@ -15,7 +15,7 @@ public:
 	explicit					ObjectStreamTextIn(istream &inStream);
 
 	///@name Input type specific operations
-	virtual bool				ReadDataType(EDataType &outType) override;
+	virtual bool				ReadDataType(EOSDataType &outType) override;
 	virtual bool				ReadName(string &outName) override;
 	virtual bool				ReadIdentifier(Identifier &outIdentifier) override;
 	virtual bool				ReadCount(uint32 &outCount) override;

+ 20 - 20
Jolt/ObjectStream/ObjectStreamTextOut.cpp

@@ -14,29 +14,29 @@ ObjectStreamTextOut::ObjectStreamTextOut(ostream &inStream) :
 	WriteWord(StringFormat("TOS%2d.%02d", ObjectStream::sVersion, ObjectStream::sRevision));
 }
 
-void ObjectStreamTextOut::WriteDataType(EDataType inType)
+void ObjectStreamTextOut::WriteDataType(EOSDataType inType)
 {
 	switch (inType) 
 	{
-	case EDataType::Declare:		WriteWord("declare ");		break;
-	case EDataType::Object:			WriteWord("object ");		break;
-	case EDataType::Instance:		WriteWord("instance ");		break;
-	case EDataType::Pointer:		WriteWord("pointer ");		break;
-	case EDataType::Array:			WriteWord("array ");		break;
-	case EDataType::T_uint8:		WriteWord("uint8");			break;
-	case EDataType::T_uint16:		WriteWord("uint16");		break;
-	case EDataType::T_int:			WriteWord("int");			break;
-	case EDataType::T_uint32:		WriteWord("uint32");		break;
-	case EDataType::T_uint64:		WriteWord("uint64");		break;
-	case EDataType::T_float:		WriteWord("float");			break;
-	case EDataType::T_bool:			WriteWord("bool");			break;
-	case EDataType::T_string:		WriteWord("string");		break;
-	case EDataType::T_Float3:		WriteWord("float3");		break;
-	case EDataType::T_Vec3:			WriteWord("vec3");			break;
-	case EDataType::T_Vec4:			WriteWord("vec4");			break;
-	case EDataType::T_Quat:			WriteWord("quat");			break;
-	case EDataType::T_Mat44:		WriteWord("mat44");			break;
-	case EDataType::Invalid:
+	case EOSDataType::Declare:		WriteWord("declare ");		break;
+	case EOSDataType::Object:		WriteWord("object ");		break;
+	case EOSDataType::Instance:		WriteWord("instance ");		break;
+	case EOSDataType::Pointer:		WriteWord("pointer ");		break;
+	case EOSDataType::Array:		WriteWord("array ");		break;
+	case EOSDataType::T_uint8:		WriteWord("uint8");			break;
+	case EOSDataType::T_uint16:		WriteWord("uint16");		break;
+	case EOSDataType::T_int:		WriteWord("int");			break;
+	case EOSDataType::T_uint32:		WriteWord("uint32");		break;
+	case EOSDataType::T_uint64:		WriteWord("uint64");		break;
+	case EOSDataType::T_float:		WriteWord("float");			break;
+	case EOSDataType::T_bool:		WriteWord("bool");			break;
+	case EOSDataType::T_string:		WriteWord("string");		break;
+	case EOSDataType::T_Float3:		WriteWord("float3");		break;
+	case EOSDataType::T_Vec3:		WriteWord("vec3");			break;
+	case EOSDataType::T_Vec4:		WriteWord("vec4");			break;
+	case EOSDataType::T_Quat:		WriteWord("quat");			break;
+	case EOSDataType::T_Mat44:		WriteWord("mat44");			break;
+	case EOSDataType::Invalid:
 	default:						JPH_ASSERT(false);			break;
 	}
 }

+ 1 - 1
Jolt/ObjectStream/ObjectStreamTextOut.h

@@ -15,7 +15,7 @@ public:
 	explicit					ObjectStreamTextOut(ostream &inStream);
 
 	///@name Output type specific operations
-	virtual void				WriteDataType(EDataType inType) override;
+	virtual void				WriteDataType(EOSDataType inType) override;
 	virtual void				WriteName(const char *inName) override;
 	virtual void				WriteIdentifier(Identifier inIdentifier) override;
 	virtual void				WriteCount(uint32 inCount) override;

+ 86 - 12
Jolt/ObjectStream/SerializableAttribute.h

@@ -3,24 +3,98 @@
 
 #pragma once
 
-#include <Jolt/ObjectStream/ObjectStreamIn.h>
-#include <Jolt/ObjectStream/ObjectStreamOut.h>
-
 JPH_NAMESPACE_BEGIN
 
-/// Attributes are members of classes that need to be serialized. This extends the
-/// basic attribute defined in RTTI.h
-class SerializableAttribute : public RTTIAttribute
+class RTTI;
+class ObjectStreamIn;
+class ObjectStreamOut;
+
+/// Data type
+enum class EOSDataType
+{
+	/// Control codes
+	Declare,																		///< Used to declare the attributes of a new object type
+	Object,																			///< Start of a new object
+	Instance,																		///< Used in attribute declaration, indicates that an object is an instanced attribute (no pointer)
+	Pointer,																		///< Used in attribute declaration, indicates that an object is a pointer attribute
+	Array,																			///< Used in attribute declaration, indicates that this is an array of objects
+		
+	// Basic types (primitives)
+	#define JPH_DECLARE_PRIMITIVE(name)	T_##name,
+
+	// This file uses the JPH_DECLARE_PRIMITIVE macro to define all types
+	#include <Jolt/ObjectStream/ObjectStreamTypes.h>
+
+	// Error values for read functions
+	Invalid,																		///< Next token on the stream was not a valid data type
+};
+
+/// Attributes are members of classes that need to be serialized.
+class SerializableAttribute
 {
 public:
+	///@ Serialization functions
+	using pGetMemberPrimitiveType = const RTTI * (*)();
+	using pIsType = bool (*)(int inArrayDepth, EOSDataType inDataType, const char *inClassName);
+	using pReadData = bool (*)(ObjectStreamIn &ioStream, void *inObject);
+	using pWriteData = void (*)(ObjectStreamOut &ioStream, const void *inObject);
+	using pWriteDataType = void (*)(ObjectStreamOut &ioStream);
+
 	/// Constructor
-	using RTTIAttribute::RTTIAttribute;
+								SerializableAttribute(const char *inName, uint inMemberOffset, pGetMemberPrimitiveType inGetMemberPrimitiveType, pIsType inIsType, pReadData inReadData, pWriteData inWriteData, pWriteDataType inWriteDataType) : mName(inName), mMemberOffset(inMemberOffset), mGetMemberPrimitiveType(inGetMemberPrimitiveType), mIsType(inIsType), mReadData(inReadData), mWriteData(inWriteData), mWriteDataType(inWriteDataType) { }
+
+	/// Construct from other attribute with base class offset
+								SerializableAttribute(const SerializableAttribute &inOther, int inBaseOffset) : mName(inOther.mName), mMemberOffset(inOther.mMemberOffset + inBaseOffset), mGetMemberPrimitiveType(inOther.mGetMemberPrimitiveType), mIsType(inOther.mIsType), mReadData(inOther.mReadData), mWriteData(inOther.mWriteData), mWriteDataType(inOther.mWriteDataType) { }
+
+	/// Name of the attribute
+	void						SetName(const char *inName)							{ mName = inName; }
+	const char *				GetName() const										{ return mName; }
+
+	/// In case this attribute contains an RTTI type, return it (note that a vector<sometype> will return the rtti of sometype)
+	const RTTI *				GetMemberPrimitiveType() const
+	{
+		return mGetMemberPrimitiveType();
+	}
+
+	/// Check if this attribute is of a specific type
+	bool						IsType(int inArrayDepth, EOSDataType inDataType, const char *inClassName) const
+	{
+		return mIsType(inArrayDepth, inDataType, inClassName);
+	}
+
+	/// Read the data for this attribute into attribute containing class inObject
+	bool						ReadData(ObjectStreamIn &ioStream, void *inObject) const
+	{
+		return mReadData(ioStream, reinterpret_cast<uint8 *>(inObject) + mMemberOffset);
+	}
+
+	/// Write the data for this attribute from attribute containing class inObject
+	void						WriteData(ObjectStreamOut &ioStream, const void *inObject) const
+	{
+		mWriteData(ioStream, reinterpret_cast<const uint8 *>(inObject) + mMemberOffset);
+	}
+
+	/// Write the data type of this attribute to a stream
+	void						WriteDataType(ObjectStreamOut &ioStream) const
+	{
+		mWriteDataType(ioStream);
+	}
+
+private:
+	// Name of the attribute
+	const char *				mName;
+
+	// Offset of the member relative to the class
+	uint						mMemberOffset;
+
+	// In case this attribute contains an RTTI type, return it (note that a vector<sometype> will return the rtti of sometype)
+	pGetMemberPrimitiveType		mGetMemberPrimitiveType;
 
-	///@name Serialization operations
-	virtual bool				IsType(int inArrayDepth, ObjectStream::EDataType inDataType, const char *inClassName) const = 0;
-	virtual bool				ReadData(ObjectStreamIn &ioStream, void *inObject) const = 0;
-	virtual void				WriteData(ObjectStreamOut &ioStream, const void *inObject) const = 0;
-	virtual void				WriteDataType(ObjectStreamOut &ioStream) const = 0;
+	// Serialization operations
+	pIsType						mIsType;
+	pReadData					mReadData;
+	pWriteData					mWriteData;
+	pWriteDataType				mWriteDataType;
 };
 
 JPH_NAMESPACE_END

+ 35 - 52
Jolt/ObjectStream/SerializableAttributeEnum.h

@@ -4,68 +4,51 @@
 #pragma once
 
 #include <Jolt/ObjectStream/SerializableAttribute.h>
+#include <Jolt/ObjectStream/ObjectStreamIn.h>
+#include <Jolt/ObjectStream/ObjectStreamOut.h>
 
 JPH_NAMESPACE_BEGIN
 
-/// Contains an serialize attribute of type enum
-template <class Class, class T>
-class SerializableAttributeEnum : public SerializableAttribute
-{
-public:
-	/// Constructor
-								SerializableAttributeEnum(T Class::*inMember, const char *inName)			: SerializableAttribute(inName), mMember(inMember) { }
-
-	///@name Serialization operations
-	virtual const void *		GetMemberPointer(const void *inObject) const override
-	{
-		return &(((const Class *)inObject)->*mMember);
-	}
-
-	virtual bool				IsType(int inArrayDepth, ObjectStream::EDataType inDataType, const char *inClassName) const override
-	{
-		return (inArrayDepth == 0 && inDataType == ObjectStream::EDataType::T_uint32);
-	}
-
-	virtual bool				ReadData(ObjectStreamIn &ioStream, void *inObject) const override
-	{
-		uint32 temporary;
-		if (OSReadData(ioStream, temporary)) 
-		{
-			((Class *)inObject)->*mMember = (T)temporary;
-			return true;
-		}
-
-		return false;
-	}
-
-	virtual void				WriteData(ObjectStreamOut &ioStream, const void *inObject) const override
-	{
-		static_assert(sizeof(T) <= sizeof(uint32));
-		uint32 temporary = uint32(((const Class *)inObject)->*mMember);
-		OSWriteData(ioStream, temporary);
-	}
-
-	virtual void				WriteDataType(ObjectStreamOut &ioStream) const override
-	{
-		ioStream.WriteDataType(ObjectStream::EDataType::T_uint32);
-	}
-
-private:
-	T Class::*					mMember;
-};
-
 //////////////////////////////////////////////////////////////////////////////////////////
 // Macros to add properties to be serialized
 //////////////////////////////////////////////////////////////////////////////////////////
 
-template <class Class, class T>
-inline void AddSerializableAttributeEnum(RTTI &inRTTI, T Class::*inMember, const char *inName)
+template <class MemberType>
+inline void AddSerializableAttributeEnum(RTTI &inRTTI, uint inOffset, const char *inName)
 {
-	inRTTI.AddAttribute(new SerializableAttributeEnum<Class, T>(inMember, inName));
+	inRTTI.AddAttribute(SerializableAttribute(inName, inOffset,
+		[]() -> const RTTI *
+		{ 
+			return nullptr;
+		},
+		[](int inArrayDepth, EOSDataType inDataType, [[maybe_unused]] const char *inClassName)
+		{
+			return inArrayDepth == 0 && inDataType == EOSDataType::T_uint32;
+		},
+		[](ObjectStreamIn &ioStream, void *inObject)
+		{
+			uint32 temporary;
+			if (OSReadData(ioStream, temporary)) 
+			{
+				*reinterpret_cast<MemberType *>(inObject) = static_cast<MemberType>(temporary);
+				return true;
+			}
+			return false;
+		},
+		[](ObjectStreamOut &ioStream, const void *inObject)
+		{
+			static_assert(sizeof(MemberType) <= sizeof(uint32));
+			uint32 temporary = uint32(*reinterpret_cast<const MemberType *>(inObject));
+			OSWriteData(ioStream, temporary);
+		},
+		[](ObjectStreamOut &ioStream)
+		{
+			ioStream.WriteDataType(EOSDataType::T_uint32);
+		}));
 }
 
 // JPH_ADD_ENUM_ATTRIBUTE
-#define JPH_ADD_ENUM_ATTRIBUTE(class_name, member_name)																\
-								AddSerializableAttributeEnum(inRTTI, &class_name::member_name, #member_name);
+#define JPH_ADD_ENUM_ATTRIBUTE(class_name, member_name) \
+	AddSerializableAttributeEnum<decltype(class_name::member_name)>(inRTTI, offsetof(class_name, member_name), #member_name);
 
 JPH_NAMESPACE_END

+ 27 - 48
Jolt/ObjectStream/SerializableAttributeTyped.h

@@ -5,64 +5,43 @@
 
 #include <Jolt/ObjectStream/SerializableAttribute.h>
 #include <Jolt/ObjectStream/GetPrimitiveTypeOfType.h>
+#include <Jolt/ObjectStream/ObjectStreamIn.h>
+#include <Jolt/ObjectStream/ObjectStreamOut.h>
 
 JPH_NAMESPACE_BEGIN
 
-/// Contains a serializable attribute of any type (except enum)
-template <class Class, class T>
-class SerializableAttributeTyped : public SerializableAttribute
-{
-public:
-	/// Constructor
-								SerializableAttributeTyped(T Class::*inMember, const char *inName)			: SerializableAttribute(inName), mMember(inMember) { }
-
-	///@name Serialization operations
-	virtual const RTTI *		GetMemberPrimitiveType() const override
-	{ 
-		return GetPrimitiveTypeOfType((T *)nullptr);
-	}
-
-	virtual const void *		GetMemberPointer(const void *inObject) const override
-	{
-		return &(((const Class *)inObject)->*mMember);
-	}
-
-	virtual bool				IsType(int inArrayDepth, ObjectStream::EDataType inDataType, const char *inClassName) const override
-	{
-		return OSIsType((T *)nullptr, inArrayDepth, inDataType, inClassName);
-	}
-
-	virtual bool				ReadData(ObjectStreamIn &ioStream, void *inObject) const override
-	{
-		return OSReadData(ioStream, ((Class *)inObject)->*mMember);
-	}
-
-	virtual void				WriteData(ObjectStreamOut &ioStream, const void *inObject) const override
-	{
-		OSWriteData(ioStream, ((const Class *)inObject)->*mMember);
-	}
-
-	virtual void				WriteDataType(ObjectStreamOut &ioStream) const override
-	{
-		OSWriteDataType(ioStream, (T *)nullptr);
-	}
-
-private:
-	T Class::*					mMember;
-};
-
 //////////////////////////////////////////////////////////////////////////////////////////
 // Macros to add properties to be serialized
 //////////////////////////////////////////////////////////////////////////////////////////
 
-template <class Class, class T>
-inline void AddSerializableAttributeTyped(RTTI &inRTTI, T Class::*inMember, const char *inName)
+template <class MemberType>
+inline void AddSerializableAttributeTyped(RTTI &inRTTI, uint inOffset, const char *inName)
 {
-	inRTTI.AddAttribute(new SerializableAttributeTyped<Class, T>(inMember, inName));
+	inRTTI.AddAttribute(SerializableAttribute(inName, inOffset,
+		[]()
+		{ 
+			return GetPrimitiveTypeOfType((MemberType *)nullptr);
+		},
+		[](int inArrayDepth, EOSDataType inDataType, const char *inClassName)
+		{
+			return OSIsType((MemberType *)nullptr, inArrayDepth, inDataType, inClassName);
+		},
+		[](ObjectStreamIn &ioStream, void *inObject)
+		{
+			return OSReadData(ioStream, *reinterpret_cast<MemberType *>(inObject));
+		},
+		[](ObjectStreamOut &ioStream, const void *inObject)
+		{
+			OSWriteData(ioStream, *reinterpret_cast<const MemberType *>(inObject));
+		},
+		[](ObjectStreamOut &ioStream)
+		{
+			OSWriteDataType(ioStream, (MemberType *)nullptr);
+		}));
 }
 
 // JPH_ADD_ATTRIBUTE
-#define JPH_ADD_ATTRIBUTE(class_name, member_name)																	\
-								AddSerializableAttributeTyped(inRTTI, &class_name::member_name, #member_name);
+#define JPH_ADD_ATTRIBUTE(class_name, member_name) \
+	AddSerializableAttributeTyped<decltype(class_name::member_name)>(inRTTI, offsetof(class_name, member_name), #member_name);
 
 JPH_NAMESPACE_END

+ 10 - 9
Jolt/ObjectStream/SerializableObject.h

@@ -3,7 +3,8 @@
 
 #pragma once
 
-#include <Jolt/ObjectStream/SerializableAttribute.h>
+#include <Jolt/ObjectStream/ObjectStreamIn.h>
+#include <Jolt/ObjectStream/ObjectStreamOut.h>
 
 JPH_NAMESPACE_BEGIN
 
@@ -15,8 +16,8 @@ JPH_NAMESPACE_BEGIN
 #define JPH_DECLARE_SERIALIZATION_FUNCTIONS(prefix, class_name)														\
 	prefix bool			OSReadData(ObjectStreamIn &ioStream, class_name &inInstance);								\
 	prefix bool			OSReadData(ObjectStreamIn &ioStream, class_name *&inPointer);								\
-	prefix bool			OSIsType(class_name *, int inArrayDepth, ObjectStream::EDataType inDataType, const char *inClassName); \
-	prefix bool			OSIsType(class_name **, int inArrayDepth, ObjectStream::EDataType inDataType, const char *inClassName); \
+	prefix bool			OSIsType(class_name *, int inArrayDepth, EOSDataType inDataType, const char *inClassName);	\
+	prefix bool			OSIsType(class_name **, int inArrayDepth, EOSDataType inDataType, const char *inClassName);	\
 	prefix void			OSWriteData(ObjectStreamOut &ioStream, const class_name &inInstance);						\
 	prefix void			OSWriteData(ObjectStreamOut &ioStream, class_name *const &inPointer);						\
 	prefix void			OSWriteDataType(ObjectStreamOut &ioStream, class_name *);									\
@@ -32,13 +33,13 @@ JPH_NAMESPACE_BEGIN
 	{																												\
 		return ioStream.ReadPointerData(JPH_RTTI(class_name), (void **)&inPointer);									\
 	}																												\
-	bool				OSIsType(class_name *, int inArrayDepth, ObjectStream::EDataType inDataType, const char *inClassName) \
+	bool				OSIsType(class_name *, int inArrayDepth, EOSDataType inDataType, const char *inClassName)	\
 	{																												\
-		return inArrayDepth == 0 && inDataType == ObjectStream::EDataType::Instance && strcmp(inClassName, #class_name) == 0; \
+		return inArrayDepth == 0 && inDataType == EOSDataType::Instance && strcmp(inClassName, #class_name) == 0;	\
 	}																												\
-	bool				OSIsType(class_name **, int inArrayDepth, ObjectStream::EDataType inDataType, const char *inClassName) \
+	bool				OSIsType(class_name **, int inArrayDepth, EOSDataType inDataType, const char *inClassName)	\
 	{																												\
-		return inArrayDepth == 0 && inDataType == ObjectStream::EDataType::Pointer && strcmp(inClassName, #class_name) == 0; \
+		return inArrayDepth == 0 && inDataType == EOSDataType::Pointer && strcmp(inClassName, #class_name) == 0;	\
 	}																												\
 	void				OSWriteData(ObjectStreamOut &ioStream, const class_name &inInstance)						\
 	{																												\
@@ -53,12 +54,12 @@ JPH_NAMESPACE_BEGIN
 	}																												\
 	void				OSWriteDataType(ObjectStreamOut &ioStream, class_name *)									\
 	{																												\
-		ioStream.WriteDataType(ObjectStream::EDataType::Instance);													\
+		ioStream.WriteDataType(EOSDataType::Instance);																\
 		ioStream.WriteName(#class_name);																			\
 	}																												\
 	void				OSWriteDataType(ObjectStreamOut &ioStream, class_name **)									\
 	{																												\
-		ioStream.WriteDataType(ObjectStream::EDataType::Pointer);													\
+		ioStream.WriteDataType(EOSDataType::Pointer);																\
 		ioStream.WriteName(#class_name);																			\
 	}
 

+ 1 - 1
Jolt/Physics/Collision/GroupFilter.cpp

@@ -39,7 +39,7 @@ GroupFilter::GroupFilterResult GroupFilter::sRestoreFromBinaryState(StreamIn &in
 	}
 
 	// Get the RTTI for the group filter
-	const RTTI *rtti = Factory::sInstance.Find(hash);
+	const RTTI *rtti = Factory::sInstance->Find(hash);
 	if (rtti == nullptr)
 	{
 		result.SetError("Failed to create instance of group filter");

+ 1 - 1
Jolt/Physics/Collision/PhysicsMaterial.cpp

@@ -42,7 +42,7 @@ PhysicsMaterial::PhysicsMaterialResult PhysicsMaterial::sRestoreFromBinaryState(
 	}
 
 	// Get the RTTI for the material
-	const RTTI *rtti = Factory::sInstance.Find(hash);
+	const RTTI *rtti = Factory::sInstance->Find(hash);
 	if (rtti == nullptr)
 	{
 		result.SetError("Failed to create instance of material");

+ 1 - 1
Jolt/Physics/Constraints/Constraint.cpp

@@ -45,7 +45,7 @@ ConstraintSettings::ConstraintResult ConstraintSettings::sRestoreFromBinaryState
 	}
 
 	// Get the RTTI for the shape
-	const RTTI *rtti = Factory::sInstance.Find(hash);
+	const RTTI *rtti = Factory::sInstance->Find(hash);
 	if (rtti == nullptr)
 	{
 		result.SetError("Failed to resolve type. Type not registered in factory?");

+ 1 - 1
Jolt/Physics/Constraints/PathConstraintPath.cpp

@@ -89,7 +89,7 @@ PathConstraintPath::PathResult PathConstraintPath::sRestoreFromBinaryState(Strea
 	}
 
 	// Get the RTTI for the shape
-	const RTTI *rtti = Factory::sInstance.Find(hash);
+	const RTTI *rtti = Factory::sInstance->Find(hash);
 	if (rtti == nullptr)
 	{
 		result.SetError("Failed to resolve type. Type not registered in factory?");

+ 1 - 1
Jolt/Physics/Vehicle/VehicleConstraint.cpp

@@ -69,7 +69,7 @@ void VehicleConstraintSettings::RestoreBinaryState(StreamIn &inStream)
 
 	uint32 hash = 0;
 	inStream.Read(hash);
-	const RTTI *rtti = Factory::sInstance.Find(hash);
+	const RTTI *rtti = Factory::sInstance->Find(hash);
 	mController = reinterpret_cast<VehicleControllerSettings *>(rtti->CreateObject());
 	mController->RestoreBinaryState(inStream);
 }

+ 45 - 37
Jolt/RegisterTypes.cpp

@@ -63,6 +63,8 @@ JPH_NAMESPACE_BEGIN
 
 void RegisterTypes()
 {
+	JPH_ASSERT(Factory::sInstance != nullptr, "Need to create a factory first!");
+
 	// Register base classes first so that we can specialize them later
 	CompoundShape::sRegister();
 	ConvexShape::sRegister();
@@ -87,43 +89,49 @@ void RegisterTypes()
 	OffsetCenterOfMassShape::sRegister();
 	ScaledShape::sRegister();
 
-	Factory::sInstance.Register(JPH_RTTI(SkeletalAnimation));
-	Factory::sInstance.Register(JPH_RTTI(Skeleton));
-	Factory::sInstance.Register(JPH_RTTI(CompoundShapeSettings));
-	Factory::sInstance.Register(JPH_RTTI(StaticCompoundShapeSettings));
-	Factory::sInstance.Register(JPH_RTTI(MutableCompoundShapeSettings));
-	Factory::sInstance.Register(JPH_RTTI(TriangleShapeSettings));
-	Factory::sInstance.Register(JPH_RTTI(SphereShapeSettings));
-	Factory::sInstance.Register(JPH_RTTI(BoxShapeSettings));
-	Factory::sInstance.Register(JPH_RTTI(CapsuleShapeSettings));
-	Factory::sInstance.Register(JPH_RTTI(TaperedCapsuleShapeSettings));
-	Factory::sInstance.Register(JPH_RTTI(CylinderShapeSettings));
-	Factory::sInstance.Register(JPH_RTTI(ScaledShapeSettings));
-	Factory::sInstance.Register(JPH_RTTI(MeshShapeSettings));
-	Factory::sInstance.Register(JPH_RTTI(ConvexHullShapeSettings));
-	Factory::sInstance.Register(JPH_RTTI(HeightFieldShapeSettings));
-	Factory::sInstance.Register(JPH_RTTI(RotatedTranslatedShapeSettings));
-	Factory::sInstance.Register(JPH_RTTI(OffsetCenterOfMassShapeSettings));
-	Factory::sInstance.Register(JPH_RTTI(RagdollSettings));
-	Factory::sInstance.Register(JPH_RTTI(PointConstraintSettings));
-	Factory::sInstance.Register(JPH_RTTI(SixDOFConstraintSettings));
-	Factory::sInstance.Register(JPH_RTTI(SliderConstraintSettings));
-	Factory::sInstance.Register(JPH_RTTI(SwingTwistConstraintSettings));
-	Factory::sInstance.Register(JPH_RTTI(DistanceConstraintSettings));
-	Factory::sInstance.Register(JPH_RTTI(HingeConstraintSettings));
-	Factory::sInstance.Register(JPH_RTTI(FixedConstraintSettings));
-	Factory::sInstance.Register(JPH_RTTI(ConeConstraintSettings));
-	Factory::sInstance.Register(JPH_RTTI(PathConstraintSettings));
-	Factory::sInstance.Register(JPH_RTTI(VehicleConstraintSettings));
-	Factory::sInstance.Register(JPH_RTTI(WheeledVehicleControllerSettings));
-	Factory::sInstance.Register(JPH_RTTI(PathConstraintPath));
-	Factory::sInstance.Register(JPH_RTTI(PathConstraintPathHermite));
-	Factory::sInstance.Register(JPH_RTTI(MotorSettings));
-	Factory::sInstance.Register(JPH_RTTI(PhysicsScene));
-	Factory::sInstance.Register(JPH_RTTI(PhysicsMaterial));
-	Factory::sInstance.Register(JPH_RTTI(PhysicsMaterialSimple));
-	Factory::sInstance.Register(JPH_RTTI(GroupFilter));
-	Factory::sInstance.Register(JPH_RTTI(GroupFilterTable));
+	// Create list of all types
+	const RTTI *types[] = {
+		JPH_RTTI(SkeletalAnimation),
+		JPH_RTTI(Skeleton),
+		JPH_RTTI(CompoundShapeSettings),
+		JPH_RTTI(StaticCompoundShapeSettings),
+		JPH_RTTI(MutableCompoundShapeSettings),
+		JPH_RTTI(TriangleShapeSettings),
+		JPH_RTTI(SphereShapeSettings),
+		JPH_RTTI(BoxShapeSettings),
+		JPH_RTTI(CapsuleShapeSettings),
+		JPH_RTTI(TaperedCapsuleShapeSettings),
+		JPH_RTTI(CylinderShapeSettings),
+		JPH_RTTI(ScaledShapeSettings),
+		JPH_RTTI(MeshShapeSettings),
+		JPH_RTTI(ConvexHullShapeSettings),
+		JPH_RTTI(HeightFieldShapeSettings),
+		JPH_RTTI(RotatedTranslatedShapeSettings),
+		JPH_RTTI(OffsetCenterOfMassShapeSettings),
+		JPH_RTTI(RagdollSettings),
+		JPH_RTTI(PointConstraintSettings),
+		JPH_RTTI(SixDOFConstraintSettings),
+		JPH_RTTI(SliderConstraintSettings),
+		JPH_RTTI(SwingTwistConstraintSettings),
+		JPH_RTTI(DistanceConstraintSettings),
+		JPH_RTTI(HingeConstraintSettings),
+		JPH_RTTI(FixedConstraintSettings),
+		JPH_RTTI(ConeConstraintSettings),
+		JPH_RTTI(PathConstraintSettings),
+		JPH_RTTI(VehicleConstraintSettings),
+		JPH_RTTI(WheeledVehicleControllerSettings),
+		JPH_RTTI(PathConstraintPath),
+		JPH_RTTI(PathConstraintPathHermite),
+		JPH_RTTI(MotorSettings),
+		JPH_RTTI(PhysicsScene),
+		JPH_RTTI(PhysicsMaterial),
+		JPH_RTTI(PhysicsMaterialSimple),
+		JPH_RTTI(GroupFilter),
+		JPH_RTTI(GroupFilterTable)
+	};
+
+	// Register them all
+	Factory::sInstance->Register(types, (uint)size(types));
 }
 
 JPH_NAMESPACE_END

+ 8 - 0
PerformanceTest/PerformanceTest.cpp

@@ -4,6 +4,7 @@
 // Jolt includes
 #include <Jolt/Jolt.h>
 #include <Jolt/RegisterTypes.h>
+#include <Jolt/Core/Factory.h>
 #include <Jolt/Core/TempAllocator.h>
 #include <Jolt/Core/JobSystemThreadPool.h>
 #include <Jolt/Physics/PhysicsSettings.h>
@@ -137,6 +138,9 @@ int main(int argc, char** argv)
 	// Install callbacks
 	Trace = TraceImpl;
 
+	// Create a factory
+	Factory::sInstance = new Factory();
+
 	// Register all Jolt physics types
 	RegisterTypes();
 
@@ -299,6 +303,10 @@ int main(int argc, char** argv)
 	NarrowPhaseStat::sReportStats();
 #endif // JPH_TRACK_NARROWPHASE_STATS
 
+	// Destroy the factory
+	delete Factory::sInstance;
+	Factory::sInstance = nullptr;
+
 	// End profiling this thread
 	JPH_PROFILE_THREAD_END();
 

+ 6 - 0
TestFramework/Application/Application.cpp

@@ -8,6 +8,7 @@
 #include <Application/DebugUI.h>
 #include <Utils/Log.h>
 #include <Jolt/Core/FPException.h>
+#include <Jolt/Core/Factory.h>
 #include <Jolt/RegisterTypes.h>
 #include <Renderer/DebugRendererImp.h>
 
@@ -37,6 +38,9 @@ Application::Application() :
 	};
 #endif // JPH_ENABLE_ASSERTS
 
+	// Create factory
+	Factory::sInstance = new Factory;
+
 	// Register physics types with the factory
 	RegisterTypes();
 	
@@ -81,6 +85,8 @@ Application::~Application()
 	delete mDebugRenderer;
 	mFont = nullptr;
 	delete mRenderer;
+	delete Factory::sInstance;
+	Factory::sInstance = nullptr;
 }
 
 // Clear debug lines / triangles / texts that have been accumulated

+ 59 - 26
UnitTests/ObjectStream/ObjectStreamTest.cpp

@@ -29,6 +29,7 @@ public:
 	uint64						mUInt64 = 0;
 	float						mFloat = 0;
 	bool						mBool = false;
+	Float3						mFloat3 = { };
 	Quat						mQuat = Quat::sIdentity();
 	Vec3						mVec3 = Vec3::sZero();
 	Vec4						mVec4 = Vec4::sZero();
@@ -36,7 +37,17 @@ public:
 	string						mString;
 };
 
-class TestSerializable : public TestSerializableBase
+class TestSerializableBase2
+{
+	JPH_DECLARE_SERIALIZABLE_VIRTUAL_BASE(TestSerializableBase2)
+
+public:
+	virtual						~TestSerializableBase2() = default;
+
+	uint32						mBase2 = 0;
+};
+
+class TestSerializable : public TestSerializableBase, public TestSerializableBase2
 {
 	JPH_DECLARE_SERIALIZABLE_VIRTUAL(TestSerializable)
 
@@ -45,6 +56,8 @@ public:
 	vector<int>					mIntVector;
 	StaticArray<bool, 10>		mBoolVector;
 	float						mFloatVector[3] = { 0, 0, 0 };
+	vector<float>				mArrayOfVector[3];
+	vector<vector<int>>			mVectorOfVector;
 	TestSerializable *			mPointer = nullptr;
 	Ref<TestSerializable>		mReference;
 	RefConst<TestSerializable>	mReferenceConst;
@@ -52,28 +65,37 @@ public:
 
 JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(TestSerializableBase)
 {
-	JPH_ADD_ATTRIBUTE(TestSerializable, mUInt8)
-	JPH_ADD_ATTRIBUTE(TestSerializable, mUInt16)
-	JPH_ADD_ATTRIBUTE(TestSerializable, mInt)
-	JPH_ADD_ATTRIBUTE(TestSerializable, mUInt32)
-	JPH_ADD_ATTRIBUTE(TestSerializable, mUInt64)
-	JPH_ADD_ATTRIBUTE(TestSerializable, mFloat)
-	JPH_ADD_ATTRIBUTE(TestSerializable, mBool)
-	JPH_ADD_ATTRIBUTE(TestSerializable, mQuat)
-	JPH_ADD_ATTRIBUTE(TestSerializable, mVec3)
-	JPH_ADD_ATTRIBUTE(TestSerializable, mVec4)
-	JPH_ADD_ATTRIBUTE(TestSerializable, mMat44)
-	JPH_ADD_ATTRIBUTE(TestSerializable, mString)
+	JPH_ADD_ATTRIBUTE(TestSerializableBase, mUInt8)
+	JPH_ADD_ATTRIBUTE(TestSerializableBase, mUInt16)
+	JPH_ADD_ATTRIBUTE(TestSerializableBase, mInt)
+	JPH_ADD_ATTRIBUTE(TestSerializableBase, mUInt32)
+	JPH_ADD_ATTRIBUTE(TestSerializableBase, mUInt64)
+	JPH_ADD_ATTRIBUTE(TestSerializableBase, mFloat)
+	JPH_ADD_ATTRIBUTE(TestSerializableBase, mBool)
+	JPH_ADD_ATTRIBUTE(TestSerializableBase, mFloat3)
+	JPH_ADD_ATTRIBUTE(TestSerializableBase, mQuat)
+	JPH_ADD_ATTRIBUTE(TestSerializableBase, mVec3)
+	JPH_ADD_ATTRIBUTE(TestSerializableBase, mVec4)
+	JPH_ADD_ATTRIBUTE(TestSerializableBase, mMat44)
+	JPH_ADD_ATTRIBUTE(TestSerializableBase, mString)
+}
+
+JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(TestSerializableBase2)
+{
+	JPH_ADD_ATTRIBUTE(TestSerializableBase2, mBase2)
 }
 
 JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(TestSerializable)
 {
 	JPH_ADD_BASE_CLASS(TestSerializable, TestSerializableBase)
+	JPH_ADD_BASE_CLASS(TestSerializable, TestSerializableBase2)
 
 	JPH_ADD_ENUM_ATTRIBUTE(TestSerializable, mEnum)
 	JPH_ADD_ATTRIBUTE(TestSerializable, mIntVector)
 	JPH_ADD_ATTRIBUTE(TestSerializable, mBoolVector)
 	JPH_ADD_ATTRIBUTE(TestSerializable, mFloatVector)
+	JPH_ADD_ATTRIBUTE(TestSerializable, mArrayOfVector)
+	JPH_ADD_ATTRIBUTE(TestSerializable, mVectorOfVector)
 	JPH_ADD_ATTRIBUTE(TestSerializable, mPointer)
 	JPH_ADD_ATTRIBUTE(TestSerializable, mReference)
 	JPH_ADD_ATTRIBUTE(TestSerializable, mReferenceConst)
@@ -91,21 +113,25 @@ TEST_SUITE("ObjectStreamTest")
 		test->mUInt64 = 0xf5f6f7f8f9fafbfc;
 		test->mFloat = 0.12345f;
 		test->mBool = true;
+		test->mFloat3 = Float3(9, 10, 11);
 		test->mVec3 = Vec3(6, 7, 8);
 		test->mVec4 = Vec4(9, 10, 11, 12);
 		test->mQuat = Quat::sRotation(Vec3::sAxisX(), 0.1234f);
 		test->mMat44 = Mat44::sRotationTranslation(Quat::sRotation(Vec3::sAxisY(), 0.4567f), Vec3(13, 14, 15));
 		test->mString = "\"test string\"";
 		test->mEnum = B;
-		test->mIntVector.push_back(1);
-		test->mIntVector.push_back(2);
-		test->mIntVector.push_back(3);
+		test->mIntVector = { 1, 2, 3, 4, 5 };
 		test->mBoolVector.push_back(true);
 		test->mBoolVector.push_back(false);
 		test->mBoolVector.push_back(true);
 		test->mFloatVector[0] = 1.0f;
 		test->mFloatVector[1] = 2.0f;
 		test->mFloatVector[2] = 3.0f;
+		test->mArrayOfVector[0] = { 1, 2, 3 };
+		test->mArrayOfVector[1] = { 4, 5 };
+		test->mArrayOfVector[2] = { 6, 7, 8, 9 };
+		test->mVectorOfVector = { { 10, 11 }, { 12, 13, 14 }, { 15, 16, 17, 18 }};
+		test->mBase2 = 0x9876;
 
 		TestSerializable *test2 = new TestSerializable();
 		test2->mFloat = 4.5f;
@@ -125,24 +151,24 @@ TEST_SUITE("ObjectStreamTest")
 		CHECK(inInput->mUInt64 == inOutput->mUInt64);
 		CHECK(inInput->mFloat == inOutput->mFloat);
 		CHECK(inInput->mBool == inOutput->mBool);
+		CHECK(inInput->mFloat3 == inOutput->mFloat3);
 		CHECK(inInput->mQuat == inOutput->mQuat);
 		CHECK(inInput->mVec3 == inOutput->mVec3);
 		CHECK(inInput->mVec4 == inOutput->mVec4);
 		CHECK(inInput->mMat44 == inOutput->mMat44);
 		CHECK(inInput->mString == inOutput->mString);
 		CHECK(inInput->mEnum == inOutput->mEnum);
-
-		CHECK(inInput->mIntVector.size() == inOutput->mIntVector.size());
-		for (size_t i = 0; i < min(inInput->mIntVector.size(), inOutput->mIntVector.size()); ++i)
-			CHECK(inInput->mIntVector[i] == inOutput->mIntVector[i]);
-
-		CHECK(inInput->mBoolVector.size() == inOutput->mBoolVector.size());
-		for (uint32 i = 0; i < min(inInput->mBoolVector.size(), inOutput->mBoolVector.size()); ++i)
-			CHECK(inInput->mBoolVector[i] == inOutput->mBoolVector[i]);
+		CHECK(inInput->mIntVector == inOutput->mIntVector);
+		CHECK(inInput->mBoolVector == inOutput->mBoolVector);
 
 		for (uint32 i = 0; i < size(inInput->mFloatVector); ++i)
 			CHECK(inInput->mFloatVector[i] == inOutput->mFloatVector[i]);
 
+		for (uint32 i = 0; i < size(inInput->mArrayOfVector); ++i)
+			CHECK(inInput->mArrayOfVector[i] == inOutput->mArrayOfVector[i]);
+
+		CHECK(inInput->mVectorOfVector == inOutput->mVectorOfVector);
+
 		CHECK(inOutput->mPointer == inOutput->mReference);
 		CHECK(inOutput->mPointer == inOutput->mReferenceConst);
 
@@ -155,11 +181,13 @@ TEST_SUITE("ObjectStreamTest")
 			CHECK(inOutput->mReference->GetRefCount() == uint32(2));
 			CHECK(inOutput->mReferenceConst->GetRefCount() == uint32(2));
 		}
+
+		CHECK(inInput->mBase2 == inOutput->mBase2);
 	}
 
 	TEST_CASE("TestObjectStreamLoadSaveText")
 	{
-		Factory::sInstance.Register(JPH_RTTI(TestSerializable));
+		Factory::sInstance->Register(JPH_RTTI(TestSerializable));
 
 		TestSerializable *test = CreateTestObject();
 
@@ -169,6 +197,11 @@ TEST_SUITE("ObjectStreamTest")
 		TestSerializable *test_out = nullptr;
 		REQUIRE(ObjectStreamIn::sReadObject(stream, test_out));
 
+		// Check that DynamicCast returns the right offsets
+		CHECK(DynamicCast<TestSerializable>(test_out) == test_out);
+		CHECK(DynamicCast<TestSerializableBase>(test_out) == static_cast<TestSerializableBase *>(test_out));
+		CHECK(DynamicCast<TestSerializableBase2>(test_out) == static_cast<TestSerializableBase2 *>(test_out));
+
 		CompareObjects(test, test_out);
 
 		delete test;
@@ -177,7 +210,7 @@ TEST_SUITE("ObjectStreamTest")
 
 	TEST_CASE("TestObjectStreamLoadSaveBinary")
 	{
-		Factory::sInstance.Register(JPH_RTTI(TestSerializable));
+		Factory::sInstance->Register(JPH_RTTI(TestSerializable));
 
 		TestSerializable *test = CreateTestObject();
 

+ 28 - 2
UnitTests/UnitTestFramework.cpp

@@ -5,6 +5,7 @@
 
 #include <cstdarg>
 #include <Jolt/Core/FPException.h>
+#include <Jolt/Core/Factory.h>
 #include <Jolt/RegisterTypes.h>
 #ifdef JPH_PLATFORM_ANDROID
 #include <Jolt/Core/Color.h>
@@ -73,10 +74,19 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine,
 	FPExceptionsEnable enable_exceptions;
 	JPH_UNUSED(enable_exceptions);
 
+	// Create a factory
+	Factory::sInstance = new Factory();
+
 	// Register physics types
 	RegisterTypes();
 
-	return Context().run(); 
+	int rv = Context().run();
+
+	// Destroy the factory
+	delete Factory::sInstance;
+	Factory::sInstance = nullptr;
+
+	return rv;
 }
 
 #elif !defined(JPH_PLATFORM_ANDROID)
@@ -97,10 +107,19 @@ int main(int argc, char** argv)
 	FPExceptionsEnable enable_exceptions;
 	JPH_UNUSED(enable_exceptions);
 
+	// Create a factory
+	Factory::sInstance = new Factory();
+
 	// Register physics types
 	RegisterTypes();
 
-	return Context(argc, argv).run(); 
+	int rv = Context(argc, argv).run(); 
+
+	// Destroy the factory
+	delete Factory::sInstance;
+	Factory::sInstance = nullptr;
+
+	return rv;
 }
 
 #else // !JPH_PLATFORM_ANDROID
@@ -153,6 +172,9 @@ void AndroidInitialize(android_app *inApp)
 	FPExceptionsEnable enable_exceptions;
 	JPH_UNUSED(enable_exceptions);
 
+	// Create a factory
+	Factory::sInstance = new Factory();
+
 	// Register physics types
 	RegisterTypes();
 
@@ -188,6 +210,10 @@ void AndroidInitialize(android_app *inApp)
 	}
 	ANativeWindow_unlockAndPost(inApp->window);
 	ANativeWindow_release(inApp->window);
+
+	// Destroy the factory
+	delete Factory::sInstance;
+	Factory::sInstance = nullptr;
 }
 
 // Handle callback from Android