comp.lang.ada
 help / color / mirror / Atom feed
* Re: "out" or "access"
  1998-10-21  0:00   ` Pat Rogers
@ 1998-10-21  0:00     ` Martin C. Carlisle
  1998-10-22  0:00       ` Pat Rogers
  1998-10-22  0:00     ` Robert A Duff
  1 sibling, 1 reply; 24+ messages in thread
From: Martin C. Carlisle @ 1998-10-21  0:00 UTC (permalink / raw)


Be careful using access as below, as this creates two different
dispatched objects in the call (which is illegal).  See LRM 6.1(24)
and 3.9.2

--Martin

In article <70kvnv$gvo$1@supernews.com>,
Pat Rogers <progers@NOclasswideSPAM.com> wrote:
>Jeff Carter wrote in message <362DF0D3.BC101364@spam.innocon.com>...
>A bit too strong there, IMHO.  Access parameters are a very handy
>alternative when you want to derive from a type that has primitive
>subprograms with a formal parameter of a named access type.  Why?
>Because access parameters are not named access types, so the
>derivation provides primitives with the desired signatures.  For
>example:
>
>  type T1 is tagged ...
>
>  type T1_Pointer is access T1;
>
>  procedure Foo( This : in T1;   That : in T1_Pointer );
>
>Then, in a derivation:
>
>  type T2 is new T1 with ...
>
>This gives us:
>
>
>  procedure Foo( This : in T2;  That : in T1_Pointer );
>
>Note that the type of the formal 'That' is still T1_Pointer,
>probably not what we wanted.
>
>On the other hand, had we defined things as:
>
>  type T1 is tagged ...
>
>  -- don't need this now...  type T1_Pointer is access T1;
>
>  procedure Foo( This : in T1;   That : access T1 );
>
>The derivation would provide
>
>  procedure Foo( This : in T2;   That : access T2 );
>
>which is probably the desired result in this case.
-- 
Martin C. Carlisle, Computer Science, US Air Force Academy
mcc@cs.usafa.af.mil, http://www.usafa.af.mil/dfcs/bios/carlisle.html
DISCLAIMER:  This content in no way reflects the opinions, standard or 
policy of the US Air Force Academy or the United States Government.




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

* "out" or "access"
@ 1998-10-21  0:00 =:-) Vincent
  1998-10-21  0:00 ` Jeff Carter
                   ` (2 more replies)
  0 siblings, 3 replies; 24+ messages in thread
From: =:-) Vincent @ 1998-10-21  0:00 UTC (permalink / raw)


What's the best way ?

package B is
    ...
    The_Procedure ( The_Parameter: out Integer );
    -- or The_Procedure ( The_Parameter: access Integer );
    ...
end B;
...
package body A is
    ...
    g_Toto: Integer; -- or aliased Integer
    ...
    procedure The_Other_Procedure is
    begin
        ...
        B.The_Procedure ( g_Toto );
        -- or  B.The_Procedure( g_Toto'Access );
        ...
    end;
    ...
end A;

Thanks.







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

* Re: "out" or "access"
  1998-10-21  0:00 "out" or "access" =:-) Vincent
  1998-10-21  0:00 ` Jeff Carter
@ 1998-10-21  0:00 ` Tucker Taft
  1998-10-22  0:00   ` Pascal Obry
  1998-10-21  0:00 ` dennison
  2 siblings, 1 reply; 24+ messages in thread
From: Tucker Taft @ 1998-10-21  0:00 UTC (permalink / raw)


=:-) Vincent (vb@bruker.fr) wrote:
: What's the best way ?

An "out" parameter is always preferable to an "access" parameter, in my view.  

Use an "access" parameter only if you need an access value when
inside the called subprogram for some reason.  This usually only
happens when interfacing to some other language, or some preexisting
subsystem of some sort.

: package B is
:     ...
:     The_Procedure ( The_Parameter: out Integer );
:     -- or The_Procedure ( The_Parameter: access Integer );
:     ...
: end B;
: ...
: package body A is
:     ...
:     g_Toto: Integer; -- or aliased Integer
:     ...
:     procedure The_Other_Procedure is
:     begin
:         ...
:         B.The_Procedure ( g_Toto );
:         -- or  B.The_Procedure( g_Toto'Access );
:         ...
:     end;
:     ...
: end A;

: Thanks.

--
-Tucker Taft   stt@inmet.com   http://www.inmet.com/~stt/
Intermetrics, Inc.  Burlington, MA  USA
An AverStar Company




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

* Re: "out" or "access"
  1998-10-21  0:00 "out" or "access" =:-) Vincent
@ 1998-10-21  0:00 ` Jeff Carter
  1998-10-21  0:00   ` Pat Rogers
  1998-10-21  0:00 ` Tucker Taft
  1998-10-21  0:00 ` dennison
  2 siblings, 1 reply; 24+ messages in thread
From: Jeff Carter @ 1998-10-21  0:00 UTC (permalink / raw)
  To: =:-) Vincent

=:-) Vincent wrote:
> 
> What's the best way ?
> 
> package B is
>     ...
>     The_Procedure ( The_Parameter: out Integer );
>     -- or The_Procedure ( The_Parameter: access Integer );
>     ...
> end B;
> ...
> package body A is
>     ...
>     g_Toto: Integer; -- or aliased Integer
>     ...
>     procedure The_Other_Procedure is
>     begin
>         ...
>         B.The_Procedure ( g_Toto );
>         -- or  B.The_Procedure( g_Toto'Access );
>         ...
>     end;
>     ...
> end A;

There's no comparison. An access parameter is closer to an "in out"
parameter than an "out" parameter. If an "out" parameter will do, do not
use an access parameter if you can avoid it. If an "in out" parameter
will do, still don't use an access parameter if you can avoid it. Access
parameters exist primarily to interfaces with subprograms from other
languages.

-- 
Jeff Carter  PGP:1024/440FBE21
E-mail: carter commercial-at innocon period com
"Monsieur Arthur King, who has the brain of a duck, you know."
Monty Python & the Holy Grail




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

* Re: "out" or "access"
  1998-10-21  0:00 "out" or "access" =:-) Vincent
  1998-10-21  0:00 ` Jeff Carter
  1998-10-21  0:00 ` Tucker Taft
@ 1998-10-21  0:00 ` dennison
  2 siblings, 0 replies; 24+ messages in thread
From: dennison @ 1998-10-21  0:00 UTC (permalink / raw)


In article <908956499.754394@dedale.pandemonium.fr>,
  "=:-) Vincent" <vb@bruker.fr> wrote:
> What's the best way ?
>

Generally you will want to use "out" unless you *need* to use access. You
didn't provide any information about what the routine needs to do with the
parameter for me to say if you need access, but in most cases you won't.

--
T.E.D.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    




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

* Re: "out" or "access"
  1998-10-21  0:00 ` Jeff Carter
