Search                        Top                                  Index
REF SHADOWCLASS                                     R.Evans January 1991

       COPYRIGHT University of Sussex 1991. All Rights Reserved.

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<                             >>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<      SHADOWCLASS DATA       >>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<         STRUCTURES          >>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<                             >>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

LIB * SHADOWCLASS and the underlying  LIB * SHADOWKEY provide a  utility
designed  to  bridge  the  gap   between  ordinary  Poplog  record   and
vectorclasses and external class  specifications as used by  * defexacc.
They allow  the creation  of data  structures which  are  simultaneously
well-behaved   Poplog   records   (with   proper   Poplog   objects   as
sub-structures etc.) and flexible  external C-struct-like records,  with
embedded structs, fixed and variable length arrays (including arrays  of
structs), etc.

         CONTENTS - (Use <ENTER> g to access required sections)

  1   Introduction

  2   Shadowclass data structures

  3   Shadowclass attributes

  4   LIB SHADOWKEY

  5   LIB SHADOWCLASS

  6   Examples



---------------
1  Introduction
---------------

Ordinary Poplog record and vectorclass definitions provide a wide  range
of field types allowing many of the struct definitions used by  external
languages to  be modelled  directly. Furthermore,  use of  fixed-address
structures allows  complex structures  which contain  pointers to  other
structures to be constructed in  a manner compatible with most  external
requirements. However, there remain number of deficiencies in the use of
Poplog data structures in this way:

    #   Poplog does not allow  'embedded' substructures in a  structure.
        Substructures are  always POINTERS  to objects  rather than  the
        fields themselves  'exploded'  into  the  parent  structure.  In
        languages such  as C,  embedded  substructures are  common:  for
        example subrecords  (or  fixed-length  arrays)  within  records,
        arrays   of   subrecords    (or   fixed-length   arrays),    and
        variable-length arrays  as final  fields in  a record  structure
        (giving variable length records).

    #   Poplog does not allow  mixed record/vector structures -  records
        whose final field is a variable-length vector.

    #   It  is  not  particularly  easy  to  build  fixed-address   data
        structures: standard  constructor  procedures and  syntax  words
        produce non-fixed structures.

    #   The Poplog and external forms  of the substructure data are  not
        always the  same, which  can  lead to  bookkeeping  difficulties
        about retaining references to  the Poplog version. For  example,
        if an external class object is assigned into an :exptr field  of
        a structure,  only  the  external_ptr field  is  copied  in.  In
        particular the parent structure does not maintain a reference to
        the external  class  object,  so  it  may  inadvertently  become
        garbage, even though the parent structure is still dependent  on
        it.

The shadowclass facilities provide  support to overcome these  problems.
They allow  the  creation  of  fixed-address  external  data  structures
(external-class Poplog  records  - see  REF * EXTERNAL_DATA)  which  are
'shadowed' by Poplog records  that retain references  to all the  Poplog
objects associated  with  the  field contents,  and  they  automatically
coerce between Poplog and external forms.




------------------------------
2  Shadowclass data structures
------------------------------

Like other Poplog dataclass  types, shadowclasses are defined  primarily
by typespecs.  However  shadowclasses allow  almost  the full  range  of
typespecs, including  embedded structs,  array fields,  variable  length
arrays as final fields. In fact, the only restrictions on typespecs  (as
defined in REF * DEFSTRUCT,  including the  'external data'  facilities)
are:

    #   procedure specifications are not permitted

    #   'Poplog' vector specifications,  consisting of a simple typespec
        are not  permitted:  use the  'external' array notation  (:int[]
        etc) instead.

Such typespecs generally include  specifications for coercion of  values
between Poplog representation and  external representation (basic  field
specs, 'implicit  access' and  conversion procedures  etc.). Taken  as a
whole, a  typespec without  embedded struct  fields provides  a  mapping
between a  sequence of  Poplog  data items,  and  a formatted  block  of
external memory,  mediated  by  the value  coercion  specifications.  An
embedded struct in a typespec corresponds itself to a mapping  between a
sequence of  Poplog items  and a  subportion of  the block  of  external
memory. Representing the  sequences of  Poplog items  with vectors,  the
general picture then is of a mapping between a tree structure of  Poplog
items - a vector  of 'top-level' fields, with  values for simple  fields
and subvectors  for embedded  struct  fields -  and  a single  block  of
formatted external memory. It is  this mapping that shadowclass  records
represent.

