comp.lang.ada
 help / color / mirror / Atom feed
From: adam@irvine.com
Subject: Subject: Active Iteration (was: How to use abstract data types)
Date: 1998/05/06
Date: 1998-05-06T00:00:00+00:00	[thread overview]
Message-ID: <6iq9cl$i3t$1@nnrp1.dejanews.com> (raw)




Matthew Heaney wrote:

> Here's the idea I had for the library iterator.  The Library_Package.Test
> package exports an active iterator that I was hoping to use to iterate over
> a Test_Library.
>
> However, I can't get this code to compile!  My compiler is telling me the
> generic actual types don't match the formal discriminant types.
>
> If your compiler can compile this, let me know and I'll report a bug to
> ACT.  If this is simply an illegal Ada program, then maybe someone can
> figure out an alternative solution.
>
> The idea was to make the List_Books operation a generic procedure that
> imports an iterator type as a generic formal type, to iterate over the
> library object passed in.  I do it this way because importing an iterator
> as derived from an abstract root iterator type won't work, because formal
> derived types cannot have a known discriminant (see RM95 12.5.1 (11), a
> bummer of a paragraph).  So I just import everything as a non-tagged type
> (even though the actual type may in fact be tagged), and import the ops
> formal subprogram parameters ("implementation inheritance").
>
> But I can't get the instantiation of List_Books to compile.  Maybe you'll
> have more luck.

I haven't yet taken the time to figure out what's wrong yet.  But I
plan to, because I'm sure that I'd learn a lot about Ada 95 details by
doing so.

Anyway, it kind of surprises me that the solution involves a generic
that takes subprogram parameters.  The reason it surprises me is this:
based on what I thought OO was "supposed" to be, I expected that the
routines in Library_Package, including the ones that step through the
iterator, would be dispatching routines.  That is, at some point the
code would have to select which routine to call, i.e. perform an
indirect subroutine call; I expected this to take place through the
dispatching mechanism, rather than through a generic formal subprogram
(the Ada 83 way?).  Having to use a generic to handle something I
thought polymorphism should be able to handle is a bit disappointing
to me.

I tried another method for solving the problem.  Since my original
problem had to do with the fact that I couldn't use an abstract type
as an "out" parameter, I tried making it a pointer.  The program
listed below compiles and runs perfectly, and it uses the dispatching
mechanism for selecting the iteration routines, like it "should".  The
drawbacks I can see with this approach are: (1) it forces you to use
the allocation mechanism, bringing up all the problems with garbage
collection, dangling references, etc. (*); (2) the parameters of type
Library now have to be "access" to avoid accessibility problems; and
unfortunately, this requirement propagates, so that if I wrote another
subprogram S to take a Library parameter and call List_Books on that
library, S would have to declare the Library to be an "access"
parameter also.  This, to me, is a flaw.  (I could perhaps avoid this
by making the private part of Test_Library be a pointer to some
internal data, and then making the private part of
Test_Iterator_Object contain a copy of this pointer instead of an
access to the whole Test_Library.  Another possibility would be to
make the Library a parameter to all the other iteration routines.)

I'm not particularly happy with either solution.

(*) Note: Personally, this isn't a serious drawback for me; but my
perception is that some programmers have reasons for trying to avoid
the allocation mechanism.

                                -- Adam


===============================================================================

package Library_Package is

    type Library is abstract tagged null record;
    type Book is abstract tagged null record;

    type Iterator_Object is abstract tagged null record;
    type Iterator is access all Iterator_Object'class;

    function The_Library return Library is abstract;

    function Title (Bk : Book) return string is abstract;

    procedure Scan_For_Substring (Lib    : access Library;
                                  Iter   : out Iterator;
                                  Substr : in string) is abstract;

    function Is_Done (Iter : access Iterator_Object) return Boolean
        is abstract;

    function Get_Book (Iter : access Iterator_Object) return Book'class
        is abstract;

    procedure Advance (Iter : access Iterator_Object) is abstract;

end Library_Package;




with Library_Package;
package Test_Library_Package is

    type Test_Library is new Library_Package.Library with private;

    type Test_Book is new Library_Package.Book with private;

    type Test_Iterator_Object is new
        Library_Package.Iterator_Object with private;

    function The_Library return Test_Library;
    function Title (Bk : Test_Book) return string;
    procedure Scan_For_Substring (Lib    : access Test_Library;
                                  Iter   : out Library_Package.Iterator;
                                  Substr : in string);
    function Is_Done (Iter : access Test_Iterator_Object) return Boolean;
    function Get_Book (Iter : access Test_Iterator_Object) return
                 Library_Package.Book'class;
    procedure Advance (Iter : access Test_Iterator_Object);

