linked lists primer
QUESTIONS
1. How are lists handled in DERVISH?
2. What TCL verbs are there to handle Dervish lists?
3. What do I need to do to use these functions?
ANSWERS
1. How are lists handled in DERVISH?
Simple tcl lists can be used for plotting coordinates and other simple
operations. We use the list utilities in Dervish for lists of more involved
objects, such as stars and galaxies.
2. What TCL verbs are there to handle Dervish lists?
Basic Functions:
listNew create a new list
listAdd add an element to a list
listRem remove an element from a list
listDel delete an entire list
Inspecting Lists:
iterInit
iterNext
iterPrev
listToHandleArray
handleArrayDel
listApply
listPrint
Commands Specific to each list type:
fooInit
fooNew
fooDel
For these three commands, "foo" will be replaced by the type of object in
the list, for example, "star" or "galaxy".
3. What do I need to do to use these functions?
There is an example already built that has a simple "foo" object in lists.
The structure looks like this:
typedef struct foo {
TYPE type;
struct foo *next, *prev;
char *name;
int i;
long l;
float f;
} FOO;
Each object on the list has a name and three numbers (an integer, long, and
float.)
You will also need a procedure called fooInit which initializes a list of
foos. This is included in file $INT_DIR/lists/fooInit.tcl:
#
# Initialize a FOO
#
proc fooInit {foo name i l f } {
foreach el "name i l f" {
memberSet $foo $el [set $el]
}
return $foo
}
First you need to build the dervish program that has foo lists installed:
sdsshost % cp $INT_DIR/lists/fooInit.tcl .
sdsshost % cp $DERVISH_DIR/examples/Makefile .
sdsshost % cp $DERVISH_DIR/examples/foo.c .
sdsshost % cp $DERVISH_DIR/examples/foo.h .
sdsshost % cp $DERVISH_DIR/examples/dervish_foo.c .
sdsshost % cp $DERVISH_DIR/examples/tclFoo.c .
sdsshost % sdssmake dervish_foo
Notice that it created new source code files called diskio_gen.c and
diskio_gen.h, along with an executable called dervish_foo.
Now run the program and build some lists:
sdsshost % dervish_foo
Executing commands in /usr/products/IRIX/dervish/v3_1/etc/dervishStartup.tcl:
foo> source fooInit.tcl
foo> fooInit [fooNew] "First Object" 1 2.3 4.5
h0
foo> structPrint h0
type FOO
type (enum)1009
next 0x0
prev 0x0
name First Object
i 1
l 2
f 4.5
The first four lines of this structure printout are for maintaining the
linked list. The last four lines are what we set for this object. Notice
that the value of l is truncated from 2.3 to 2, since it is an integer,
but that the value 4.5 is retained in a float.
Now we can use TCL to fill up a short list:
foo> listNew FOO
h1
foo> foreach n {"Object One" "Object Two" "Object Three" } {
>> set foo [fooInit [fooNew] $n 0 1 2.3]
>> listAdd h1 $foo
>> handleDel $foo
>> }
And we can look at what this gives by:
foo> listPrint h1
type FOO
type (enum)1009
next 0x100b65b0
prev 0x0
name Object One
i 0
l 1
f 2.3
type FOO
type (enum)1009
next 0x100b6dc4
prev 0x100b5dc8
name Object Two
i 0
l 1
f 2.3
type FOO
type (enum)1009
next 0x0
prev 0x100b65b0
name Object Three
i 0
l 1
f 2.3
To delete the second item from this list:
foo> iterInit h1
h3
This initializes the handle h3 and has it point to the first one on the list.
foo> iterNext h3
h3
This has the handle h3 point to the next member of the list.
foo> listRem h1 h3
This actually removes the element of the list.
foo> listPrint h1
type FOO
type (enum)1009
next 0x100b6dc4
prev 0x0
name Object One
i 0
l 1
f 2.3
type FOO
type (enum)1009
next 0x0
prev 0x100b5dc8
name Object Three
i 0
l 1
f 2.3
foo>
------------------------------------------------------------
Appendix: Steve Kent's notes on lists
LINKED LISTS in DERVISH - Steve Kent June 23, 1993
[WARNING: I make no guarantees for the accuracy of the information herein].
[I assume that you have DERVISH installed on your system and that if you
type "setup dervish", something useful will happen].
*******************************************************************************
I. Introduction
A common function in image processing is to create and store lists of things
such as objects in an image. Typically one might store such lists in
arrays that are pre-dimensioned to some large value. Since one cannot predict
in advance how many objects will be found, if the dimensions are
picked badly, one either wastes space or ends up with lists that are too
big for the arrays. Linked lists provide an alternative mechanism to
arrays for storing these lists. The idea is that one allocates storage
for parameters for a single object at a time and then uses pointers to
connect the objects together. Although such linked lists are a bit
more complicated to use than arrays, they are more flexible and efficient
of storage and can be arbitrarily long.
NOTE: Linked lists are not the same thing at all as TCL lists. In this
document, the term list always refers to a linked list unless otherwise
specified.
A linked list starts with three members: An integer that gives the type
of things on the list, a pointer to the first
item in the list, and a pointer to the last element.
When a list is first created, all pointers are NULL.
An element is a structure whose first member is also an integer that gives
the type and whose next two members are pointers that serve
to link the list. The first pointer points to the next element in the
list; the second pointer points to the previous element in the list.
The first element in the list has its prev pointer set to NULL.
The last element in the list has its next pointer set to NULL.
The remaining members of each element are the data items that you are
interested in storing.
Schematically, here is a linked list of 3 objects where each object has a row,
column, and counts field:
---------
| Type |
---------
---------------------------- | First |
| ---------
| | Last | -------------------------------
| --------- |
| |
| |
| Object 1 Object 2 Object 3 |
| |
| --------- --------- --------- |
---> | Type | <----- ----> | Type | <----- ----> | Type | <------
--------- | | --------- | | ---------
| Next | -----|--- | Next | -----|--- | NULL |
--------- | --------- | ---------
| NULL | -------- | Prev | -------- | Prev |
--------- --------- ---------
| Row | | Row | | Row |
--------- --------- ---------
| Col | | Col | | Col |
--------- --------- ---------
| Cnts | | Cnts | | Cnts |
--------- --------- ---------
The type field everywhere is a value that indicates that this list contains
OBJECTs.
Dervish provides both C and TCL level functions to create lists, add and
remove elements from lists, and traverse lists. There are also TCL only
routines to print out the contents of lists or their elements.
WARNING: The TCL routines are not yet very robust. It is possible to do
apparently sensible things (which aren't) that cause the program to core dump.
*******************************************************************************
II. CREATING NEW LIST TYPES
I will assume that you know how to compile and link a new version of dervish
using the template Makefile and dervish_template.c file that are found in
$DERVISH_DIR/example.
Each list has a type associated with it (e.g., one could have OBJECTS,
STARS, STREAKS, whatever). Dervish does not have ANY built-in types. For
each type of list that you want, you need to write some supporting C
code. Here is a list of the things that need to be created for each
new type of list:
1) A .h include file that defines a structure for an element in the list.
2) A .c file that has two routines: one to create a new element (e.g.,
by using shMalloc) and one to delete an element (e.g., by using
shFree).
3) A .c file that creates two new Tcl commands to interface to the create
and delete routines.
In addition, you need to modify the following files:
4) $DERVISH_DIR/include/types.h to add a new definition of your list.
5) $DERVISH_DIR/examples/Makefile to add your new .c files to the make process.
6) $DERVISH_DIR/examples/dervish_foo.c to add code to define your new TCL
commands
The $DERVISH_DIR/examples has a set of files to illustrate the above process.
They contain code to create a structure called FOO that can be linked into
lists. The FOO structure is not very interesting - it contains just 3 numbers.
Here a sample of how to create a version of dervish that has FOO objects in
it.
1. Create a new subdirectory and cd there.
2. setup dervish (hopefully this gives you v3_1 or later of dervish - if not,
get it or forget the rest of this document).
3. cp $DERVISH_DIR/examples/foo.c .
cp $DERVISH_DIR/examples/foo.h .
cp $DERVISH_DIR/examples/tclFoo.c .
cp $DERVISH_DIR/examples/dervish_foo.c .
cp $DERVISH_DIR/examples/Makefile .
4. Now type
sdssmake dervish_foo
This should compile a new version of dervish called dervish_foo. Along the
way, it creates new source files called diskio_gen.c and diskio_gen.h
that will sit in your current directory.
*******************************************************************************
III. Using Lists
A. TCL Level
You can now run the program dervish_foo and try out
the TCL level list manipulation
functions. These are documented more fully in the help file tclLists.html
located in the $DERVISH_DIR/doc/www area. Here is a list of all the
TCL level commands and their arguments:
listNew
listAdd [where]
listDelIfEmpty
listDel *
listRem
iterInit *
iterNext *
iterPrev *
listToHandleArray *
handleArrayDel *
listApply *
listPrint
NOTE: commands marked with (*) are implemented as tcl procedures and are
defined in the dervish startup file. Normally, this file is loaded when
you first run dervish provided you did "setup dervish" first. If you forgot
to do so, then these commands will not exist.
In addition, you have created two new commands:
fooNew
fooDel
CAUTION: At the tcl level, handles are used to refer to both lists and
elements of lists. [If you don't know what handles are, they are symbolic
names that are used to refer to C structures and so forth. When you type
fooNew for example, it prints out the name of the handle that refers to
the FOO element that you have just created]. Handles are also used to
refer to other things that are not lists or capable of being on lists
(regions, for example). Not all of the TCL list code is yet capable of
checking to see that a handle refers to a valid thing for the type of
operation that it is about to carry out.
B. C Level
Much of the time you will want to used linked lists at the C level.
Here is a list of the C commands that let you use lists:
Descriptions of C functions
shListNew Create a new list
shListDel Delete an existin list
shListAdd Add an element to a list
shListRem Remove an element from a list
The definitions of these functions and how to use them are given in
the document $DERVISH_DIR/doc/www/lists.html. There are some additional
functions defined there that I did not list above. Note that there are
no equivalents to the TCL commands that let you initialize and travserse
a list. You are expected to do so explictly by using the pointers in
the linked list structures. Actually, it would not be difficult to create
generic C routines that peform those functions. Perhaps someone wants to
do so?
*******************************************************************************
IV. More on creating Linked Lists
The above foo example was rather easy to implement and did not give
you a chance to see all that is involved in making new list types.
Here I will explain how to make a new list type called OBJECT that
looks like the example given in section I.
The steps are as follows:
1. Make copies of the files foo.c, foo.h, and tclFoo.c and call them
object.c, object.h, and tclObject.c.
2. Copy the file $DERVISH_DIR/include/types.h to your local working directory.
3. Edit the files and change all the foo references to object.
4. Edit object.h, so that the OBJECT typedef looks like the following:
typedef struct object {
TYPE type;
struct object *next, *prev;
float row; /* New line */
float col; /* New line */
float cnts; /* New line */
} OBJECT;
5. Edit object.c so that the procedure shObjectNew looks as follows:
OBJECT *
shObjectNew(void)
{
OBJECT *new = shMalloc(sizeof(OBJECT));
new->type = TYPE_OBJECT;
new->next = new->prev = NULL;
new->row = new->col = new->cnts = 0.; /* New line */
return(new);
}
6. Edit the files tclFoo.c and tclObject.c. Near the top of the file
where the various include statements are listed, insert the following line
#include "types.h"
immediately after the line #include "tcl.h". This causes these routines
to use the local copy of the header file types.h rather than the one
stored in $SHIV_DIR/include.
7. Edit the file types.h. Add an extra item at the end of the TYPE definition
to define TYPE_OBJECT. Thus, the definition in this file looks like
typedef enum {
TYPE_UNKNOWN = 1000,
TYPE_INT, TYPE_LONG, TYPE_FLOAT, TYPE_STR, TYPE_ENUM, TYPE_PTR,
TYPE_THING, TYPE_LIST,
TYPE_FOO,
TYPE_MASK,
TYPE_REGION,
TYPE_REGINFO,
TYPE_NTYPE, /* Add a comma here */
TYPE_OBJECT /* New line */
} TYPE;
8. Edit the Makefile. On the line that begins FOO_OBJS, add two new modules.
This line now reads...
FOO_OBJS = dervish_foo.o foo.o tclFoo.o diskio_gen.o object.o tclObject.o
In the definition of DISKIO_FILES, add object.h to the end of the line.
These lines now read
DISKIO_FILES = $(DERVISH_DIR)/include/types.h \
$(DERVISH_DIR)/include/lists.h \
$(DERVISH_DIR)/include/region.h foo.h object.h
9. Edit the file dervish_foo.c. Add an extra line to declare the new object
tcl commands as follows:
ftclCreateCommands(handle.interp); /* Fermi enhancements to TCL */
ftclCreateHelp (handle.interp); /* Define help command */
ftclMsgDefine (handle.interp); /* Define help for tcl verbs */
ftclParseInit(handle.interp); /* Ftcl command parsing */
shTclFooDeclare(handle.interp);
shTclObjectDeclare(handle.interp); /* NEW LINE!!!! */
10. Now you are ready to remake the executable. Type
sdssmake dervish_foo
If all is well, you will get a new version of the program of dervish_foo that
include the new object definitions.
NOTE: The Makefile has two ways of making executable programs. One uses
dervish_template.c as its main and the other uses dervish_foo.c as its main.
The latter is derived from dervish_template.c (with a few transparent
modifications). These two ways should probably be merged.
Good luck and have fun!