comp.lang.ada
 help / color / mirror / Atom feed
* "with private" and generics
@ 2002-09-20 14:20 Stephen Leake
  0 siblings, 0 replies; only message in thread
From: Stephen Leake @ 2002-09-20 14:20 UTC (permalink / raw)


I've run across a need for "with private" in a generic, and I think it
adds a new twist.

"with private" is a proposed addition to Ada; see 
http://www.ada-auth.org/cgi-bin/cvsweb.cgi/AIs/AI-00262.TXT?rev=1.13

Basically, it allows "with"ing a private child package, if it is only
mentioned in the private part of a package spec.

The code below demonstrates a case I came across recently (while using
OpenToken; cool stuff :); I need to mention a private child package in
a generic formal association. You can gnatchop and compile it; GNAT
3.15a1 gives the error:

commands-ints_set_parsed_value.ads:2:06: current unit must also be private descendant of "Commands"

The problem is with this generic instantiation:

with Interfaces;
with Commands.Ints; -- a private child
with Commands.Gen_Set_Parsed_Value;
procedure Commands.Ints_Set_Parsed_Value is new Commands.Gen_Set_Parsed_Value
  (Actual_Type      => Interfaces.Integer_16,
   Parsed_Type      => Commands.Ints.Parsed_Type,
   Value            => Commands.Ints.Value,
   Actual_Type_Name => "Int");


Here Commands.Ints is a private child package. It is, in fact, only
mentioned in the body of the generic. The analogous procedure
Commands.Reals_Set_Parsed_Value in the code below was written by hand,
to show that the intended result is legal. So if we have "with
private" we could do:

with Interfaces;
with private Commands.Ints;
with Commands.Gen_Set_Parsed_Value;
procedure Commands.Ints_Set_Parsed_Value is new Commands.Gen_Set_Parsed_Value
  (Actual_Type      => Interfaces.Integer_16,
   Parsed_Type      => Commands.Ints.Parsed_Type,
   Value            => Commands.Ints.Value,
   Actual_Type_Name => "Int");

However, I think we also need to add the keyword "private" on the
generic formals that are allowed to come from a private package:

generic
   type Actual_Type is private;
   private type Parsed_Type is new Commands.Parsed_Value_Type with private;
   with private function Value (Item : in Parsed_Type) return Actual_Type;
   Actual_Type_Name : in String;
procedure Commands.Gen_Set_Parsed_Value
     (Item         : in out Actual_Type;
      Parsed_Value : in     Commands.Parsed_Value_Type'Class);

That way, the compiler can check while compiling the generic that
these formals are not referenced in the public part of the generic
spec. Thus the generic contract is preserved.

Any comments? I'll send this in to the ARG soon. Also to ACT, to ask
that they implement "with private" soon :).

I have two work-arounds at the moment; I can make all the private
stuff public, and keep the generic, or I can use gnatprep instead of a
generic. I think I prefer the gnatprep approach.


--------- code follows --------------

