From: "Matthew Heaney" <mheaney@on2.com>
Subject: Re: Look what I caught! was re:Ada paper critic
Date: Tue, 24 Sep 2002 11:23:48 -0400
Date: 2002-09-24T11:23:48-04:00 [thread overview]
Message-ID: <up10s62iedli55@corp.supernews.com> (raw)
In-Reply-To: 3D10E366.2010303@mail.com
"Hyman Rosen" <hyrosen@mail.com> 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 <no analog>
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);
<do something with E>
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
<do something with E>
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<typename II, typename OI>
> 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.
next prev parent reply other threads:[~2002-09-24 15:23 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2002-06-17 17:35 Look what I caught! was re:Ada paper critic Alderson, Paul A.
2002-06-17 18:31 ` Darren New
2002-06-17 21:40 ` Vinzent Hoefler
2002-06-17 23:14 ` Darren New
2002-06-18 14:49 ` Hyman Rosen
2002-06-18 22:36 ` Vinzent Hoefler
2002-06-18 13:28 ` Marin David Condic
2002-06-24 19:17 ` Vinzent Hoefler
2002-06-18 19:16 ` Kevin Cline
2002-06-18 22:36 ` Vinzent Hoefler
2002-06-19 14:29 ` Wes Groleau
2002-06-19 16:59 ` Darren New
2002-06-19 17:48 ` Wes Groleau
2002-06-19 17:56 ` Darren New
2002-06-19 17:11 ` Frank J. Lhota
2002-06-19 19:31 ` Robert A Duff
2002-06-19 20:02 ` Hyman Rosen
2002-09-24 15:23 ` Matthew Heaney [this message]
2002-06-19 19:37 ` Robert A Duff
2002-06-19 13:52 ` Ted Dennison
replies disabled
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox