comp.lang.ada
 help / color / mirror / Atom feed
* generic formal object of class-wide type
@ 1999-04-29  0:00 Matthew Heaney
  1999-04-29  0:00 ` David C. Hoos, Sr.
  1999-04-29  0:00 ` Jean-Pierre Rosen
  0 siblings, 2 replies; 6+ messages in thread
From: Matthew Heaney @ 1999-04-29  0:00 UTC (permalink / raw)


When I try to compile package Q (see below), I get this error:

q.ads:3:25: type of actual does not match type of "O"

Why?  

Generic formal object O (in GQ) is of type P.T'Class.  

Actual object O is of specific type NT, yes, but that type is in
T'Class.  So why isn't object O allowed as the actual for formal GC.O?




package P is
   type T is abstract tagged limited null record;
end;


package P.C is
   type NT is new T with null record;
   O : NT;
end;


with P;
generic
  O : in out P.T'Class;
package GQ is
end;


with GQ, P.C;
package Q is new GQ (P.C.O);







^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: generic formal object of class-wide type
  1999-04-29  0:00 generic formal object of class-wide type Matthew Heaney
@ 1999-04-29  0:00 ` David C. Hoos, Sr.
  1999-04-29  0:00   ` Matthew Heaney
  1999-04-29  0:00 ` Jean-Pierre Rosen
  1 sibling, 1 reply; 6+ messages in thread
From: David C. Hoos, Sr. @ 1999-04-29  0:00 UTC (permalink / raw)



Matthew Heaney wrote in message ...
>When I try to compile package Q (see below), I get this error:
>
>q.ads:3:25: type of actual does not match type of "O"
>
>Why?
>
The actual needs to be explicitly converted to the type of the
formal, like so:

package Q is new GQ (P.T'Class (P.C.O));







^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: generic formal object of class-wide type
  1999-04-29  0:00 ` David C. Hoos, Sr.
@ 1999-04-29  0:00   ` Matthew Heaney
  1999-04-30  0:00     ` Robert A Duff
  0 siblings, 1 reply; 6+ messages in thread
From: Matthew Heaney @ 1999-04-29  0:00 UTC (permalink / raw)


"David C. Hoos, Sr." <david.c.hoos.sr@ada95.com> writes:

