[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: make-vtable

From: Kevin Ryde
Subject: Re: make-vtable
Date: Thu, 15 Feb 2007 07:51:15 +1100
User-agent: Gnus/5.110006 (No Gnus v0.6) Emacs/21.4 (gnu/linux)

address@hidden (Ludovic Courtès) writes:
> I'm not sure the indirection in `scm_init_stacks ()' is needed since it

Not sure what you mean there.

> and `make-struct'
> doesn't look at the vtable's vtable anyway (when creating instances of

> More generally, a three-level architecture like the one you suggest
> would look fishy.  For instance, GOOPS and other CLOS derivatives have
> <object> and <class>, representing respectively the "base" and "meta"
> levels, but they have no need for <class-class>, <class-class-class> or
> some such.

The closest I've got to imagining it is that make-vtable-vtable
creates a root, a vtable whose parent vtable is itself.  (What I'm not
sure if it's supposed to be the root of a whole heirarchy, or just of
two levels, if you know what I mean.)

At any rate, below is where I'm up to so far with trying to make the
docs a bit easier.  It includes my proposed make-vtable.  Could be
possible to leave that out, but perhaps those like me who managed to
never understand structs can see if it makes the understanding easier


A "structure" is a first class data type which holds Scheme values or C
words in slots numbered 0 upwards.  A "vtable" represents a structure
type, giving read/write permissions on slots, whether they're Scheme
values or uninterpreted words, and giving an optional print function
for the structure (for use by `write' etc).

   Structures are lower level than records (*note Records::) but have
some extra features.  The vtable system allows sets of types be
constructed, complete with per-class data.  The uninterpreted word
slots feature can be used to inter-operate with C code, so arbitrary
pointers or other values can be stored along side usual Scheme `SCM'


A vtable specifies the format of a structure.  A vtable is actually
itself a structure, but there's no need to worray about that initially.
(For more details see *note Vtable Contents::.)

 -- Scheme Procedure: make-vtable fields [print]
     Create a new vtable.

     FIELDS is a string describing the slots in the structures to be
     created from this vtable.  Each slot is represented by two
     characters, a type letter and a permissions letter, for example
     `"pw"'.  The types are as follows.

        * `p' - a Scheme value.  "p" stands for "protected" meaning
          that it's protected against garbage collection.

        * `u' - an arbitrary word of data (an `scm_t_bits').  At the
          Scheme level it's read and written as an unsigned integer.
          "u" stands for "uninterpreted" (it's not treated as a Scheme
          value), or "unprotected" (it's not marked during GC), or
          "unsigned long" (its size), or all of these things.

        * `s' - a self-reference.  Such a slot holds the `SCM' value of
          the structure itself (a circular reference).  This can be
          useful in C code where you might have a pointer to the data
          array, and want to get the Scheme `SCM' handle for the
          structure.  In Scheme code it has no use.

     The second letter for each field is a permission code, as follows.

        * `w' - writable, the field can be read and written.

        * `r' - readable, the field can be read, but not written.

        * `o' - opaque, the field can be neither read nor written at the
          Scheme level.  This can be used for fields which should only
          be used from C code.

        * `W',`R',`O' - a tail array, with permissions for the array
          slots as per `w',`r',`o'.

     A tail array is further slots at the end of a structure.  The last
     field in the layout string might be for instance `pW' to have a
     tail of writable Scheme-valued slots.  The given `pW' field itself
     holds a count of the extra slots, and then those slots follow.

     Here are some examples.

          (make-vtable "pw")      ;; one writable field
          (make-vtable "prpw")    ;; one read-only and one writable
          (make-vtable "pwuwuw")  ;; one scheme and two uninterpreted

          (make-vtable "prpW")    ;; one fixed then a tail array

     The optional PRINT argument is a function called by `display' and
     `write' (etc) to give a printed representation of a structure of
     this type.  It's called `(PRINT struct port)' and should look at
     STRUCT and write to PORT.  The default print merely gives a form
     like `#<struct ADDR:ADDR>' with a pair of machine addresses.

     The following print function for example shows the two fields of
     its structure.

          (make-vtable "prpw"
                       (lambda (struct port)
                         (display "#<")
                         (display (struct-ref 0))
                         (display " and ")
                         (display (struct-ref 1))
                         (display ">")))

Structure Basics

This section describes the basic procedures for creating and accessing

 -- Scheme Procedure: make-struct vtable tail-size [init...]
 -- C Function: scm_make_struct (vtable, tail_size, init_list)
     Create a new structure.  VTABLE is a vtable giving the layout of
     the structure (*note Vtables::).

     TAIL-SIZE is the size of the tail array, if VTABLE specifies a
     tail array.  TAIL-SIZE should be 0 when VTABLE doesn't specify a
     tail array.

     The optional INIT... arguments are initial values for the slots of
     the structure (and the tail array).  For read-only slots this is
     the only way to set values.  If there are fewer INIT arguments
     than fields then the defaults are `#f' for a Scheme field (type
     `p') or 0 for an uninterpreted field (type `u').

     Type `s' self-reference fields and permissions `o' opaque fields
     are skipped for the INIT arguments.  An `s' is always set to the
     structure itself, and an `o' is always set to `#f' or 0 (with the
     intention that C code will use it in some way later).  The count
     field of a tail array is skipped by the INIT arguments too.

     For example,

          (define v (make-vtable "prpwpw" 0))
          (define s (make-struct v 0 123 "abc" 456))
          (struct-ref s 0) => 123
          (struct-ref s 1) => "abc"

          (define v (make-vtable "prpW" 0))
          (define s (make-struct v 2 "fixed field" 'x 'y))
          (struct-ref s 0) => "fixed field"
          (struct-ref s 1) => 2    ;; tail size
          (struct-ref s 2) => x
          (struct-ref s 3) => y

 -- Scheme Procedure: struct? obj
 -- C Function: scm_struct_p (obj)
     Return `#t' if OBJ is a structure, or `#f' if not.

 -- Scheme Procedure: struct-ref struct n
 -- C Function: scm_struct_ref (struct, n)
     Return the contents of slot number N in STRUCT.

     An error is thrown if N is out of range, or if the slot cannot be
     read because it's `o' opaque.

 -- Scheme Procedure: struct-set! struct n value
 -- C Function: scm_struct_set_x (struct, n, value)
     Set slot number N in STRUCT to VALUE.

     An error is thrown if N is out of range, or if the slot cannot be
     written because it's `r' read-only or `o' opaque.

 -- Scheme Procedure: struct-vtable struct
 -- C Function: scm_struct_vtable (struct)
     Return the vtable used by STRUCT.

     This can be used to examine the layout of an unknown structure, see
     *note Vtable Contents::.

Vtable Contents

A vtable is itself a structure, it has slots which hold the slot layout
for the structures created from it, and a print function for those
structures.  The variables below allow access to those slots.

 -- Scheme Procedure: struct-vtable? obj
 -- C Function: scm_struct_vtable_p (obj)
     Return `#t' if OBJ is a vtable structure.

     Note that because vtables are simply structures with a particular
     layout, `struct-vtable?' can potentially return true on an
     application structure that happens to look like a vtable.

 -- Scheme Variable: vtable-index-layout
 -- C Macro: scm_vtable_index_layout
     The slot number of the layout specification.  The layout
     specification is a symbol like `pwpw' formed from the fields
     string passed to `make-vtable', or created by `make-struct-layout'
     (*note Vtable Vtables::).

          (define v (make-vtable "pwpw" 0))
          (struct-ref v vtable-index-layout) => pwpw

     This field is read-only, since the layout of the structures using a
     vtable cannot be changed.

 -- Scheme Variable: vtable-index-vtable
 -- C Macro: scm_vtable_index_vtable
     A self-reference to the vtable, ie. a type `s' field.  This is
     used by C code within Guile and has no use at the Scheme level.

 -- Scheme Variable: vtable-index-printer
 -- C Macro: scm_vtable_index_printer
     The slot number of the printer function.  This slot contains `#f'
     if the default print function should be used.

          (define (my-print-func struct port)
          (define v (make-vtable "pwpw" my-print-func))
          (struct-ref v vtable-index-printer) => my-print-func

     This field is writable, so the print function can be changed

 -- Scheme Procedure: struct-vtable-name vtable
 -- Scheme Procedure: set-struct-vtable-name! vtable name
 -- C Function: scm_struct_vtable_name (vtable)
 -- C Function: scm_set_struct_vtable_name_x (vtable, name)
     Get or set the name of VTABLE.  NAME is a symbol and is used in
     the default structure printing.

          (define v (make-vtable "pw"))
          (set-struct-vtable-name! v 'my-name)
          (define s (make-struct v 0))
          (display s) -| #<my-name b7ab3ae0:b7ab3730>

 -- Scheme Procedure: struct-vtable-tag handle
 -- C Function: scm_struct_vtable_tag (handle)
     Return the vtable tag of the structure HANDLE.

Vtable Vtables

As noted above, vtables are structures and such a structure is itself
described by a vtable.  Such a "vtable of a vtable" can be created with
`make-vtable-vtable' below and used to build sets of related vtables,
possibly with extra application fields.

   This second level of vtable can be a little confusing.  An example,
and indeed a typical use, is Guile's own record system (*note
Records::).  Currently record types are implemented as vtables, and
those vtables have an extra slot holding the list of field names for
that type (as passed to `make-record-type').

 -- Scheme Procedure: make-vtable-vtable user-fields tail-size [print]
 -- C Function: scm_make_vtable_vtable (user_fields, tail_size,
     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' creates a vtable from the vtable-vtable.  The first
     initializer is a layout object (slot `vtable-index-layout').  The
     `make-struct-layout' function (below) can create this from a
     string description.  An optional second initializer to
     `make-struct' is a printer function (slot `vtable-index-printer'),
     used as described under `make-vtable' (*note Vtables::).

     USER-FIELDS is a layout string giving extra slots to have in the
     vtables.  A vtable starts with some base fields (as per *note
     Vtable Contents::), and USER-FIELDS is appended.  The USER-FIELDS
     slots start at `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 can be zero
     if nothing extra is required or the format has no tail array.  The
     tail array slot 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 gives just `#<struct ...>'.

 -- Scheme Procedure: make-struct-layout fields
 -- C Function: scm_make_struct_layout (fields)
     FIELDS is a string such as `"pwpw"' describing structure fields.
     Check the format is valid and convert it to a symbol, ready for
     use in vtable creation.  See `make-vtable' (*note Vtables::) for
     the format of FIELDS.

          (make-struct-layout "prpW") => prpW
          (make-struct-layout "blah") => ERROR

 -- Scheme Variable: vtable-offset-user
 -- C Macro: scm_vtable_offset_user
     The first slot in a vtable which is available for application use.
     Such slots 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 balls
are created and each 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)))
     (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>

reply via email to

[Prev in Thread] Current Thread [Next in Thread]