@ 1998-10-21  0:00   ` Pat Rogers
  1998-10-21  0:00     ` Martin C. Carlisle
  1998-10-22  0:00     ` Robert A Duff
  0 siblings, 2 replies; 24+ messages in thread
From: Pat Rogers @ 1998-10-21  0:00 UTC (permalink / raw)


Jeff Carter wrote in message <362DF0D3.BC101364@spam.innocon.com>...
>=:-) Vincent wrote:
>>

>> <snipped question of access parameters versus mode out
parameters>
>
> <snipped good answer by Jeff>
> Access parameters exist primarily to interfaces with subprograms
from other
>languages.

A bit too strong there, IMHO.  Access parameters are a very handy
alternative when you want to derive from a type that has primitive
subprograms with a formal parameter of a named access type.  Why?
Because access parameters are not named access types, so the
derivation provides primitives with the desired signatures.  For
example:

  type T1 is tagged ...

  type T1_Pointer is access T1;

  procedure Foo( This : in T1;   That : in T1_Pointer );

Then, in a derivation:

  type T2 is new T1 with ...

This gives us:


  procedure Foo( This : in T2;  That : in T1_Pointer );

Note that the type of the formal 'That' is still T1_Pointer,
probably not what we wanted.

On the other hand, had we defined things as:

  type T1 is tagged ...

  -- don't need this now...  type T1_Pointer is access T1;

  procedure Foo( This : in T1;   That : access T1 );

The derivation would provide

  procedure Foo( This : in T2;   That : access T2 );

which is probably the desired result in this case.

---
Pat Rogers                            Training, Development and
Consulting in:
http://www.classwide.com    Deadline Schedulability Analysis
progers@acm.org                  Software Fault Tolerance
(281)648-3165                       Real-Time/OO Languages







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

* Re: "out" or "access"
  1998-10-21  0:00   ` Pat Rogers
  1998-10-21  0:00     ` Martin C. Carlisle
@ 1998-10-22  0:00     ` Robert A Duff
  1 sibling, 0 replies; 24+ messages in thread
From: Robert A Duff @ 1998-10-22  0:00 UTC (permalink / raw)


"Pat Rogers" <progers@NOclasswideSPAM.com> writes:

> A bit too strong there, IMHO.  Access parameters are a very handy
> alternative when you want to derive from a type that has primitive
> subprograms with a formal parameter of a named access type.  Why?
> Because access parameters are not named access types, so the
> derivation provides primitives with the desired signatures.  For
> example:
> 
>   type T1 is tagged ...
> 
>   type T1_Pointer is access T1;
> 
>   procedure Foo( This : in T1;   That : in T1_Pointer );

But in *most* cases, "That" should be either "in out T1" or "in out
T1'Class".  You only need it to be a pointer in certain special cases
(eg you want Foo to save the pointer into some global data structure).
I agree that if you need a pointer, "access T1" is often what you want.

I agree with the other posters, who said to use "out" or "in out"
usually, and only use "access" if there's some particular reason for it.

I must admit, thought that it's annoying that T2_Pointer is not
implicitly convertible to T1_Pointer.  That conversion is perfectly safe
(whereas the other direction involves a run-time check, and should be
explicit).

> Then, in a derivation:
> 
>   type T2 is new T1 with ...
> 
> This gives us:
> 
> 
>   procedure Foo( This : in T2;  That : in T1_Pointer );
> 
> Note that the type of the formal 'That' is still T1_Pointer,
> probably not what we wanted.
> 
> On the other hand, had we defined things as:
> 
>   type T1 is tagged ...
> 
>   -- don't need this now...  type T1_Pointer is access T1;
> 
>   procedure Foo( This : in T1;   That : access T1 );
> 
> The derivation would provide
> 
>   procedure Foo( This : in T2;   That : access T2 );
> 
> which is probably the desired result in this case.

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




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

* Re: "out" or "access"
  1998-10-21  0:00 ` Tucker Taft
@ 1998-10-22  0:00   ` Pascal Obry
  1998-10-29  0:00     ` Robert A Duff
  0 siblings, 1 reply; 24+ messages in thread
From: Pascal Obry @ 1998-10-22  0:00 UTC (permalink / raw)


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 934 bytes --]


Tucker Taft a �crit dans le message ...
>=:-) Vincent (vb@bruker.fr) wrote:
>: What's the best way ?
>
>An "out" parameter is always preferable to an "access" parameter, in my
view.
>
>Use an "access" parameter only if you need an access value when
>inside the called subprogram for some reason.  This usually only
>happens when interfacing to some other language, or some preexisting
>subsystem of some sort.
>


Ok but be carefull. With an access type you can't pass a null pointer. This
is a big problem.
In fact if you look at the Win32 C API or UNIX C library you'll find that it
is often possible
to pass a null pointer to some parameters to say : "ignore this one, I don't
care for the result" !

So, in many many cases it is better to use an access type (not an access
formal parameter)
when interfacing with some other language.

Pascal.

PS : The Win32Ada binding is full of access parameters that are just wrong !







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

* Re: "out" or "access"
  1998-10-21  0:00     ` Martin C. Carlisle
@ 1998-10-22  0:00       ` Pat Rogers
  0 siblings, 0 replies; 24+ messages in thread
From: Pat Rogers @ 1998-10-22  0:00 UTC (permalink / raw)


Martin C. Carlisle wrote in message
<70l1nq$mrb$1@cnn.Princeton.EDU>...
>Be careful using access as below, as this creates two different
>dispatched objects in the call (which is illegal).  See LRM 6.1(24)
>and 3.9.2


You seem to be saying that there is no legal call with this
approach, but neither RM reference supports that conclusion.

The actual parameters to a call make a given call illegal or not,
and that depends upon what is passed, per call.  For example, if the
actuals are statically-tagged -- the typical case -- there is no
problem.  If both actuals are dynamically-tagged they have to have
the same tag, which is checked at run-time per RM 3.9.2(16).  A call
cannot have both statically-tagged and dynamically-tagged
controlling operands, per RM 3.9.(8), but that is just one case.

>>  type T1 is tagged ...
>>
>>  type T1_Pointer is access T1;
>>
>>  procedure Foo( This : in T1;   That : in T1_Pointer );
>>
>>Then, in a derivation:
>>
>>  type T2 is new T1 with ...
>>
>>This gives us:
>>
>>
>>  procedure Foo( This : in T2;  That : in T1_Pointer );
>>
>>Note that the type of the formal 'That' is still T1_Pointer,
>>probably not what we wanted.
>>
>>On the other hand, had we defined things as:
>>
>>  type T1 is tagged ...
>>
>>  -- don't need this now...  type T1_Pointer is access T1;
>>
>>  procedure Foo( This : in T1;   That : access T1 );
>>
>>The derivation would provide
>>
>>  procedure Foo( This : in T2;   That : access T2 );
>>
>>which is probably the desired result in this case.



---
Pat Rogers                          Training & Development in:
http://www.classwide.com    Deadline Schedulability Analysis
progers@acm.org                 Software Fault Tolerance
(281)648-3165                       Real-Time/OO Languages






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

* Re: "out" or "access"
  1998-10-22  0:00   ` Pascal Obry
