11.6 LINK, NAME, and REFERENCE

Purpose:

LINK provides a simple relational link between two objects. NAME provides a simple link between an object and a character string, typically a name. An object can have any number of links and names. Each link is equivalent to one pointer. REFERENCE provides a simple pointer plus a reference counter on the target object. This can be used for automatic garbage collection, Smalltalk style.

The advantage of using these organizations instead of simple pointers is that when saving/retrieving whole organizations from disk even these simple pointer relations are automatically converted to new values. Any name declared through the NAME organization is treated as a separate object. The global util.save() automatically saves all names with other objects. However, when saving individual objects with the macro ZZ_OBJECT_SAVE(), each name has to be saved individually (see Chap.13).

Five types of LINK:

SINGLE_LINK provides a simple one-way link from structure type A to object type T.

A DOUBLE_LINK provides a two-way link between objects of type A and T. Object A is always considered primary and T secondary; forward means from A to T, backward means from T to A.

A GENERAL_LINK is a single link to an object of unknown type. Internally, the pointer is cast as (char *). Automatic SAVE/OPEN resets this pointer; however, the SWEEP operation does not pass through this pointer when collecting objects.

A NAME links the object to a character string.

A REFERENCE provides a fully typed pointer link from type A to an object of type T. The T object keeps an integer counter, which counts number of reference pointers pointing to it.

Adding a link:

add() adds a new link between two objects. The object types must agree with the definition of the link. For a REFERENCE, this operation adds 1 to the count.

Deleting a link:

del() deletes the link. For NAME types, just like in other links, no de-allocation is done. The string must be freed by calling sfree(). For a REFERENCE, this operation substracts 1 from the count.

Jumping across the link:

fwd() moves you from A to T; for NAME, it returns the name of the object.

bwd() moves you from T to A (available only for DOUBLE_LINK).

Accessing the counter:

When working with a REFERENCE, the following commands permit to access the counter:

id.get(T*) returns the current content of the counter,

id.set(T*,int) sets the counter to a given value. Normally, the user should not set the counter. The counter is initialized to 0 for a new object, and the counter is updated automatically by all add() and del() calls.

SAVE/OPEN:

It may appear strange to use special organizations for such simple pointer relations. The reason is that when you are saving data to disk (structure blasting), only the pointers registered under OrgC++ are properly restored. Unregistered pointers become invalid (meaningless) when restoring data from disk.

The method of allocating the name string may be important, if you plan to save data to disk later. There are two ways of adding a name to an object:

ZZ_HYPER_NAME(name,Employee);
Employee *e;
name.add(e,"Brown J");

ZZ_HYPER_NAME(name,Employee);
Employee *e; char *n;
n=util.strAlloc("Brown J");
name.add(e,n);

In the first case, we simply use a string provided by the compiler. In the second case, we allocate a special copy with malloc.

When saving to disk, the saving algorithm has to temporarily change some bytes on all objects, including the name strings. However, SUN and some other compilers consider compiler-supplied strings sacred, and if any function attempts to change them, the program core dumps. For this reason, the second approach (using util.strAlloc()) should be used if you will be saving data to disk.

The text string stored by the NAME organization may contain any characters except for `\0' (end of string character), including the blank (white space) character.

Syntax:

ZZ_HYPER_SINGLE_LINK(id,FROM,TO);
ZZ_HYPER_DOUBLE_LINK(id,FROM,TO);
ZZ_HYPER_GENERAL_LINK(id,FROM);
ZZ_HYPER_NAME(id,FROM);
Declare the three types of links and the NAME organization. FROM and TO are the object types. For the NAME, the TO-type is char* by default, for the GENERAL_LINK (void *) is the default.
void id.add(FROM *a, TO *b); Adds a link from a to b.
TO* id.del(FROM *a); Deletes the link starting at a, and returns a pointer to the deleted object.
TO* id.fwd(FROM *a); For a given object a, it returs the object connected by the link (fwd=FORWARD).
FROM* id.bwd(TO *b); // for DOUBLE_LINK only Returns the source of the link that points to this object (bwd=BACKWARD).

Examples:

  1. While working with the Rectangle record, test8.c assumes that, temporarily, more data such as perimeter and area are required. LINK is used to link the Rectangle record with the temporary record, tempRect.
  2. When working with two record types, Couple and Person, it may be useful to have a link from a Couple to the two people that form it. In test16a.c this is implemented as two LINK organizations between Couple and Person.
  3. The program shown in test15a.c handles Employee records that contain, besides other information, the employee name. The name is linked by a regular pointer, without using the organization of NAME. However, when we want to save the same data on disk (test15b.c), NAME is used instead of the pointer.
  4. In test14a.c, both States and Towns have names declared through the NAME organization.
  5. test19a and test19b illustrate the use of SAVE on an organization that involves a GENERAL_LINK. For more information on REFERENCE see files orgC/macro/hyprefer and test42b.c.

 

Previous Section 11.5 GRAPH Next Section 11.7 STACK