procedure Commands.Gen_Set_Parsed_Value
  (Item         : in out Actual_Type;
   Parsed_Value : in     Commands.Parsed_Value_Type'Class)
is
begin
   if Parsed_Value in Parsed_Type then
      Item := Value (Parsed_Type (Parsed_Value));
   else
      raise Constraint_Error;
   end if;

end Commands.Gen_Set_Parsed_Value;

generic
   type Actual_Type is private;
   type Parsed_Type is new Commands.Parsed_Value_Type with private;
   with function Value (Item : in Parsed_Type) return Actual_Type;
   Actual_Type_Name : in String;
procedure Commands.Gen_Set_Parsed_Value
     (Item         : in out Actual_Type;
      Parsed_Value : in     Commands.Parsed_Value_Type'Class);

package body Commands.Ints is

   function Parse (Item : in String) return Parsed_Type
   is begin
      return (Value => Interfaces.Integer_16'Value (Item));
   end Parse;

   function Value (Item : in Parsed_Type) return Interfaces.Integer_16 is
   begin
      return Item.Value;
   end Value;

end Commands.Ints;

with Interfaces;
private package Commands.Ints is

   type Parsed_Type is new Commands.Parsed_Value_Type with private;

   function Value (Item : in Parsed_Type) return Interfaces.Integer_16;

   function Parse (Item : in String) return Parsed_Type;

   Grammar : constant Production_Type;
   --  This constant must be public to be used in Commands.Parser

private
   type Parsed_Type is new Commands.Parsed_Value_Type with record
      Value : Interfaces.Integer_16;
   end record;

   --  Stuff to actually parse a Real value.

   Grammar : constant Production_Type := (A => 1);

end Commands.Ints;

with Interfaces;
with Commands.Ints;
with Commands.Gen_Set_Parsed_Value;
procedure Commands.Ints_Set_Parsed_Value is new Commands.Gen_Set_Parsed_Value
  (Actual_Type      => Interfaces.Integer_16,
   Parsed_Type      => Commands.Ints.Parsed_Type,
   Value            => Commands.Ints.Value,
   Actual_Type_Name => "Int");

package body Commands.Reals is

   function Parse (Item : in String) return Parsed_Type
   is begin
      return (Value => Float'Value (Item));
   end Parse;

   function Value (Item : in Parsed_Type) return Float is
   begin
      return Item.Value;
   end Value;

end Commands.Reals;

private package Commands.Reals is

   type Parsed_Type is new Commands.Parsed_Value_Type with private;

   function Value (Item : in Parsed_Type) return Float;

   function Parse (Item : in String) return Parsed_Type;

   Grammar : constant Production_Type;
   --  This constant must be public to be used in Commands.Parser

private
   type Parsed_Type is new Commands.Parsed_Value_Type with record
      Value : Float;
   end record;
   --  Stuff to actually parse a Real value.

   Grammar : constant Production_Type := (A => 1);

end Commands.Reals;

with Commands.Reals;
procedure Commands.Reals_Set_Parsed_Value
  (Item         : in out Float;
   Parsed_Value : in     Commands.Parsed_Value_Type'Class)
is
begin
   if Parsed_Value in Commands.Reals.Parsed_Type then
      Item := Commands.Reals.Value (Commands.Reals.Parsed_Type (Parsed_Value));
   else
      raise Constraint_Error;
   end if;

end Commands.Reals_Set_Parsed_Value;

procedure Commands.Reals_Set_Parsed_Value
  (Item         : in out Float;
   Parsed_Value : in     Commands.Parsed_Value_Type'Class);
with Commands.Reals;
with Commands.Ints;
package body Commands is

   function "+" (Left, Right : in Production_Type) return Production_Type is
   begin
      return (A => Left.A + Right.A);
   end "+";

   --  Gather all grammar fragments together. Normally used to build
   --  a real parser.

   Grammar : constant Production_Type :=
     Commands.Reals.Grammar +
     Commands.Ints.Grammar;

   function Parse (Item : in String) return Parsed_Value_Type'Class
   is begin
      --  for this demo, we always return a real.
      return Parsed_Value_Type'Class (Commands.Reals.Parse (Item));
   end Parse;

end Commands;
package Commands is

   type Parsed_Value_Type is abstract tagged null record;
   --  Holds values from parsed commands; used by Symbols.Set.

   function Parse (Item : in String) return Parsed_Value_Type'Class;

private
   --  Stuff for actually parsing a command file or string; should
   --  _not_ be visible to clients of Command.

   type Production_Type is tagged record
      A : Integer;
   end record;

   function "+" (Left, Right : in Production_Type) return Production_Type;

end Commands;
with Commands.Ints_Set_Parsed_Value;
with Commands.Reals_Set_Parsed_Value;
with Interfaces;
procedure Demo
is
   Real : Float;
   Int  : Interfaces.Integer_16;
begin
   Commands.Reals_Set_Parsed_Value (Real, Commands.Parse ("1.0"));
   Commands.Ints_Set_Parsed_Value (Int, Commands.Parse ("1"));
end Demo;


-- 
-- Stephe



^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2002-09-20 14:20 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-09-20 14:20 "with private" and generics Stephen Leake

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