comp.lang.ada
 help / color / mirror / Atom feed
* Type conversions on pool-specific access types
@ 2003-10-13 10:34 Dmitry A. Kazakov
  2003-10-16  1:43 ` Nick Roberts
  0 siblings, 1 reply; 6+ messages in thread
From: Dmitry A. Kazakov @ 2003-10-13 10:34 UTC (permalink / raw)


Hi all!

Who knows any reason why conversions between pool-specific access
types are not allowed, provided that the storage pools are same? Isn't
storage pool statically known?

It is a real pain to use Unchecked_Conversion for:

type Base is new Ada.Limited_Controlled with ..;
type Base_Ptr is access Base'Class;
for Base_Ptr'Storage_Pool use ...;

procedure Foo (Object : Base_Ptr);
...
type Derived is new Base with ..;
type Derived_Ptr is access Derived'Class;
for Derived_Ptr'Storage_Pool use Base_Ptr'Storage_Pool;
...
declare
   Ptr : Derived_Ptr := new Derived;
begin
   Foo (Base_Ptr (Ptr)); -- ILLEGAL!

I do not understand why.

----------------
Another thing is initialization / finalization of controlled objects
allocated on some specific storage pool. It could be a great problem
if a pool-specific pointer is required:

type Base_Specific_Ptr is access Base;
for Base_Specific_Ptr'Storage_Pool use ...;
...
procedure Finalize (Object : in out Base) is
begin
   -- How to get a Base_Specific_Ptr to Object?
   -- We know that it is in the pool, but the compiler does not!

This requires general to pool-specific conversion. Which could
probably be non-portable, if Unchecked_Conversion is used to
Object'Unchecked_Access to Base_Specific_Ptr. (?)

---
Regards,
Dmitry Kazakov
www.dmitry-kazakov.de



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

* Re: Type conversions on pool-specific access types
  2003-10-13 10:34 Type conversions on pool-specific access types Dmitry A. Kazakov
@ 2003-10-16  1:43 ` Nick Roberts
  2003-10-16 12:13   ` Dmitry A. Kazakov
  0 siblings, 1 reply; 6+ messages in thread
From: Nick Roberts @ 2003-10-16  1:43 UTC (permalink / raw)


"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:7kukov45dif9p7u2ohu6ml29mu9cgu621o@4ax.com...

> Who knows any reason why conversions between pool-
> specific access types are not allowed, provided that
> the storage pools are same? Isn't storage pool static-
> ally known?
>
> It is a real pain to use Unchecked_Conversion ...

I think it's also dangerous and unnecessary.

> type Base is new Ada.Limited_Controlled with ..;
> type Base_Ptr is access Base'Class;
> for Base_Ptr'Storage_Pool use ...;
>
> procedure Foo (Object : Base_Ptr);
> ...
> type Derived is new Base with ..;
> type Derived_Ptr is access Derived'Class;
> for Derived_Ptr'Storage_Pool use Base_Ptr'Storage_Pool;
> ...
> declare
>    Ptr : Derived_Ptr := new Derived;
> begin
>    Foo (Base_Ptr (Ptr)); -- ILLEGAL!
>
> I do not understand why.

In this situation, I suggest that you do not need to declare Derived_Ptr,
since Base_Ptr can -- and generally should -- be used to point to objects of
type Derived (as well as Base).

   declare
      Ptr : Base_Ptr := new Derived;
   begin
      Foo(Ptr);

> Another thing is initialization / finalization of controlled
> objects allocated on some specific storage pool. It could
> be a great problem if a pool-specific pointer is required ...

The answer is "Don't do that!"

Finalize should finalise whatever is inside the given Object, and not deal
in any way with where (in which pool) the object resides. After the
implementation has called Finalize for the object, it will then -- when
appropriate -- call Deallocate for the object's pool, in order to deallocate
the object's storage space from the pool. Do not mess with this mechanism.

--
Nick Roberts





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

* Re: Type conversions on pool-specific access types
  2003-10-16  1:43 ` Nick Roberts
@ 2003-10-16 12:13   ` Dmitry A. Kazakov
  2003-10-16 20:28     ` Nick Roberts
  0 siblings, 1 reply; 6+ messages in thread
From: Dmitry A. Kazakov @ 2003-10-16 12:13 UTC (permalink / raw)


