comp.lang.ada
 help / color / mirror / Atom feed
From: "John G. Volan" <johnvolan@sprintmail.com>
Subject: Re: Ada can-do list
Date: 1997/05/21
Date: 1997-05-21T00:00:00+00:00	[thread overview]
Message-ID: <33838D65.458D@sprintmail.com> (raw)
In-Reply-To: 5lutl4$207s$1@prime.imagin.net


Samuel Mize wrote:
> 
>                                ADA CAN DO IT!
>                                 Samuel Mize
> 
>           Common false claims about things Ada won't let you do,
>                             and how to do them
>                  (and why some are not directly supported)

An excellent contribution!  I applaud your effort!  I do hope this is
accepted at adahome.

> 9. Assignment and increment operators (like += and ++ in C) 

One thing you might add is that it is easy to write Ada procedures which
are equivalent in functionality to these C operators, but which are much
safer because they would not be functions-with-side-effect.

It might have been nice if Ada somehow included such procedures as
built-in features (e.g., as attributes), so that you wouldn't have to
write them every time you introduced a new scalar type, but this is not
the case.  However, generic packages can help reduce the work of writing
them by hand.  Here's an example for integer types (similar generic
packages can be worked up for modular, enumeration, floating and
fixed-point types):

  generic
    type Integer_Type is range <>;
  package Generic_Integer_Ops is

    procedure Increment (This : in out Integer_Type'Base); -- ++ analog
    procedure Decrement (This : in out Integer_Type'Base); -- -- analog
    procedure Increase  (This : in out Integer_Type'Base;  -- += analog
                         By   : in     Integer_Type'Base);
    procedure Decrease  (This : in out Integer_Type'Base;  -- -= analog
                         By   : in     Integer_Type'Base);
    procedure Multply   (This : in out Integer_Type'Base;  -- *= analog
                         By   : in     Integer_Type'Base);
    procedure Divide    (This : in out Integer_Type'Base;  -- /= analog
                         By   : in     Integer_Type'Base);
    procedure Modulo    (This : in out Integer_Type'Base;  -- %= analog
                         By   : in     Integer_Type'Base);
    procedure Remainder (This : in out Integer_Type'Base;  -- %= analog
                         By   : in     Integer_Type'Base);

    pragma Inline (Increment, Decrement, Increase, Decrease,
                   Multiply, Divide, Modulo, Remainder);

  end Generic_Integer_Ops;

Here's the body for this generic.  As you can see, the implementations
of these procedures are fairly trivial:

  package body Generic_Integer_Ops is

    procedure Increment (This : in out Integer_Type'Base) is -- ++
analog
    begin
      This := Integer_Type'Succ(This);
    end Increment;
 
    procedure Decrement (This : in out Integer_Type'Base) is -- --
analog
    begin
      This := Integer_Type'Pred(This);
    end Decrement;

    procedure Increase  (This : in out Integer_Type'Base;  -- +- analog
                         By   : in     Integer_Type'Base) is
    begin
      This := This + By;
    end Increase;

    procedure Decrease  (This : in out Integer_Type'Base;  -- -= analog
                         By   : in     Integer_Type'Base) is
    begin
      This := This - By;
    end Decrease;

    procedure Multply   (This : in out Integer_Type'Base;  -- *= analog
                         By   : in     Integer_Type'Base) is
    begin
      This := This * By;
    end Multiply;

    procedure Divide    (This : in out Integer_Type'Base;  -- /= analog
                         By   : in     Integer_Type'Base) is
    begin
      This := This / By;
    end Divide;

    procedure Modulo    (This : in out Integer_Type'Base;  -- %= analog
                         By   : in     Integer_Type'Base) is
    begin
      This := This mod By;
    end Modulo;

    procedure Remainder (This : in out Integer_Type'Base;  -- %= analog
                         By   : in     Integer_Type'Base) is
    begin
      This := This rem By;
    end Remainder;

  end Generic_Integer_Ops;

Users could instantiate this generic for any of their own integer types:

  Error_Limit : constant := ...;
  type Error_Count_Type is range 0 .. Error_Limit;
  package Error_Count_Ops is new Generic_Integer_Ops (Error_Count_Type);
  ...
  Error_Count, Unhandled_Error_Count, Handled_Error_Count : 
    Error_Count_Type := 0;

  ...
  -- when a given error first occurs:
  Error_Count_Ops.Increment (Error_Count);
  Error_Count_Ops.Increment (Unhandled_Error_Count);

  ...
  -- once a given error is handled:
  Error_Count_Ops.Decrement (Unhandled_Error_Count);
  Error_Count_Ops.Increment (Handled_Error_Count);

The users' work isn't reduced to zero: They still have to do the
instantiation, and they have to include the instance name as a prefix on
every call to one of these procedures (unless they throw in
use-clauses).

There is also the possibility of code-bloat if these generics are
instantiated for many user-defined types.  However, this is alleviated
somewhat by inlining all of the procedures: They'll only get
inline-expanded where they are called, so they will be equivalent to the
user's having written "X := X + 1;" and so forth by hand.  (Also,
code-bloat is really an implementation concern; it depends on how your
particular compiler happens to implement generics.)

Another alternative is to exploit Ada's inheritance mechanism for
non-tagged derived types.  Instead of just providing the ops, the
generic could provide a new type as well, in which case the ops become
inheritable primitives for that type. For example:

  generic
    type Implementation_Type is range <>;
  package Generic_Integers is

    type Integer_Type is new Implementation_Type'Base;

    procedure Increment (This : in out Integer_Type); -- ++ analog
    procedure Decrement (This : in out Integer_Type); -- -- analog
    procedure Increase  (This : in out Integer_Type;  -- += analog
                         By   : in     Integer_Type);
    procedure Decrease  (This : in out Integer_Type;  -- -= analog
                         By   : in     Integer_Type);
    procedure Multply   (This : in out Integer_Type;  -- *= analog
                         By   : in     Integer_Type);
    procedure Divide    (This : in out Integer_Type;  -- /= analog
                         By   : in     Integer_Type);
    procedure Modulo    (This : in out Integer_Type;  -- %= analog
                         By   : in     Integer_Type);
    procedure Remainder (This : in out Integer_Type;  -- %= analog
                         By   : in     Integer_Type);

    pragma Inline (Increment, Decrement, Increase, Decrease,
                   Multiply, Divide, Modulo, Remainder);
  end Generic_Integers;

(The body of Generic_Integers would be almost identical to
Generic_Integer_Ops.)

This generic could be instantiated just for the standard types supported
by the given compiler (avoiding potential code-bloat):

  with Generic_Integers;
  package Integers is new Generic_Integers (Integer);

  with Generic_Integers;
  package Long_Integers is new Integers (Long_Integer);

  with Generic_Integers;
  package Short_Integers is new Integers (Short_Integer);

  ... etc.

(Or the users could define a few application-specific base types and
instantiate for those.  Or they could use types from Interfaces or
whatever...)

Then a given user-defined type could be declared by deriving from one of
the types from one of these instantiations:

  Error_Limit : constant := ...;
  type Error_Count_Type is new Short_Integers.Integer_Type
    range 0 .. Error_Limit;
  -- "automagically" inherits Increment, Decrement, etc...

  ...
  -- when a given error first occurs:
  Increment (Error_Count);
  Increment (Unhandled_Error_Count);

  ...
  -- once a given error is handled:
  Decrement (Unhandled_Error_Count);
  Increment (Handled_Error_Count);

The downside of this is that a given user-defined type must be tied to
some underlying implementation, which reduces its portability.

------------------------------------------------------------------------
Internet.Usenet.Put_Signature 
  (Name => "John G. Volan",  Home_Email => "johnvolan@sprintmail.com",
   Slogan => "Ada95: The World's *FIRST* International-Standard OOPL",
   Disclaimer => "These opinions were never defined, so using them " & 
     "would be erroneous...or is that just nondeterministic now? :-) ");
------------------------------------------------------------------------




      reply	other threads:[~1997-05-21  0:00 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1997-05-21  0:00 Ada can-do list Samuel Mize
1997-05-21  0:00 ` John G. Volan [this message]
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox