added serial

This commit is contained in:
Kah Wei 2022-09-16 13:58:08 +08:00
parent b933b0f7fc
commit da582e0f1d
4 changed files with 531 additions and 2 deletions

View File

@ -0,0 +1,429 @@
/************************************************************************************//*!
\file ReflectionUtilities.cxx
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Nov 6, 2021
\brief Contains the definition of the functions for the ReflectionUtilities
managed static class.
Note: This file is written in C++17/CLI.
Copyright (C) 2021 DigiPen Institute of Technology.
Reproduction or disclosure of this file or its contents without the prior written consent
of DigiPen Institute of Technology is prohibited.
*//*************************************************************************************/
// Precompiled Headers
#include "SHpch.h"
// Primary Header
#include "Serialisation/ReflectionUtilities.hxx"
// External Dependencies
// Project Includes
#include "SerializeFieldAttribute.hxx"
#include "Utility/Convert.hxx"
/*-------------------------------------------------------------------------------------*/
/* Macro Functions */
/*-------------------------------------------------------------------------------------*/
/// <summary>
/// Macro expansion that is used in FieldToRapidJsonValue() to check the type of a field
/// named "fieldInfo" against the specified type and if it matches, store said field
/// of an object named "object" it into rapidjson::Value called "jsonValue" as that
/// specified type.
/// </summary>
/// <param name="T">The type to check.</param>
#define PRIMITIVE_FIELD_CAST(T) \
(fieldInfo->FieldType == T::typeid) \
{ \
jsonValue = safe_cast<T>(fieldInfo->GetValue(object)); \
} \
/// <summary>
/// Macro expansion that is used in RapidJsonValueToField() to check the type of a field
/// named "fieldInfo" against the specified type and if it matches, retrieves the value
/// using the function FUNC provided from a rapidjson::Value called "jsonValue" and sets
/// the value of the field to the retrieved value.
/// </summary>
/// <param name="T">The type to check.</param>
/// <param name="FUNC">Member function of jsonValue to use to retrieve the data.</param>
#define PRIMITIVE_FIELD_ASSIGN(T, FUNC) \
(fieldInfo->FieldType == T::typeid) \
{ \
fieldInfo->SetValue(object, jsonValue.FUNC()); \
} \
/// <summary>
/// Macro expansion that is used in RapidJsonValueToField() to retrieve the specified
/// member of a Vector type that is stored into a Vector named "vec".
/// </summary>
/// <param name="MEMBER">The name of the member to retrieve.</param>
#define PRIMITIVE_VECTOR_FIELD_ASSIGN(MEMBER) \
iter = jsonValue.FindMember(#MEMBER); \
if (iter != jsonValue.MemberEnd()) \
{ \
vec.MEMBER = iter->value.GetDouble(); \
} \
/// <summary>
/// Macro expansion that is used in RapidJsonValueToField() to retrieve the specified
/// member of a Color type that is stored into a Color named "col".
/// </summary>
/// <param name="MEMBER">The name of the member to retrieve.</param>
#define PRIMITIVE_COLOR_FIELD_ASSIGN(MEMBER) \
iter = jsonValue.FindMember(#MEMBER); \
if (iter != jsonValue.MemberEnd()) \
{ \
col.MEMBER = iter->value.GetFloat(); \
} \
/// <summary>
/// Macro expansion that is used to write the name of a field named fieldInfo into a
/// rapidjson writer called writer.
/// </summary>
#define PRIMITIVE_WRITE_FIELD_NAME writer.String(Convert::ToNative(fieldInfo->Name).c_str());
/// <summary>
/// Macro expansion that is used in serialiseFieldJson() to check the type of a field
/// named "fieldInfo" against the specified type and if it matches, writes the value
/// using the function FUNC in to a rapidjson writer object named "writer".
/// </summary>
/// <param name="T">The type to check.</param>
/// <param name="FUNC">
/// Member function of the rapidjson writer object to use to write the data.
/// </param>
#define PRIMITIVE_FIELD_SERIALISE_JSON(T, FUNC) \
(fieldInfo->FieldType == T::typeid) \
{ \
PRIMITIVE_WRITE_FIELD_NAME \
writer.FUNC(safe_cast<T>(fieldInfo->GetValue(object))); \
} \
/// <summary>
/// Macro expansion that is used in serialiseFieldJson() to write the specified
/// member of a Vector named "vec" using the rapidjson writer named "writer".
/// </summary>
/// <param name="MEMBER">The name of the member to write.</param>
#define PRIMITIVE_VECTOR_FIELD_SERIALISE_JSON(MEMBER) \
writer.String(#MEMBER); \
writer.Double(vec.MEMBER); \
/*-------------------------------------------------------------------------------------*/
/* Function Definitions */
/*-------------------------------------------------------------------------------------*/
namespace SHADE
{
System::Collections::Generic::IEnumerable<System::Reflection::FieldInfo^>^ ReflectionUtilities::GetInstanceFields(System::Object^ object)
{
using namespace System::Reflection;
return object->GetType()->GetFields
(
BindingFlags::Public | BindingFlags::NonPublic | BindingFlags::Instance
);
}
bool ReflectionUtilities::FieldIsSerialisable(System::Reflection::FieldInfo^ fieldInfo)
{
return fieldInfo->IsPublic || fieldInfo->GetCustomAttributes(SerializeField::typeid, true)->Length > 0;
}
/*---------------------------------------------------------------------------------*/
/* Serialisation Functions */
/*---------------------------------------------------------------------------------*/
//void ReflectionUtilities::Serialise(System::Object^ object, YAML::Emitter& yaml)
//{
// // Create YAML object
// yaml << YAML::Key << Convert::ToNative(object->GetType().FullName);
// yaml << YAML::BeginMap;
// // Get all fields
// IEnumerable<FieldInfo^>^ fields = GetInstanceFields(object);
// for each (FieldInfo^ field in fields)
// {
// // Ignore private and non-SerialiseField
// if (!FieldIsSerialisable(field))
// continue;
// // Serialise
// rapidjson::Value jsonValue = FieldToRapidJsonValue(field, object, allocator);
// rapidjson::Value key(Convert::ToNative(field->Name).c_str(), allocator);
// scriptRoot.AddMember(key.Move(), jsonValue, allocator);
// }
// /* Add the Script JSON Object to the JSON root node*/
// rapidjson::Value key(Convert::ToNative(object->GetType()->FullName).c_str(), allocator);
// json.AddMember(key.Move(), scriptRoot, allocator);
// /* Prepare to send to the caller */
// // Convert to string
// rapidjson::StringBuffer buffer;
// rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
// json.Accept(writer);
// // Return the result
// return Convert::ToCLI(std::string(buffer.GetString()));
//}
//void ReflectionUtilities::Deserialise(System::String^ jsonString, Object^ object)
//{
// // Load the JSON
// rapidjson::Document json;
// auto& allocator = json.GetAllocator();
// json.Parse(Convert::ToNative(jsonString).c_str());
// // Get the root object
// auto scriptRoot = json.MemberBegin();
// if (scriptRoot == json.MemberEnd())
// {
// Pls::Debug::LogError("[ReflectionUtilities] Attempted read of corrupted serialised object.");
// return;
// }
//
// // Get all fields
// IEnumerable<FieldInfo^>^ fields = GetInstanceFields(object);
// for each (FieldInfo^ field in fields)
// {
// // Ignore private and non-SerialiseField
// if (!FieldIsSerialisable(field))
// continue;
// // Deserialise
// auto iter = scriptRoot->value.FindMember(Convert::ToNative(field->Name).c_str());
// if (iter != scriptRoot->value.MemberEnd())
// {
// // Get the data and set it in
// RapidJsonValueToField(iter->value, field, object);
// }
// }
//}
//void ReflectionUtilities::Serialise(Object^ object, JsonWriter& writer)
//{
// /* Script Name */
// writer.String(Convert::ToNative(object->GetType()->FullName).c_str());
// /* Script Contents */
// writer.StartObject();
// IEnumerable<FieldInfo^>^ fields = GetInstanceFields(object);
// for each (FieldInfo^ field in fields)
// {
// // Ignore private and non-SerialiseField
// if (!FieldIsSerialisable(field))
// continue;
// // Serialise
// serialiseFieldJson(field, object, writer);
// }
// writer.EndObject();
//}
///*---------------------------------------------------------------------------------*/
///* Serialisation Helper Functions */
///*---------------------------------------------------------------------------------*/
//rapidjson::Value ReflectionUtilities::FieldToRapidJsonValue(FieldInfo^ fieldInfo, Object^ object, JsonAllocator& allocator)
//{
// rapidjson::Value jsonValue;
// // Check each type of value and convert it into a rapidjson::Value
// if PRIMITIVE_FIELD_CAST(Int16)
// else if PRIMITIVE_FIELD_CAST(Int32)
// else if PRIMITIVE_FIELD_CAST(Int64)
// else if PRIMITIVE_FIELD_CAST(UInt16)
// else if PRIMITIVE_FIELD_CAST(UInt32)
// else if PRIMITIVE_FIELD_CAST(UInt64)
// else if PRIMITIVE_FIELD_CAST(Byte)
// else if PRIMITIVE_FIELD_CAST(bool)
// else if PRIMITIVE_FIELD_CAST(float)
// else if PRIMITIVE_FIELD_CAST(double)
// else if (fieldInfo->FieldType->IsSubclassOf(Enum::typeid))
// {
// jsonValue = safe_cast<int>(fieldInfo->GetValue(object));
// }
// else if (fieldInfo->FieldType == String::typeid)
// {
// String^ str = safe_cast<String^>(fieldInfo->GetValue(object));
// jsonValue.SetString(Convert::ToNative(str).c_str(), str->Length, allocator);
// }
// else if (fieldInfo->FieldType == Vector2::typeid)
// {
// jsonValue.SetObject();
// Vector2 vec = safe_cast<Vector2>(fieldInfo->GetValue(object));
// jsonValue.AddMember(rapidjson::Value("x"), rapidjson::Value(vec.x), allocator);
// jsonValue.AddMember(rapidjson::Value("y"), rapidjson::Value(vec.y), allocator);
// }
// else if (fieldInfo->FieldType == Vector3::typeid)
// {
// jsonValue.SetObject();
// Vector3 vec = safe_cast<Vector3>(fieldInfo->GetValue(object));
// jsonValue.AddMember(rapidjson::Value("x"), rapidjson::Value(vec.x), allocator);
// jsonValue.AddMember(rapidjson::Value("y"), rapidjson::Value(vec.y), allocator);
// jsonValue.AddMember(rapidjson::Value("z"), rapidjson::Value(vec.z), allocator);
// }
// else if (fieldInfo->FieldType == Color::typeid)
// {
// jsonValue.SetObject();
// Color col = safe_cast<Color>(fieldInfo->GetValue(object));
// jsonValue.AddMember(rapidjson::Value("r"), rapidjson::Value(col.r), allocator);
// jsonValue.AddMember(rapidjson::Value("g"), rapidjson::Value(col.g), allocator);
// jsonValue.AddMember(rapidjson::Value("b"), rapidjson::Value(col.b), allocator);
// jsonValue.AddMember(rapidjson::Value("a"), rapidjson::Value(col.a), allocator);
// }
// else if (IResourceID::typeid->IsAssignableFrom(fieldInfo->FieldType))
// {
// IResourceID^ rscId = safe_cast<IResourceID^>(fieldInfo->GetValue(object));
// jsonValue = rscId->Id;
// }
// else // Not any of the supported types
// {
// Pls::Debug::LogWarning(Convert::ToNative(String::Format
// (
// "[ReflectionUtilities] Failed to parse \"{0}\" of \"{1}\" type for serialisation.",
// fieldInfo->Name, fieldInfo->FieldType)
// ));
// }
// return jsonValue;
//}
//void ReflectionUtilities::RapidJsonValueToField(const rapidjson::Value& jsonValue, FieldInfo^ fieldInfo, Object^ object)
//{
// if PRIMITIVE_FIELD_ASSIGN(Int16, GetInt)
// else if PRIMITIVE_FIELD_ASSIGN(Int32, GetInt)
// else if PRIMITIVE_FIELD_ASSIGN(Int64, GetInt64)
// else if PRIMITIVE_FIELD_ASSIGN(UInt16, GetUint)
// else if PRIMITIVE_FIELD_ASSIGN(UInt32, GetUint)
// else if PRIMITIVE_FIELD_ASSIGN(UInt64, GetUint64)
// else if PRIMITIVE_FIELD_ASSIGN(Byte, GetInt)
// else if PRIMITIVE_FIELD_ASSIGN(bool, GetBool)
// else if PRIMITIVE_FIELD_ASSIGN(float, GetFloat)
// else if PRIMITIVE_FIELD_ASSIGN(double, GetDouble)
// else if (fieldInfo->FieldType->IsSubclassOf(Enum::typeid))
// {
// fieldInfo->SetValue(object, jsonValue.GetInt());
// }
// else if (fieldInfo->FieldType == String::typeid)
// {
// fieldInfo->SetValue(object, Convert::ToCLI(jsonValue.GetString()));
// }
// else if (fieldInfo->FieldType == Vector2::typeid)
// {
// Vector2 vec;
// auto iter = jsonValue.MemberEnd();
// PRIMITIVE_VECTOR_FIELD_ASSIGN(x)
// PRIMITIVE_VECTOR_FIELD_ASSIGN(y)
// fieldInfo->SetValue(object, vec);
// }
// else if (fieldInfo->FieldType == Vector3::typeid)
// {
// Vector3 vec;
// auto iter = jsonValue.MemberEnd();
// PRIMITIVE_VECTOR_FIELD_ASSIGN(x)
// PRIMITIVE_VECTOR_FIELD_ASSIGN(y)
// PRIMITIVE_VECTOR_FIELD_ASSIGN(z)
// fieldInfo->SetValue(object, vec);
// }
// else if (fieldInfo->FieldType == Color::typeid)
// {
// Color col;
// auto iter = jsonValue.MemberEnd();
// PRIMITIVE_COLOR_FIELD_ASSIGN(r)
// PRIMITIVE_COLOR_FIELD_ASSIGN(g)
// PRIMITIVE_COLOR_FIELD_ASSIGN(b)
// PRIMITIVE_COLOR_FIELD_ASSIGN(a)
// fieldInfo->SetValue(object, col);
// }
// else if (IResourceIDInternal::typeid->IsAssignableFrom(fieldInfo->FieldType))
// {
// IResourceIDInternal^ rsc = safe_cast<IResourceIDInternal^>(Activator::CreateInstance(fieldInfo->FieldType));
// rsc->Id = jsonValue.GetInt64();
// fieldInfo->SetValue(object, rsc);
// }
// else // Not any of the supported types
// {
// Pls::Debug::LogWarning(Convert::ToNative(String::Format
// (
// "[ReflectionUtilities] Failed to parse \"{0}\" of \"{1}\" type for deserialisation.",
// fieldInfo->Name, fieldInfo->FieldType)
// ));
// }
//}
///*---------------------------------------------------------------------------------*/
///* Internal Serialisation Helper Functions */
///*---------------------------------------------------------------------------------*/
//void ReflectionUtilities::serialiseFieldJson(FieldInfo^ fieldInfo, Object^ object, JsonWriter& writer)
//{
// if PRIMITIVE_FIELD_SERIALISE_JSON(Int16, Int)
// else if PRIMITIVE_FIELD_SERIALISE_JSON(Int32, Int)
// else if PRIMITIVE_FIELD_SERIALISE_JSON(Int64, Int64)
// else if PRIMITIVE_FIELD_SERIALISE_JSON(UInt16, Uint)
// else if PRIMITIVE_FIELD_SERIALISE_JSON(UInt32, Uint)
// else if PRIMITIVE_FIELD_SERIALISE_JSON(UInt64, Uint64)
// else if PRIMITIVE_FIELD_SERIALISE_JSON(Byte, Int)
// else if PRIMITIVE_FIELD_SERIALISE_JSON(bool, Bool)
// else if PRIMITIVE_FIELD_SERIALISE_JSON(float, Double)
// else if PRIMITIVE_FIELD_SERIALISE_JSON(double, Double)
// else if PRIMITIVE_FIELD_SERIALISE_JSON(double, Double)
// else if (fieldInfo->FieldType->IsSubclassOf(Enum::typeid))
// {
// PRIMITIVE_WRITE_FIELD_NAME
// writer.Int(safe_cast<Int32>(fieldInfo->GetValue(object)));
// }
// else if (fieldInfo->FieldType == String::typeid)
// {
// PRIMITIVE_WRITE_FIELD_NAME
// writer.String(Convert::ToNative(safe_cast<String^>(fieldInfo->GetValue(object))).c_str());
// }
// else if (fieldInfo->FieldType == Vector2::typeid)
// {
// // Get handle to the Vector to edit
// Vector2 vec = safe_cast<Vector2>(fieldInfo->GetValue(object));
// // Write the vector data
// PRIMITIVE_WRITE_FIELD_NAME
// writer.StartObject();
// {
// PRIMITIVE_VECTOR_FIELD_SERIALISE_JSON(x)
// PRIMITIVE_VECTOR_FIELD_SERIALISE_JSON(y)
// }
// writer.EndObject();
// }
// else if (fieldInfo->FieldType == Vector3::typeid)
// {
// // Get handle to the Vector to edit
// Vector3 vec = safe_cast<Vector3>(fieldInfo->GetValue(object));
// // Write the vector data
// PRIMITIVE_WRITE_FIELD_NAME
// writer.StartObject();
// {
// PRIMITIVE_VECTOR_FIELD_SERIALISE_JSON(x)
// PRIMITIVE_VECTOR_FIELD_SERIALISE_JSON(y)
// PRIMITIVE_VECTOR_FIELD_SERIALISE_JSON(z)
// }
// writer.EndObject();
// }
// else if (fieldInfo->FieldType == Color::typeid)
// {
// // Get handle to the Vector to edit
// Color vec = safe_cast<Color>(fieldInfo->GetValue(object));
// // Write the vector data
// PRIMITIVE_WRITE_FIELD_NAME
// writer.StartObject();
// {
// PRIMITIVE_VECTOR_FIELD_SERIALISE_JSON(r)
// PRIMITIVE_VECTOR_FIELD_SERIALISE_JSON(g)
// PRIMITIVE_VECTOR_FIELD_SERIALISE_JSON(b)
// PRIMITIVE_VECTOR_FIELD_SERIALISE_JSON(a)
// }
// writer.EndObject();
// }
// else if (IResourceID::typeid->IsAssignableFrom(fieldInfo->FieldType))
// {
// PRIMITIVE_WRITE_FIELD_NAME
// IResourceID^ rsc = safe_cast<IResourceID^>(fieldInfo->GetValue(object));
// writer.Int64(rsc->Id);
// }
// else // Not any of the supported types
// {
// Pls::Debug::LogWarning(Convert::ToNative(String::Format
// (
// "[ReflectionUtilities] Failed to parse \"{0}\" of \"{1}\" type for serialisation.",
// fieldInfo->Name, fieldInfo->FieldType)
// ));
// }
//}
}

View File

@ -0,0 +1,73 @@
/************************************************************************************//*!
\file ReflectionUtilities.hxx
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Nov 6, 2021
\brief Contains the definition of the managed ReflectionUtilities static class.
Note: This file is written in C++17/CLI.
Copyright (C) 2021 DigiPen Institute of Technology.
Reproduction or disclosure of this file or its contents without the prior written consent
of DigiPen Institute of Technology is prohibited.
*//*************************************************************************************/
#pragma once
// External Dependencies
#include <yaml-cpp/node/node.h>
#include <yaml-cpp/emitter.h>
namespace SHADE
{
/// <summary>
/// Contains useful static functions for working with Reflection.
/// </summary>
private ref class ReflectionUtilities abstract sealed
{
public:
/*-----------------------------------------------------------------------------*/
/* Type Definitions */
/*-----------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------*/
/* Utility Functions */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Retrieves a set of all non-static (instance) fields from a specified object.
/// </summary>
/// <param name="object">The object to get non-static fields from.</param>
/// <returns>Immutable list of non-static fields.</returns>
static System::Collections::Generic::IEnumerable<System::Reflection::FieldInfo^>^ GetInstanceFields(System::Object^ object);
/// <summary>
/// Checks if a specified field is a candidate for serialisation. This means that
/// the field is public or private with the [SerialiseField] attribute.
/// </summary>
/// <param name="fieldInfo">The field to check.</param>
/// <returns>
/// True if the specified field is a candidate for serialisation.
/// </returns>
static bool FieldIsSerialisable(System::Reflection::FieldInfo^ fieldInfo);
/*-----------------------------------------------------------------------------*/
/* Serialisation Functions */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Creates a JSON node that represents the specified object and its associated
/// serialisable fields. Public fields and fields marked with the SerialiseField
/// attribute will be serialised.
/// </summary>
/// <param name="object">The object to serialise.</param>
//static void Serialise(Object^ object, YAML::Emitter& yaml);
/// <summary>
/// Deserialises a JSON string and copies the deserialised data into the
/// specified object if there are matching fields.
/// </summary>
/// <param name="jsonString">
/// The JSON string that contains the data to copy into this PlushieScript
/// object.
/// </param>
/// <param name="object">The object to copy deserialised data into.</param>
//static void Deserialise(System::String^ jsonString, Object^ object);
};
}

View File

@ -0,0 +1,27 @@
/************************************************************************************//*!
\file SerializeFieldAttribute.cxx
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Nov 5, 2021
\brief Contains the definition of the functions of the managed SerializeField
Attribute class.
Note: This file is written in C++17/CLI.
Copyright (C) 2021 DigiPen Institute of Technology.
Reproduction or disclosure of this file or its contents without the prior written consent
of DigiPen Institute of Technology is prohibited.
*//*************************************************************************************/
// Precompiled Headers
#include "SHpch.h"
// Primary Header
#include "SerializeFieldAttribute.hxx"
namespace SHADE
{
/*---------------------------------------------------------------------------------*/
/* Constructors */
/*---------------------------------------------------------------------------------*/
SerializeField::SerializeField()
{}
}

View File

@ -19,7 +19,7 @@ namespace SHADE
/// <summary>
/// Simple attribute to mark that a field in a Script should be serialised.
/// </summary>
[System::AttributeUsage(AttributeTargets::Field)]
[System::AttributeUsage(System::AttributeTargets::Field)]
public ref class SerializeField : public System::Attribute
{
public:
@ -29,7 +29,7 @@ namespace SHADE
/// <summary>
/// Default Constructor
/// </summary>
SerializeField() = default;
SerializeField();
};
}