@ 1998-10-29  0:00     ` Robert A Duff
  1998-10-29  0:00       ` Matthew Heaney
  0 siblings, 1 reply; 24+ messages in thread
From: Robert A Duff @ 1998-10-29  0:00 UTC (permalink / raw)


"Pascal Obry" <p.obry@der.edf.fr> writes:
> PS : The Win32Ada binding is full of access parameters that are just wrong !

That's because (1) the author didn't know about this rule (that access
parameters don't allow null), and (2) the compiler he was using didn't
check this rule (back then).

The access-parameter feature was invented specifically to solve the
following problem: You want to pass a tagged thing as a parameter to a
dispatching operation, and of course have it dispatch as usual, but you
want to store a pointer to that thing in a global data structure.
Therefore, access parameters have these features: (1) if the procedure
is in the right place, it's a dispatching op, (2) no null's are allowed,
because of course you can't dispatch unless you have a tag, and so the
pointer has to point to some tagged object, rather than be null, and (3)
you can pass pointers to more-nested things into less-nested procedures
(with some run-time accessibility checks).  You also get implicit
conversion of pointer-to-class-wide types (in the safe direction).

In retrospect, I think it would have been better to separate the
individual features more orthogonally.  Eg, I sometimes want to declare
a record component that can never be null -- Ada has a way to declare
never-null things, but only if they're formal parameters.

Another eg, if "type T2 is new T1...", then T2 implicitly converts to
T1'Class, but access-to-T2 does not implicitly convert to
access-to-T1'Class, except in the case of access parameters -- I would
like a more general feature, to avoid making "safe" conversions
explicit.

Given the specific problem access params were intended to solve, "access
constant" made little sense.  But in restrospect, I think we should have
allowed it.

It was only later that we noticed access params might be useful in
interface-to-C.  Of course that's only true for C functions that don't
expect a NULL.  It also requires the compiler to omit the extra dope
needed to do the run-time accessibility checks -- of course the C
compiler can't deal with that extra dope.

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




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

* Re: "out" or "access"
  1998-10-29  0:00     ` Robert A Duff
@ 1998-10-29  0:00       ` Matthew Heaney
  1998-10-29  0:00         ` Robert A Duff
  0 siblings, 1 reply; 24+ messages in thread
From: Matthew Heaney @ 1998-10-29  0:00 UTC (permalink / raw)


Robert A Duff <bobduff@world.std.com> writes:

> Another eg, if "type T2 is new T1...", then T2 implicitly converts to
> T1'Class, but access-to-T2 does not implicitly convert to
> access-to-T1'Class, except in the case of access parameters -- I would
> like a more general feature, to avoid making "safe" conversions
> explicit.

Agreed.  It only adds syntactic overhead, with no added value.  Worse,
it makes you not pay attention to those type casts that really do
matter.
 
> Given the specific problem access params were intended to solve,
> "access constant" made little sense.  But in restrospect, I think we
> should have allowed it.

We absolutely need access constant params.  It's the only (safe) way to
implement a factory method that returns an iterator, so that you can
traverse a read-only data structure.  For example:

generic
   type Item_Type is private;
...
package Queues is
 
   type Root_Queue is
      abstract tagged limited private;
...
   type Root_Iterator is
      abstract tagged private;

   function New_Iterator 
      (Queue : access constant Root_Queue)
      return Root_Iterator'Class is abstract;

   function Done
     (Iterator : Root_Iterator)
     return Boolean is abstract;

   procedure Get_Item
     (Iterator : Root_Iterator)
      return Item_Type is abstract;

   procedure Advance
     (Iterator : in out Root_Iterator);
...
end;

Every type that derives from Root_Queue is required to implement a
factory method (New_Iterator), that allows you to iterate over that
structure.  This way, you can iterate over class-wide objects:

generic
   with Image (Item : Item_Type) return String is <>;
procedure Queues.Put (Queue : in Root_Queue'Class);

procedure Queues.Put (Queue : in Root_Queue'Class) is

   Iter : Root_Iterator'Class :=
      New_Iterator (Queue);
begin
   while not Done (Iter) loop
     Ada.Text_IO.Put (Image (Get_Item (Iter)));
     Advance (Iter);
   end loop;
end;


Without access constant, you have to use access, which requires a
read-write (and aliased) view of the object.  It's not so bad in the
example above, because we could always declare procedure Put to take the
queue object as in out.  But this will not work for functions, ie

   function "=" (L, R : Root_Queue'Class) return Boolean;

because functions can't take in out params, and a comparison of
class-wide objects should only be a read-only activity anyway.

So you have to work around this by declaring functions to take access
params:

   function "=" (L, R : access Root_Queue'Class) return Boolean;

which is really ugly.  

Not having access constant parameters tends to cause a ripple effect,
were you have to go back declare objects as aliased, or to change
subprograms that have args of mode in to mode in out or access.

In my work on the Ada Components Library, I've worked around this by
resorting to ugly hacks like using System.Address_To_Access_Conversions
and parameters of a named access-to-constant type.  The latter approach
means I lose the ability to dispatch, which is a REAL bummer.

Please, let's fix this omission, sooner rather than later.

We need access constant parameters.







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

* Re: "out" or "access"
  1998-10-29  0:00       ` Matthew Heaney
@ 1998-10-29  0:00         ` Robert A Duff
  1998-10-30  0:00           ` dennison
  0 siblings, 1 reply; 24+ messages in thread
From: Robert A Duff @ 1998-10-29  0:00 UTC (permalink / raw)


Matthew Heaney <matthew_heaney@acm.org> writes:

> Agreed.  It only adds syntactic overhead, with no added value.  Worse,
> it makes you not pay attention to those type casts that really do
> matter.

Yes.  The boy who cried "wolf".

> Not having access constant parameters tends to cause a ripple effect,
> ...

I agree with your points.

But how about this workaround: declare New_Iterator to take an 'in'
parameter of type Root_Queue.  (Presuming you really do want an iterator
that can't modify the thing it's iterating over.)  So it's dispatching,
which you want.  Inside the various overridings of it, you will normally
have to do 'Unchecked_Access to produce a pointer to the thing, which
you would presumably store inside the iterator object.  This works
because all tagged parameters are aliased.  It's slightly ugly, and
slightly unsafe, but at least it's fairly localized.  You do have to
warn your clients that they had better not let an iterator live longer
that what it's iterating over.

Don't you agree that this workaround is better than the ones that cause
those nasty ripple effects?

> We need access constant parameters.

Well, we at least *want* them.

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




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

* Re: "out" or "access"
  1998-10-29  0:00         ` Robert A Duff
@ 1998-10-30  0:00           ` dennison
  1998-10-30  0:00             ` Matthew Heaney
  0 siblings, 1 reply; 24+ messages in thread
From: dennison @ 1998-10-30  0:00 UTC (permalink / raw)


In article <wcc3e87hrl8.fsf@world.std.com>,
  Robert A Duff <bobduff@world.std.com> wrote:

> But how about this workaround: declare New_Iterator to take an 'in'
> parameter of type Root_Queue.  (Presuming you really do want an iterator
> that can't modify the thing it's iterating over.)  So it's dispatching,
> which you want.  Inside the various overridings of it, you will normally
> have to do 'Unchecked_Access to produce a pointer to the thing, which

I must be missing something here. Isn't it possible that taking an
'Unchecked_Access of an in parameter will just give you a pointer to a copy of
the object on the stack, which will disappear when the procedure ends? Or are
you saying to do a ".all'Unchecked_Access" on the in parameter?

--
T.E.D.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    




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

* Re: "out" or "access"
  1998-10-30  0:00           ` dennison
@ 1998-10-30  0:00             ` Matthew Heaney
  1998-10-30  0:00               ` Robert A Duff
  0 siblings, 1 reply; 24+ messages in thread
From: Matthew Heaney @ 1998-10-30  0:00 UTC (permalink / raw)


dennison@telepath.com writes:

> In article <wcc3e87hrl8.fsf@world.std.com>,
>   Robert A Duff <bobduff@world.std.com> wrote:
> 
>> But how about this workaround: declare New_Iterator to take an 'in'
>> parameter of type Root_Queue.  (Presuming you really do want an
>> iterator that can't modify the thing it's iterating over.)  So it's
>> dispatching, which you want.  Inside the various overridings of it,
>> you will normally have to do 'Unchecked_Access to produce a pointer
>> to the thing, which
> 
> I must be missing something here. Isn't it possible that taking an
> 'Unchecked_Access of an in parameter will just give you a pointer to a
> copy of the object on the stack, which will disappear when the
> procedure ends? Or are you saying to do a ".all'Unchecked_Access" on
> the in parameter?

We're talking about tagged types here, and there are special rules that
apply to objects of a tagged type.

The RM mandates that tagged types be passed by reference, and so tagged
types passed as subprogram arguments are "implicitly aliased."  That's
why you can say:

procedure Op (O : in T) is -- T is a tagged type

   type TA is access constant T;

   OA : constant TA := O'Access;
begin

without O being declared as "access T" --because T is tagged.  Ditto for

procedure Op (O : in out T) is

   type TA is access all T;

   OA : constant TA := O'Access;
begin


The RM also recommends that implementations "deliver a useful result"
when you take the O'Address of a by-reference type (tagged types,
limited types) passed as a subprogram parameter.  That's why
Sys.Addr_To_Acc_Conv can be used to work around this problem.