When a shadowclass record is created, a fixed-address area of memory  is
allocated to hold the external form of the data. Furthermore the  record
is an  external-class record  whose external_ptr  field points  to  this
block of memory, so that the record can be used directly as an  external
instance  of   the  typespec   in   external  procedure   calls,   exacc
constructions etc.. Thus the record can be treated as though it were  an
external struct.  In addition,  however, the  tree structure  of  Poplog
items  is  created  and  used  to  maintain  references  to  the  Poplog
correlates of the  external data fields.  This allows the  record to  be
viewed much like an ordinary Poplog  data class - assigning Poplog  data
items into  fields and  retrieving  them (preserving  strict  identity),
correct  garbage  collector  behaviour  etc..  So  the  record  can   be
conveniently viewed as an 'ordinary' Poplog data structure too.

Shadowclass   fields   are   generally   accessed   via   their   Poplog
representations. If you update  a field, you supply  the Poplog form  of
that field,  and  the update  procedure  runs the  appropriate  typespec
converters to update the external representation. If you access a  field
then by default you  are simply returned  the Poplog representation  (on
the basis that the  two representations are  by default consistent  with
each other, and so checking the external form is just a waste of  time).
However it may well happen that  the external form has been updated  (eg
by external code), so  that the Poplog  representation is incorrect.  In
such cases, special 'refreshing' accessors can be used, which coerce the
external  form  back  into   a  Poplog  form   and  then  return   that.
Alternatively the entire  structure can be  refreshed from its  external
form if desired.

A  further  important  feature  of  shadowclasses  is  the  ability   to
manipulate  embedded  struct  fields  without  creating  spurious   data
structures. If you access a field which is actually an embedded  struct,
the shadowclass  routines  (by  default)  create  an  instance  of  that
embedded struct (which is a COPY,  ie the fields of the embedded  struct
are copied  into  the  freestanding instance)  and  return  that  (often
another shadowclass instance). Similarly, on update, an instance of  the
struct is expected, and  its contents are copied  into the main  struct.
However this often generates unwanted data structures, especially  since
'dest' and 'cons' are treated  as though they were access/assignment  to
all the fields - constructing an array of substructs, for example, would
involve creating  instances  of all  the  substructs to  initialise  the
struct, and then probably throwing them all away.

To overcome this, shadowclass allows an alternative format for the  data
supplied and returned  for its  fields. As well  as this  'constructive'
format, where substructs are  presented as real  struct records of  some
sort, it is possible to create 'non-constructive' versions of the access
procedures. These expect their field  data to be 'recursively  exploded'
on the stack. That is, they expect all the lowest level simple fields of
the struct to be presented  (in the right order),  and they fill in  the
Poplog vector-tree representation and  the external representation  from
them. Similarly, they  return fields  in exactly the  same format.  This
allows  fields  to  be  manipulated  without  creating  any  superfluous
structures.

Note that when  using the non-constructive  format, any variable  length
structure will have a final  count argument supplied. This count  should
be the size of the array constituting the variable length field (NOT the
number of stacked items used to construct it, which may be larger if  it
consists of recursively exploded embedded structs).

Finally, shadowclass allows the  specification of a record-props  value.
That  is,  a  value   can  be  specified  which   is  assigned  to   the
'external_ptr_props' field of every instance of the shadowclass when  it
is created. This  is used, for  example, by the  X Toolkit libraries  to
specify props  for  shadowclasses which  conform  with the  'weak'  type
checking used by the Toolkit interface (see REF * XptDescriptor).




-------------------------
3  Shadowclass attributes
-------------------------

