OOC:en:3.6 Dynamic Linkage
来自 ChinaUnix Wiki
The recognizer is complete. value.h completely hides the evaluator for arithmetic expressions and at the same time specifies what we have to implement. new() takes a description such as Add and suitable arguments such as pointers to the operands of the addition and returns a pointer representing the sum:
struct Type {
void * (* new) (va_list ap);
double (* exec) (const void * tree);
void (* delete) (void * tree);
};
void * new (const void * type, ...)
{ va_list ap;
void * result;
assert(type && ((struct Type *) type) —> new);
va_start(ap, type);
result = ((struct Type *) type) —> new(ap);
* (const struct Type **) result = type;
va_end(ap);
return result;
}
We use dynamic linkage and pass the call to a node-specific routine which, in the case of Add, has to create the node and enter the two pointers:
struct Bin {
const void * type;
void * left, * right;
};
static void * mkBin (va_list ap)
{ struct Bin * node = malloc(sizeof(struct Bin));
assert(node);
node —> left = va_arg(ap, void *);
node —> right = va_arg(ap, void *);
return node;
}
Note that only mkBin() knows what node it creates. All we require is that the various nodes start with a pointer for the dynamic linkage. This pointer is entered by new() so that delete() can reach its node-specific function: void delete (void * tree) {
assert(tree && * (struct Type **) tree
&& (* (struct Type **) tree) —> delete);
(* (struct Type **) tree) —> delete(tree);
} static void freeBin (void * tree) {
delete(((struct Bin *) tree) —> left); delete(((struct Bin *) tree) —> right); free(tree);
} Dynamic linkage elegantly avoids complicated nodes. .new() creates precisely the right node for each type description: binary operators have two descendants, unary operators have one, and a value node only contains the value. delete() is a very simple function because each node handles its own destruction: binary operators delete two subtrees and free their own node, unary operators delete only one subtree, and a value node will only free itself. Variables or constants can even remain behind — they simply would do nothing in response to delete().