You don't ever have to pass tagged types as access parameters --unless
you need the operation to dispatch.

My problem is I need to do this:

procedure Visit (S : Stack'Class) is

   I : Iterator'Class := Initialize (S'Access);
begin

where Initialize is a function that takes a _constant_ access parameter
and returns an iterator (whose type is class-wide).

The problem is ... I can't do that, because access constant parameters
don't exist in the language.  

The workaround is to pass the argument as an in param --not access
constant param-- so it will dispatch, then use Addr_To_Acc_Conv to turn
that into a useful pointer:

   function Initialize (S : Stack) return Iterator'Class;

This func is primitive for type Stack, and therefore it dispatches:

package Stacks.My_Stacks is

   type My_Stack is new Stack with private;

   type My_Iterator is new Iterator with private;

   function Initialize (S : My_Stack) return Iterator'Class;
...
private
...

   package Conv is new Sys.Addr_To_Acc_Conv (My_Stack);

   type My_Iterator is 
      new Iterator with record
         Stack : Conv.Object_Pointer;
         Index : <some index type>;
      end record;

   function Initialize (S : My_Stack) return Iterator'Class is
   begin
      return My_Iterator'(To_Pointer (S'Address), S.Top);
   end;


So now the iterator object contains a pointer to the stack object passed
in.  The works --and I'm not unhappy with it-- it's just that there's no
way to prevent dangling references, as you can with access parameters.  

The declaration

   procedure Print (S : Stack'Class) is

      Iter : Iterator'Class := Start_At_Top (S);
   begin

doesn't make it obvious that the _address_ of S is being looked at, and
that object Iter contains a pointer to S.  That's why it's important to
make sure that the stack object lives at least as long as the iterator,
or else a dangling reference will occur.

I'd much rather say

   procedure Print (S : Stack'Class) is

      Iter : Iterator'Class := Start_At_Top (S'Access);
   begin

because it states explicitly that I'm manipulating a reference, but I
can't do that, because the language doesn't allow it.

So although technically speaking you don't _need_ access constant
parameters --because an in param dispatches, and you can sensibly take
the address of a tagged type passed as a param-- having access constant
parameters is still _strongly_ desireable.

It frustrates me that you can't implement a factory method for an
iterator, without having to use back-door methods.





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

* Re: "out" or "access"
  1998-10-30  0:00             ` Matthew Heaney
@ 1998-10-30  0:00               ` Robert A Duff
  1998-10-31  0:00                 ` dewar
  1998-10-31  0:00                 ` Matthew Heaney
  0 siblings, 2 replies; 24+ messages in thread
From: Robert A Duff @ 1998-10-30  0:00 UTC (permalink / raw)


Matthew Heaney <matthew_heaney@acm.org> writes:

> The RM also recommends that implementations "deliver a useful result"
> when you take the O'Address of a by-reference type (tagged types,
> limited types) passed as a subprogram parameter.  That's why
> Sys.Addr_To_Acc_Conv can be used to work around this problem.

I don't understand why you want to use 'Address at all.
My earlier suggestion was to use 'Unchecked_Access.
It has the same problem with possible dangling references,
but at least it doesn't throw type checking completely out
the window, as 'Address does.

>    type My_Iterator is 
>       new Iterator with record
>          Stack : Conv.Object_Pointer;
                   ^^^^^^^^^^^^^^^^^^^
Say My_Stack_Ptr, which is declared "access constant My_Stack", and then...
>          Index : <some index type>;
>       end record;
> 
>    function Initialize (S : My_Stack) return Iterator'Class is
>    begin
>       return My_Iterator'(To_Pointer (S'Address), S.Top);

       return My_Iterator'(S'Unchecked_Access, S.Top);