On Thu, 16 Oct 2003 02:43:08 +0100, "Nick Roberts"
<nick.roberts@acm.org> wrote:

>"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
>news:7kukov45dif9p7u2ohu6ml29mu9cgu621o@4ax.com...
>
>> Who knows any reason why conversions between pool-
>> specific access types are not allowed, provided that
>> the storage pools are same? Isn't storage pool static-
>> ally known?
>>
>> It is a real pain to use Unchecked_Conversion ...
>
>I think it's also dangerous and unnecessary.
>
>> type Base is new Ada.Limited_Controlled with ..;
>> type Base_Ptr is access Base'Class;
>> for Base_Ptr'Storage_Pool use ...;
>>
>> procedure Foo (Object : Base_Ptr);
>> ...
>> type Derived is new Base with ..;
>> type Derived_Ptr is access Derived'Class;
>> for Derived_Ptr'Storage_Pool use Base_Ptr'Storage_Pool;
>> ...
>> declare
>>    Ptr : Derived_Ptr := new Derived;
>> begin
>>    Foo (Base_Ptr (Ptr)); -- ILLEGAL!
>>
>> I do not understand why.
>
>In this situation, I suggest that you do not need to declare Derived_Ptr,
>since Base_Ptr can -- and generally should -- be used to point to objects of
>type Derived (as well as Base).
>
>   declare
>      Ptr : Base_Ptr := new Derived;
>   begin
>      Foo(Ptr);

Yes, in this case. But consider specialized containers of Derived
descendants:

procedure Derived_Specific (X : Derived);
type Array_Of_Derived is array (...) of Derived_Ptr;

Now you can call Derived_Specific on any element of Array_Of_Derived
without an explicit type conversion. It is also type safe. If I have
only Base_Ptr, I lose that advantage and forced to always work in the
terms of the base type and what is worse, to make casts.

>> Another thing is initialization / finalization of controlled
>> objects allocated on some specific storage pool. It could
>> be a great problem if a pool-specific pointer is required ...
>
>The answer is "Don't do that!"
>
>Finalize should finalise whatever is inside the given Object, and not deal
>in any way with where (in which pool) the object resides. After the
>implementation has called Finalize for the object, it will then -- when
>appropriate -- call Deallocate for the object's pool, in order to deallocate
>the object's storage space from the pool. Do not mess with this mechanism.

Theoretically true, but practically, consider a situation when objects
have to be always allocated in a specific pool. For example, on a
user-defined stack. Further each object has to remove itself from some
list. It is made upon finalization. The problem is that in this case
you cannot use the advantages of thin pool-specific pointers. You have
to switch to thick general-access ones. This would cause cascading
effects leading to abadoning any use of pool-specific ones at all.
Note also that if all that is a generic package, then you have no
choice, because the formal access parameter should either have the
modifier "all" or not.

And of course there is another problem that anonymous access types are
always general-access. I would expect that:

type Object (Other : access Other_Type) is ...

would mean a specific access type. With an ability to say:

for Object.Other'Storage_Pool use Some_Pool;

And for a generic-access type one would have to write:

type Object (Other : access all Other_Type) is ...

---
Regards,
Dmitry Kazakov
www.dmitry-kazakov.de



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

* Re: Type conversions on pool-specific access types
  2003-10-16 12:13   ` Dmitry A. Kazakov
@ 2003-10-16 20:28     ` Nick Roberts
  2003-10-17  4:27       ` Nick Roberts
  2003-10-18  9:59       ` Dmitry A. Kazakov
  0 siblings, 2 replies; 6+ messages in thread
From: Nick Roberts @ 2003-10-16 20:28 UTC (permalink / raw)


"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:g5ksovoq9nkg0tb53rgrn40vjjl7l5cqkb@4ax.com...

> > ...
> >   declare
> >      Ptr : Base_Ptr := new Derived;
> >   begin
> >      Foo(Ptr);
>
> Yes, in this case. But consider specialized containers of
> Derived descendants:
>
> procedure Derived_Specific (X : Derived);
> type Array_Of_Derived is array (...) of Derived_Ptr;
>
> Now you can call Derived_Specific on any element of
> Array_Of_Derived without an explicit type conversion.
> It is also type safe. If I have only Base_Ptr, I lose that
> advantage and forced to always work in the terms of
> the base type and what is worse, to make casts.

