The Forth 2012 standard defines a number of words for defining fields and structures.
A typical example of defining a structure with several fields is:
0 \ offset of first field, 0 in the usual case field: intlist-next ( intlist -- addr1 ) field: intlist-val ( intlist -- addr2 ) constant intlist ( -- u )
An equivalent alternative way of defining this structure is:
begin-structure intlist ( -- u ) field: intlist-next ( intlist -- addr1 ) field: intlist-val ( intlist -- addr2 ) end-structure
Intlist
returns the size of the structure. The convention for
the field names here is to prepend the structure name, so that you
don’t run into conflicts when several structures have next
and
val
fields; in Forth, by default field names are in the same
wordlist (i.e., the same name space) as the other words (including
other field names), and trying to use the search order (see Word Lists) for avoiding conflicts is rather cumbersome (unless you use
the scope recognizer !! pxref).
You can then use that to allocate an instance of that structure and then use the field words to access the fields of that instance:
intlist allocate throw constant my-intlist1 0 my-intlist1 intlist-next ! 5 my-intlist1 intlist-val ! intlist allocate throw constant my-intlist2 my-intlist1 my-intlist2 intlist-next ! 7 my-intlist2 intlist-val ! : intlist-sum ( intlist -- n ) \ "intlist" is a pointer to the first element of a linked list \ "n" is the sum of the intlist-val fields in the linked list 0 BEGIN ( intlist1 n1 ) over WHILE ( list1 n1 ) over intlist-val @ + swap intlist-next @ swap REPEAT nip ; my-intlist2 intlist-sum . \ prints "12"
In addition to field:
for cell-aligned and cell-sized fields,
you can define fields sized and aligned for various types with:
begin-structure
( "name" – struct-sys 0 ) facility-ext “begin-structure”
end-structure
( struct-sys +n – ) facility-ext “end-structure”
end a structure started wioth begin-structure
cfield:
( u1 "name" – u2 ) facility-ext “c-field-colon”
Define a char-sized field
field:
( u1 "name" – u2 ) facility-ext “field-colon”
Define an aligned cell-sized field
2field:
( u1 "name" – u2 ) gforth-0.7 “two-field-colon”
Define an aligned double-cell-sized field
ffield:
( u1 "name" – u2 ) floating-ext “f-field-colon”
Define a faligned float-sized field
sffield:
( u1 "name" – u2 ) floating-ext “s-f-field-colon”
Define a sfaligned sfloat-sized field
dffield:
( u1 "name" – u2 ) floating-ext “d-f-field-colon”
Define a dfaligned dfloat-sized field
wfield:
( u1 "name" – u2 ) gforth-1.0 “w-field-colon”
Define a naturally aligned field for a 16-bit value.
lfield:
( u1 "name" – u2 ) gforth-1.0 “l-field-colon”
Define a naturally aligned field for a 32-bit value.
xfield:
( u1 "name" – u2 ) gforth-1.0 “x-field-colon”
Define a naturally aligned field for a 64-bit-value.
If you need something beyond these field types, you can use
+field
to define fields of arbitrary size. You have to ensure
the correct alignment yourself in this case. E.g., if you want to put
one struct inside another struct, you would do it with
0 cfield: nested-foo aligned intlist +field nested-bar constant nested
In this example the field nested-bar
contains an intlist
structure, so the size of intlist
is passed to +field
.
An intlist
must be cell-aligned (it contains cell fields), and
this is achieved by aligning the current field offset with
aligned
before the field definition. Our recommendation is to
always precede the usage of +field
with an appropriate
alignment word (except if character-alignment is good enough for the
field); this ensures that the field will stay correctly aligned even
if other fields are later inserted before the +field
-defined
field.
+field
( noffset1 nsize "name" – noffset2 ) facility-ext “plus-field”
Defining word; defines name ( addr1 -- addr2 )
, where
addr2 is addr1+noffset1. noffset2 is
noffset1+nsize.
The first field is at the base address of a structure and the word for
this field (e.g., list-next
) actually does not change the
address on the stack. You may be tempted to leave it away in the
interest of run-time and space efficiency. This is not necessary,
because Gforth and other Forth systems optimize this case: If you
compile a first-field word, no code is generated. So, in the interest
of readability and maintainability you should include the word for the
field when accessing the field.