consshadowkey and shadowclass both accept lists of attributes which  are
used to specify the detail of their behaviour. These are ordinary  Pop11
lists consisting of attribute specifications. Attributes names are words
and fall into two  groups, those which take  no argument ("fast",  "nc",
"refresh") and  those which  take an  argment ("props",  "typespec"  and
"prefix"). Commas between attributes (but  not between an attribute  and
its argument)  are required  (currently there  are no  checks for  their
presence, but there may be  in future releases). Attribute testing  uses
simple list membership - the absence  of an attribute selects a  default
value, and the presence of superfluous attributes is ignored.

consshadowkey uses  its  attribute list  for  the specification  of  the
shadowclass instance  props  (using  the  "props"  attribute),  and  for
specification of  "fast", "nc"  and  "refresh" attributes  for  accessor
procedures. shadowclass  uses in  addition the  "prefix" and  "typespec"
attributes for controlling  the names  of variables  initialised by  the
declaration. shadowclass  allows  multiple  attribute  lists,  each  one
specifying a combination of parameters  for the class access  procedures
to be  created. (This  generally only  makes sense  if each  list  has a
different "prefix" attribute,  otherwise variable assignments  requested
by later attribute lists simply override those requested by the  earlier
ones.)

    fast
        This attribute  controls whether  the procedures  created  check
        their arguments. If present, access procedures do no checks.  If
        absent, access  procedures will  check for  an instance  of  the
        right shadowclass, and subscripters will check the subscript  is
        in range.

    nc
        This attribute controls the format of the embedded  substructure
        data expected and returned by the relevant access procedures. If
        "nc" is  specified, accessors  are 'non-constructive',  that  is
        they do not  expect or  create new Poplog  data structures  when
        embedded structs are  accessed. Instead  they expect/return  the
        fields of the substructure recursively exploded on the stack. If
        "nc" is not specified, access procedures expect/return instances
        of the substructure  as discussed above  (note: these  instances
        are COPIES of  the substructure  data, not  pointers 'into'  the
        parent structure).

    refresh
        This  attribute controls whether accessors 'refresh' the  Poplog
        representation from the value of  the external one. The  default
        is not to  refresh, ie to  assume that the  Poplog and  external
        representations are  consistent with  each other.  A  refreshing
        subscripter will update the Poplog  value from the external  one
        before returning it.  (Note: there is  also a refresh  procedure
        which automatically  refreshes  the entire  structure  from  its
        external representation).

    props <data>
        This attribute  specifies  a  value  for  th PROPS argument for
        consshadowkey,  that  is  a  value  to  be  assigned  to   the
        'external_ptr_props' of  instances  when they  are  created.  If
        absent,  the   props  of   instances  will   be  false.   (NOTE:
        shadowclass  passes  the  FIRST  attribute  list  supplied  to
        consshadowkey, so  only a  props specification  in this  first
        list will have any effect.)

    prefix <word>
        This attribute,  used by  shadowclass only, specifies a prefix
        to   be  attached   to   the  identifiers   created   by
        shadowclass which  respect  this attribute  list.  (See  final
        section of 'LIB  SHADOWCLASS' below  for a  list of  identifiers
        affected). This allows multiple attribute lists to be  specified
        (creating multiple sets of procedures), without name clashes. If
        no prefix  is specified,  no  prefix is  attached to  the  names
        listed below.

    typespec <word>
        This  attribute,  used  by  shadowclass  only,  specifies  a
        typespec name which  is initialised to  the given spec.  Without
        it, shadowclass  does  not  create any  new  types.  Thus  for
        instance the following declaration:

            shadowclass fooptr [typespec foo] {a:int, b:int};

        is equivalent to:

            p_typespec foo {a:int, b:int};
            shadowclass fooptr {:foo};

        Notice here  that  shadowclass, like  exacc,  etc.,  effectively
        ignores {...} brackets around single unnamed fields.




----------------
4  LIB SHADOWKEY
----------------

