Проблема осознанной деинсталляции хука возникает еще и потому что обычно указатель на хук передается на хранение вовне без знания того, кому он передается, поэтому, даже внутри финализатора не всегда можно определить, откуда этот хук должен быть деинсталлирован, если такая возможность вообще существует. Поэтому реализация финализатора не обеспечит бескровную возможность выгрузить модуль.
Иными словами, объект не может сам сообщить о невалидности своему владельцу.
Получается, что для проверки валидности всегда нужен промежуточный объект (возможно хранимый в объектном пуле, для эффективности).
Для критичных мест получается что-то типа:
Код:
MODULE Abase;
TYPE
A* = POINTER TO ABSTRACT RECORD END;
(* всегда рядом с абстракцией, то есть, не выгрузится пока есть клиенты *)
SafeA = POINTER TO RECORD (A0)
unsafe: A;
END;
PROCEDURE (a: A) Do*, NEW, ABSTRACT;
PROCEDURE (a: SafeA) Do;
BEGIN
IF Valid(a.unsafe) THEN a.Do END
END Do;
PROCEDURE New*(): A;
VAR a: SafeA;
BEGIN
NEW(a);
a.unsafe:=dir.New();
RETURN a
END New;
END Abase.
Сработает ли финализатор объекта из модуля реализаций, который заякорен в базовом модуле? По идее - должен.
Код:
MODULE Aimpl;
TYPE
HostA = POINTER TO RECORD (Abase.A)
host: WinApi.A;
END;
PROCEDURE (a: HostA) Do;
BEGIN
(* smth *)
END Do;
PROCEDURE (a: HostA) FINALIZE;
BEGIN
Free(a.host)
END FINALIZE;
END Aimpl.
Для менее критичных случаев подойдет
Код:
TYPE
Ref* = RECORD
target: ANYPTR;
END;
PROCEDURE (VAR r: Ref) Set*(x: ANYPTR), NEW;
BEGIN
r.target:=x;
END Set;
PROCEDURE (VAR r: Ref) This*(): ANYPTR, NEW;
VAR ret: ANYPTR;
BEGIN
IF r.target#NIL THEN
IF Valid(r.target) THEN ret:=r.target ELSE r.target:=NIL END;
END;
RETURN ret
END This;