From: Kevin Ryde <user42@zip.com.au>
To: guile-devel@gnu.org
Subject: Re: make-vtable
Date: Thu, 15 Feb 2007 07:51:15 +1100 [thread overview]
Message-ID: <87tzxopinw.fsf@zip.com.au> (raw)
In-Reply-To: <871wktrwur.fsf@laas.fr> (Ludovic Courtès's message of "Wed, 14 Feb 2007 09:01:48 +0100")
ludovic.courtes@laas.fr (Ludovic Courtès) writes:
>
> I'm not sure the indirection in `scm_init_stacks ()' is needed since it
> uses STACK_LAYOUT for both VTABLE and SCM_STACK_TYPE,
Not sure what you mean there.
> and `make-struct'
> doesn't look at the vtable's vtable anyway (when creating instances of
> SCM_STACK_TYPE).
> 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
:-).
Structures
----------
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'
values.
Vtables
.......
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
structures.
-- 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
dynamically.
-- 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,
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' 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)))
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>
_______________________________________________
Guile-devel mailing list
Guile-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-devel
next prev parent reply other threads:[~2007-02-14 20:51 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-02-12 21:13 make-vtable Kevin Ryde
2007-02-13 8:13 ` make-vtable Ludovic Courtès
2007-02-13 21:26 ` make-vtable Kevin Ryde
2007-02-14 8:01 ` make-vtable Ludovic Courtès
2007-02-14 20:51 ` Kevin Ryde [this message]
2007-02-15 8:45 ` make-vtable Ludovic Courtès
2007-02-16 0:07 ` make-vtable Kevin Ryde
2007-02-16 8:12 ` make-vtable Ludovic Courtès
2007-02-18 18:10 ` make-vtable Neil Jerram
2007-02-18 20:05 ` make-vtable Ludovic Courtès
2007-02-18 23:56 ` make-vtable Neil Jerram
2007-02-19 0:14 ` make-vtable Kevin Ryde
2007-02-19 0:39 ` make-vtable Neil Jerram
2007-02-19 8:39 ` make-vtable Ludovic Courtès
2007-03-06 0:27 ` make-vtable Kevin Ryde
2007-03-06 8:32 ` make-vtable Ludovic Courtès
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/guile/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87tzxopinw.fsf@zip.com.au \
--to=user42@zip.com.au \
--cc=guile-devel@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).