CVS difference for ai05s/ai05-0142-4.txt
--- ai05s/ai05-0142-4.txt 2009/06/26 01:49:35 1.4
+++ ai05s/ai05-0142-4.txt 2009/10/23 06:06:31 1.5
@@ -2078,3 +2078,170 @@
did look up I read in the paper RM, so I didn't see the AARM notes.
****************************************************************
+
+From: Christoph Grein
+Sent: Monday, July 6, 2009 4:44 AM
+
+I have a reference counting safe pointers package:
+
+generic
+
+ type Object is limited private;
+
+package Safe_Pointers.On_Limited_Types is
+
+ type Safe_Pointer is private;
+
+ function Value (Pointer: Safe_Pointer) return Object;
+ -- further operations
+
+private
+
+ -- not shown
+
+end Safe_Pointers.On_Limited_Types;
+
+This was valid in Ada 95: Value was a return-by-reference function and returned a constant
+view. Ada 2005 removed the return-by-reference-types, so I have to replace Value by:
+
+ function Value (Pointer: Safe_Pointer) return access Object;
+
+which ruins the safety I had before with safe pointers.
+
+If I understand the AI correctly, it would allow to define
+
+ type Accessor_Type (Element: not null access Object) is taged limited private;
+
+ function Accessor (Pointer: aliased in out Safe_Pointer) return Accessor_Type;
+
+and I could write
+
+ X: Accessor_Type := Accessor (My_Pointer);
+
+where X.Element.all is a constant view of the object denoted by My_Pointer (a variable view
+if Limited is removed everywhere above), which would restore the safety of my safe pointers.
+
+How would I define the complete type and the body of Value?
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Monday, July 6, 2009 1:13 PM
+
+...
+> where X.Element.all is a constant view of the object denoted by
+> My_Pointer (a variable view if Limited is removed everywhere above),
+> which would restore the safety of my safe pointers.
+
+X.Element.all is always a variable view; if you wanted a constant view you would need to
+add "constant" to the discriminant declaration.
+
+> How would I define the complete type and the body of Value?
+
+Well, I assume you mean the body of "Accessor" in your example (you'd probably call that
+Value). It depends of course on what's in your private part, since you didn't give that I
+can only guess.
+
+Assuming that the type Safe_Pointer is implemented as follows:
+
+ type Unsafe_Pointer is access all Object;
+
+ type Safe_Pointer is record
+ Ptr : Unsafe_Pointer;
+ Refs : Natural := 0;
+ ...
+ end record;
+
+You can make your function Value very simple:
+
+ function Value (Pointer : aliased in Safe_Pointer) return access Object;
+
+with a body of
+
+ function Value (Pointer : aliased in Safe_Pointer) return access Object is
+ begin
+ return Pointer.Ptr;
+ end Value;
+
+This is safe because of the accessibility checks. The Pointer parameter to Value will be
+required to live longer than the function result. Typically, the function result will have
+a very local lifetime. That means that it cannot be assigned to any other access type, so
+about all that can be done with it is to dereference it. If you do manage to use the value
+in a context (that's easier for "access Object" than for the discriminant case) where it
+lives a long time, the parameter has to live as long or longer, so it stays safe.
+
+Note that you don't need "in out" here, because you aren't (ever) going to return part of
+the actual object. If your Safe_Pointer type could be defined:
+ type Safe_Pointer is record
+ Obj : aliased Object;
+ Refs : Natural := 0;
+ ...
+ end record;
+then you would need "in out". (That can't happen in this case, but it can for the containers.)
+
+Note that you'd probably want to declare Safe_Pointer "tagged" so that your users wouldn't
+have to declare every object as "aliased".
+
+For this definition, I'm assuming that there aren't any operations that can invalidate an
+existing Safe_Pointer object. If such operations exist (seems likely), then you would want
+a more complex scheme where you can keep track of the existence of the (raw) pointer:
+
+ type Accessor_Type (Element: not null access Object) is tagged limited private;
+
+ function Value (Pointer: aliased in out Safe_Pointer) return Accessor_Type;
+
+For uses of Value, generally you would ignore the Accessor_Type object and just dereference
+the discriminant directly:
+
+ ... Value (My_Ptr).Element.all ...
+
+We'd add a Raw_Refs counter to the Safe_Pointer:
+
+ type Safe_Pointer is tagged record
+ Ptr : Unsafe_Pointer;
+ Refs : Natural := 0;
+ Raw_Refs : Natural := 0;
+ ...
+ end record;
+
+Accessor_Type would be defined as limited controlled so that you get an indication when
+the access ceases to exist:
+
+ type Accessor_Type (Element: not null access Object) is new Ada.Finalization.Limited_Controlled with record
+ My_Ptr : not null access Safe_Pointer; -- Default initialization will raise Constraint_Error.
+ end record;
+
+ procedure Finalize (Object : in out Accessor_Type);
+
+We want a default-initialized object of Accessor_Type to raise an exception, as we only want
+Value to create one. We can't use the unknown discriminants
+(<>) trick here, as the whole point is the discriminant.
+
+And the bodies:
+
+ function Value (Pointer: aliased in out Safe_Pointer) return Accessor_Type is
+ begin
+ Pointer.Raw_Refs := Pointer.Raw_Refs + 1;
+ return (Element => Pointer.Ptr, My_Ptr => Pointer'Access);
+ end Value;
+
+ procedure Finalize (Object : in out Accessor_Type) is
+ begin
+ Object.My_Ptr.Raw_Refs := Object.My_Ptr.Raw_Refs - 1;
+ -- Will raise Constraint_Error if there is an underflow, that shouldn't be possible.
+ end Finalize;
+
+Then, any operation that invalidates a Safe_Pointer object should first check that the Raw_Refs counter
+is equal to 0; if it is not, an exception should be raised instead. (The containers raise Program_Error
+for a "tampering" event.)
+
+This way, once someone creates an access value to access the actual object, the Safe_Pointer will be
+"locked" against modification until the access value is no longer accessible (that is, the locking will
+continue so long as the access value exists).
+
+Of course, there are a number of ways to circumvent this safety (using Unchecked_Deallocation,
+Unchecked_Access, or Unchecked_Conversion). But that is true for any Ada feature; one has to assume
+that the programmers are not intentionally shooting themselves in the foot. (Uncontrolled use of
+"Unchecked" anything is dangerous - that's why they're called "Unchecked", after all.)
+
+****************************************************************
Questions? Ask the ACAA Technical Agent