>    end;

By the way, have you considered making the iterators limited,
and initializing them with a discriminant that points to the
Stack (or whatever is being iterated)?

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




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

* Re: "out" or "access"
  1998-10-30  0:00               ` Robert A Duff
@ 1998-10-31  0:00                 ` dewar
  1998-10-31  0:00                   ` Matthew Heaney
  1998-10-31  0:00                 ` Matthew Heaney
  1 sibling, 1 reply; 24+ messages in thread
From: dewar @ 1998-10-31  0:00 UTC (permalink / raw)


In article <wccpvb9vh0k.fsf@world.std.com>,
  Robert A Duff <bobduff@world.std.com> wrote:
> Matthew Heaney <matthew_heaney@acm.org> writes:
>
> > The RM also recommends that implementations "deliver a useful result"
> > when you take the O'Address of a by-reference type (tagged types,
> > limited types) passed as a subprogram parameter.  That's why
> > Sys.Addr_To_Acc_Conv can be used to work around this problem.
>
> I don't understand why you want to use 'Address at all.
> My earlier suggestion was to use 'Unchecked_Access.
> It has the same problem with possible dangling references,
> but at least it doesn't throw type checking completely out
> the window, as 'Address does.


Note that Unchecked_Access still has restrictions which can be annoying, it
cannot be applied to parameters for example, because it still requires the
object to be aliased. In GNAT, the Unrestricted_Access attribute can be used
anywhere 'Address is usable, and is comparable in effect, except that strong
typing of the resulting pointer is retained.

Robert Dewar
Ada Core Technologies

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    




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

* Re: "out" or "access"
  1998-10-30  0:00               ` Robert A Duff
  1998-10-31  0:00                 ` dewar
@ 1998-10-31  0:00                 ` Matthew Heaney
  1998-11-01  0:00                   ` Robert A Duff
  1 sibling, 1 reply; 24+ messages in thread
From: Matthew Heaney @ 1998-10-31  0:00 UTC (permalink / raw)


Robert A Duff <bobduff@world.std.com> writes:

> I don't understand why you want to use 'Address at all.
> My earlier suggestion was to use 'Unchecked_Access.
> It has the same problem with possible dangling references,
> but at least it doesn't throw type checking completely out
> the window, as 'Address does.
> 
> >    type My_Iterator is 
> >       new Iterator with record
> >          Stack : Conv.Object_Pointer;
>                    ^^^^^^^^^^^^^^^^^^^
> Say My_Stack_Ptr, which is declared "access constant My_Stack", and
> then...

You are correct.  However, the library I'm building has one iterator
type, that can be used to both query and modify objects in the
collection.  The representation of the iterator must therefore include
an access-to-variable access type.

(Actually, I could do it the other way around: implement the iterator
type using an access-to-constant access type, and use addr-to-acc-conv
inside the one operation that modifies state.  Hmmm...)

So perhaps I'm going beyond what even access constant params buy you.
Maybe my real problem is how to "cast away const."  It would be hip if
the language provided a way to do that explicitly, analogous to
something like Unchecked_Conversion.

BTW: does the language define --or give implementation advice-- on what
the effect of unchecked conversion from access-to-constant to
access-to-variable is?  Or is it the intention of the designers that you
never try to do that?

Real programmers --like me-- are occasionally going to need to cast away
const.  The language designers should state explictly 1) that you
shouldn't do that, or 2) that it's OK to do it, and here's how.

If you don't say, then I'm going to do it _some_ way, perhaps using a
technique you didn't intend.  Just tell me what your specific intent is.

> By the way, have you considered making the iterators limited,
> and initializing them with a discriminant that points to the
> Stack (or whatever is being iterated)?

Oh yes, but this doesn't meet my needs, because you need to call a
dispatching operation ("factory method") that returns an iterator of a
class-wide type.  Class-wide types are indefinate, and therefore require
initialization during object declaration, which isn't possible if the
type is limited.




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

* Re: "out" or "access"
  1998-10-31  0:00                 ` dewar
@ 1998-10-31  0:00                   ` Matthew Heaney
  0 siblings, 0 replies; 24+ messages in thread
From: Matthew Heaney @ 1998-10-31  0:00 UTC (permalink / raw)


dewar@gnat.com writes:

> Note that Unchecked_Access still has restrictions which can be annoying, it
> cannot be applied to parameters for example, because it still requires the
> object to be aliased. 

I wish we could liberalize the rules here.  Right now, you can only
apply the Access attribute to an aliased object, which means the object
has to be publically tagged.

I've had to declare types as publically tagged --even though I don't
think the abstraction requires it-- because elsewhere I require an
aliased view.

It would be hipper if you were allowed to use the Access attribute for
objects whose _full_ view is limited or tagged, even if the partial
(public) isn't.

There's a precedent for this: the full view of a type in a generic
package may privately derive from Finalization.Controlled.  This means
instantations can only be done at library level.

This feature of the abstraction isn't stated publically stated -- the
compiler has to tell you at time of instantation.  Why not use a similar
rule for Unchecked_Access: you can only do it if the _full_ view of the
type is limited or tagged, irrespective of what the partial view says.


> In GNAT, the Unrestricted_Access attribute can be used anywhere
> 'Address is usable, and is comparable in effect, except that strong
> typing of the resulting pointer is retained.

This is a very nice attribute, but I've been loathe to use it, because
of potential portability problems.

It would be helpful if all vendors provided at least a weak form of the
Unrestricted_Access attribute.  For example, I don't really care about
downward closures.  But using it to effect type-safe conversion from an
access-to-constant into an access-to-variable seems pretty painless.




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

* Re: "out" or "access"
  1998-10-31  0:00                 ` Matthew Heaney
@ 1998-11-01  0:00                   ` Robert A Duff
  1998-11-01  0:00                     ` Matthew Heaney
  0 siblings, 1 reply; 24+ messages in thread
From: Robert A Duff @ 1998-11-01  0:00 UTC (permalink / raw)


Matthew Heaney <matthew_heaney@acm.org> writes:

> You are correct.  However, the library I'm building has one iterator
> type, that can be used to both query and modify objects in the
> collection.  The representation of the iterator must therefore include
> an access-to-variable access type.

Perhaps you should have two different sorts of iterators, one that can
modify, and one that can't.

> So perhaps I'm going beyond what even access constant params buy you.
> Maybe my real problem is how to "cast away const."

Sounds dangerous.

>...  It would be hip if
> the language provided a way to do that explicitly, analogous to
> something like Unchecked_Conversion.
> 
> BTW: does the language define --or give implementation advice-- on what
> the effect of unchecked conversion from access-to-constant to
> access-to-variable is?  Or is it the intention of the designers that you
> never try to do that?

I think you should not do that.  What should it mean?  I mean, if you
modify a constant?  An Ada compiler is within it's rights to put
constant stuff in read-only memory -- and if you modify it, you get a
core dump or some such nasty thing.  Or share memory for the same
constant data -- you better not change it.

Perhaps you should give an example.  It seems like cast-away-const is
even worse than any type-cheating one might want to do.

> Oh yes, but this doesn't meet my needs, because you need to call a
> dispatching operation ("factory method") that returns an iterator of a
> class-wide type.  Class-wide types are indefinate, and therefore require
> initialization during object declaration, which isn't possible if the
> type is limited.

I wish we could initialize limited things, but still not copy them
around.  Sigh.

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




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

* Re: "out" or "access"
  1998-11-01  0:00                   ` Robert A Duff
