Нашёл сегодня при написании тестового приложения. Заодно опишу задачу, может быть кто-то посоветует более элегантное решение.
Пусть у нас есть тип Item и его наследник ItemExt:
Код:
TYPE
Str: ARRAY 64 OF CHAR;
Item = POINTER TO ItemDesc;
ItemDesc = RECORD next: Item; name: Str END;
ItemExt = POINTER TO RECORD (ItemDesc) field: INTEGER END;
Item'ы объединены в односвязанный список и поименованы. Важно, чтобы в списке элементы были отсортированы по имени. Помимо ItemExt есть много других наследников от Item, поэтому захотел я написать единую процедуру Add, которая будет добавлять новый элемент сразу в нужное место списка:
Код:
PROCEDURE Add (VAR list: Item; i: Item);
BEGIN
IF list = NIL THEN
list := i
ELSE
(* добавляем i в список list*)
END
END Add;
Идея процедуры такова: подаём первым параметром переменную-корень списка. Если список пуст (корень = NIL), то инициализируем корень первым элементом i. В противном случае добавляем i в существующий список, возможно, изменяя корень - если нужно вставить i в самое начало. Далее я создал список типа ItemExt, но как только я попытался вызвать Add (list, new), то компилятор на меня заругался, поскольку в качестве list я хотел передать ему ItemExt, что не соответствует VAR-параметру типа Item (предок). В самом деле, мало ли что там процедура может присвоить. Пришлось переделать, возвращая новый корень в результате:
Код:
PROCEDURE Add (list, i: Item): Item;
BEGIN
IF list = NIL THEN
RETURN i
ELSE
(* добавляем i в список list*)
RETURN list
END
END Add;
Соответственно, на выходе возникла необходимость в охране типа:
Код:
VAR list, new: ItemExt;
...
NEW (new); new.name := ...
list := Add (list, new)(ItemExt);
И вот тут случилось самое интересное. Оказывается, чтобы применить охрану типа к результату процедуры, XDS по-тихому
вызывает процедуру второй раз! Хорошо, что у меня в Add был ASSERT (i.next = NIL), а то поди долго бы искал, почему зацикливается проход по списку.
Получается, что надо мне писать теперь
Код:
tmp := Add (list, new); list := tmp(ItemExt);
а это уже совсем далеко от элегантности первого решения. Подскажите, если кто с подобными списками работал, как мне быть. Общая задача такова: каждый список содержит только объекты одного типа, просто не хочется дублировать для каждого типа процедуру Add. У меня вариант только один: всегда создавать неиспользуемый корень списка, чтобы исключить причину для использования VAR-параметра в первом примере и возвращения результата процедуры во втором (т.е. исключить необходимость изменения корня списка).