Thursday, September 6, 2012

AdaTutor - Access Types, User Defined Types, and Derived Types (2)

User Defined Types and Portability

In some implementations of Ada, Integers are represented by 32-bit two's complement numbers, giving the range -2_147_483_648 .. 2_147_483_647.  Other implementations use 16-bit two's complement Integers, giving the range -32_768 .. 32_767.  However, some of the 16-bit implementations also provide a 32-bit type called Long_Integer.

Suppose we need a variable called Number to count from zero to one million.  We could declare Number: Integer; for the 32-bit implementation, and change this to Number: Long_Integer; when we port the program to a machine running a 16-bit version that provides Long_Integer.  However, we could also declare

   type Counter is range 0 .. 1_000_000;
   Number : Counter;
and both implementations of Ada will automatically select the appropriate internal representation for our type Counter!  The 32-bit Ada will select Integer, and the 16-bit Ada will select Long_Integer.  This gives us the advantage that no code has to be changed when the program is ported.  Counter is called a user-defined type.  Of course, we must use explicit type conversion to mix objects of type Counter with objects of other types.

Similarly, different implementations of Ada provide different representations for type Float, and some provide a type Long_Float. We can declare

   type Real is digits 8;
   F : Real;
and be certain that F will have at least 8 digits of accuracy on any machine that accepts this type declaration.  A range constraint is optional.

User defined types also apply to fixed point numbers; these will be discussed in the section on More Records and Types.

It's possible to make a declaration that will be accepted by only some implementations of Ada.  For example, if we declare

   type X is digits 30 range 0.0 .. 100.0;
some implementations of Ada might have to report that there's no available type that gives at least 30 digits of accuracy.

No language can give perfectly portable programs, but Ada truly advanced the state of the art in portability.

Derived Types

Derived types are created to prevent accidental mixing of objects.  Unlike subtypes, derived types are distinct types.  For example,

   type No_Of_Apples  is new Integer;
   type No_Of_Oranges is new Integer range 0 .. 100;
   NOA : No_Of_Apples;
   NOO : No_Of_Oranges;
   I   : Integer;
   NOA := NOA + NOO;  -- illegal
   NOA := NOA + NOA;
   NOA := NOA + I;  -- illegal
   NOA := NOA + No_Of_Apples(I);
   NOA := NOA + No_Of_Apples(NOO);

A derived type is denoted by the reserved word new followed by an existing type like Integer.  The operations that Ada knows for Integers, such as addition, are "inherited" by the derived types so that, for example, Ada knows how to add two objects of type No_Of_Oranges.  As the examples above show, we can't mix types accidentally, but we can deliberately mix them by converting first.

In summary,

  • Subtypes are usually created to provide range constraints:
       subtype Day_Subtype is Integer range 1 .. 31;
  • Derived types are usually created to prevent accidental mixing:
       type No_Of_Apples  is new Integer;
       type No_Of_Oranges is new Integer ... ;
  • User-defined types are usually created to gain portability:
       type Counter is range 0 .. 1_000_000;
       type Real    is digits 8;


  type Meters            is new Float;
  type Seconds           is new Float;
  type Meters_Per_Second is new Float;
  function "*"(Left : in Meters_Per_Second; Right : in Seconds)
    return Meters is
     return Meters(Left) * Meters(Right);
  end "*";
  function "*"(Left : in Seconds; Right : in Meters_Per_Second)
    return Meters is
     return Right * Left;
  end "*";
  function "/"(Left : in Meters; Right : in Seconds)
    return Meters_Per_Second is
     return Meters_Per_Second(Left) / Meters_Per_Second(Right);
  end "/";
The above program segment is an example of ...
  1. derived types.
  2. user-defined types.
  3. subtypes.

< prev   next >

No comments: