Files
UnrealEngine/Engine/Plugins/Experimental/PlainProps/Source/Public/PlainPropsSaveMember.h
2025-05-18 13:04:45 +08:00

471 lines
12 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "PlainPropsTypes.h"
#include "PlainPropsBind.h"
#include "PlainPropsBuild.h"
#include "PlainPropsDiff.h"
#include "PlainPropsLoadMember.h"
#include "PlainPropsSave.h"
namespace PlainProps
{
FORCEINLINE bool Track(bool bDiff, const FBindContext&, FMemberBindType, FMemberId, FDiffMetadata, const void*, const void*)
{
return bDiff;
}
FORCEINLINE bool Track(bool bDiff, FDiffContext& Ctx, FMemberBindType Type, FMemberId Name, FDiffMetadata Meta, const void* A, const void* B)
{
if (bDiff)
{
Ctx.Out.Emplace(Type, ToOptional(Name), Meta, A, B);
}
return bDiff;
}
// TMemberSerializer helper
template<typename T>
struct TStructSerializer : FBothStructId
{
static constexpr EMemberKind Kind = EMemberKind::Struct;
template<class Ids>
TStructSerializer(TCustomInit<Ids>& Init, TConstArrayView<FMemberId> Names, FBothStructId Both = GetStructBothId<Ids, T>())
: FBothStructId(Both)
{
Init.RegisterInnerStruct(Both, Names);
}
FMemberSpec SpecMember() const { return FMemberSpec(DeclId); }
FTypedValue SaveMember(const T& Value, const FSaveContext& Ctx) const
{
FMemberSchema Schema = { DefaultStructType, DefaultStructType, 0, FInnerId(BindId), nullptr};
return { Schema, {.Struct = SaveItem(Value, Ctx)} };
}
void LoadMember(T& Dst, /* in-out */ FMemberLoader& Src) const
{
LoadStruct(&Dst, Src.GrabStruct());
}
void ConstructAndLoadMember(void* Dst, /* in-out */ FMemberLoader& Src) const
{
if constexpr (std::is_default_constructible_v<T>)
{
LoadMember(*new (Dst) T, Src);
}
else
{
static_assert(!std::is_void_v<CustomBind<T>>, "Non-default constructible types must load via custom bindings");
ConstructAndLoadStruct(Dst, Src.GrabStruct());
}
}
template<class ContextType>
bool DiffMember(const T& A, const T& B, FMemberId Name, ContextType& Ctx) const
{
return Track(DiffStructs(&A, &B, BindId, Ctx), Ctx, DefaultStructBindType, Name, {.Struct = BindId}, &A, &B);
}
////////// TRangeSaver API //////////
using BuiltItemType = FBuiltStruct*;
FBuiltStruct* SaveItem(const T& Value, const FSaveContext& Ctx) const
{
return SaveStruct(&Value, BindId, Ctx);
}
FMemberSchema MakeMemberRangeSchema(ERangeSizeType MaxSize) const
{
return { FMemberType(MaxSize), DefaultStructType, 1, FInnerId(BindId), nullptr };
}
};
//////////////////////////////////////////////////////////////////////////
template<typename T>
inline constexpr FMemberType ReflectInnermostType()
{
return DefaultStructType;
}
template<Arithmetic T>
inline constexpr FMemberType ReflectInnermostType()
{
return ReflectArithmetic<T>.Pack();
}
template<Enumeration T>
inline constexpr FMemberType ReflectInnermostType()
{
return ReflectEnum<T>.Pack();
}
// TRangeSerializer constructor helper, FMemberBindType isn't default-constructible
union FMemberBindTypeInitializer
{
FMemberBindTypeInitializer() : Unused() {}
uint8_t Unused;
FMemberBindType Value;
};
// TMemberSerializer helper
template<typename T, typename RangeBinding>
struct TRangeSerializer
{
static constexpr EMemberKind Kind = EMemberKind::Range;
static constexpr bool bLeafRange = std::is_base_of_v<ILeafRangeBinding, RangeBinding>;
static constexpr uint16 NumRanges = CountRangeBindings<RangeBinding>();
static constexpr uint16 NumTypes = NumRanges + 1;
FRangeBinding const* const Bindings = nullptr;
FOptionalInnerId InnermostId;
FOptionalInnerId InnermostSpecId;
FMemberBindTypeInitializer BindTypes[NumTypes];
FMemberType Types[NumTypes];
template<class Ids>
TRangeSerializer(TCustomInit<Ids>& Init, TConstArrayView<FMemberId> Names)
: Bindings(GetRangeBindings<RangeBinding, Ids, NumRanges>().GetData())
{
for (uint16 Idx = 0; Idx < NumRanges; ++Idx)
{
ERangeSizeType MaxSize = Bindings[Idx].GetSizeType();
Types[Idx] = FMemberType(MaxSize);
BindTypes[Idx].Value = FMemberBindType(MaxSize);
}
using InnermostType = typename TInnermostType<RangeBinding, NumRanges>::Type;
Types[NumRanges] = ReflectInnermostType<InnermostType>();
FMemberSpec Spec;
BindTypes[NumRanges].Value = BindInnermostType<InnermostType, Ids>(/* out */ InnermostId, /* out */ Spec);
InnermostSpecId = InnermostId;
if constexpr (!LeafType<InnermostType>)
{
FBothStructId Both = {InnermostId.Get().AsStructBindId(), GetStructDeclId<Ids, InnermostType>() };
InnermostSpecId = FInnerId(Both.DeclId);
Init.RegisterInnerStruct(Both, Names);
}
}
FMemberSpec SpecMember() const
{
return FMemberSpec(Types, InnermostSpecId);
}
void LoadMember(T& Dst, /* in-out */ FMemberLoader& Src) const
{
LoadRange(&Dst, Src.GrabRange(), MakeArrayView(Bindings, NumRanges));
}
void ConstructAndLoadMember(void* Dst, /* in-out */ FMemberLoader& Src) const
{
static_assert(std::is_default_constructible_v<T>, TEXT("Ranges must be default-constructible"));
LoadMember(*new (Dst) T, Src);
}
FTypedValue SaveMember(const T& Value, const FSaveContext& Ctx) const
{
return { MakeMemberSchema(), {.Range = SaveItem(Value, Ctx)} };
}
template<typename ContextType>
bool DiffItems(const void* A, const void* B, const IItemRangeBinding& Binding, ContextType& Ctx) const
{
if constexpr (Kind == EMemberKind::Leaf)
{
return DiffRanges(A, B, Binding, ReflectLeaf<T>);
}
else if constexpr (Kind == EMemberKind::Struct)
{
return DiffRanges(A, B, Binding, InnermostId.Get().AsStructBindId(), Ctx);
}
else
{
return DiffRanges(A, B, Binding, MakeInnerRangeBinding(), Ctx);
}
}
inline FDiffMetadata MakeDiffMetadata() const
{
if constexpr (Kind == EMemberKind::Leaf)
{
return { .Leaf = ToOptionalEnum(InnermostId) };
}
else if constexpr (Kind == EMemberKind::Struct)
{
return { .Struct = InnermostId.Get().AsStructBindId() };
}
else
{
return { .Range = Bindings[1] };
}
}
template<typename ContextType>
bool DiffMember(const T& A, const T& B, FMemberId Name, ContextType& Ctx) const
{
bool bDiff = bLeafRange ? Bindings[0].AsLeafBinding().DiffLeaves(&A, &B)
: DiffItems(&A, &B, Bindings[0].AsItemBinding(), Ctx);
return Track(bDiff, Ctx, BindTypes[0].Value, Name, MakeDiffMetadata(), &A, &B);
}
FMemberSchema MakeMemberSchema() const
{
return { Types[0], Types[1], NumRanges, InnermostId, NumRanges > 1 ? Types + 1 : nullptr };
}
FRangeMemberBinding MakeInnerRangeBinding() const
{
return { &BindTypes[1].Value, Bindings, NumRanges, InnermostId, /* offset */ 0 };
}
////////// TRangeSaver API //////////
using BuiltItemType = FBuiltRange*;
FMemberSchema MakeMemberRangeSchema(ERangeSizeType MaxSize) const
{
return { FMemberType(MaxSize), Types[0], NumTypes, InnermostId, Types };
}
FBuiltRange* SaveItem(const T& Item, const FSaveContext& Ctx) const
{
if constexpr (bLeafRange)
{
return SaveLeafRange(&Item, Bindings[0].AsLeafBinding(), Types[1].AsLeaf(), Ctx);
}
else
{
FRangeMemberBinding MemberBinding = { &BindTypes[1].Value, Bindings, NumRanges, InnermostId, /* offset */ 0 };
return SaveRange(&Item, MakeInnerRangeBinding(), Ctx);
}
}
};
//////////////////////////////////////////////////////////////////////////
// TMemberSerializer helper
template<typename T>
struct TLeafSerializer
{
static void LoadMember(T& Dst, /* in-out */ FMemberLoader& Src)
{
Dst = Src.GrabLeaf().As<T>();
}
static void ConstructAndLoadMember(void* Dst, /* in-out */ FMemberLoader& Src)
{
LoadMember(*static_cast<T*>(Dst), Src);
}
static FTypedValue SaveLeaf(FMemberType Type, FOptionalInnerId Id, T Value)
{
return { {Type, Type, 0, Id, nullptr}, {.Leaf = ValueCast(Value)} };
}
////////// TRangeSaver API //////////
using BuiltItemType = T;
static T SaveItem(T Value, const FSaveContext&) { return Value;}
};
// TMemberSerializer helper
template<Arithmetic T>
struct TArithmeticSerializer : TLeafSerializer<T>
{
static constexpr FMemberType MemberType = ReflectArithmetic<T>.Pack();
static constexpr FMemberBindType MemberBindType{ReflectArithmetic<T>};
template<class Ids>
TArithmeticSerializer(TCustomInit<Ids>&, TConstArrayView<FMemberId>)
{}
static FMemberSpec SpecMember()
{
return Specify<T>();
}
static FTypedValue SaveMember(T Value, const FSaveContext&)
{
return TLeafSerializer<T>::SaveLeaf(MemberType, NoId, Value);
}
template<class ContextType>
inline static bool DiffMember(const T& A, const T& B, FMemberId Name, ContextType& Ctx)
{
return Track(A != B, Ctx, MemberBindType, Name, {.Leaf = NoId}, &A, &B);
}
////////// TRangeSaver API //////////
FMemberSchema MakeMemberRangeSchema(ERangeSizeType MaxSize) const
{
return { FMemberType(MaxSize), MemberType, 1, NoId, nullptr };
}
};
// TMemberSerializer helper
template<Enumeration T>
struct TEnumSerializer : TLeafSerializer<T>
{
static constexpr FMemberType MemberType = ReflectEnum<T>.Pack();
static constexpr FMemberBindType MemberBindType = ReflectEnum<T>;
const FEnumId Id;
template<class Ids>
TEnumSerializer(TCustomInit<Ids>&, TConstArrayView<FMemberId>)
: Id(GetEnumId<Ids, T>())
{}
FMemberSpec SpecMember() const
{
return Specify<T>(Id);
}
FTypedValue SaveMember(T Value, const FSaveContext& Ctx) const
{
return TLeafSerializer<T>::SaveLeaf(MemberType, Id, Value);
}
template<class ContextType>
inline bool DiffMember(const T& A, const T& B, FMemberId Name, ContextType& Ctx) const
{
return Track(A != B, Ctx, MemberBindType, Name, {.Leaf = Id}, &A, &B);
}
////////// TRangeSaver API //////////
FMemberSchema MakeMemberRangeSchema(ERangeSizeType MaxSize) const
{
return { FMemberType(MaxSize), MemberType, 1, FInnerId(Id), nullptr };
}
};
//////////////////////////////////////////////////////////////////////////
template<typename T>
struct TSelectSerializer
{
using CustomBinding = CustomBind<T>;
using RangeBinding = RangeBind<T>;
static constexpr bool bRange = std::is_void_v<CustomBinding> && !std::is_void_v<RangeBinding>;
using Type = std::conditional_t<bRange, TRangeSerializer<T, RangeBinding>, TStructSerializer<T>>;
};
template<Arithmetic T>
struct TSelectSerializer<T>
{
using Type = TArithmeticSerializer<T>;
};
template<Enumeration T>
struct TSelectSerializer<T>
{
using Type = TEnumSerializer<T>;
};
// Helps templated custom bindings save generic members
template<typename T>
using TMemberSerializer = typename TSelectSerializer<T>::Type;
//////////////////////////////////////////////////////////////////////////
// Helps hide FBuiltRange internals
class FRangeSaverBase
{
public:
PLAINPROPS_API FRangeSaverBase(FScratchAllocator& Scratch, uint64 Num, SIZE_T ItemSize);
protected:
template<typename BuiltItemType>
inline void AddBuiltItem(BuiltItemType Item)
{
#if DO_CHECK
check(It < End);
#endif
reinterpret_cast<BuiltItemType&>(*It) = Item;
It += sizeof(Item);
}
[[nodiscard]] FTypedRange Finalize(FMemberSchema RangeSchema)
{
#if DO_CHECK
check(It == End);
#endif
return { RangeSchema, Range };
}
FBuiltRange* Range;
uint8* It;
#if DO_CHECK
const uint8* End;
#endif
};
// Saves a range of T without a range binding
template<typename T>
class TRangeSaver : public FRangeSaverBase
{
public:
using FMemberSerializer = TMemberSerializer<T>;
using BuiltItemType = typename FMemberSerializer::BuiltItemType;
static constexpr SIZE_T ItemSize = sizeof(BuiltItemType);
TRangeSaver(const FSaveContext& InCtx, uint64 Num, const FMemberSerializer& InSchema)
: FRangeSaverBase(InCtx.Scratch, Num, sizeof(BuiltItemType))
, Schema(InSchema)
, Ctx(InCtx)
{}
void AddItem(const T& Item)
{
FRangeSaverBase::AddBuiltItem(Schema.SaveItem(Item, Ctx));
}
[[nodiscard]] FTypedRange Finalize(ERangeSizeType MaxSize)
{
return FRangeSaverBase::Finalize(Schema.MakeMemberRangeSchema(MaxSize));
}
private:
const FMemberSerializer& Schema;
const FSaveContext& Ctx;
};
template<Arithmetic LeafType>
class TLeafRangeSaver : public FRangeSaverBase
{
public:
TLeafRangeSaver(FScratchAllocator& Scratch, uint64 Num) : FRangeSaverBase(Scratch, Num, sizeof(LeafType)) {}
inline void AddItem(LeafType Item) {FRangeSaverBase::AddBuiltItem(Item); }
using FRangeSaverBase::Finalize;
};
class FNestedRangeSaver : public FRangeSaverBase
{
public:
FNestedRangeSaver(FScratchAllocator& Scratch, uint64 Num) : FRangeSaverBase(Scratch, Num, sizeof(FBuiltRange*)) {}
inline void AddItem(const FBuiltRange* Item) { FRangeSaverBase::AddBuiltItem(Item); }
using FRangeSaverBase::Finalize;
};
class FStructRangeSaver : public FRangeSaverBase
{
public:
FStructRangeSaver(FScratchAllocator& Scratch, uint64 Num) : FRangeSaverBase(Scratch, Num, sizeof(FBuiltStruct*)) {}
inline void AddItem(const FBuiltStruct* Item) { FRangeSaverBase::AddBuiltItem(Item); }
using FRangeSaverBase::Finalize;
};
} // namespace PlainProps