Previous: Vtable Contents, Up: Structures


6.7.9.4 Vtable Vtables

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 with 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.

— Scheme Procedure: make-vtable-vtable user-fields tail-size [print]
— C Function: scm_make_vtable_vtable (user_fields, tail_size, print_and_init_list)

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-struct is 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 display and write (etc) to print the vtable-vtable and any vtables created from it. It's called as (print vtable port) and should look at vtable and write to port. The default is the usual structure print function, which just gives machine addresses.

— Scheme Procedure: make-struct-layout fields
— C Function: scm_make_struct_layout (fields)

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
— Scheme Variable: vtable-offset-user
— C Macro: scm_vtable_offset_user

The first field in a vtable which is available for application use. Such fields only exist when specified by user-fields in make-vtable-vtable above.


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>