[EMAIL PROTECTED] (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