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 they're 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 returnsFDcResultso 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 implementation that only allows 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 starts a 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 that takes every write and does nothing with it.
Composition
Reader/Writers can also be composited and nested:
FDcWeakCompositeWriteris a writer that multiplex into a list of writers. You can combine an arbitrary writer with aFPrettyPrintWriterthen 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.