@ 1998-11-01  0:00                     ` Matthew Heaney
  1998-11-01  0:00                       ` Robert A Duff
  0 siblings, 1 reply; 24+ messages in thread
From: Matthew Heaney @ 1998-11-01  0:00 UTC (permalink / raw)


Robert A Duff <bobduff@world.std.com> writes:

> Perhaps you should have two different sorts of iterators, one that can
> modify, and one that can't.

Yes, I thought of that too, but I was trying to keep things simple.

> > So perhaps I'm going beyond what even access constant params buy you.
> > Maybe my real problem is how to "cast away const."
> 
> Sounds dangerous.

"const" in the sense of in param:

   function Set_Top
     (Stack : Stack_Type)
     return Item_Access is

      SA : constant Object_Pointer :=
        To_Pointer (Stack'Address);
   begin
      pragma Assert (Stack.Length > 0);
      return SA.Items (Stack.Top)'Access;
   end Set_Top;

So the client can:

   Set_Top (Stack).all := 10;

Of course, here it's not strictly necessary, since I can declare Set_Top
to take an access parameter.

The "const" thing is the entity passed as the in-param of the function
call.  In my case, the entities are always tagged, and so are aliased,
and so taking the address is well-defined.

The behavior I'm thinking of is analogous to what happens when you call
function Random of the random number generator.

Essentially, I'm trying to get around Ada's lack of in out params for
functions.  How does the Ada programmer implement his own abstractions
whose functions have state-changing behavior -- like Random does?

Yes, yes, I know you can implement the abstraction as a controlled
record containing a pointer to the "real" data, but I was investigating
cheaper alternatives.

Why is the RM so silent on intended uses of package
System.Address_To_Access_Conversions?

If the designers don't intend for you to use it as in my example above,
then why doesn't the RM just come right out and say that?


> > BTW: does the language define --or give implementation advice-- on what
> > the effect of unchecked conversion from access-to-constant to
> > access-to-variable is?  Or is it the intention of the designers that you
> > never try to do that?
> 
> I think you should not do that.  What should it mean?  I mean, if you
> modify a constant?  An Ada compiler is within it's rights to put
> constant stuff in read-only memory -- and if you modify it, you get a
> core dump or some such nasty thing.  Or share memory for the same
> constant data -- you better not change it.

I should have asked, How do the language designers intend Ada programmers
to implement a side-effect producing function, whose param (of mode in)
is a tagged type?

When I do this:

   function Set_Top (Stack : Stack_Type) return Item_Access is
   begin
      ... Stack'Access ...

this gives me an access-to-constant view.  I want an access-to-variable
view. 

I wasn't trying to change a "constant," in the sense of a constant
object declaration (which isn't possible for limited types anyway), but
rather I was trying to turn a constant view --of an otherwise modifiable
object-- into a variable view.
 

> Perhaps you should give an example.  It seems like cast-away-const is
> even worse than any type-cheating one might want to do.

> > Oh yes, but this doesn't meet my needs, because you need to call a
> > dispatching operation ("factory method") that returns an iterator of a
> > class-wide type.  Class-wide types are indefinate, and therefore require
> > initialization during object declaration, which isn't possible if the
> > type is limited.
> 
> I wish we could initialize limited things, but still not copy them
> around.  Sigh.

I would be really swell if we had constructors too, not just functions
trying to do the job.  The latter technique doesn't work for limited
types.





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

* Re: "out" or "access"
  1998-11-01  0:00                     ` Matthew Heaney
@ 1998-11-01  0:00                       ` Robert A Duff
  1998-11-02  0:00                         ` Matthew Heaney
  0 siblings, 1 reply; 24+ messages in thread
From: Robert A Duff @ 1998-11-01  0:00 UTC (permalink / raw)


Matthew Heaney <matthew_heaney@acm.org> writes:

> Robert A Duff <bobduff@world.std.com> writes:
> 
> > Perhaps you should have two different sorts of iterators, one that can
> > modify, and one that can't.
> 
> Yes, I thought of that too, but I was trying to keep things simple.

Too simple?

> > > So perhaps I'm going beyond what even access constant params buy you.
> > > Maybe my real problem is how to "cast away const."
> > 
> > Sounds dangerous.
> 
> "const" in the sense of in param:
> 
>    function Set_Top
>      (Stack : Stack_Type)
>      return Item_Access is
> 
>       SA : constant Object_Pointer :=
>         To_Pointer (Stack'Address);
>    begin
>       pragma Assert (Stack.Length > 0);
>       return SA.Items (Stack.Top)'Access;
>    end Set_Top;
> 
> So the client can:
> 
>    Set_Top (Stack).all := 10;

But this allows clients to write upon constants, which sounds like a Bad
Thing.

> Of course, here it's not strictly necessary, since I can declare Set_Top
> to take an access parameter.
> 
> The "const" thing is the entity passed as the in-param of the function
> call.  In my case, the entities are always tagged, and so are aliased,
> and so taking the address is well-defined.

But writing upon constants is not.

> The behavior I'm thinking of is analogous to what happens when you call
> function Random of the random number generator.

...which is pretty ugly, IMHO.

> Essentially, I'm trying to get around Ada's lack of in out params for
> functions.  How does the Ada programmer implement his own abstractions
> whose functions have state-changing behavior -- like Random does?

I wish functions allowed 'in out' params.

In the absence of that, you can do the Random trick, but that's kind of
messy.  I'd suggest an explicit access type instead.

> Yes, yes, I know you can implement the abstraction as a controlled
> record containing a pointer to the "real" data, but I was investigating
> cheaper alternatives.
> 
> Why is the RM so silent on intended uses of package
> System.Address_To_Access_Conversions?

Because the RM isn't a text book, I guess.

> I should have asked, How do the language designers intend Ada programmers
> to implement a side-effect producing function, whose param (of mode in)
> is a tagged type?

I think the language designers intend that you shouldn't do that.

As far as the language design issue goes, I'm on your side -- 'in out'
should be allowed in functions.  But since it isn't, I wouldn't resort
to the extreme trickery you suggest.

> When I do this:
> 
>    function Set_Top (Stack : Stack_Type) return Item_Access is
>    begin
>       ... Stack'Access ...
> 
> this gives me an access-to-constant view.  I want an access-to-variable
> view. 
> 
> I wasn't trying to change a "constant," in the sense of a constant
> object declaration (which isn't possible for limited types anyway), but
> rather I was trying to turn a constant view --of an otherwise modifiable
> object-- into a variable view.

I would suggest you either pass explicit access values, or else give up
on functions and use procedures.

> I would be really swell if we had constructors too, not just functions
> trying to do the job.  The latter technique doesn't work for limited
> types.

That was my point -- *initializing* a limited object with a function
call should not be illegal.  It's assigning them around after that that
causes trouble.  Likewise, an aggregate of a limited type could make
perfect sense, if it's only used to *initialize* an object.

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




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

* Re: "out" or "access"
  1998-11-01  0:00                       ` Robert A Duff
@ 1998-11-02  0:00                         ` Matthew Heaney
  1998-11-03  0:00                           ` Simon Wright
  0 siblings, 1 reply; 24+ messages in thread
From: Matthew Heaney @ 1998-11-02  0:00 UTC (permalink / raw)


Robert A Duff <bobduff@world.std.com> writes:

> > > Perhaps you should have two different sorts of iterators, one that can
> > > modify, and one that can't.
> > 
> > Yes, I thought of that too, but I was trying to keep things simple.
> 
> Too simple?

Ah, the process of design -- something about which I'm sure you are
familiar.

I'm designing a data structure library --which I've been calling the Ada
Components Library-- which comprises pluggable units.  You assemble the
data structure having the space & time characteristics you desire, from
a simpler set of primitives.  (Sound like our favorite language?)

This avoids the combinatorial explosion the plagued the original Booch
components.  He had to build tens and tens of data structures,
anticipating every combination.

The ACL isn't like that.  It's very simple -- only a few hundred lines.
(Actually, what made it all possible is composition via access
discriminants.)

Every data structure allows you to traverse that data structure one
element at a time.  Typically, there are two ways to do this - passive
iteration and active iteration.

From time-to-time, it's desirable to change the values of the items in
the data structure, without resorting to inefficient "solutions" like
copying the original data structure, and changing the source values
prior to insertion into the target structure.  You just want to change
the values in place.

You could use a passive iterator to do this.  The data structure is
passed inout, and the Process procedure passed as a generic formal takes
each item as mode inout.

You therefore have to provide two kinds of passive iterators: a
read-only version, and a read-write version.  The former is necessary
because you could be in a subprogram (say, a Print routine) that takes
the data structure as mode in.

So now things are starting to get overwhelming, if you consider that I
have to provide possibly two kinds of active iterators too.

My desire is to keep the library as simple as possible.  By providing
primitive components, the client can assemble those primitives together
to effect the desired behavior.  You don't have to everything for him.

We can keep the library simpler by providing only an active iterator,
and letting the client assemble a passive iterator himself (by using the
active iterator).  But if there are two kinds of passive iterators, that
means that either 1) I have to provide two active iterators, one for
read-only, and one for read-write, or 2) provide one active iterator
that can be used in both read-only or read-write contexts.

