[log in to unmask] wrote: > > You need an Adjust that increments the reference count, and > Create should not increment it. A new object can come into > existence either with an Initialize call or (as above) with an > Adjust call, so both must be there and ready to increment the > reference count (or whatever). Other functions which happen > to "return Final.Finis" (like Create in your example) should > not be fiddling with the reference count because they are not > creating or destroying objects - other than the creation or > destruction that is already accounted for by compiler > generated Initialize/Adjust/Finalize calls - they are merely > accessing existing objects. I stand corrected. Below is the modified code which takes this into account, in case anyone else was as confused as me (or if I confused someone besides myself!): ------------------------------- 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; -------------------------------- package body Final is Ref_Count : Integer := 0; procedure Initialize (Obj : in out Finis) is begin Ref_Count := Ref_Count + 1; end Initialize; procedure Adjust (Obj : in out Finis) is begin Ref_Count := Ref_Count + 1; end Adjust; procedure Finalize (Obj : in out Finis) is begin Ref_Count := Ref_Count - 1; end Finalize; function Get_Count return Integer is begin return Ref_Count; end Get_Count; function Create return Finis is begin return Null_Finis; end Create; end Final; ------------------------------- with Final; with Text_Io; use Text_Io; with Ada.Finalization; use Ada.Finalization; procedure Finish is begin Put_Line ("Count is:" & Integer'Image (Final.Get_Count)); declare X : Final.Finis; begin X := Final.Create; Put_Line ("Count is:" & Integer'Image (Final.Get_Count)); end; Put_Line ("Count is:" & Integer'Image (Final.Get_Count)); declare Y : Final.Finis := Final.Create; begin Put_Line ("Count is:" & Integer'Image (Final.Get_Count)); end; Put_Line ("Count is:" & Integer'Image (Final.Get_Count)); end Finish; ------------------------ The output of this is what I expected/wanted: Count is: 0 Count is: 1 Count is: 0 Count is: 1 Count is: 0 Hurrah! Thank you, Mr. Moran. > >Ada 95 Initialize/Adjust/Finalize are not as flexible > >as C++ constructor/destructors because the have only > >to do with the *contents* of an object during its life > >*between* creation and deletion. It would be nice to > >extend the programmer's control to permit activity at > >the endpoints of the object lifetime as well. > Initialize/Adjust/Finalize are called by the compiler > exactly at the endpoints of existence. I don't understand > what you might mean by accessing the object before it is > created or after it is deleted. > Is this accurate? Finalize is called not only at the point of object destruction but also at the point of assignment, when the value is being overwritten. That's why I say that Init/Adj/Fin confuse the issues of object creation/destruction with the changing of object contents. Because of this, there is a loose end, the situation which may cause Finalize to be called twice -- the abort-during-finalize problem. A Destroy procedure would take care of this, because it would not be called as part of assignment, only when an object is destroyed. > BTW, this has apparently been difficult for compiler > writers, too. If you are making test programs make sure you > have a compiler that's doing the correct thing. There have > been problems in the past. Unfortunately, I can't tell if the following two examples reflect problems in the compilers I am using or problems in my understanding of Finalization. Example #1, assume the spec of Final is as given above. If the body looks like this: ------------------------------ with Text_Io; use Text_Io; with Ada.Integer_Text_Io; use Ada.Integer_Text_Io; package body Final is Ref_Count : Integer := 0; procedure Initialize (Obj : in out Finis) is begin Ref_Count := Ref_Count + 1; Put (':'); Put (Ref_Count, Width => 0); New_Line; end Initialize; procedure Adjust (Obj : in out Finis) is begin Ref_Count := Ref_Count + 1; Put ('='); Put (Ref_Count, Width => 0); New_Line; end Adjust; procedure Finalize (Obj : in out Finis) is begin Ref_Count := Ref_Count - 1; Put ('.'); Put (Ref_Count, Width => 0); New_Line; end Finalize; function Get_Count return Integer is begin return Ref_Count; end Get_Count; function Create return Finis is begin return Null_Finis; end Create; end Final; -------------------------------- Here I print the Ref_Count after an indicator in each of the three automatic routines. Initialize's indicator is a colon (':'), Adjust's is an equal sign ('='); Finalize's is a period ('.'). Given this program: ----------------------- with Final; with Text_Io; use Text_Io; procedure Finish2 is with Final; begin Put_Line ("Z"); declare Z1 : Final.Finis := Final.Create; Z2 : Final.Finis; begin Z2 := Z1; end; Put_Line ("end"); end Finish2; ------------------------- I see the following output from both GNAT and Aonix: Z =1 =2 .1 :2 .1 =2 .1 .0 end .-1 Notice the negative one after the "end" output. This means that Finalize is being called one last time as the program ends. I suppose this is the finalization of the object Null_Finis. But does this make sense? Null_Finis was never Initialized or Adjusted. Now my ref counts are messed up at the end. The second example is similar; both GNAT and Aonix give the same output. Change the spec of Final to eliminate the declaration of Null_Finis; change the body of Final so that the Create procedure does this instead of returning Null_Finis: return (Controlled with null record); According to my reading of Cohen, this return statement should actually cause two temporary objects to be created, one by the evaluation of the aggregate, the other by the return statement. Running Finish2 after these changes gets me this: Z =1 .0 =1 .0 :1 .0 =1 .0 .-1 end Still end up with that negative one! And all through this section of code, it never thinks I have two objects: my ref count is one at most the whole time. Is Ada 95's finalization broken? Are these compilers? Or are my synapses? Stanley Allen mailto:[log in to unmask]