1

I have a collection of things, which I deliberately want to allocate on the heap and access them 'by reference':

with Ada.Text_IO; use Ada.Text_IO; with Ada.Containers.Indefinite_Hashed_Maps; with Ada.Containers; use Ada.Containers; procedure Main is type Thing_Key is new Integer; type Thing is record Key : Thing_Key; Data : Integer; end record; type Thing_Access is access all Thing; function Image (T : Thing) return String is (T.Key'Image & '(' & T.Data'Image & ')'); function "=" (A, B : Thing) return Boolean is (A.Key = B.Key); function Thing_Hash (K : Thing_Key) return Hash_Type is (Hash_Type (K)); package Thing_Map is new Ada.Containers.Indefinite_Hashed_Maps (Key_Type => Thing_Key, Element_Type => Thing, Hash => Thing_Hash, Equivalent_Keys => "="); use Thing_Map; Map : Thing_Map.Map; C : Cursor; P : Thing_Access; begin P := new Thing '(Key => 1, Data => 2); -- on the heap Map.Insert (P.Key, P.all); Put_Line (Image (P.all)); -- '1( 2)', as expected P.Data := 99; Put_Line (Image (P.all)); -- '1( 99)', as expected C := Map.Find (1); -- Get cursor to thing -- Set P to point at the thing at the cursor? -- Following lines don't compile P := Map (C)'Access; -- access-to-variable designates constant P := Map (C).Reference; -- undefined selector "Reference" for overloaded prefix P := Map (C).Get_Element_Access; -- undefined selector "Get_Element_Access" for overloaded prefix P := Map.Reference (C); -- no visible interpretation of "Reference" matches expected type "Thing_Access" end Main; 

What is the syntax to get a pointer from a cursor?

5
  • The question is why you want to deal with pointers here in the first place? Especially since you allocated on the heap, then makes a copy of the data when inserting them into the map. Commented Feb 20, 2021 at 15:02
  • @egilhh This is a simplified example, in the real world, 'Thing' can by quite big (and lasts forever). In addition, the program will be continuously updating the contents of 'Thing', Maps return a copy of the object, not the object itself. Commented Feb 20, 2021 at 15:28
  • You don't seem to have a vector. Commented Feb 21, 2021 at 8:38
  • 1
    @smirkingman Yes I indeed removed the answer. The answer was down-voted by someone which is not that common on Ada-related answers. I guess someone was really troubled by the answer. Unfortunately, I don't know what the problem was. Maybe I was too liberal in my advice on using a Hashed_Set instead of a 'Hashed_Map` given that I do not know the exact use-case, maybe the answer was not enough to-the-point, or maybe I really made a mistake in my example. Anyway, Timor's answer was already good and so I decided to remove it just to be sure. I'll put it back for you to have another look at it. Commented Feb 21, 2021 at 13:47
  • Presumably the down-voter will read this: Your down-vote caused DeeDee to remove his answer, which was the solution to a problem that I couldn't solve alone. Luckily, I saw it before deletion and remembered DeeDee's name, I then wasted quite some time getting the answer restored. In future, when down-voting a cogent answer, have the decency to add a comment explaining why. Commented Feb 22, 2021 at 8:57

2 Answers 2

7

I assume that you only want to store elements on the heap in order to access them by reference for manipulation. However, you don't need to do that when using Ada containers. All containers have some way of accessing the elements by reference readily available (via some Constant_Reference or Reference function that can typically be omitted because of the Variable_Indexing aspect defined on the container type; see, for example, section 6.3 in the Ada 2012 rationale, and/or the answer of @Timur Samkharadze).

If you want to store the key as part of the element, then I think it might be more appropriate to use a hashed set (see RM A.18.7, RM A.18.8 and on learn.adacore.com). An element in a hashed set can be accessed by reference via the function Reference_Preserving_Key (see also RM 96.10 (3)).

Below are two examples: the first example shows how to update an element in a Hashed_Map and the second example shows how to update an element in a Hashed_Set, both using a key:

main.adb (Hashed_Map)

with Ada.Text_IO; use Ada.Text_IO; with Ada.Containers; use Ada.Containers; with Ada.Containers.Hashed_Maps; procedure Main is type Thing_Key is new Integer; type Thing is record Key : Thing_Key; Data : Integer; end record; function Image (T : Thing) return String is ("Key = " & T.Key'Image & ", Value = " & T.Data'Image); function Hash (K : Thing_Key) return Hash_Type is (Hash_Type (K)); package Things is new Ada.Containers.Hashed_Maps (Key_Type => Thing_Key, Element_Type => Thing, Hash => Hash, Equivalent_Keys => "="); Map : Things.Map; begin -- Inserting 4 elements. Note that the key is now stored twice: once in -- the map's key index (its hash, to be more precise), and once in the item -- itself (unhashed). You must now ensure that the key value in the -- element does not accidentally get out-of-sync with the hashed key in the -- map's key index (e.g. when you update the stored element). Of course, -- you could also you just omit the key in the element itself if possible -- given your use-case. Map.Insert (Key => 1, New_Item => (Key => 1, Data => 10)); Map.Insert (Key => 2, New_Item => (Key => 2, Data => 20)); Map.Insert (Key => 3, New_Item => (Key => 3, Data => 30)); Map.Insert (Key => 4, New_Item => (Key => 4, Data => 40)); for T of Map loop Put_Line (Image (T)); end loop; New_Line; -- Update element with key 3. -- -- Note that the following expressions are all equivalent: -- -- Map.Reference (3).Element.all.Data := 300; -- Original expression -- Map.Reference (3).Element.Data := 300; -- Omit "all" due to implicit dereferencing of access types in Ada. -- Map.Reference (3).Data := 300; -- Omit "Element" due to the "Implicit_Dereferencing" aspect on the "Hashed_Maps.Reference_Type". -- Map (3).Data := 300; -- Omit "Reference" due to the "Variable_Indexing" aspect on the "Hashed_Maps.Map" type. -- Map (3).Data := 300; -- Example if you really need a pointer to element with key 3. declare type Thing_Access is not null access all Thing; type Thing_Constant_Access is not null access constant Thing; -- Element is mutable via P , i.e. P.Data := 301 (OK) -- Element is not mutable via CP, i.e. CP.Data := 302 (Error) P : Thing_Access := Map.Reference (3).Element; CP : Thing_Constant_Access := Map.Constant_Reference (3).Element; begin null; end; for T of Map loop Put_Line (Image (T)); end loop; New_Line; end Main; 

main.adb (Hashed_Set)

with Ada.Text_IO; use Ada.Text_IO; with Ada.Containers; use Ada.Containers; with Ada.Containers.Hashed_Sets; procedure Main is type Thing_Key is new Integer; type Thing is record Key : Thing_Key; Data : Integer; end record; function Image (T : Thing) return String is ("Key = " & T.Key'Image & ", Value = " & T.Data'Image); function Key (T : Thing) return Thing_Key is (T.Key); function Hash (T : Thing) return Hash_Type is (Hash_Type (T.Key)); function Hash (K : Thing_Key) return Hash_Type is (Hash_Type (K)); package Things is new Ada.Containers.Hashed_Sets (Element_Type => Thing, Hash => Hash, Equivalent_Elements => "="); package Things_Keys is new Things.Generic_Keys (Key_Type => Thing_Key, Key => Key, Hash => Hash, Equivalent_Keys => "="); Set : Things.Set; begin -- Inserting 4 elements. Note that the key is stored only in the element. Set.Insert ((Key => 1, Data => 10)); Set.Insert ((Key => 2, Data => 20)); Set.Insert ((Key => 3, Data => 30)); Set.Insert ((Key => 4, Data => 40)); for T of Set loop Put_Line (Image (T)); end loop; New_Line; -- Update the element. See also RM 96.10 (3). Opposed to most other -- containers, you cannot omit "Reference_Preserving_Key" as the "Set" type -- does not have a "Variable_Indexing" aspect specifying "Reference_Preserving_Key". -- Hence, you need write it out explicitly. Things_Keys.Reference_Preserving_Key (Set, 3).Data := 300; -- Example if you really need a pointer to element with key 3. declare type Thing_Access is not null access all Thing; type Thing_Constant_Access is not null access constant Thing; -- Element is mutable via P , i.e. P.Data := 301 (OK) -- Element is not mutable via CP, i.e. CP.Data := 302 (Error) P : Thing_Access := Things_Keys.Reference_Preserving_Key (Set, 3).Element; CP : Thing_Constant_Access := Things_Keys.Constant_Reference (Set, 3).Element; begin null; end; for T of Set loop Put_Line (Image (T)); end loop; New_Line; end Main; 

output (same for both)

Key = 1, Value = 10 Key = 2, Value = 20 Key = 3, Value = 30 Key = 4, Value = 40 Key = 1, Value = 10 Key = 2, Value = 20 Key = 3, Value = 300 Key = 4, Value = 40 
Sign up to request clarification or add additional context in comments.

2 Comments

Great answer, I wouldn't have found Reference_Preserving_Key in a month of Sundays, thank you. Could you add examples of how to use 'Reference' and 'Constant_Reference' that you allude to please? (and I'll accept ;-)
@smirkingman I updated the original and added an additional example showing the use of Reference and Constant_Reference. I hope it's useful.
4

You might want to use P := Map.Reference(C).Element;

Function Reference returns a value of Reference_Type that has aspect Implicit_Dereference whose value is Element and whose type is not null access Element_Type.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.