As noted above, a vtable is a structure and that structure is itself
described by a vtable. Such a “vtable of a vtable” can be created
make-vtable-vtable below. This can be used to build sets
of related vtables, possibly with extra application fields.
This second level of vtable can be a little confusing. The ball example below is a typical use, adding a “class data” field to the vtables, from which instance structures are created. The current implementation of Guile's own records (see Records) does something similar, a record type descriptor is a vtable with room to hold the field names of the records to be created from it.
Create a “vtable-vtable” which can be used to create vtables. This vtable-vtable is also a vtable, and is self-describing, meaning its vtable is itself. The following is a simple usage.(define vt-vt (make-vtable-vtable "" 0)) (define vt (make-struct vt-vt 0 (make-struct-layout "pwpw")) (define s (make-struct vt 0 123 456)) (struct-ref s 0) ⇒ 123
make-structis used to create a vtable from the vtable-vtable. The first initializer is a layout object (field
vtable-index-layout), usually obtained from
make-struct-layout(below). An optional second initializer is a printer function (field
vtable-index-printer), used as described under
make-vtable(see Vtables).user-fields is a layout string giving extra fields to have in the vtables. A vtable starts with some base fields as per Vtable Contents, and user-fields is appended. The user-fields start at field number
vtable-offset-user(below), and exist in both the vtable-vtable and in the vtables created from it. Such fields provide space for “class data”. For example,(define vt-of-vt (make-vtable-vtable "pw" 0)) (define vt (make-struct vt-of-vt 0)) (struct-set! vt vtable-offset-user "my class data")
tail-size is the size of the tail array in the vtable-vtable itself, if user-fields specifies a tail array. This should be 0 if nothing extra is required or the format has no tail array. The tail array field such as ‘pW’ holds the tail array size, as usual, and is followed by the extra space.(define vt-vt (make-vtable-vtable "pW" 20)) (define my-vt-tail-start (1+ vtable-offset-user)) (struct-set! vt-vt (+ 3 my-vt-tail-start) "data in tail")
The optional print argument is used by
write(etc) to print the vtable-vtable and any vtables created from it. It's called as
vtable port)and should look at vtable and write to port. The default is the usual structure print function, which just gives machine addresses.
Return a structure layout symbol, from a fields string. fields is as described under
make-vtable(see Vtables). An invalid fields string is an error.(make-struct-layout "prpW") ⇒ prpW (make-struct-layout "blah") ⇒ ERROR
The first field in a vtable which is available for application use. Such fields only exist when specified by user-fields in
Here's an extended vtable-vtable example, creating classes of “balls”. Each class has a “colour”, which is fixed. Instances of those classes are created, and such each such ball has an “owner”, which can be changed.
(define ball-root (make-vtable-vtable "pr" 0)) (define (make-ball-type ball-color) (make-struct ball-root 0 (make-struct-layout "pw") (lambda (ball port) (format port "#<a ~A ball owned by ~A>" (color ball) (owner ball))) ball-color)) (define (color ball) (struct-ref (struct-vtable ball) vtable-offset-user)) (define (owner ball) (struct-ref ball 0)) (define red (make-ball-type 'red)) (define green (make-ball-type 'green)) (define (make-ball type owner) (make-struct type 0 owner)) (define ball (make-ball green 'Nisse)) ball ⇒ #<a green ball owned by Nisse>