|
CX Framework
Cross-platform C utility framework
|
Every call names a backend, a type, and the value – it reads like any other generic cx call, with the type name immediately preceding the value it describes:
Backends follow Create/Destroy: each supplies its own create function taking whatever transport it needs (a StreamBuffer for JSON and binary, nothing for SSD trees) and returns a plain SerWriter* / SerReader* – the concrete backend is never named again after that. serWriterFinish() is the writer's last chance to report failure; either way, release the handle with serWriterDestroy() / serReaderDestroy().
A struct member declared [default value] (see Struct System) is omitted from the document when it still holds that value, and an omitted member reads back as that value – for every backend, since it's the traverser doing the omitting, not the format. Pass SER_EmitDefaults to write those members anyway.
The macros above work from a schema – an STypeInfoExt, which says what a slot is declared to be, exactly, including nested and nominal names. An ordinary stype isn't enough on its own: every object class collapses to plain object, and a container's element type lives in the container header rather than in its descriptor, while the wire needs the class's actual name and the container's actual element type.
serWrite(w, X, val) finds that schema for you, as stExt(X). Some slots have no single type name to give it, though – a struct member's declared element types, a container you described by hand, or a structp in a structset slot, where the schema and the value's type come from different names. Those call _serWrite() / _serRead() with the descriptor directly:
The underscore forms are the same traversal with the schema supplied explicitly; they are fair game where the macro can't reach, but the macro is what application code should use. A schema is never NULL – a slot whose type isn't fixed by its own declaration passes the built-in descriptor for whatever kind of value it holds, e.g. stExt(stvar).
A structp member declared over a structset, or an object member declared over a classset, resolves itself: the set travels with the slot's schema, so a reader reconstructs the right concrete type from a wire name with nothing registered. See Struct System for the struct side (structset); classset is the same idea for classes:
Writing a value whose type isn't in the declared set fails, and a document naming a type outside it is rejected on the way in – the set is the slot's vocabulary in both directions.
A document whose top-level value is dynamic, or a slot declared as bare object or structp, has no set to resolve through – hand the reader explicit resolvers instead:
A class opts into the same reflection a struct gets automatically – annotate it [serialize] for member-by-member reflection like a struct, or implement the Serializable interface for full control over its own wire format. Either way the class carries a wire name and reads and writes like any other value; see Classes for the class-side details.