LIB * SHADOWKEY  provides  the   procedural  interface  to   shadowclass
construction  and  is  used  by  the  more  convenient  syntactic  form,
LIB * SHADOWCLASS  (described   below).  The   principal  procedure   is
consshadowkey,  which  creates  a  new  shadowkey  record.   Shadowclass
management routines (create, destroy, access etc.) can then be  obtained
from the key using the routines shadowclass_cons, shadowclass_dest etc..
This mechanism is designed by analogy  with system keys and their  class
procedures (class_cons, class_dest etc. - see REF * KEYS). Note  however
that unlike  keys, shadowkeys  are just  ordinary user  records with  no
special system status.


consshadowkey(name,spec,props) -> shkey                      [procedure]
        This procedure  creates a  new  shadowkey record,  shkey,  which
        determines  a  new  shadowclass.  name  is  a  word  giving  the
        shadowclass a  name (used  for printing  class instances  etc.).
        spec is a valid data specification, subject to the  restrictions
        discussed below. props is either an attribute list (as described
        above) or an  arbitrary non-list Poplog  item. It specifies  the
        contents of the 'external_ptr_props'  field of new instances  of
        the class. In the attribute list case, the 'props' attribute (or
        <false> if  there is  no 'props'  attribute) is  used for  these
        instance props, otherwise props itself is used.


Shadowkey records should be treated  as opaque objects with no  directly
accessible internal structure. Their primary use is as arguments to  the
following routines which return the class-specific management routines.


shadowclass_recognise(shkey) -> proc                         [procedure]
        This  procedure   returns  a   recogniser  procedure   for   the
        shadowclass represented by shkey. That  is, proc is a  procedure
        which takes  a single  argument and  returns <true>  or  <false>
        according to whether  that argument  is an  instance of  shkey's
        shadowclass.


shadowclass_cons(shkey,flags) -> proc                        [procedure]
        This procedure returns a constructor procedure for instances  of
        the shadowclass represented by shkey. flags is an attribute list
        which specifies what kind  of constructor procedure is  required
        (see above). The arguments expected  by proc depend on the  spec
        of the shadowclass and the data format information contained  in
        flags  (see  'Attribute  Lists'  above),  and  proc  returns  an
        instance of shkey as its result. The 'external_ptr_props'  field
        of the instance created will be set to the value passed as third
        argument to consshadowkey


shadowclass_init(shkey) -> proc                              [procedure]
        This procedure returns an initialiser procedure for instances of
        the shadowclass represented by shkey. proc expects no  arguments
        for a  fixed-length  shadowclass,  and a  single  integer  for a
        variable  length  shadowclass  (the   length  of  the   variable
        component). It returns  an instance  of shkey  with full  fields
        initialised to undef  and :exptr  fields initialised  to a  null
        external pointer. All  other fields are  initialised by  setting
        their external  representation  to  zero and  then  setting  the
        Poplog representation by accessing  the external data using  the
        typespec for the  field. The 'external_ptr_props'  field of  the
        instance created  will  be set  to  the value  passed  as  third
        argument to consshadowkey


shadowclass_dest(shkey,flags) -> proc                        [procedure]
        This procedure returns a  destructor procedure for instances  of
        the shadowclass represented by  shkey. proc expects an  instance
        shkey  as  its  single   argument  and  returns  the   component
        substructures in a format specified  by the flags argument  (see
        'Attribute Lists' above).


shadowclass_fill(shkey,flags) -> proc                        [procedure]
        This procedure returns  a fill  procedure for  instances of  the
        shadowclass represented  by  shkey.  proc  expects  an  instance
        shkey, plus arguments  for constructing  a new  instance in  the
        format specified by  the flags argument  (see 'Attribute  Lists'
        above), and updates  the entire shkey  instance with the  values
        given. It returns the shkey instance.


shadowclass_refresh(shkey) -> proc                           [procedure]
        This procedure returns a refresh procedure for instances of  the
        shadowclass represented  by  shkey.  proc  expects  an  instance
        shkey,  which  it  returns,  after  refreshing  the   'internal'
        representation from the 'external' one (see above).