Obviously, my choice is for one active iterator, that allows you to
modify data structure elements, but that can be used anywhere, including
those places where the data structure is Officially read-only.

It may seem like using Addr_To_Acc_Conv for this is a "dirty trick," but
the client is going to build an infrastructure on top of the primitives,
and it's that ultimate data structure that is going to allow only
"legal" things.  For example, here's our two passive iterators:

   generic
      with procedure Process (Item : in Item_Type);
   procedure Select_Items (Stack : in Stack_Type);

   generic
      with procedure Process (Item : in out Item_Type);
   procedure Modify_Items (Stack : in out Stack_here's);

There's nothing "illegal" here: items can be selected from a read-only
stack, and can be modified from a read-write stack.  The client allows
only meaningful behavior for his stack.

We can use one active iterator to build both kinds of passive iterators:

   procedure Select_Items (Stack : in Stack_Type) is

      Iter : Stack_Iterator := Start_At_Top (Stack'Access);
   begin
      for I in 1 .. Get_Length (Stack) loop
         Process (Get_Item (Iter));
         Advance (Iter);
      end loop;
   end;


   procedure Modify_Items (Stack : in out Stack_Type) is

      Iter : Stack_Iterator := Start_At_Top (Stack'Access);
   begin
      for I in 1 .. Get_Length (Stack) loop
         Process (Get_Item (Iter));
         Advance (Iter);
      end loop;
   end;


As you can see, these have the same body.  Only one active iterator type
is required, in order to effect both kinds of ultimate behavior. 

Is that _too_ simple?  My philosophy is, less is more.


> But this allows clients to write upon constants, which sounds like a Bad
> Thing.

All of these data structures are tagged limited private.  They can't
ever be constant, can they?


> > The "const" thing is the entity passed as the in-param of the function
> > call.  In my case, the entities are always tagged, and so are aliased,
> > and so taking the address is well-defined.
> 
> But writing upon constants is not.

What's your defination of "constant"?  Is a (limited) by-reference
object passed as an in parameter a constant?  That's the only kind of
constant I was talking about.

Actually, I think Ada goes too far wrt to parameter modes.  For an
abstract data type, the arguments to subprograms should describe only a
logical view of the abstraction; what the client sees.

But internally, the implementor of an abstraction should be able to
change the state of the object as a side-effect of the call -- even if
the parameter mode is in.  It's his business how to implement his
abstraction.

In Ada, the outside view of the abstraction tends to constrain the
inside view too.  In a sense, by specifying the mode (in vs inout) for
the object of the abstract data type, you've overspecified.  By passing
the arg as inout --just so you can produce a side-effect legally--
you've exposed an aspect of the implementation, that isn't necessarily
important to clients.

That's why the "pure" object-oriented notation:

   Object.Do_Something

is nice, because it allows the implementor to make whatever state
changes are required in order to effect logical behavior, without having
to announce whether state changes are in fact required.  It's not the
client's business to know.


> > Essentially, I'm trying to get around Ada's lack of in out params for
> > functions.  How does the Ada programmer implement his own abstractions
> > whose functions have state-changing behavior -- like Random does?
> 
> I wish functions allowed 'in out' params.
> 
> In the absence of that, you can do the Random trick, but that's kind of
> messy.  I'd suggest an explicit access type instead.

For my Set_Top function, that's what I did:

   function Set_Top (Stack : access Stack_Type) return Item_Access;
...

   Set_Top (Stack'Access).all := 42;

What's motivating this is that I can write a stack that works for
limited private items.  Ever needed a stack of File_Types?  Something
like:

   Stack : access Stack_Type; -- of File_Type
...
   declare
      File : File_Type renames
         Push (Stack'Access).all;
   begin
      Create (File, Name => "matt.dat");
...
 
   declare
      File : File_Type renames
         Set_Top (Stack'Access).all;
   begin
      Close (File);
      Pop (Stack);
   end;


> > I should have asked, How do the language designers intend Ada programmers
> > to implement a side-effect producing function, whose param (of mode in)
> > is a tagged type?
> 
> I think the language designers intend that you shouldn't do that.

Then the intentions of the language designers are unclear, because of
function Random.

Programmers are going expect that they can do that kind of thing too,
efficiently, and within the language.


> > I would be really swell if we had constructors too, not just functions
> > trying to do the job.  The latter technique doesn't work for limited
> > types.
> 
> That was my point -- *initializing* a limited object with a function
> call should not be illegal.  It's assigning them around after that that
> causes trouble.  Likewise, an aggregate of a limited type could make
> perfect sense, if it's only used to *initialize* an object.

Yes!




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

* Re: "out" or "access"
  1998-11-02  0:00                         ` Matthew Heaney
@ 1998-11-03  0:00                           ` Simon Wright
  1998-11-16  0:00                             ` Matthew Heaney
  0 siblings, 1 reply; 24+ messages in thread
From: Simon Wright @ 1998-11-03  0:00 UTC (permalink / raw)


Matthew Heaney <matthew_heaney@acm.org> writes:

> I'm designing a data structure library --which I've been calling the Ada
> Components Library-- which comprises pluggable units.  You assemble the
> data structure having the space & time characteristics you desire, from
> a simpler set of primitives.  (Sound like our favorite language?)
[..]
> From time-to-time, it's desirable to change the values of the items in
> the data structure, without resorting to inefficient "solutions" like
> copying the original data structure, and changing the source values
> prior to insertion into the target structure.  You just want to change
> the values in place.

Yes, I'm going through the same throes with the Booch Components at
the moment.

Having the ability to change components in place during iteration
causes a whole lot of grief, in the BCs showing up as

generic
  type Item is private;
  type Item_Ptr is access all Item;
package BC.Containers is

yecch




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

* Re: "out" or "access"
  1998-11-03  0:00                           ` Simon Wright
@ 1998-11-16  0:00                             ` Matthew Heaney
  0 siblings, 0 replies; 24+ messages in thread
From: Matthew Heaney @ 1998-11-16  0:00 UTC (permalink / raw)


Simon Wright <simon@pogner.demon.co.uk> writes:

> > From time-to-time, it's desirable to change the values of the items in
> > the data structure, without resorting to inefficient "solutions" like
> > copying the original data structure, and changing the source values
> > prior to insertion into the target structure.  You just want to change
> > the values in place.
> 
> Yes, I'm going through the same throes with the Booch Components at
> the moment.
> 
> Having the ability to change components in place during iteration
> causes a whole lot of grief, in the BCs showing up as
> 
> generic
>   type Item is private;
>   type Item_Ptr is access all Item;
> package BC.Containers is


Here are some ideas:

o Just use a passive iterator:

generic
   with procedure Process 
     (Item : in out Item_Type;
      Quit : in out Boolean);
procedure Modify_Items (Stack : in out Stack_Type);


o Use an read-only active iterator, but with a special op that takes the
structure as an access parameter:

type Stack_Iterator is private;

type Stack_Access is access constant Stack_Type;

function Start_At_Top
  (Stack : Stack_Access)
  return Stack_Iterator;

function Set_Item
  (Stack    : access Stack_Type;
   Iterator : in     Stack_Iterator)
   return Item_Access;

Use it like:

   Stack : aliased Stack_Type;
...
   declare
      Iterator : Stack_Iterator := 
         Start_At_Top (Stack'Access);
   begin
      for I in 1 .. Get_Length (Stack) loop
        declare
           Item : Item_Type renames
             Set_Item (Stack'Access, Iterator).all;
        begin
           Item := Item + 1;
        end;
      end loop;
   end;


This is legal Ada, without requiring any "tricks."


o  Use an normally read-only active iterator, but use
Sys.Addr_To_Acc_Conv to convert the read-only view of the structure into
a read-write view:

type Stack_Iterator is private;

type Stack_Access is access constant Stack_Type;

function Start_At_Top
  (Stack : Stack_Access)
  return Stack_Iterator;

but now Set_Item looks like:

function Set_Item
  (Iterator : Stack_Iterator)
   return Item_Access;

The implementation is

function Set_Item 
  (Iterator : Stack_Iterator) 
  return Item_Access is

  Read_Only : Stack_Type renames
     Iterator.Stack.all;

  package Conversions is
    new Sys.Addr_To_Acc_Conv (Stack_Type);
  use Conversions;

  Read_Write : Stack_Type renames
     To_Pointer (Read_Only'Address).all;
begin
  return Read_Write.Items (Iterator.Index)'Access;
end Set_Item;


Now you only have to say:


   Stack : aliased Stack_Type;
...
   declare
      Iterator : Stack_Iterator := 
         Start_At_Top (Stack'Access);
   begin
      for I in 1 .. Get_Length (Stack) loop
        declare
           Item : Item_Type renames
             Set_Item (Iterator).all;
        begin
           Item := Item + 1;
        end;
      end loop;
   end;






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

end of thread, other threads:[~1998-11-16  0:00 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1998-10-21  0:00 "out" or "access" =:-) Vincent
1998-10-21  0:00 ` Jeff Carter
1998-10-21  0:00   ` Pat Rogers
1998-10-21  0:00     ` Martin C. Carlisle
1998-10-22  0:00       ` Pat Rogers
1998-10-22  0:00     ` Robert A Duff
1998-10-21  0:00 ` Tucker Taft
1998-10-22  0:00   ` Pascal Obry
1998-10-29  0:00     ` Robert A Duff
1998-10-29  0:00       ` Matthew Heaney
1998-10-29  0:00         ` Robert A Duff
1998-10-30  0:00           ` dennison
1998-10-30  0:00             ` Matthew Heaney
1998-10-30  0:00               ` Robert A Duff
1998-10-31  0:00                 ` dewar
1998-10-31  0:00                   ` Matthew Heaney
1998-10-31  0:00                 ` Matthew Heaney
1998-11-01  0:00                   ` Robert A Duff
1998-11-01  0:00                     ` Matthew Heaney
1998-11-01  0:00                       ` Robert A Duff
1998-11-02  0:00                         ` Matthew Heaney
1998-11-03  0:00                           ` Simon Wright
1998-11-16  0:00                             ` Matthew Heaney
1998-10-21  0:00 ` dennison

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