Skip to content

Commit 2e75ac1

Browse files
committed
Document serialization compiler intrinsics
1 parent 6a1b632 commit 2e75ac1

1 file changed

Lines changed: 49 additions & 12 deletions

File tree

_doc/manual.md

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -880,30 +880,67 @@ function foo()
880880

881881
### Compiler-assisted field mapping
882882

883-
For dedicated state classes, the compiler provides two save/load intrinsics that expand into direct field
884-
accesses:
883+
The compiler provides three prefixed operations for libraries that need type-safe structural mapping without
884+
runtime reflection: `wurstForFields`, `wurstMapFields`, and `wurstNewInstance<T>()`. They are declared in
885+
`MagicFunctions` with `@compilerintrinsic`, so editors can show their documentation and signatures while the
886+
compiler still replaces calls with direct accesses and constructor calls.
885887

886888
```wurst
887889
class PlayerState
888890
int level = 1
889891
string name = ""
890892
891893
function save(FieldWriter writer)
892-
__wurst_forFields((fieldName, value) -> writer.write(fieldName, value))
894+
wurstForFields((fieldName, value) -> writer.write(fieldName, value))
893895
894896
function load(FieldReader reader)
895-
__wurst_mapFields((fieldName, value) -> reader.read(fieldName, value))
897+
wurstMapFields((fieldName, value) -> reader.read(fieldName, value))
898+
899+
function loadPlayerState(FieldReader reader) returns PlayerState
900+
let result = wurstNewInstance<PlayerState>()
901+
result.load(reader)
902+
return result
903+
```
904+
905+
`wurstForFields` invokes the callback once for every accessible, non-static instance field, including inherited
906+
and module-injected fields. Readonly and constant fields are included. `wurstMapFields` assigns each callback
907+
result back to its field, so every included field must be mutable. The callback's value parameter has the concrete
908+
type of the current field; leave it inferred and overload the reader or writer for every supported field type.
909+
This allows a library to compose codecs for primitives, enums, nullable values, nested state classes, tuples, and
910+
generic collections without a runtime type registry.
911+
912+
Both operations also accept an explicit target as their first argument. Class targets are evaluated exactly once.
913+
Tuple targets use direct component accesses; a tuple passed to `wurstMapFields` must be a variable so the updated
914+
tuple can be written back safely.
915+
916+
```wurst
917+
tuple Position(int x, int y)
918+
919+
function savePosition(Position position, FieldWriter writer)
920+
wurstForFields(position, (fieldName, value) -> writer.write(fieldName, value))
921+
922+
function loadPosition(Position position, FieldReader reader) returns Position
923+
var result = position
924+
wurstMapFields(result, (fieldName, value) -> reader.read(fieldName, value))
925+
return result
896926
```
897927

898-
`__wurst_forFields` invokes the callback once for every non-static instance field. The callback receives the
899-
field name and current value and must produce a statement. `__wurst_mapFields` invokes a reader callback and
900-
assigns its result back to each field. Leave the two callback parameter types inferred; overload the reader or
901-
writer for the field types used by the state class.
928+
`wurstNewInstance<T>()` calls the normal accessible zero-argument constructor of a concrete class. Constructor
929+
initializers therefore provide defaults for fields absent from older records; the operation never allocates an
930+
uninitialized object or performs class-name lookup.
931+
932+
These operations generate ordinary constructor calls and direct field or tuple accesses in both Jass and Lua.
933+
They do not define a save format or make field names stable persisted identities. A serialization library must
934+
own numeric field IDs, object versions, migrations, parsing, and integrity checks. Cyclic object graphs and field
935+
types for which the library has no codec still require an explicit user-provided codec.
936+
937+
The original unprefixed `forFields`, `mapFields`, and `newInstance<T>()` spellings remain available as compatibility
938+
fallbacks. New code should use the `wurst`-prefixed names to avoid accidental collisions. If an applicable ordinary
939+
function with the same name is visible, normal overload resolution selects that function instead of the compiler
940+
operation.
902941

903-
These are compile-time transformations, not runtime reflection, and generate equivalent direct accesses in both
904-
Jass and Lua. Keep serializable state in a small class with mutable instance fields and keep the persistence
905-
codec separate from the rest of the game logic. See the [Save and Load tutorial](/tutorials/saveload.html) for
906-
integration with Warcraft III's file API.
942+
Keep serializable state in a small class and keep persistence policy separate from the rest of the game logic.
943+
See the [Save and Load tutorial](/tutorials/saveload.html) for integration with Warcraft III's file API.
907944

908945
### Array Members
909946

0 commit comments

Comments
 (0)