shadowclass_import(shkey) -> proc                            [procedure]
        This procedure returns an import procedure for instances of  the
        shadowclass represented by shkey.

        proc expects an external  pointer to an external  representation
        of the  shkey  shadowclass.  In  the  case  of  variable  length
        structures the external pointer should be followed by a  length.
        proc returns an  instance of  the shadowclass  shkey which  will
        refer to the external representation pointed to by the  external
        pointer passed to the procedure.

        If the import procedure is called with an external pointer  that
        has already been imported with proc then the previously imported
        shadowclass is returned, with its internal field's refreshed  by
        its   shadowclass_refresh   procedure.   For   variable   length
        structures  this  only  applys  when  the  previously   imported
        structure has the same length.


shadowclass_subscr(shkey,flags) -> proc                      [procedure]
        This procedure returns a subscripter procedure for instances  of
        the shadowclass represented by  shkey. proc expects an  instance
        of shkey and an  index and returns/updates  that element of  the
        instance.  The  format  of  the  substructure  returned/expected
        depends on  the flags  argument (see  'Attribute Lists'  above).
        NOTE: subscripter procedures currently work for both vector  and
        record-type structures. However this should not be relied  upon:
        a future version  of this library  may not support  subscripting
        into record structures.


shadowclass_field(fnum,shkey,flags) -> proc                  [procedure]
        This procedure returns a field-access procedure for instances of
        the shadowclass represented by  shkey. proc expects an  instance
        of shkey and an index  and returns/updates the fnum'th field  of
        that instance. The format of the substructure  returned/expected
        depends on the flags argument (see 'Attribute Lists' above).


Additional identifiers defined by LIB * SHADOWKEY:


shadow_construct -> property                         [constant property]
        To support the  'constructive' accessors  for embedded  structs,
        the shadowclass libraries  include a mapping,  shadow_construct,
        which they use to decide what structure to return/expect for  an
        embedded struct field. This is a property mapping from  typespec
        data   structures   to   procedures   for   converting   between
        non-constructive and constructive formats. The base procedure is
        a destructor - given an  instance of the substruct structure  it
        recursively explodes  it  into  its  fields.  The  updater  is a
        constructor, given the recursively exploded fields it creates an
        instance. This property is maintained by consshadowkey, so  that
        every typespec used in a shadowclass will be recognised if  used
        as a substruct in another shadowclass definition (regardless  of
        whether a named  type is  created for it,  using the  "typespec"
        attribute). In addition  it is publically  available and can  be
        augmented by the user to provide sensible constructive behaviour
        for non-shadowclass data. Note that look-up uses == on the  spec
        structure occurring in the parent structure, so two = specs  can
        have different constructive behaviour, but two instances of  the
        same NAMED typespec will  have the same constructive  behaviour.
        Also if no construct procedure is  found for a spec accessed  as
        an embedded struct, a simple 'vector of Poplog items' format  is
        returned/expected.


shadow_length(shrec) -> len                                  [procedure]
        This procedure takes  an instance of  a shadowclass and  returns
        its length, that  is, the  number of  'top-level' components  it
        has. The  top-level components  are  the immediate  fields  of a
        record  structure  and  the  individual  elements  of  an  array
        structure. Embedded arrays count as single top-level fields.


shadow_=(item_1, item_2) -> bool                             [procedure]
        Returns true  if item_1  and item_2  are two  equal  shadowclass
        records, false  otherwise. For  two  shadowclass records  to  be
        equal they must  have the  same props, the  same shadowkey,  and
        have the  same  Pop11/external  representations.  The  procedure
        -shadow_=- is the  -class_=- procedure  of shadowclass  records.
        This means that:

            (shadow_=(foo,baz))

        is the same as

            (foo = baz)

        NOTE: when two shadowclass records are equal, it means they have
        the same content, NOT the same address. For example:

            shadowclass int_ptr { :int };

            (consint_ptr(1) = consint_ptr(1)) =>
            ** <true>

        This is because both shadowclass  records have the same  content
        (ie, the integer 1). But:

            (consint_ptr(1) == consint_ptr(1)) =>
            ** <false>

        This is because the two records have different addresses.


