[log in to unmask] wrote: > > Stanley Allen wrote: > >Finalize is called not only at the point of object > >destruction but also at the point of assignment, when the > >value is being overwritten. > > Consider X := Y; > Just before its execution there are two separate and perhaps > differing object X and Y. The old X must first be destroyed > since it is going to go out of existence. For instance, if X > is on a linked list, it will need to be unlinked. So > Finalize is called. Then the area of memory formerly > occupied by X can be used for a copy of the bits of Y. At > this point there is only one object, Y, in existence, though > the same bit pattern may be present at X'address. But if, > say, Y was on a linked list, that list will only have Y, not > the copy. Then Adjust is called to turn the bit pattern at > X'address into a new object X. Continuing the linked list > example, Adjust would splice the new X into the list. > (Ignoring the stuff about a temp to allow X := X; to work.) Ok, this helps some more; I may need to chew more on it before trying to swallow, however. > > >which may cause Finalize to be called twice -- > The RM explicitly mentions that Finalize may be called > multiple times, so the simple reference counting in the > example won't do. But you can add > Has_Been_Finalized : Boolean := False; > to the record definition and start Finalize with > if Obj.Has_Been_Finalized then return; > else Obj.Has_Been_Finalized := True;end if; Excellent suggestion. > > >Example #1, assume the spec of Final is as given > >... > >end > >.-1 > >... > >But does this make sense? Null_Finis was never > >Initialized or Adjusted. Now my ref counts are > >messed up at the end. > I agree. It seems it should have been. And > removing 'constant', or moving from private > spec to body, or adding an 'aliased' component, > none seem to have any affect. This example gets more confusing to me the more I look at it. For convenience, let me repeat the spec: ------------------------------- with Ada.Finalization; use Ada.Finalization; package Final is type Finis is private; function Get_Count return Integer; function Create return Finis; private type Finis is new Controlled with null record; procedure Initialize (Obj : in out Finis); procedure Adjust (Obj : in out Finis); procedure Finalize (Obj : in out Finis); Null_Finis : constant Finis := (Controlled with null record); end Final; -------------------------------- At the point of declaration of Null_Finis, the specs of the Init/Adj/Fin procedures for type Finis have been declared, but the bodies have not. (BTW, the compilers complain that the declaration of the constant Null_Finis above these spec declarations is illegal because Null_Finis 'freezes' the type.) This declaration should cause Adjust to be called for Null_Finis; however, Adjust's body has not yet been declared. I would normally interpret this as cause for raising a Program_Error, the failure occuring at an elaboration check. Or... is it that the body of Adjust inherited from Controlled is being called? If this is true, it's counter-intuitive, and something of a gotcha. It means that the package body must explicitly do the work of making "Adjust" happen for any constant objects of types derived from Controlled, which must be done in some other way than directly calling Adjust for the constant object, because it's a constant and Adjust can only accept a variable as an argument! This would be even worse when we have constants of types further down in the heirarchy: a deferred constant of type Y indirectly decended from Controlled through X would always be adjusted using X's Adjust, which may have the wrong meaning for an object of type Y. Unfortunately, I cannot determine what the effect is of declaring such constants because when I feed the following code to my two compilers at home (GNAT and Aonix), the results diverge: ---------------------------------------- with Ada.Finalization; use Ada.Finalization; package Xxx is type X is tagged private; private type X is new Controlled with record A : Integer; B : Float; end record; procedure Initialize (Obj : in out X); procedure Adjust (Obj : in out X); procedure Finalize (Obj : in out X); end Xxx; ------------------------------------------ with Text_Io; use Text_Io; package body Xxx is procedure Initialize (Obj : in out X) is begin Put_Line ("Initialize called for object of type X"); end; procedure Adjust (Obj : in out X) is begin Put_Line ("Adjust called for object of type X"); end; procedure Finalize (Obj : in out X) is begin Put_Line ("Finalize called for object of type X"); end; end Xxx; ------------------------------------------- with Xxx; use Xxx; package Yyy is type Y is tagged private; Null_Y : constant Y; -- common, innocuous private type Y is new X with null record; procedure Initialize (Obj : in out Y); procedure Adjust (Obj : in out Y); procedure Finalize (Obj : in out Y); -- the big question: what's happening here? Null_Y : constant Y := (X with null record); end Yyy; -------------------------------------------- with Text_Io; use Text_Io; package body Yyy is procedure Initialize (Obj : in out Y) is begin Put_Line ("Initialize called for object of type Y"); end; procedure Adjust (Obj : in out Y) is begin Put_Line ("Adjust called for object of type Y"); end; procedure Finalize (Obj : in out Y) is begin Put_Line ("Finalize called for object of type Y"); end; end Yyy; --------------------------------------------- with Yyy; use Yyy; with Text_Io; use Text_Io; procedure Usey is -- main program Y1 : Y; begin Y1 := Null_Y; end Usey; ---------------------------------------------- Aonix (Win95 7.1) says this when I run Usey: Initialize called for object of type X Initialize called for object of type X Finalize called for object of type X Adjust called for object of type X Finalize called for object of type X Finalize called for object of type X which can't be right. GNAT (Win95 3.10p) says this: Initialize called for object of type Y Finalize called for object of type Y Adjust called for object of type Y Finalize called for object of type Y Finalize called for object of type Y Wierd, huh? For GNAT, there are no objects of type X; for Aonix, no objects of type Y. In GNAT's scheme, there is an "extra" Finalize which matches no Initialize or Adjust. For Aonix, they match up, but it's obviously bogus. > [for example 2] > > If it had Initialized/Adjusted the (Controlled with ...) > object, or failed to Finalize it, then all would be well. > It seems clear that I/A shouldn't be called on a constant > aggregate (see RM 7.6(11)), but one would think neither > should Finalize. What happens if Finalize modifies (eg, > a Has_Been_Finalied flag) in the constant aggregate? I'll check, hold on a second. Ok, it's no dice: the same results as before, with both compilers. It's not clear to me that RM 7.6(11) that the aggregate as a whole does not cause Init/Adj to be called; that sentence seems to be discussing what happens to the contents of the aggregate rather than the temporary object being creating by the aggregate itself. However, the RM is often opaque to me and I won't assume this paragraph doesn't mean what you think it does. For fast relief I run to Cohen's book (the common man's substitute for RM95!), which says this on page 572: "...if an object to which a value has been assigned is itself controlled, Adjust is called for that object. An object is considered to have a value assigned to it in the following circumstances: ... (*) Evaluation of an aggregate results in the creation of a new object. The component values specified in the aggregate are assigned to the components of this object. (*) Except in the case of a function result returned by reference ... the execution of a return statement in a function body creates a new object to hold the function result. The value of the expression in the return statement is assigned to this object." To me this means that a statement like this in a function body: return (Controlled with null record); creates two objects, and both of them should have Adjust called. I expect both of these temporary objects to be subject to finalization also. Somebody's wrong somewhere. Stanley Allen mailto:[log in to unmask]