Reader/Writer
FDcReader/FDcWriter defines the set of API for accessing DataConfig data model. Here's a check list for implementing a reader/writer:
- Implement
GetID()/ClassID()
for RTTI. - Implement
PeekRead()/PeekWriter()
and selected set ofReadXXX()/WriteXXX()
. - Implement
FormatDiagnostic()
for error reporting.
You should look at builtin implementation for references. Here's some general rules and caveats:
-
PeekRead()/PeekWrite()
should act like it's side-effect free.This means that it's OK to call
PeekRead()/PeekWrite()
multiple times. In comparison access methods likeReadBool()/WriteBool()
consume the data and alternate internal state. Note that under the hood it might do anything. Both returnsFDcResult
so the peek can fail. The reason behind this is that callingPeekRead()/PeekWrite()
is totally optional. InFDcJsonReader::PeekRead()
we do parsing and cache the parsed result to follow this convention. -
CastByID()
does not respect inheritance hierarchy.We have this very minimal RTTI implemetantion that only allow casting to the exact type.
-
Implement a subset of the data model.
The API is designed in a way that it covers the whole Property System. It's also a super set that can express common formats like JSON/MsgPack. For example JSON don't have struct, class or set. It's actually the job of Serializer/Deserializer to convert between these subsets to the property system.
Builtin Reader/Writer
We have 3 major pairs of reader/writers:
FDcPropertyReader/FDcPropertyWriter
- Accesing Unreal Engin property system.FDcJsonReader/FDcJsonWriter
- JSON support.FDcMsgPackReader/FDcMsgPackWriter
- MsgPack support.
These are all talked about in details in the formats section. We'll go through other builtin Reader/Writers below.
FDcPipeVisitor
and FDcPrettyPrintWriter
FDcPipeVisitor
takes a FDcReader
and a FDcWriter
then start peek-read-write loop until it peeks EDcDataEntry::Ended
from reader or an error happens
Then there's FDcPrettyPrintWriter
that dumps everything that got write to it as string.
Combining these two we get a way to dump arbitrary FDcReader
into a string!. This is how built-in debug dump features are implemented:
// DataConfigCore/Private/DataConfig/Automation/DcAutomationUtils.cpp
void DumpToOutputDevice(...)
{
//...
FDcPropertyReader PropReader(Datum);
FDcPrettyPrintWriter PrettyWriter(Output);
FDcPipeVisitor PrettyPrintVisit(&PropReader, &PrettyWriter);
if (!PrettyPrintVisit.PipeVisit().Ok())
ScopedEnv.Get().FlushDiags();
//...
}
FDcPipeVisitor
is a handy utility that we use it extensively through the code base for various cases. Try FDcPipeVisitor
when you got a reader/writer pair.
There's also FNoopWriter
takes every write and do nothing with it.
Composition
Reader/Writers can also be composited and nested:
FDcWeakCompositeWriter
is a writer that multiplex into a list of writers. You can combine an arbitrary writer with aFPrettyPrintWriter
then get a tracing writer.FDcPutbackReader/FPutbackWriter
: Reader/writers don't support lookahead. It can only peek next item's type but not value. This class is used to support limited lookahead by putting back read value. We'll see it being used in implementing custom deserializer handlers.
Conclusion
Implement new FDcReader/FDcWriter
when you want to support a new file format. You can also write utility reader/writer that composite existing ones.