isshadow(item) -> bool                                       [procedure]
        Returns true if item is a shadowclass record, false otherwise.




------------------
5  LIB SHADOWCLASS
------------------

shadowclass                                                     [syntax]
        Used to construct new shadowclasses. It uses consshadowkey  (see
        above) to create a new  shadowkey structure for the  shadowclass
        being  defined,  and  then  assigns  to  identifiers  both   the
        shadowkey itself  and some  of the  'shadowclass' procedures  it
        contains, as specified by the <attributes> field.

        The construct has the form

           shadowclass <declaration> <name> <attributes> <typespec> ;

        with the following parts:

        <declaration>
            Specifies the declaration for the identifiers to receive the
            key and the procedures, and is identical in all respects  to
            the  declaration  in  a  define  header,  including  default
            declarations  when  omitted.  (Except  that  dlocal  is  NOT
            allowed, and if "procedure" identprops are specified  either
            explicitly or by default, this applies only to the procedure
            identifiers, not to the key identifier.)

        <name>
            The name of the new class (a word).

        <attributes>
            This is  optional; if  present it  is one  or more  (square)
            bracketed  lists  of  attributes  for  the  shadowclass  and
            associated procedures. Permissible attributes are  described
            in 'Attribute Lists' above. The default value is []  causing
            'ordinary' checking accessors to be declared and created.

            Multiple attribute lists can be used to specify the creation
            of  several   groups   of  procedures   (eg   checking   and
            non-checking versions), in one call of shadowclass.

            NOTE: Attribute lists are read  using listread, so '^',  '%'
            etc. are not interpreted. However an attribute list can also
            be a literal list in proglist, allowing constructs such as

                shadowclass foo #_< [props ^myprops] >_# ...

        <typespec>
            A <typespec>  as described  above. If  this is  a  structure
            (i.e. {...}  ) it  specifies a  record class,  otherwise  it
            should be an  array spec  (eg :int[]  - NOTE:  the use  of a
            'bare' typespec to denote an array is not permitted).

            This <typespec>  part is  also  optional. When  present,  it
            triggers the  creation  of  a new  shadowclass,  that  is, a
            shadowkey  record   and  associated   class  procedures   as
            discussed  below.  If  omitted,   and  there  is   already a
            shadowclass associated with  <name> (at compile-time),  this
            declaration can  be used  to create  access procedures  with
            different attributes. For example, the following will create
            checking versions of the access procedures a and b:

                shadowclass foo {a:full, b:full};

            Subsequently this:

                shadowclass foo [nc, prefix nc_];

            will  create   non-constructive   versions  for   the   same
            shadowclass. (Note the  specification of  the prefix  "nc_".
            Without  this,  the  non-constructive  procedures  would  be
            assigned  as  REDEFINITIONS  of  the  variables  a  and  b.)
            Alternatively these two operations can be combined into:

                shadowclass foo [] [nc, prefix nc_] {a:full, b:full};

            When no <typespec> is  supplied, the <declaration> part  (if
            present) is  taken  to  apply only  to  the  new  procedures
            created, not the shadowkey itself. This allows, for example,
            the creation of lconstant access procedures for a  permanent
            shadowclass.

            If no <typespec>  is given, and  the named shadowclass  does
            not already exist, a mishap results.

            Finally, if  the  "typespec" attribute  is  specified,  then
            shadowclass declares the spec associated with the class as a
            typespec  using  p_typespec  or  l_typespec,  depending   on
            <declaration>. (The "typespec" attribute can be used both in
            an initial shadowclass declaration, to name the given  spec,
            or in a 'repeat' declaration  without a typespec, to  give a
            name  to  the  underlying   spec  of  an  already   existing
            shadowclass).


The identifiers declared  and initialised by  shadowclass fall into  two
sets. Firstly  there are  identifiers  declared if  a new  shadowkey  is
created (ie if <typespec> is present). For a shadowclass named X,  these
identifiers are:

        Identifier      Value
        ----------      -----
        X_shadowkey     (class shadowkey)
        isX             (recogniser procedure)
        importX         (importer procedure)
        initX           (initialiser procedure)
        refreshX        (refresh procedure)