In this situation, I suggest you declare:

   type Derived_Ptr is access Derived'Class;
   type Array_Of_Derived is array (...) of Derived_Ptr;

exactly as you suggest, so you can call:

   Derived_Specific( Array_Of_Derived(...).all );

If you now wish to call Foo for an object designated by a component of the
Array_Of_Derived array, you do indeed need to do a conversion. I think this
can be adequately achieved in Ada 95 by the use of an intermediate general
access type:

   type General_Base_Ptr is access all Base'Class;
   procedure Foo (Object: in General_Base_Ptr);

Now we can call Foo for objects of type Base_Ptr or Derived_Ptr:

   X: Base_Ptr;
   ...
   Foo( General_Base_Ptr(X) );
   Foo( General_Base_Ptr(Array_Of_Derived(...)) );

If you do not need to be able to pass null values, an alternative is to make
the parameter to Foo of an anonymous access type:

   procedure Foo (Object: access Base'Class);

Then the conversions are implicit:

   Foo( X );
   Foo( Array_Of_Derived(...) );

I recognise that this is not the neatest solution imaginable, but I think it
works, and will be reasonably efficient. It has the possible advantage that
you could choose to have Base_Ptr and Derived_Ptr objects stored in
different pools (and the new Foo will still work, since it accepts a general
access type).

> [Finalize should not deallocate Object]
> Theoretically true, but practically, consider a situation
> when objects have to be always allocated in a specific
> pool. For example, on a user-defined stack. Further each
> object has to remove itself from some list. It is made
> upon finalization. The problem is that in this case you
> cannot use the advantages of thin pool-specific pointers.

I repeat that a controlled object should not attempt to deallocate itself
during its own finalisation. In order to cause the deallocation of an
object, an instance of Unchecked_Deallocation should be called at the
appropriate time, which can be expected to: (1) call Finalize for the
object; (2) call Deallocate for the object's pool.

One way to obtain the calling of an instance of Unchecked_Deallocation for
all the objects in a linked list is to declare a controlled type which
contains a pointer to one of the objects in the list, and to implement the
Finalize procedure of this type to chase down the list deallocating objects
as it goes. For example:

   type My_Object;

   type My_Object_Ptr is access My_Object;

   type My_Object is new Ada.Finalization.Controlled with
      record
         Next: My_Object_Ptr;
         ...
      end record;

   type My_Object_List is new Ada.Finalization.Controlled with
      record
         First: My_Object_Ptr;
         ...
      end record;

   procedure Free is new
Ada.Unchecked_Deallocation(My_Object,My_Object_Ptr);

   procedure Finalize (List: in out My_Object_List) is
      Obj, Tmp: My_Object_Ptr;
   begin
      Obj := List.First;
      List.First := null;
      while Obj /= null loop
         Tmp := Obj;
         Obj := Obj.Next;
         Free(Tmp); -- will call member's Finalize and its pool's Deallocate
      end loop;
   end Finalize;

   procedure Do_Something_Using_a_List is
      L: My_Object_List;
   begin
      ... -- add members to the list L
   end; -- L will be finalised here, which will cause the whole list to be
finalised and freed properly

Hope this is helpful!

--
Regards,
Nick Roberts





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

* Re: Type conversions on pool-specific access types
  2003-10-16 20:28     ` Nick Roberts
@ 2003-10-17  4:27       ` Nick Roberts
  2003-10-18  9:59       ` Dmitry A. Kazakov
  1 sibling, 0 replies; 6+ messages in thread
From: Nick Roberts @ 2003-10-17  4:27 UTC (permalink / raw)


I think I should add the following note. In the example starting with:

>    type My_Object;

if instead of:

>    type My_Object_Ptr is access My_Object;

I had put:

   type My_Object_Ptr is access My_Object'Class;

and instead of :

>    procedure Free is new
Ada.Unchecked_Deallocation(My_Object,My_Object_Ptr);

I had put:

   procedure Free is new
Ada.Unchecked_Deallocation(My_Object'Class,My_Object_Ptr);

then within:

>    procedure Finalize (List: in out My_Object_List) is
>       Obj, Tmp: My_Object_Ptr;
>    begin
>       Obj := List.First;
>       List.First := null;
>       while Obj /= null loop
>          Tmp := Obj;
>          Obj := Obj.Next;
>          Free(Tmp); -- will call member's Finalize and its pool's
Deallocate
>       end loop;
>    end Finalize;

the call to Free would within itself make a dispatching call to the
appropriate Finalize, according to the actual type of each member object.

--
Regards,
Nick





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

* Re: Type conversions on pool-specific access types
  2003-10-16 20:28     ` Nick Roberts
  2003-10-17  4:27       ` Nick Roberts
@ 2003-10-18  9:59       ` Dmitry A. Kazakov
  1 sibling, 0 replies; 6+ messages in thread
From: Dmitry A. Kazakov @ 2003-10-18  9:59 UTC (permalink / raw)


Nick Roberts wrote:

> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
> news:g5ksovoq9nkg0tb53rgrn40vjjl7l5cqkb@4ax.com...
> 
>> > ...
>> >   declare
>> >      Ptr : Base_Ptr := new Derived;
>> >   begin
>> >      Foo(Ptr);
>>
>> Yes, in this case. But consider specialized containers of
>> Derived descendants:
>>
>> procedure Derived_Specific (X : Derived);
>> type Array_Of_Derived is array (...) of Derived_Ptr;
>>
>> Now you can call Derived_Specific on any element of
>> Array_Of_Derived without an explicit type conversion.
>> It is also type safe. If I have only Base_Ptr, I lose that
>> advantage and forced to always work in the terms of
>> the base type and what is worse, to make casts.
> 
> In this situation, I suggest you declare:
> 
>    type Derived_Ptr is access Derived'Class;
>    type Array_Of_Derived is array (...) of Derived_Ptr;
> 
> exactly as you suggest, so you can call:
> 
>    Derived_Specific( Array_Of_Derived(...).all );
> 
> If you now wish to call Foo for an object designated by a component of the
> Array_Of_Derived array, you do indeed need to do a conversion. I think
> this can be adequately achieved in Ada 95 by the use of an intermediate
> general access type:
> 
>    type General_Base_Ptr is access all Base'Class;
>    procedure Foo (Object: in General_Base_Ptr);

Which in effect just means that we abadon use of pool-specific access types.

Say, we introduce the language design principle:

"a program dealing with objects allocated in ONE pool should to remain valid
after removing the word "all" in all access type declarations."

Don't you think it would be a reasonable principle? [ Note that all nasty
cases of 'Unchecked_Access are excluded by the one-pool-requirement. ] 

>> [Finalize should not deallocate Object]
>> Theoretically true, but practically, consider a situation
>> when objects have to be always allocated in a specific
>> pool. For example, on a user-defined stack. Further each
>> object has to remove itself from some list. It is made
>> upon finalization. The problem is that in this case you
>> cannot use the advantages of thin pool-specific pointers.
> 
> I repeat that a controlled object should not attempt to deallocate itself
> during its own finalisation.

It is not what required. What is required is that the object could delete
itself from a global list [of pointers] upon finalization. For this a
pointer to the object is needed. So you have to get a pointer to the object
[of a limited type of course]. This is impossible for pool-specific access
types. And I see no solution for that. Here I mean not a work-around, but
how to fix it in Ada design.

First of all the anonymous access types are general-access types. Otherwise,
one could try to define Finalize [Initialize] for limited controlled types
as:

   procedure Finalize (Object_Ptr : access Object_Type);

as opposed to

   procedure Finalize (Object_Ptr : access all Object_Type);

But then the actual storage pool of Object_Ptr would not be statically
known. So it would not work anyway. And it is too late of course. So the
situation with pool-specific access types is pretty grim at the moment.

Not to mention other problems with storage pools:

1. How to make a user-defined storage pool usable both concurrently and
inside a protected operation?

2. How to have task-specific storage pools, when task are created
dynamically?

--
Regards,
Dmitry A. Kazakov
www.dmitry-kazakov.de



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

end of thread, other threads:[~2003-10-18  9:59 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2003-10-13 10:34 Type conversions on pool-specific access types Dmitry A. Kazakov
2003-10-16  1:43 ` Nick Roberts
2003-10-16 12:13   ` Dmitry A. Kazakov
2003-10-16 20:28     ` Nick Roberts
2003-10-17  4:27       ` Nick Roberts
2003-10-18  9:59       ` Dmitry A. Kazakov

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