DataConfig Data Model
Conceptually the DataConfig data model is defined by 3 C++ types:
EDcDataEntry
- enum covers every possible data type.FDcReader
- methods to read from the data model.FDcWriter
- methods to write into the data model.
And that's it. The obvious missing thing is a DOM like object that you can random access and serialize into - we choose to not implement that and it's crucial to understand this to get to know how DataConfig works.
EDcDataEntry
The enum covers all possible types:
// DataConfigCore/Public/DataConfig/DcTypes.h
UENUM()
enum class EDcDataEntry : uint16
{
None,
Bool,
Name,
String,
Text,
Enum,
Float,
Double,
Int8,
Int16,
Int32,
Int64,
UInt8,
UInt16,
UInt32,
UInt64,
// Struct
StructRoot,
StructEnd,
// Class
ClassRoot,
ClassEnd,
// Map
MapRoot,
MapEnd,
// Array
ArrayRoot,
ArrayEnd,
// Set,
SetRoot,
SetEnd,
// Optional
OptionalRoot,
OptionalEnd,
// Reference
ObjectReference,
ClassReference,
WeakObjectReference,
LazyObjectReference,
SoftObjectReference,
SoftClassReference,
InterfaceReference,
// Delegates
Delegate,
MulticastInlineDelegate,
MulticastSparseDelegate,
// Field
FieldPath,
// Extra
Blob,
// Extension
Extension,
// End
Ended,
};
Most enumerators directly maps to a FProperty
type:
EDcDataEntry::Bool
-FBoolProperty
EDcDataEntry::Name
-FNameProperty
EDcDataEntry::String
-FStrProperty
EDcDataEntry::ArrayRoot/ArrayEnd
-FArrayProperty
It should've covered all possible FProperty
types. There're some additions that has no direct FProperty
mapping:
EDcDataEntry::None
- It's mapsnull
in JSON, and it's also used to explicitly represent null object reference.EDcDataEntry::Ended
- It's a phony type that is returned when there's no more data or reader/writer is in a invalid state.EDcDataEntry::Blob
- It's an extension to allow direct memory read/write from given fields.EDcDataEntry::Extension
- It's an extension that allows additional data formats. MsgPack reader/writer uses this to support itsextension
data types.
FDcReader
FDcReader
is the one and only way to read from DataConfig data model. For every enumerator in EDcDataEntry
there's a member method on FDcReader
to from it.
Here we set up a simple struct trying out the reader methods:
// DataConfigTests/Private/DcTestBlurb.h
USTRUCT()
struct FDcTestExampleSimple
{
GENERATED_BODY()
UPROPERTY() FString StrField;
UPROPERTY() int IntField;
};
// DataConfigTests/Private/DcTestBlurb.cpp
FDcTestExampleSimple SimpleStruct;
SimpleStruct.StrField = TEXT("Foo Str");
SimpleStruct.IntField = 253;
Since we know exactly how the FDcTestExampleSimple
looks like we can manually arrange the read calls:
// DataConfigTests/Private/DcTestBlurb.cpp
FDcPropertyReader Reader{FDcPropertyDatum(&SimpleStruct)};
DC_TRY(Reader.ReadStructRoot(&Struct)); // `FDcTestExampleSimple` Struct Root
DC_TRY(Reader.ReadName(&FieldName)); // 'StrField' as FName
DC_TRY(Reader.ReadString(&StrValue)); // "Foo STr"
DC_TRY(Reader.ReadName(&FieldName)); // 'IntField' as FName
DC_TRY(Reader.ReadInt32(&IntValue)); // 253
DC_TRY(Reader.ReadStructEnd(&Struct)); // `FDcTestExampleSimple` Struct Root
In the example above FDcReader
behave like a iterator as each ReadXXX()
call emits value and move the internal cursor into the next slot. In case we're reading a unknown structure, we can use FReader::PeekRead()
to peek what's coming next.
FDcWriter
FDcWriter
is the counter part of writing into the data config model. To write into the example instance above:
DC_TRY(Writer.WriteStructRoot(FDcStructStat{})); // `FDcTestExampleSimple` Struct Root
DC_TRY(Writer.WriteName(TEXT("StrField"))); // 'StrField' as FName
DC_TRY(Writer.WriteString(TEXT("Alt Str"))); // "Foo STr"
DC_TRY(Writer.WriteName(TEXT("IntField"))); // 'IntField' as FName
DC_TRY(Writer.WriteInt32(233)); // 233
DC_TRY(Writer.WriteStructEnd(FDcStructStat{})); // `FDcTestExampleSimple` Struct Root
There's also FDcWriter::PeekRead()
to query whether it's possible to write given data type.
Sum Up
DataConfig provide FDcReader
and FDcWriter
to access the property system. It can be considered as a friendly alternative to the property system API. This is also how we support JSON and MsgPack in an uniform API.