Then there  are  identifiers whose  names  and data  formats  depend  on
settings in attribute  lists. If a  prefix P is  specified (the  default
prefix is empty, ie no prefix), the following are always created

        Identifier      Value
        ----------      -----
        PconsX          (constructor procedure)
        PdestX          (destructor  procedure)
        PfillX          (fill  procedure)

The following is specific to vector shadowclasses,

        Identifier      Value
        ----------      -----
        PsubscrX        (subscripter procedure with updater)

(NOTE: unlike defclass, the non-checking  subscripter is NOT created  by
default, but only by specific request). For records, each field name  in
the structure  defines an  identifier  of that  name (prefixed  with  P)
containing the field access/update procedure (if a field name is omitted
from the structure definition, no identifier is generated). If a typspec
name T is specified, then the typespec :T is also created.




-----------
6  Examples
-----------

Suppose we  have a  C struct  type,  foo, consisting  of just  two  ints
declared thus (in C):

    typedef foo { int a,b; };

Our goal is to construct shadowclasses for the basic struct foo,  arrays
of foo, embedded foo's etc..

First we note that the Poplog notation for the typespec of the struct is
as follows:

    { a:int, b:int }

So the simplest approach using shadowclass is simply to declare:

    shadowclass fooptr { a:int, b:int };

This creates  a  shadowkey  for  the fooptr  class,  and  the  following
identifiers, (all global vars):

    # fooptr_shadowkey
    # isfooptr
    # importfooptr
    # initfooptr
    # refreshfooptr
    # consfooptr
    # destfooptr
    # fillfooptr
    # a
    # b

Using these we can create and access instances of foo's, thus:

    consfooptr(1,2) -> myfoo;
    a(myfoo) =>
    ** 1
    3 -> b(myfoo);
    myfoo =>
    ** <fooptr 1 3>

Notice that we have called the shadowclass fooptr, rather than foo. This
is because that is  really what it  is - the object  held in myfoo  is a
POINTER to a foo struct (Poplog cannot directly manipulate structs,  but
only pointers to them). Of course nothing prevented us calling the class
just 'foo', but it  is conceptually cleaner  to be accurate,  especially
when we introduce type specs below.

The fooptr records manipulated by  the above procedures can be  accessed
directly in Poplog, or passed  as arguments to external procedures  (any
procedure expecting a (foo *) argument), or imported as the result of an
expternal procedure which  returns a  (foo *).  So suppose  we have  two
simple C procedures which swap the arguments in a foo struct. The  first
does an in situ swap, while the  second returns a copy of the foo,  with
the arguments swapped.

    typedef foo {int a,b;};

    void swap1(fp)
    foo * fp;
    {   int tmp;
        tmp = fp->a;
        fp->a = fp->b;
        fp->b = tmp;
    }

    foo * swap2(fp1)
    foo * fp1;
    {   foo fp2;
        fp2.a = fp1->b;
        fp2.b = fp1->a;
        return(&fp2);
    }

A suitable exload declaration for these routines might look like this:

    exload 'swap' ['swap.o']

        swap1(1),
        swap2(1):exptr,

    endexload;

We might call swap1 as follows:

    consfooptr(1,2) -> myfoo;
    exacc swap1(myfoo);
    myfoo =>
    ** <fooptr 1 2>
    ;;; here the external values of myfoo have been swapped, but the
    ;;; Poplog representation doesn't know about it, so it looks
    ;;; unchanged - we must refresh myfoo to get the right result
    refreshfooptr(myfoo) -> ;
    myfoo =>
    ** <fooptr 2 1>

And for swap2, it might look like this:

    consfooptr(1,2) -> myfoo1;
    exacc swap2(myfoo1) -> ep;
    ;;; exacc itself will just return an external pointer result, but
    ;;; we can import it into a fooptr record
    importfooptr(ep) -> myfoo2;
    myfoo2 =>
    ** <fooptr 2 1>

