Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In ada programming language, Is there a way to create a subtype which takes differently positioned enums of a type?

Tags:

subtype

ada

I am trying create a subtype which takes certain enumerations of a type for example,

type Integers_Type is (1,2,3,4,5,6,7,8,9,10);

I want something like this,

subtype Odd_Numbers_Type is Integers_Type (1,3,5,7,9);

I understand that when we use the keyword subtype of a type we need to use range, but the problem is the enumeration is not in a series.

like image 258
S.Abishek Avatar asked Nov 28 '25 19:11

S.Abishek


2 Answers

For this kind of values filtering, I would use subtype predicates.

In your case, modifying your enumeration according to what Keith said :

type Integers_Type is (ONE,TWO,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT,NINE,TEN);

subtype Odd_Numbers_Type is Integers_Type 
   with Static_Predicate => Odd_Numbers_Type in ONE | THREE | FIVE | SEVEN | NINE;

If you want to use numeric types instead of enumeration, use the following

type Integers_Type is range 1 .. 10;

subtype Odd_Numbers_Type is Integers_Type
   with Dynamic_Predicate => Odd_Numbers_Type mod 2 /= 0;

For more information, you can read the rationale

EDIT :

For Enumeration types, the following compiles using gnatmake -gnata -gnatVa test_enum.adb but warns about the affectation line 14 and fails at execution because of the assert linked to the static predicate.

with Ada.Text_IO; use Ada.Text_IO;

Procedure Test_Enum is
   type Integers_Type is (ONE,TWO,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT,NINE,TEN);

   subtype Odd_Numbers_Type is Integers_Type 
     with Static_Predicate => Odd_Numbers_Type in ONE | THREE | FIVE | SEVEN | NINE;
   
   Test_I : Integers_Type := TWO;
   Test_O : Odd_Numbers_Type := ONE;
begin
   Put_Line("Test_I=" & Integers_Type'Image (Test_I));
   Put_Line ("Test_O=" & Odd_Numbers_Type'Image (Test_O));
   Test_O := Test_I;
   Put_Line ("Test_O=" & Odd_Numbers_Type'Image (Test_O));
end Test_Enum;

For integer types, using the gnatmake -gnata -gnatVa test_int.adb compilation command, the compiler warns that the check will fail at runtime which is the case as the assertion is triggered.

with Ada.Text_IO; use Ada.Text_IO;

Procedure Test_Int is
   type Integers_Type is range 1 .. 10;

   subtype Odd_Numbers_Type is Integers_Type
     with Dynamic_Predicate => Odd_Numbers_Type mod 2 /= 0;
   
   Test_I : Integers_Type := 2;
   Test_O : Odd_Numbers_Type := 1;
begin
   Put_Line("Test_I=" & Integers_Type'Image (Test_I));
   Put_Line ("Test_O=" & Odd_Numbers_Type'Image (Test_O));
   Test_O := Test_I;
   Put_Line ("Test_O=" & Odd_Numbers_Type'Image (Test_O));
end Test_Int;

In both cases, removing the -gnata flag will make the program working without taking care of the predicate as asserts are desactivated.

like image 152
Frédéric Praca Avatar answered Dec 01 '25 14:12

Frédéric Praca


You're confusing enumerations and integers, this is a big difference if you're coming from something like C: in Ada enumerations are not merely labels for integers.

The answer given by Frédéric Praca is absolutely correct, but (a) you can use the Static_Predicate on numeric-types; and (b) there's a way to do what you ask in the question's title which is subtly different than what you're asking in the question-body.

The problem with using subtype-predicates is that you lose the ability to use certain

(a) — Static_Predicate

Type Digit_Range is 0..9;
Subtype Odd_Digit is Digit_Range
  with Static_Predicate => 1|3|5|7|9;

(b) — Positions of an Enumeration
In Ada there are several attributes, among these are Pos & Val, and Pred & Succ. Given an enumeration (Type Example is (A,B,C,D,E);, you can say Example'Pos(D) which will return 3, and you can say Example'Val(1) which will return B.

You could then combine things together to create multiple-types, though this is heavy-handed and not normally what you want to do.

Type Base_Numeric_Range is range 0..9;
Type Odds is range 1..Base_Numeric_Range'Pos(Base_Numeric_Range'Last)) - Base_Numeric_Range'Pos(Base_Numeric_Range'First)) / 2;

-- This is from memory, and untested; so probably wrong in the details.
Function Convert(X : Base_Numeric_Range) return Odds is
        ( Odds'Val((Base_Numeric_Range'Pos(X) / 2) + 1) );
Function Convert(X : Odds) return Base_Numeric_Range is
        ( Base_Numeric_Range'Val(Odds'Pos(X) * 2 - 1) );
like image 37
Shark8 Avatar answered Dec 01 '25 13:12

Shark8



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!