From 2842a17112418052975728e3dd75e03aa6540aed Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Sat, 28 Jul 2012 12:43:46 +0200 Subject: [PATCH] improve documentation for structs * doc/ref/api-compound.texi (Structures): Update to describe , to remove documentation for make-vtable-vtable, to describe meta-vtables, and to add a long example. --- doc/ref/api-compound.texi | 329 ++++++++++++++++++++++++-------------- 1 file changed, 206 insertions(+), 123 deletions(-) diff --git a/doc/ref/api-compound.texi b/doc/ref/api-compound.texi index 779c76d85..d02077409 100644 --- a/doc/ref/api-compound.texi +++ b/doc/ref/api-compound.texi @@ -2370,11 +2370,12 @@ data abstractions, and for that purpose structures are useful. Indeed, records in Guile are implemented with structures. @menu -* Vtables:: -* Structure Basics:: -* Vtable Contents:: -* Vtable Vtables:: -* Tail Arrays:: +* Vtables:: +* Structure Basics:: +* Vtable Contents:: +* Meta-Vtables:: +* Vtable Example:: +* Tail Arrays:: @end menu @node Vtables @@ -2447,11 +2448,9 @@ structure. @example (make-vtable "prpw" (lambda (struct port) - (display "#<" port) - (display (struct-ref struct 0) port) - (display " and " port) - (display (struct-ref struct 1) port) - (display ">" port))) + (format port "#<~a and ~a>" + (struct-ref struct 0) + (struct-ref struct 1)))) @end example @end deffn @@ -2542,26 +2541,24 @@ Contents}, for more on vtables. @node Vtable Contents @subsubsection Vtable Contents -A vtable is itself a structure, with particular fields that hold -information about the structures to be created. These include the -fields of those structures, and the print function for them. The -variables below allow access to those fields. +A vtable is itself a structure. It has a specific set of fields +describing various aspects of its @dfn{instances}: the structures +created from a vtable. Some of the fields are internal to Guile, some +of them are part of the public interface, and there may be additional +fields added on by the user. -@deffn {Scheme Procedure} struct-vtable? obj -@deffnx {C Function} scm_struct_vtable_p (obj) -Return @code{#t} if @var{obj} is a vtable structure. - -Note that because vtables are simply structures with a particular -layout, @code{struct-vtable?} can potentially return true on an -application structure which merely happens to look like a vtable. -@end deffn +Every vtable has a field for the layout of their instances, a field for +the procedure used to print its instances, and a field for the name of +the vtable itself. Access to the layout and printer is exposed directly +via field indexes. Access to the vtable name is exposed via accessor +procedures. @defvr {Scheme Variable} vtable-index-layout @defvrx {C Macro} scm_vtable_index_layout The field number of the layout specification in a vtable. The layout specification is a symbol like @code{pwpw} formed from the fields string passed to @code{make-vtable}, or created by -@code{make-struct-layout} (@pxref{Vtable Vtables}). +@code{make-struct-layout} (@pxref{Meta-Vtables}). @example (define v (make-vtable "pwpw" 0)) @@ -2572,12 +2569,6 @@ This field is read-only, since the layout of structures using a vtable cannot be changed. @end defvr -@defvr {Scheme Variable} vtable-index-vtable -@defvrx {C Macro} scm_vtable_index_vtable -A self-reference to the vtable, ie.@: a type @code{s} field. This is -used by C code within Guile and has no use at the Scheme level. -@end defvr - @defvr {Scheme Variable} vtable-index-printer @defvrx {C Macro} scm_vtable_index_printer The field number of the printer function. This field contains @code{#f} @@ -2612,125 +2603,217 @@ from @var{vtable}. @end deffn -@node Vtable Vtables -@subsubsection 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 @code{make-vtable-vtable} below. This can be used to build sets -of related vtables, possibly with extra application fields. +@node Meta-Vtables +@subsubsection Meta-Vtables -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 (@pxref{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. +As a structure, a vtable also has a vtable, which is also a structure. +Structures, their vtables, the vtables of the vtables, and so on form a +tree of structures. Making a new structure adds a leaf to the tree, and +if that structure is a vtable, it may be used to create other leaves. -@deffn {Scheme Procedure} make-vtable-vtable user-fields tail-size [print] -@deffnx {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. +If you traverse up the tree of vtables, via calling +@code{struct-vtable}, eventually you reach a root which is the vtable of +itself: @example -(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) @result{} 123 +scheme@@(guile-user)> (current-module) +$1 = # +scheme@@(guile-user)> (struct-vtable $1) +$2 = # +scheme@@(guile-user)> (struct-vtable $2) +$3 = #< 12c30a0> +scheme@@(guile-user)> (struct-vtable $3) +$4 = #< 12c3fa0> +scheme@@(guile-user)> (struct-vtable $4) +$5 = #< 12c3fa0> +scheme@@(guile-user)> +$6 = #< 12c3fa0> @end example -@code{make-struct} is used to create a vtable from the vtable-vtable. -The first initializer is a layout object (field -@code{vtable-index-layout}), usually obtained from -@code{make-struct-layout} (below). An optional second initializer is -a printer function (field @code{vtable-index-printer}), used as -described under @code{make-vtable} (@pxref{Vtables}). +In this example, we can say that @code{$1} is an instance of @code{$2}, +@code{$2} is an instance of @code{$3}, @code{$3} is an instance of +@code{$4}, and @code{$4}, strangely enough, is an instance of itself. +The value bound to @code{$4} in this console session also bound to +@code{} in the default environment. -@sp 1 -@var{user-fields} is a layout string giving extra fields to have in -the vtables. A vtable starts with some base fields as per @ref{Vtable -Contents}, and @var{user-fields} is appended. The @var{user-fields} -start at field number @code{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, +@defvr {Scheme Variable} +A meta-vtable, useful for making new vtables. +@end defvr -@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") -@end example +All of these values are structures. All but @code{$1} are vtables. As +@code{$2} is an instance of @code{$3}, and @code{$3} is a vtable, we can +say that @code{$3} is a @dfn{meta-vtable}: a vtable that can create +vtables. -@var{tail-size} is the size of the tail array in the vtable-vtable -itself, if @var{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 @samp{pW} holds the tail array size, as -usual, and is followed by the extra space. +With this definition, we can specify more precisely what a vtable is: a +vtable is a structure made from a meta-vtable. Making a structure from +a meta-vtable runs some special checks to ensure that the first field of +the structure is a valid layout. Additionally, if these checks see that +the layout of the child vtable contains all the required fields of a +vtable, in the correct order, then the child vtable will also be a +meta-table, inheriting a magical bit from the parent. + +@deffn {Scheme Procedure} struct-vtable? obj +@deffnx {C Function} scm_struct_vtable_p (obj) +Return @code{#t} if @var{obj} is a vtable structure: an instance of a +meta-vtable. +@end deffn + +@code{} is a root of the vtable tree. (Normally there +is only one root in a given Guile process, but due to some legacy +interfaces there may be more than one.) + +The set of required fields of a vtable is the set of fields in the +@code{}, and is bound to @code{standard-vtable-fields} +in the default environment. It is possible to create a meta-vtable that +with additional fields in its layout, which can be used to create +vtables with additional data: @example -(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") +scheme@@(guile-user)> (struct-ref $3 vtable-index-layout) +$6 = pruhsruhpwphuhuhprprpw +scheme@@(guile-user)> (struct-ref $4 vtable-index-layout) +$7 = pruhsruhpwphuhuh +scheme@@(guile-user)> standard-vtable-fields +$8 = "pruhsruhpwphuhuh" +scheme@@(guile-user)> (struct-ref $2 vtable-offset-user) +$9 = module @end example -The optional @var{print} argument is used by @code{display} and -@code{write} (etc) to print the vtable-vtable and any vtables created -from it. It's called as @code{(@var{print} vtable port)} and should -look at @var{vtable} and write to @var{port}. The default is the -usual structure print function, which just gives machine addresses. -@end deffn +In this continuation of our earlier example, @code{$2} is a vtable that +has extra fields, because its vtable, @code{$3}, was made from a +meta-vtable with an extended layout. @code{vtable-offset-user} is a +convenient definition that indicates the number of fields in +@code{standard-vtable-fields}. + +@defvr {Scheme Variable} standard-vtable-fields +A string containing the orderedq set of fields that a vtable must have. +@end defvr + +@defvr {Scheme Variable} vtable-offset-user +The first index in a vtable that is available for a user. +@end defvr @deffn {Scheme Procedure} make-struct-layout fields @deffnx {C Function} scm_make_struct_layout (fields) Return a structure layout symbol, from a @var{fields} string. @var{fields} is as described under @code{make-vtable} (@pxref{Vtables}). An invalid @var{fields} string is an error. +@end deffn + +With these definitions, one can define @code{make-vtable} in this way: @example -(make-struct-layout "prpW") @result{} prpW -(make-struct-layout "blah") @result{} ERROR +(define* (make-vtable fields #:optional printer) + (make-struct/no-tail + (make-struct-layout fields) + printer)) @end example -@end deffn -@defvr {Scheme Variable} vtable-offset-user -@defvrx {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 @var{user-fields} in -@code{make-vtable-vtable} above. -@end defvr -@sp 1 -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. +@node Vtable Example +@subsubsection Vtable Example -@lisp -(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 "#" - (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 @result{} # -@end lisp +Let us bring these points together with an example. Consider a simple +object system with single inheritance. Objects will be normal +structures, and classes will be vtables with three extra class fields: +the name of the class, the parent class, and the list of fields. + +So, first we need a meta-vtable that allocates instances with these +extra class fields. + +@example +(define + (make-vtable + (string-append standard-vtable-fields "pwpwpw") + (lambda (x port) + (format port "< ~a>" (class-name x))))) + +(define (class? x) + (and (struct? x) + (eq? (struct-vtable x) ))) +@end example + +To make a structure with a specific meta-vtable, we will use +@code{make-struct/no-tail}, passing it the computed instance layout and +printer, as with @code{make-vtable}, and additionally the extra three +class fields. + +@example +(define (make-class name parent fields) + (let* ((fields (compute-fields parent fields)) + (layout (compute-layout fields))) + (make-struct/no-tail + layout + (lambda (x port) + (print-instance x port)) + name + parent + fields))) +@end example + +Instances will store their associated data in slots in the structure: as +many slots as there are fields. The @code{compute-layout} procedure +below can compute a layout, and @code{field-index} returns the slot +corresponding to a field. + +@example +(define-syntax-rule (define-accessor name n) + (define (name obj) + (struct-ref obj n))) + +;; Accessors for classes +(define-accessor class-name (+ vtable-offset-user 0)) +(define-accessor class-parent (+ vtable-offset-user 1)) +(define-accessor class-fields (+ vtable-offset-user 2)) + +(define (compute-fields parent fields) + (if parent + (append (class-fields parent) fields) + fields)) + +(define (compute-layout fields) + (make-struct-layout + (string-concatenate (make-list (length fields) "pw")))) + +(define (field-index class field) + (list-index (class-fields class) field)) + +(define (print-instance x port) + (format port "<~a" (class-name (struct-vtable x))) + (for-each (lambda (field idx) + (format port " ~a: ~a" field (struct-ref x idx))) + (class-fields (struct-vtable x)) + (iota (length (class-fields (struct-vtable x))))) + (format port ">")) +@end example + +So, at this point we can actually make a few classes: + +@example +(define-syntax-rule (define-class name parent field ...) + (define name (make-class 'name parent '(field ...)))) + +(define-class #f + width height) + +(define-class + x y) +@end example + +And finally, make an instance: + +@example +(make-struct/no-tail 400 300 10 20) +@result{} < width: 400 height: 300 x: 10 y: 20> +@end example + +And that's that. Note that there are many possible optimizations and +feature enhancements that can be made to this object system, and the +included GOOPS system does make most of them. For more simple use +cases, the records facility is usually sufficient. But sometimes you +need to make new kinds of data abstractions, and for that purpose, +structs are here. @node Tail Arrays @subsubsection Tail Arrays -- 2.20.1