private
    type String_P is access string;
    type Book_Info is record
        Title : String_P;
    end record;
    type Book_Array is array (Natural range <>) of Book_Info;

    type Test_Library is new Library_Package.Library with null record;

    type Test_Library_Acc is access all Test_Library;

    type Test_Book is new Library_Package.Book with record
        Info : Book_Info;
    end record;

    type Test_Iterator_Object is new
            Library_Package.Iterator_Object with record
        Lib           : Test_Library_Acc;
        Search_String : String_P;
        Index         : Natural;
    end record;

end Test_Library_Package;



with Ada.Strings.Fixed;
package body Test_Library_Package is

    My_Library : constant Book_Array :=
        ( (Title => new string' ("A Time To Kill")),
          (Title => new string' ("The Firm")),
          (Title => new string' ("The Pelican Brief")),
          (Title => new string' ("The Client")),
          (Title => new string' ("The Rainmaker")),
          (Title => new string' ("The Runaway Jury")) );

    function The_Library return Test_Library is
    begin
        return (null record);
    end The_Library;

    function Title (Bk : Test_Book) return string is
    begin
        return Bk.Info.Title.all;
    end Title;

    procedure Search (Iter : in out Test_Iterator_Object) is
    begin
        while Iter.Index <= My_Library'last loop
            exit when Ada.Strings.Fixed.Index
                          (My_Library (Iter.Index).Title.all,
                           Iter.Search_String.all) /= 0;
            Iter.Index := Iter.Index + 1;
        end loop;
    end Search;

    procedure Scan_For_Substring (Lib    : access Test_Library;
                                  Iter   : out Library_Package.Iterator;
                                  Substr : in string) is
        Iter_Obj : Test_Iterator_Object;
    begin
        Iter_Obj.Lib := Lib;
            -- the .Lib isn't actually used for anything in this
            -- package body, but in real life the routines below would
            -- use it
        Iter_Obj.Search_String := new string' (Substr);
        Iter_Obj.Index := My_Library'first;
        Search (Iter_Obj);
        Iter := new Test_Iterator_Object' (Iter_Obj);
    end Scan_For_Substring;

    function Is_Done (Iter : access Test_Iterator_Object) return Boolean is
    begin
        return (Iter.Index > My_Library'last);
    end Is_Done;

    function Get_Book (Iter : access Test_Iterator_Object) return
                 Library_Package.Book'class is
    begin
        return Test_Book' (Info => My_Library (Iter.Index));
    end Get_Book;

    procedure Advance (Iter : access Test_Iterator_Object) is
    begin
        Iter.Index := Iter.Index + 1;
        Search (Iter.all);
    end Advance;

end Test_Library_Package;



with Library_Package;
package List_Package is

    procedure List_Books (Lib    : access Library_Package.Library'class;
                          Substr : in string);

end List_Package;


with Text_IO;
package body List_Package is

    procedure List_Books (Lib    : access Library_Package.Library'class;
                          Substr : in string) is
        Iter : Library_Package.Iterator;
    begin
        Library_Package.Scan_For_Substring (Lib, Iter, Substr);
        while not Library_Package.Is_Done (Iter) loop
            declare
                Bk : Library_Package.Book'class :=
                         Library_Package.Get_Book (Iter);
            begin
                Text_IO.Put_Line (Library_Package.Title (Bk));
            end;
            Library_Package.Advance (Iter);
        end loop;
    end List_Books;

end List_Package;



with Test_Library_Package;
with List_Package;
procedure Libtest3 is
    The_Lib : aliased Test_Library_Package.Test_Library;
begin
    The_Lib := Test_Library_Package.The_Library;
    List_Package.List_Books (The_Lib'access, "m");
end Libtest3;


-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/   Now offering spam-free web-based newsreading




             reply	other threads:[~1998-05-06  0:00 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1998-05-06  0:00 adam [this message]
1998-05-07  0:00 ` Subject: Active Iteration (was: How to use abstract data types) Matthew Heaney
1998-05-08  0:00 ` Matthew Heaney
1998-05-08  0:00   ` Brian Rogoff
replies disabled

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