3.6 Динамическая компоновка

Предыдущая  Содержание  Следующая V*D*V

Распознаватель завершён. value.h полностью скрывает блок вычислений арифметических выражений и в то же время определяет, что мы должны реализовать. new() принимает описание, например, Add (сложение) и подходящие аргументы, например, указатели на операнды сложения и возвращает указатель, представляющий собой данную сумму:

 

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;

}

 

Мы используем динамическую компоновку и передаём вызов в зависимую от узла подпрограмму, которая в случае Add должна создать узел и записать два указателя:

 

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;

}

 

Обратите внимание, что только mkBin() знает, какой узел создаётся. Всё, что требуется, чтобы различные узлы начинались с указателя для динамической компоновки. Этот указатель вводится new() так, чтобы delete() могла добраться своей до зависимой от узла функции:

 

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);

}

 

Динамическая компоновка элегантно избегает сложных узлов. .new() создаёт совершенно правильный узел для каждого описания типа: бинарные операторы имеют два потомка, унарные операторы имеют одного, а узел со значением содержит только это значение. delete() очень простая функция, потому что каждый узел обрабатывает свою собственное уничтожение: бинарные операторы удаляют два поддерева и очищают свой собственный узел, унарные операторы удаляют только одно поддерево, а узел со значением будет очищать только самого себя. Переменные или константы даже могут быть оставлены — они просто ничего не будут делать в ответ на delete().

 

Предыдущая  Содержание  Следующая