Carl Sturtivant
Computer Science & Engineering Department
University of Minnesota
Gregg M. Townsend
Department of Computer Science
The University of Arizona
www.cs.arizona.edu/icon/uguide/extlvals.htm
Last updated March 25, 2010
External values provide a way for dynamically loaded C functions to create and return opaque data structures to an Icon program. This allows state to be maintained across multiple calls of loaded functions.
The creation of an external value in C defines not just the data itself but also some related attributes. These control the sorting behavior, image, and other such aspects. Icon provides defaults and also adds a serial number similar to those of lists, sets, and tables.
The specification of attributes effectively creates a number
of distinct types.
Each loadable library can define the types it needs for its own purposes.
From an object-oriented perspective, these can be seen as subtypes
of a common external
type.
External values are opaque to Icon. They are created only by dynamically loaded C functions, any of which in turn may have access to their internals. In this way a library of Icon functions to work with a specific kind of external value is possible. However, such values can be seen and manipulated (assigned, passed to procedures or functions, sorted, copied, etc.) in Icon. The behavior of an external value in Icon may be modified to some extent by the implementation of the dynamically loaded function that is used to create it, as described below.
In what follows let E
, E1
, and E2
be external values
produced by some loaded function or functions. Icon prescribes the following
default behavior of such values, which is exhibited if the functions that
created them did not override such at the time of creation. Otherwise behavior
in the following contexts is determined by the external values' creators.
External values always sort after values of all other types.
Within themselves external
values are first sorted by type name (which is a set by the creator,
is returned by the Icon function type
,
and specifies a subtype of the external type).
The default sort within such a subtype is by serial number. Only sorting
within a subtype (i.e., externals with a specific type name) can be overridden
by the creator. See the next section for a description of the relevant C
internals. The default behavior follows.
type(E)
- returns the string
"external"
image(E)
- returns a string indicating the type, serial number, and the number of data words, e.g.
"external_12(3)"
copy(E)
- returns
E
itself without copyingsort()
- external values sort first by type name and then by serial number
E1 === E2
- produces
E1
whenE1
andE2
are the same external object; otherwise failsE1 ~=== E2
- produces
E2
whenE1
andE2
are distinct; otherwise fails
These next sections describe the C interface for a reader who is familiar with the use of loadable functions.
An Icon external value is implemented by a descriptor that points to an external block containing several components. The data area and the function list are the most important of these.
An integer word count indicates the size of the data area. This is specified when the external block is created. Often external data is a single pointer to other data (a handle) and so only one word is required.
The function list allows the programmer to override, for all values of the same external type, the default behaviors listed in the previous section. The list is a callback table pointing to programmer-defined C functions as described in more detail below. The function list also acts as a unique type identifier, because external values with different function lists behave as values of distinct type.
A dynamically loaded C function allocates an external value by including
ipl/cfuncs/icall.h
and calling alcexternal
:
externalblock *alcexternal
(long size, funclist *funcs, void *data)- allocates and returns a pointer to an external block.
size specifies the number of bytes in the entire external block (and by implication the size of the data block inside it).
funcs, if not null, specifies a list of functions to override the default behavior. See the next section.
data, if not null, specifies the location of data that is copied in to initialize the data block (until it is full).
A typical call might be
blk = alcexternal(sizeof(externalblock) + sizeof(mydata), funcs, &mydata);The result is returned to Icon by the macro call
RetExternal(
blk)
.
The block may eventually be freed by garbage collection if it
is not saved or if it later becomes inaccessible to the Icon program.
A loadable function that accepts an external value as an argument can call
ArgExternal(i,f);to validate argv[i] as an external value of the type associated with function list f, and can then call
blk = ExternalBlock(i);to get the address of the associated external block; the associated data is at blk–>data.
A more complete example is found in the file
ipl/cfuncs/external.c
.
The function list associated with an external value is a struct containing pointers to C functions. It is reminiscent of a "dispatch table" or "class pointer" for dynamic method calls in an implementation of an object oriented programming language. Indeed an Icon external value is very much like a traditional object with its own data and methods. Typically such a function list would be static and shared among many Icon external values of the same kind ("class" or "type").
Every external value has a function list; a default list is supplied if NULL is passed to alcexternal. A null entry within a function list produces the default behavior for the associated action.
Functions in the list use the same interface as loadable C functions. Incoming arguments are passed beginning at argv[1]. A result is produced by storing it in argv[0] and returning 0 as the outcome of the function. When extlcmp is called, argc has a value of 2; for the other functions, argc is 1.
The possible custom functions are as follows:
int extlcmp
(int argc, descriptor *argv)- returns an Icon integer for use in sorting two external values that both have this function list and are therefore considered to be of the same external subtype. The function result should be negative if the first external value is deemed less than the second, zero if they are deemed equal, and positive if the first is deemed greater than the second. This overrides the default behavior of the
sort
function which compares serial numbers.int extlcopy
(int argc, descriptor *argv)- returns an external value defined as a copy of its argument. This overrides the default behavior of the
copy
function, which is to return another reference to the external value without copying.int extlname
(int argc, descriptor *argv)- returns an Icon string naming the type of the external value. This overrides the default behavior of the
type
function, and thus also affects the ordering relative to other external values when sorting.int extlimage
(int argc, descriptor *argv)- returns an Icon string to serve as the image of the external value. This overrides the default behavior of the
image
function.
A descriptor for an external value has a vword containing the bit pattern D_External which contains the value T_External indicating the external type, along with the bit F_Nqual indicating that the value is not a string and the bit F_Ptr indicating to the garbage collector that the dword is a pointer that needs tending. The dword contains a pointer to an external block. This block is implemented as a C struct with a pointer to a function block C struct as follows. The structs below, along with types word and descriptor, are defined in ipl/cfuncs/icall.h.
typedef struct funclist { /* list of user defined callbacks */ int (*extlcmp) (int argc, descriptor *argv); /* compare */ int (*extlcopy) (int argc, descriptor *argv); /* copy */ int (*extlname) (int argc, descriptor *argv); /* type name */ int (*extlimage) (int argc, descriptor *argv); /* image */ } funclist; typedef struct externalblock { word title; /* the block header, including type */ word size; /* the number of bytes in the block */ word id; /* the serial number */ funclist *funcs; /* pointer to the callback list */ word data[]; /* arbitrary custom data */ } externalblock;
A call of alcexternal initializes all fields of an external block. Data is copied without interpretation, and the function list pointer is stored. The header is assigned an appropriate constant for the garbage collector, and the size is set. alcexternal maintains a static count of the number of external blocks allocated, and this is incremented and assigned to id.
The following error code numbers are assigned for use with external values:
errno errtext meaning 131
external expected
not an external value 132
incorrect external type
external of wrong flavor 133
invalid external value
right flavor in wrong context 134
malformed external value
data is bogus, not just inappropriate