Previous: Structure Basics, Up: Structures


5.6.9.4 Vtables

Vtables are structures that are used to represent structure types. Each vtable contains a layout specification in field vtable-index-layout – instances of the type are laid out according to that specification. Vtables contain additional fields which are used only internally to libguile. The variable vtable-offset-user is bound to a field number. Vtable fields at that position or greater are user definable.

— Scheme Procedure: struct-vtable handle
— C Function: scm_struct_vtable (handle)

Return the vtable structure that describes the type of struct.

— Scheme Procedure: struct-vtable? x
— C Function: scm_struct_vtable_p (x)

Return #t iff x is a vtable structure.

If you have a vtable structure, V, you can create an instance of the type it describes by using (make-struct V ...). But where does V itself come from? One possibility is that V is an instance of a user-defined vtable type, V', so that V is created by using (make-struct V' ...). Another possibility is that V is an instance of the type it itself describes. Vtable structures of the second sort are created by this procedure:

— Scheme Procedure: make-vtable-vtable user_fields tail_array_size . init
— C Function: scm_make_vtable_vtable (user_fields, tail_array_size, init)

Return a new, self-describing vtable structure.

user-fields is a string describing user defined fields of the vtable beginning at index vtable-offset-user (see make-struct-layout).

tail-size specifies the size of the tail-array (if any) of this vtable.

init1, ... are the optional initializers for the fields of the vtable.

Vtables have one initializable system field—the struct printer. This field comes before the user fields in the initializers passed to make-vtable-vtable and make-struct, and thus works as a third optional argument to make-vtable-vtable and a fourth to make-struct when creating vtables:

If the value is a procedure, it will be called instead of the standard printer whenever a struct described by this vtable is printed. The procedure will be called with arguments STRUCT and PORT.

The structure of a struct is described by a vtable, so the vtable is in essence the type of the struct. The vtable is itself a struct with a vtable. This could go on forever if it weren't for the vtable-vtables which are self-describing vtables, and thus terminate the chain.

There are several potential ways of using structs, but the standard one is to use three kinds of structs, together building up a type sub-system: one vtable-vtable working as the root and one or several "types", each with a set of "instances". (The vtable-vtable should be compared to the class <class> which is the class of itself.)

          (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>
     
— Scheme Procedure: struct-vtable-name vtable
— C Function: scm_struct_vtable_name (vtable)

Return the name of the vtable vtable.

— Scheme Procedure: set-struct-vtable-name! vtable name
— C Function: scm_set_struct_vtable_name_x (vtable, name)

Set the name of the vtable vtable to name.

— Scheme Procedure: struct-vtable-tag handle
— C Function: scm_struct_vtable_tag (handle)

Return the vtable tag of the structure handle.