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().

个主工具