From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,e95e8407f65e1cfb X-Google-Attributes: gid103376,public X-Google-ArrivalTime: 2002-09-24 08:23:51 PST Path: archiver1.google.com!news1.google.com!newsfeed.stanford.edu!paloalto-snf1.gtei.net!news.gtei.net!enews.sgi.com!nntp1.phx1.gblx.net!nntp.gblx.net!nntp.gblx.net!newsfeed.news2me.com!newsfeed-west.nntpserver.com!hub1.meganetnews.com!nntpserver.com!telocity-west!TELOCITY!sn-xit-03!sn-xit-06!sn-post-01!supernews.com!corp.supernews.com!not-for-mail From: "Matthew Heaney" Newsgroups: comp.lang.ada Subject: Re: Look what I caught! was re:Ada paper critic Date: Tue, 24 Sep 2002 11:23:48 -0400 Organization: Posted via Supernews, http://www.supernews.com Message-ID: References: <3d0e5750_2@news.bluewin.ch> <3d0fb5eb_3@news.bluewin.ch> <3D10952F.17A62CCF@despammed.com> <3D10E366.2010303@mail.com> X-Priority: 3 X-MSMail-Priority: Normal X-Newsreader: Microsoft Outlook Express 6.00.2600.0000 X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2600.0000 X-Complaints-To: abuse@supernews.com Xref: archiver1.google.com comp.lang.ada:29312 Date: 2002-09-24T11:23:48-04:00 List-Id: "Hyman Rosen" wrote in message news:3D10E366.2010303@mail.com... > Robert A Duff wrote: > > > > Really? I thought the above was OK. The STL of C++ depends heavily on > > this idiom. Is this a difference between C and C++? If so, what's the > > rationale (in C)? > > It's not OK, and the STL never uses this idiom. The > STL uses half-open intervals [begin,end) where the > begin iterator points to the start of the sequence > being manipulated, and the end iterator points to > one past the end of the sequence being manipulated, > and the canonical STL algorithm loop is > > while (begin != end) { operate_on(*begin++); } STL iterators are a generalization of normal pointers. It is true that you can't address the element "before" the start of the array, because the compiler can allocate the array at the base of a segment, which doesn't have a "before" address. To iterate in reverse, and use a sentinel allowing you to fall off the beginning of the sequence to terminate the iteration, the STL uses a reverse iterator which is implemented as a wrapper around a normal, forward iterator (which you can always recover using the base() member function of the reverse iterator class). You don't really use roaming pointers in Ada; rather, you use an array object and a separate index. You never index the array prior to the first element of the array (nor past the last element, for that matter), but of course the value of the index can have a value the preceeds the T'First of the array. This has precedent in Ada; for example, you often see string searches implemented this way: function Find (S : String; C : Character) return Natural; Here, the function returns the value 0 to indicate that the character C wasn't found in array (string) S. In Charles, I have generalized the STL iterator behavior, such that you're allowed to have an iterator that designates the element "before" the start of the sequence. The Front iterator value has the sense of Index_Subtype'Pred (Array_Object'First), and the Back iterator value has the sense of Index_Subtype'Succ (Array_Object'Last). The First and Last iterators have their traditional sense. The complete mapping is: Charles STL -------- --- Front First begin() Last iter = end(), --iter; Back end() The loop above can be written as: declare I : Iterator_Type := First (C); J : constant Iterator_Type := Back (C); begin while I /= J loop E := Element (I); I := Succ (I); end loop; end; Of course, it's often simpler to just use a passive iterator, similer to an STL algorithm: declare procedure Process (E : T) is begin end; procedure Iterate is new Generic_Select_Elements (Process); begin Iterate (First (C), Back (C)); end; In C++, (non-const) iterators return T&. You don't have reference types in Ada, but you can accomplish something very similar using access types. All the containers in Charles have a generic operation for returning an access type designating the container element (which are always aliased). For example, given a subprogram like this: procedure Do_Something (E : in out T); then you can do this: type T_Access is access all T; for T_Access'Storage_Size use 0; function To_Access is new Generic_Element (T_Access); I : Iterator_Type := First (C); J : constant Iterator_Type := Back (C); while I /= J loop declare E : Element_Type renames To_Access (I).all; begin Do_Something (E); end; I := Succ (I); end loop; which is exactly analogous to this STL fragment: void do_something(t& e); iterator i = c.begin(); const iterator j = c.end(); while (i != j) { t& e = *i; do_something(e); ++i; } > The "one past the end" pointer can be logical instead > of physical, depending on the underlying container, but > the algorithms don't care. That's also true in Charles. For example, the copy algorithm spec looks like this: generic type Source_Type is private; type Target_Type (<>) is limited private; with procedure Assign (Target : in Target_Type; Source : in Source_Type) is <>; with procedure Succ (Source : in out Source_Type) is <>; with procedure Succ (Target : in out Target_Type) is <>; with function "=" (L, R : Source_Type) return Boolean is <>; procedure Charles.Algorithms.Generic_Copy (First, Back : in Source_Type; Target : in out Target_Type); You don't have automatic instantiation in Ada as you have in C++, but you don't really need it. An instantiation of the algorithm above would look like this: declare procedure Assign (T : P1.Iterator_Type; S : P2.Iterator_Type) is begin Replace_Element (T, By => Element (S)); end; procedure Copy is new Generic_Copy (P2.Iterator_Type; P1.Iterator_Type); --everything else defaults I : P1.Iterator_Type := First (C2); begin ... Copy (First (C1), Back (C1), I); end; This is why reverse iterators aren't needed, either. All you need to do to iterate over the source in reverse is supply a different function as the generic actual: declare procedure Assign (T : P1.Iterator_Type; S : P2.Iterator_Type) is begin Replace_Element (T, By => Element (S)); end; procedure Copy is new Generic_Copy (P2.Iterator_Type; P1.Iterator_Type, Assign, Pred); -- this line is different I : P1.Iterator_Type := First (C2); begin ... Copy (Last (C1), Front (C1), I); --this is dif't too end; Of course, it's usually simpler to just use a passive iterator: declare I : P1.Iterator_Type := First (C2); procedure Process (E : T) is begin Replace_Element (I, By => E); I := Succ (I); end; procedure Iterate is new P2.Generic_Reverse_Select_Elements (Process); begin Iterate (First (C1), Back (C1)); end; You can even iterate over the target container in reverse using this technique: declare I : P1.Iterator_Type := Last (C2); procedure Process (E : T) is begin Replace_Element (I, By => E); I := Pred (I); end; procedure Iterate is new P2.Generic_Select_Elements (Process); begin Iterate (First (C1), Back (C1)); end; > The code one writes for something like copy (copy itself is > part of the standard library) is completely ignorant of these > shenaningans. It looks like a simple pointer loop - > > template > OI copy(II begin, II end, OI to) > { > while (begin != end) > *to++ = *begin++; > return to; > } The copy algorithm in Charles is implemented this way: procedure Charles.Algorithms.Generic_Copy (First, Back : in Source_Type; Target : in out Target_Type) is Source : Source_Type := First; begin while Source /= Back loop Assign (Target, Source); Succ (Source); Succ (Target); end loop; end Charles.Algorithms.Generic_Copy; which is basically the same as in the C++ example above. You can get the latest version of Charles at my home page: http://home.earthlink.net/~matthewjheaney/charles-20020923.zip I'll probably submit a paper to the Ada-Europe conference about Charles.