I recently came across a situation where it would be convenient to have a pointer-to-member (a la C++). Ada doesn’t have this feature natively, so I’m trying to see how close I can come to emulating it.
For those not familiar with this concept, I’ll explain briefly. Normally an access type points to a single value (which could be a member of a record or not). For any given pointer value, the value it points to remains constant, because what the access type holds is an absolute memory address. A pointer-to-member, in contrast, points to a specific member of any record. For any given pointer-to-member value, the actual value it points to changes based on which record instance it is paired with. Instead of an absolute address, it holds an offset from the start of a record.
Here’s my attempt to recreate that in Ada.
The pointer-to-member type itself is pretty simple:
type Ptr_Type is new System.Storage_Elements.Storage_Offset;
A function that dereferences this type has to do some magic internally:
procedure Set (Base : in out T; Ptr : Ptr_Type; Value : Integer) is
use System.Storage_Elements;
package Cast is new System.Address_To_Access_Conversions (Integer);
begin
Cast.To_Pointer (Base'Address + Ptr).all := Value;
end Set;
Creating a pointer-to-member is unfortunately awkward. Ada provides the ’Position attribute, which is nice, but it requires an object. The best way I’ve found to do this is to create a temporary:
Ptr : Ptr_Type := T'(others => <>).Some_Member'Position;
Unfortunately, this is all very hacky. Type safety is thrown out the window – there’s nothing requiring Some_Member is an Integer or that it’s a member of T. I’m not sure what this will do to the compiler’s alias analysis or what happens with tagged types and OOP. Wrapping some of it with utility functions and a generic package will help, but creating the pointer will always be an issue as far as I can tell.
How else have you solved this particular issue? Feedback is welcome.