> Matthew Heaney wrote in message ...
> >When I try to compile package Q (see below), I get this error:
> >
> >q.ads:3:25: type of actual does not match type of "O"
> >
> >Why?
> >
> The actual needs to be explicitly converted to the type of the
> formal, like so:
> 
> package Q is new GQ (P.T'Class (P.C.O));

Yes, but why?

If I have a subprogram with a parameter of a class-wide type, like this:

procedure Op (O : T'Class);

and NT is a type in T'Class, then I can do this:

declare
  O : NT;
begin
  Op (O);
end;


No conversion to type T'Class is required in order to call Op.

It is inconsistent that a type conversion is required in order pass the
object as a generic actual.  I'd like to know the specific rule, and a
rationale for the inconsistency.








^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: generic formal object of class-wide type
  1999-04-29  0:00 generic formal object of class-wide type Matthew Heaney
  1999-04-29  0:00 ` David C. Hoos, Sr.
@ 1999-04-29  0:00 ` Jean-Pierre Rosen
  1 sibling, 0 replies; 6+ messages in thread
From: Jean-Pierre Rosen @ 1999-04-29  0:00 UTC (permalink / raw)


Matthew Heaney a �crit dans le message ...
>When I try to compile package Q (see below), I get this error:
>
>q.ads:3:25: type of actual does not match type of "O"
>
>Why?
>
>Generic formal object O (in GQ) is of type P.T'Class.
>
>Actual object O is of specific type NT, yes, but that type is in
>T'Class.  So why isn't object O allowed as the actual for formal
GC.O?
>
If the formal is declared as P.T'Class, the actual has to be P.T'Class
(P.T'Class IS a type that follows (mostly) usual typing rules).
If you want to allow for a type IN the class, you say that it is
derived from P.T:

generic
   type D is new P.T with private;
---------------------------------------------------------
           J-P. Rosen (Rosen.Adalog@wanadoo.fr)
Visit Adalog's web site at http://perso.wanadoo.fr/adalog






^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: generic formal object of class-wide type
  1999-04-29  0:00   ` Matthew Heaney
@ 1999-04-30  0:00     ` Robert A Duff
  1999-04-30  0:00       ` Richard D Riehle
  0 siblings, 1 reply; 6+ messages in thread
From: Robert A Duff @ 1999-04-30  0:00 UTC (permalink / raw)


Matthew Heaney <matthew_heaney@acm.org> writes:

> "David C. Hoos, Sr." <david.c.hoos.sr@ada95.com> writes:
...
> > The actual needs to be explicitly converted to the type of the
> > formal, like so:
> > 
> > package Q is new GQ (P.T'Class (P.C.O));
> 
> Yes, but why?
> 
> If I have a subprogram with a parameter of a class-wide type, like this:
> 
> procedure Op (O : T'Class);
> 
> and NT is a type in T'Class, then I can do this:
> 
> declare
>   O : NT;
> begin
>   Op (O);
> end;
> 
> 
> No conversion to type T'Class is required in order to call Op.
> 
> It is inconsistent that a type conversion is required in order pass the
> object as a generic actual.  I'd like to know the specific rule, and a
> rationale for the inconsistency.

There is some discussion of this in AARM-8.5.1(3.a-3.f).  Note that
generic formal 'in out' objects are not analogous to formal subprogram
parameters -- they are analogous to renaming declarations.  Compilers
take advantage of this -- they typically use the same code to analyze
generic instantiation with a generic formal 'in out', and to analyze an
object renaming declaration.  In fact, I noticed in an early version of
one Ada 95 compiler, that the compiler would complain about "illegal
renaming declaration" or some such, when there was no renaming in sight
-- just a generic instantiation.  The compiler was eventually fixed.

The Ada 83 language designers went to some trouble to make the rules
the same for both, and we tried to keep it that way for Ada 95.

See also AARM-12.4(1.a-1.b).

Note also that renaming declarations are kind of weird, in that certain
information in the declaration is ignored.  I don't like that, because
it causes confusion:

    X: constant Integer := -10;
    ...
    Y: Positive renames X;

Y is constant, even though it doesn't say so explicitly, and (worse) it
pretends to be Positive, but is actually negative.

- Bob
-- 
Change robert to bob to get my real email address.  Sorry.




^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: generic formal object of class-wide type
  1999-04-30  0:00     ` Robert A Duff
@ 1999-04-30  0:00       ` Richard D Riehle
  0 siblings, 0 replies; 6+ messages in thread
From: Richard D Riehle @ 1999-04-30  0:00 UTC (permalink / raw)


>Matthew Heaney <matthew_heaney@acm.org> writes:
>
>> "David C. Hoos, Sr." <david.c.hoos.sr@ada95.com> writes:
>....
>> > The actual needs to be explicitly converted to the type of the
>> > formal, like so:

 This posting is longer than I usually write.  However, I think the
 topic of generics is important enough that I should include as 
 complete an example as appropriate to illustrate my point.  If you
 are short of time you might want to save this for later reading. Or
 if you are having difficulty getting to sleep at night, you may find
 it useful to print and read just before retiring. 

 One of the features of Ada that is more powerful than C++ templates
 is the persistence of type conformity throughout the entire generic
 model.  Modula-3 comes pretty close to Ada in this respect because
 it also requires type-checking (and parameter conformity) of 
 instantiated units.  This benefit of Ada is especially important
 when creating generic units that approximate what we see in C++.
 That is, in C++, we can instantiate a class with one or more 
 entire templates.  Although the C++ syntax is simpler, the conformity
 checking is less reliable than Ada or Modula-3 where we can instantiate
 a module (Ada package) with an entire module as the generic formal
 parameter.   In doing this with Ada, you will sometimes have to include
 some type (view) conversion in the design.  We provide the following
 abbreviated example of a numeric package.  Some of the features of the
 full example have been eliminated in the interest of brevity, but it 
 should be easily seen how this can be useful in creating reusable
 software components.  Also, I realize the original discussion dealt with
 class-wide types, but Ada's consistency model suggests that the same  
 principles would apply even in the example shown.


 with Numeric_Model;
 generic
     with package Number_Ops is new Numeric_Model(<>);
 package Number_Operations is
    type Real is private;
    type Real_Reference is access all Real;
    function Zero return Real;
    function "+"  (L, R : Real) return Real;
    function "-"  (L, R : Real) return Real;
    function "*"  (L, R : Real) return Real;
    function "/"  (L, R : Real) return Real;
    function "="  (L, R : Real) return Boolean;
    function ">"  (L, R : Real) return Boolean;
    function "<"  (L, R : Real) return Boolean;
    function ">=" (L, R : Real) return Boolean;
    function "<=" (L, R : Real) return Boolean;
    function SQRT (V    : Real) return Real;
    -- More Math Operations on Real
    function Machine_Number (V : Real) return Real;
    -- More attribute operations as functions
    function  Get return Real;
    procedure Put (V : Real; Fore, Aft : Natural);
    -- Specialize the exceptions for this type 
    Real_Constraint_Error : exception;
    Divide_By_Zero        : exception;
    Outside_Base_Range    : exception;
  private
    type Real is new Number_Ops.Number;
  end Number_Operations;

In this example we are defining our own Real number type with associated
operations.  It is possible to actually declare the full type of Real
as a tagged type or a simple record using this model, thereby encapsualting
some unique properties for a specialized numeric application.  The with
statement refers to a generic package.  In this case, the generic package
only contains generic formal parameters for a set of functions.  Here
is the specification for package Numeric_Model. 

generic
  type Number is digits <>;
  with function "+"    (L, R : Number) return Number  is <>;
  with function "*"    (L, R : Number) return Number  is <>;
  with function "-"    (L, R : Number) return Number  is <>;
  with function "/"    (L, R : Number) return Number  is <>;
  with function "abs"  (N : Number)    return Number  is <>;
  with function "="    (L, R : Number) return Boolean is <>;
  with function ">"    (L, R : Number) return Boolean is <>;
  with function "<"    (L, R : Number) return Boolean is <>;
  with function ">="   (L, R : Number) return Boolean is <>;
  with function "<="   (L, R : Number) return Boolean is <>;
package Numeric_Model is end Numeric_Model;

At first glance, one might conclude that this is not useable because
it has no body.  That is exactly what makes it so powerful.  It is 
also what makes it possible to preserve type and profile conformity
across the entire compilation process.  In this respect it is slightly
more complex syntax than that of Modula-3 but also checked at earlier
stages of the design process.  

Notice that the statement,

    with package Number_Ops is new Numeric_Model(<>);

looks like an instantiation itself.  In fact, the package for
which this is a generic formal parameter will always refer to
Number_Ops not to Numeric_Model just as if Number_Ops was a
true instantiation.  This is frequently a point of confusion for
Ada programmers but once you understand it, it becomes quite sensible.
How else could we preserve full profile conformity checking throughout
the entire compilation process?  Some of us have taken to calling this
kind of generic, one with only generic formal parameters and no body,
as a "generic formal model."  We need to find some agreement in the 
Ada community on what to finally call it so it can be referred to 
in the literature more concisely.

Our purpose in using this package might include declaring our own versions
of division and equality checking.  This is not an unusual requirement
for floating point numbers.  Here is a simple procedure with some of the
calls "stubbed out" to illustrate the idea.

with Numeric_Model;
with Number_Operations;
procedure Test_Own_Float_Operations is
  type Own_Float is digits 12;
  function Divide    (L, R : Own_Float) return Own_Float is
     Result : Own_Float := 0.0;
  begin
     -- your own division operation;
     return Result;
  end Divide;
  
  function Is_Equal  (L, R : Own_Float) return Boolean is
     Result : Boolean := False;
  begin
     -- your own equality operator
     return Result;
  end Is_Equal;
  package Math_Ops is new Numeric_Model (Number => Own_Float,
                                         "=" => Is_Equal,
                                         "/" => Divide);
  package Own_Math is new Number_Operations (Number_Ops => Math_Ops);
  use type Own_Math.Real;
  Data : Own_Math.Real := Own_Math.Zero;
  Left, Right : Own_Math.Real := Own_Math.Zero;
begin

  Data := Left / Right;
  Data := Left * Right;
  if Left = Right then
    null;
  end if; 
end Test_Own_Float_Operations;

You will observe that we have two instantiations, one for the 
"generic formal model" and another for the actual generic package.
The model is instantiated with the operations.  The generic package
is instantiated with an instance of the model.  A more interesting
case of this would be one where the generic package has multiple
generic formal package parameters.  This latter case would suggest the 
potential for generic reusable frameworks, a larger idea than that
used in Ada 83.  It makes Ada every bit as powerful as the corresponding
capability in C++, but safer, much safer.

This has already been a long posting, and I apologize for that.  However,
the discussion originally centered around the notion of type and view
conversions.  Therefore, the following package body demonstrates at least
one example of how the type conversions become useful in the actual
implementation.  

with Ada.Numerics.Generic_Elementary_Functions;
with Ada.Text_IO;
package body Number_Operations is
  package GEF is new Ada.
                     Numerics.
                     Generic_Elementary_Functions
                          (Float_Type => Real);
  package FIO is new Ada.Text_IO.Float_IO(Num => Real);
  F_Zero : constant := 0.0;
  function Zero return Real is
  begin
     return F_Zero;
  end Zero;
  
  function "+"  (L, R : Real) return Real is
  begin
     return Real(Number_Ops."+"(Number_Ops.Number(L),
                                Number_Ops.Number(R)));
  end "+" ;

  function "-"  (L, R : Real) return Real is
  begin
     return Real(Number_Ops."-"(Number_Ops.Number(L),
                                Number_Ops.Number(R)));
  end "-" ;
  function "*"  (L, R : Real) return Real is
  begin
     return Real(Number_Ops."*"(Number_Ops.Number(L),
                                Number_Ops.Number(R)));
  end "*" ;

  function "/"  (L, R : Real) return Real is
  begin
     if R = F_Zero then
        raise Divide_By_Zero;
     else
       return Real(Number_Ops."/"(Number_Ops.Number(L),
                                  Number_Ops.Number(R)));
     end if;
  end "/" ;


  function "="  (L, R : Real) return Boolean is
  begin
     return Number_Ops."="(Number_Ops.Number(L),
                                Number_Ops.Number(R));
  end "=" ;
  
  function ">"  (L, R : Real) return Boolean is
  begin
       return Number_Ops.">"(Number_Ops.Number(L),
                             Number_Ops.Number(R));
  end ">" ;

  function "<"  (L, R : Real) return Boolean is
  begin
       return Number_Ops."<"(Number_Ops.Number(L),
                             Number_Ops.Number(R));
  end "<" ;

  function ">=" (L, R : Real) return Boolean is
  begin
       return Number_Ops.">="(Number_Ops.Number(L),
                              Number_Ops.Number(R));
  end ">=" ;

  function "<=" (L, R : Real) return Boolean is
  begin
     return Number_Ops."<="(Number_Ops.Number(L),
                            Number_Ops.Number(R));
  end "<=" ;

  function SQRT (V    : Real) return Real is
  begin
     return GEF.SQRT(V);
  end SQRT;
  
  function Machine_Number (V : Real) return Real is
  begin
     return Real'Machine(V); -- renaming does not work for this
  end Machine_Number;
                              
  function Get return Real is
     Result : Real;
  begin
     FIO.Get(Result);
     return Result;
  end Get;
  
  procedure Put (V : Real; Fore, Aft : Natural) is
  begin
     FIO.Put(V, Fore => Fore, Aft => Aft);
  end Put;


end Number_Operations;

I hope this helps a little bit in understanding generic formal 
package parameters and some of the issues related to type conversion,
where and when.  If you have read this far, I thank you for your 
patience.  

Richard Riehle
richard@adaworks.com
http://www.adaworks.com















^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~1999-04-30  0:00 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1999-04-29  0:00 generic formal object of class-wide type Matthew Heaney
1999-04-29  0:00 ` David C. Hoos, Sr.
1999-04-29  0:00   ` Matthew Heaney
1999-04-30  0:00     ` Robert A Duff
1999-04-30  0:00       ` Richard D Riehle
1999-04-29  0:00 ` Jean-Pierre Rosen

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