ATL_DEV
ATL_DEV

Reputation: 9591

How do I declare a range within an array using an enumeration in Ada?

Say we have a type representing a box of a dozen donuts,

type Bakers_Dozen is range 1 to 13;  -- Indices from 1 to 6 are donuts

Each Donut has a unique value that's within Bakers_Dozen:

subtype Donut is Bakers_Dozen (Plain, Chocolate, Strawberry, Glazed, Blueberry, Cannabis);
for Donut use (Plain=>1, Chocolate=>2, Strawberry=>3, Glazed=>4, Blueberry=>5, Cannabis=>6);  

The intent is to refer to a Donut by either its name or value and ensure it only occupies range 1..6 of an array.

Unfortunately, my code results in an error message "incorrect constraint for this kind of type."

  PastriesBox[Plain].Message = "Plain Donut is boring but costs less";
  PastriesBox[Donut'Last].Message = "Please read contradictions and disclaimer inside box.";
  PastriesBox[12].Message = "A great tasting muffin with fresh goat milk.";

Is there a way of declaring a range of an array as an enumeration?

Upvotes: 1

Views: 621

Answers (2)

Jere
Jere

Reputation: 3641

There are some different means you can go about doing this. The simplest is to declare a constant array using Donuts as the index and Bakers_Dozen as the Element type for the array. Then you just use the array to convert the indexes:

package Indexes is
    type Bakers_Dozen is range 1 .. 13;  -- Indices from 1 to 6 are donuts
    type Donut is  
        (Plain, 
         Chocolate, 
         Strawberry, 
         Glazed, 
         Blueberry, 
         Cannabis);
    
    To_Index : constant array (Donut'Range) of Bakers_Dozen :=
        (Plain      => 1,
         Chocolate  => 2,
         Strawberry => 3,
         Glazed     => 4,
         Blueberry  => 5,
         Cannabis   => 6);
    
    
end Indexes;

Then you can do:

P1 : array(Indexes.Bakers_Dozen'Range) of Integer;

and

P1(Indexes.To_Index(Indexes.Plain)) := 2;

An alternative to this method is to ensure that Donuts is the same size as Bakers_Dozen and then use Ada.Unchecked_Conversion to convert a Donut object to a Bakers_Dozen object

package Conversions is
    type Bakers_Dozen is range 1 .. 13;  -- Indices from 1 to 6 are donuts
    type Donut is 
        (Plain, 
         Chocolate, 
         Strawberry, 
         Glazed, 
         Blueberry, 
         Cannabis)
    with Size => Bakers_Dozen'Size;
    
    for Donut use
        (Plain      => 1,
         Chocolate  => 2,
         Strawberry => 3,
         Glazed     => 4,
         Blueberry  => 5,
         Cannabis   => 6);
         
    function To_Index is new Ada.Unchecked_Conversion(Donut,Bakers_Dozen);
    
end Conversions;

Then you can do:

P2 : array(Conversions.Bakers_Dozen'Range) of Integer;

and

P2(Conversions.To_Index(Conversions.Chocolate)) := 3;

If you want to get fancy you can also wrap one of those methods into a private type and use more advanced concepts such as "Reference_Type" objects and the aspects Variable_Indexing and Constant_Indexing to make your private type act like an array

package Wrappers is

    type Bakers_Dozen is range 1 .. 13;  -- Indices from 1 to 6 are donuts
    type Donut is 
        (Plain, 
         Chocolate, 
         Strawberry, 
         Glazed, 
         Blueberry, 
         Cannabis);
         
    type Bakers_Array is tagged private
    with
        Variable_Indexing => Element;
        
    type Element_Holder(Element : access Integer) is null record
        with Implicit_Dereference => Element;
        
    function Element
        (Self  : aliased in out Bakers_Array;
         Index : Donut)
         return Element_Holder;
    function Element
        (Self  : aliased in out Bakers_Array;
         Index : Bakers_Dozen)
         return Element_Holder;
         
    

private

    type Element_Array is array (Bakers_Dozen'Range) of aliased Integer;

    type Bakers_Array is tagged record
        Elements : Element_Array;
    end record;
    
    To_Index : constant array (Donut'Range) of Bakers_Dozen :=
        (Plain      => 1,
         Chocolate  => 2,
         Strawberry => 3,
         Glazed     => 4,
         Blueberry  => 5,
         Cannabis   => 6);
    
    function Element
        (Self  : aliased in out Bakers_Array;
         Index : Donut)
         return Element_Holder
    is (Element => Self.Elements(To_Index(Index))'Access);
    
    function Element
        (Self  : aliased in out Bakers_Array;
         Index : Bakers_Dozen)
         return Element_Holder
    is (Element => Self.Elements(Index)'Access);

end Wrappers;

Then you can do:

P3 : Wrappers.Bakers_Array;

and

P3(Wrappers.Strawberry) := 4;

NOTE: I didn't add the Constant_Indexing aspect to save on typing, but you might want that too. You can look at Ada.Containers.Vectors to see how this is done.

There are probably other methods, but these are just off the top of my head.

Note also that I am just using Integers as the array elements for simplicity. You can always make these arrays of other types.

Upvotes: 3

flyx
flyx

Reputation: 39808

If your base type is an integer type, the subtype is an integer type. You can't make it an enumeration type.

You can declare constants for your Donuts if you want, then you can refer to them by name. You can instead give the other values names and then have Bakers_Dozen be an enumeration type. If arithmetic operations do semantically not make sense on values of your type, it shouldn't be an integer type anyway.

Upvotes: 1

Related Questions