1

I have the following spec:

generic
    type Value_Type is private;
package Containers.Arrays is

    type Static_Array is array(Positive range <>) of Value_Type;
    type Static_Array_Access is access all Static_Array;

    type Dynamic_Array is tagged private;

    function Length(Self : Dynamic_Array) return Natural with Inline;

    function "&"(Left : Dynamic_Array; Right : Value_Type) return Dynamic_Array;

private
    type Dynamic_Array is tagged record
        Backing : Static_Array_Access;
    end record with
        Variable_Indexing => Variable_Indexer;

    type Variable_Reference(Data : access Value_Type) is null record with
        Implicit_Dereference => Data;
    function Variable_Indexer(Self : Dynamic_Array; Index : Positive)
        return Variable_Reference with Pre => Index <= Self.Length;

Along with an implementation of Variable_Indexer as follows:

function Variable_Indexer(Self : Dynamic_Array; Index : Positive)
    return Variable_Reference is
begin
    return Variable_Reference(Data => new Value_Type'(Self.Backing(Index)));
end Variable_Indexer;

I know the problem here already, but I'll still give my test code and explain what happens.

DA : Dynamic_Array := 'a' & 'b' & 'c';
pragma Assert(DA.Length = 3);
pragma Assert(DA(1) = 'a');
pragma Assert(DA(2) = 'b');
pragma Assert(DA(3) = 'c');

All good so far

DA(1) := 'A'; --Change to majiscule from miniscule
pragma Assert(DA(1) = 'A'); --fails

I know this is because Variable_Indexer didn't reference DA(1), but actually allocated a new variable on the heap with the value that DA(1) just so happened to have. I figure I need to alias the value and return an access to the alias. I, however, have been unable to get such a thing to compile, let alone work.

Simon Wright
  • 25,108
  • 2
  • 35
  • 62
Patrick Kelly
  • 633
  • 5
  • 22
  • 1
    I haven't fully studied the new Ada 2012 user-defined references and indexing. But off the top of my head, I think you need `type Static_Array is array(Positive range <>) of aliased Value_Type`, and then `return Variable_Reference'(Data => Self.Backing(Index)'access)`. See if that helps. – ajb May 25 '15 at 05:52
  • Is there a reason that you have made type `Static_Array` public? – Jacob Sparre Andersen May 25 '15 at 06:11
  • @ajb, why is `Self.Backing (Index)'Access` okay in this case? Is it due to one of the mysterious rules in 3.10.2? – Jacob Sparre Andersen May 25 '15 at 06:47
  • Is there a reason `Static_Array` should be private? I made it public because after initialization of the package, there is little reason I can see that it couldn't be used without problem. It's not featureful, but it's there anyways. – Patrick Kelly May 25 '15 at 07:22
  • @ajb, yes, it did the trick. I completely forgot about aliasing the elements of an array. I wrote the changes into an answer. – Patrick Kelly May 25 '15 at 07:33
  • @JacobSparreAndersen the 'Access returns a pointer to the element, forcing by-reference semantics over by-value semantics. Because a character (the elements of my test array) can easily fit into register, the compiler is free to pass by-value. This behavior is fine if I only want constant indexing. But I want a fully dynamic array: to be able to edit at any point as well. Aliasing the array creates addresses to each element, and the 'Access gets me the element itself, not just the value. – Patrick Kelly May 25 '15 at 07:36
  • @JacobSparreAndersen If by "why is ...'Access okay" you mean why doesn't it raise an accessibility exception or get an accessibility violation at compile time--I don't know. For that matter, I don't know that it's actually okay; the fact that GNAT apparently accepts it doesn't prove anything. I was ready to suggest using `'Unchecked_Access` if `'Access` didn't work. – ajb May 26 '15 at 04:10
  • @PatrickKelly My reason for thinking that `Static_Array` should be private is that the package doesn't provide any operations on it. – Jacob Sparre Andersen May 26 '15 at 05:55
  • 1
    @JacobSparreAndersen, it's a work in progress. The indexer and variable reference happened to be the third and fourth features. More will be added in time, and any relevant to both, will be added to both. A big advantage I can see already, is that `"&"(Left : Static_Array; Right : Value_Type) return Dynamic_Array` requires substantially less work by the library, as would `"&"(Left : Dynamic_Array; Right : Static_Array) return Dynamic_Array`. Sizing and swapping the backing array would only be done once, instead of possibly many times to construct the same array. – Patrick Kelly May 27 '15 at 03:01

1 Answers1

3

Thanks to @ajb for this one, as I completely forgot about aliasing the elements of an array.

Change Static_Array to:

type Static_Array is array(Positive range <>) of aliased Value_Type;

and change Variable_Indexer to:

function Variable_Indexer(Self : Dynamic_Array; Indexer : Positive) return Variable_Reference is
begin
    return Variable_Reference'(Data => Self.Backing(Index)'Access);
end Variable_Indexer;

Now, instead of creating a new value on the heap, and returning a pointer to that, it now returns a pointer to the element in the array; this is proper behavior.

Patrick Kelly
  • 633
  • 5
  • 22
  • I had to write `Variable_Indexer(Self : in out Dynamic_Array; Indexer : Positive)` to make it work. – Jossi Jan 30 '17 at 18:05