An alternative, neater way of using swap2 would be like this:

    exload 'swap' ['swap.o']

        swap2(1):exptr#importfooptr,

    endexload;

    consfooptr(1,2) -> myfoo1;
    exacc swap2(myfoo1) -> myfoo2;
    ;;; here the import is handled automatically by the declared type
    ;;; of swap2's result
    myfoo2 =>
    ** <fooptr 2 1>

All the above can be achieved without reference to the foo typespec. But
to create, for example, an array of foo records, we want to write:

    shadowclass foolist :foo[];

As things stand we cannot do this, because the type :foo is not defined.
However we can define it thus:

    shadowclass fooptr [typespec foo];

This is a repeat declaration for  fooptr - requesting the creation  of a
typespec called foo  for the  underlying spec  of the  fooptr class.  We
could have  added the  [typespec foo]  attrribute list  to the  original
definition to achieve the same thing. Its effect is as if we had said

    p_typespec foo {a:int, b:int};

So now our  foolist shadowclass declaration  will be fine.  Furthermore,
the subscriptor  procedure will  create/expect  fooptr records  for  the
elements of the array. So we might write code like this:

    consfooptr(1,2) -> myfoo1;
    consfooptr(3,4) -> myfoo2;
    consfoolist(myfoo1,myfoo2,2) -> myfoolist;
    myfoolist =>
    ** <foolist {1 2} {3 4}>
    myfoolist(2) =>
    ** <fooptr 3 4>

Notice here that when myfoolist is printed the embedded foo structs  are
printed as simple vectors. This is in fact how shadowclass stores  them.
But the subscriptor  procedure returns fooptr  records. Note that  these
records are created anew on each access  - myfoolist(2) is = but not  ==
myfoo2.

Clearly this creation of fooptr structs is quite wasteful in many cases.
to avoid it,  we can use  non-constructive accessors. First  we have  to
create them, using a repeat shadowclass declaration for foolist:

    shadowclass foolist [nc, prefix nc_];

here,  we  use  the  same  typespec  as  previously,  but  request   the
nonconstructive versions of the accessor procedures. And we specify that
their names should  be prefixed  with nc_  (so they  don't override  our
normal accessors.  So now  we have  nc_consfoolist, nc_destfoolist,  and
nc_subscrfoolist, which we can use like this:

    nc_consfoolist(1,2,3,4,2) -> myfoolist;
    myfoolist =>
    ** <foolist {1 2} {3 4}>
    nc_subscrfoolist(2,myfoolist) =>
    ** 3 4

Here, instead  of  expecting/returning fooptr  records,  the  procedures
expect their  (recursively exploded)  contents on  the stack.  Note  the
final count arg in nc_consfoolist is still 2, because there are  still 2
foo records to  be built,  but since each  has two  fields, there  are 4
items on the  stack to construct  them. The subscriptor  just dumps  the
contents onto the stack. So foo records could be moved around within the
array without creating any superfluous fooptr records like this:

    nc_subscrfoolist(1,myfoolist) -> nc_subscrfoolist(2,myfoolist);

Another use of explicit typespecs is that you can use defexacc to create
'raw' accessor functions for shadowclass records. Thus the following

    defexacc raw :foo;

will create procedures raw_a and raw_b which access the EXTERNAL data of
a fooptr  record (or  any external  pointer class  object) directly.  As
before, the  refresh  procedures  may  be  needed  to  keep  the  Poplog
representations in sync.

Finally, note that the declaration part of shadowclass allows lexical as
well as permanent  scoping for  the identifiers created.  So suppose  we
have a global shadowclass fooptr as before:

    shadowclass fooptr {a:int, b:int};

and we want to talk about arrays of foos, but only inside one procedure.
We can do this:

    define myproc;
        shadowclass lconstant fooptr [typespec foo];
        shadowclass lconstant foolist [],[nc, prefix nc_] :foo[];

        ;;; here we have standard and non-constructive procs for
        ;;; foolists

        <code>
    enddefine;




--- C.all/ref/shadowclass
--- Copyright University of Sussex 1991. All rights reserved.