comp.lang.ada
 help / color / mirror / Atom feed
* smart pointer dangerous (no -> operator)
@ 2009-01-16 10:04 Oliver Kowalke
  2009-01-16 11:09 ` Dmitry A. Kazakov
                   ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: Oliver Kowalke @ 2009-01-16 10:04 UTC (permalink / raw)


Hello,
Ada doesn't support the dereference operator -> as C++.

So Ada provides only two ways to access the managed object?

- returning it via value : function Get(Pointer : Smart_Pointer) return
Object;

- returning it via access type : function Get(Pointer : Smart_Pointer)
return Access_Object;

- returning a proxy which implements the functions applicable to the managed
object

In the first case I can not manipulate the state of the managed object
(setting some internal data).
The second case is dangerous because the ownership 'constraint' can be
violated -> other code can call Unchecked_Delete on the returned access
type or assign it to another one.

How can case three be implemented in Ada if the smart ponter package is
generic?


regards,
Oliver



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

* Re: smart pointer dangerous (no -> operator)
  2009-01-16 10:04 smart pointer dangerous (no -> operator) Oliver Kowalke
@ 2009-01-16 11:09 ` Dmitry A. Kazakov
  2009-01-16 11:42   ` Georg Bauhaus
                     ` (2 more replies)
  2009-01-16 20:08 ` Tero Koskinen
  2009-01-18 12:21 ` Samuel Tardieu
  2 siblings, 3 replies; 13+ messages in thread
From: Dmitry A. Kazakov @ 2009-01-16 11:09 UTC (permalink / raw)


On Fri, 16 Jan 2009 11:04:57 +0100, Oliver Kowalke wrote:

> Ada doesn't support the dereference operator -> as C++.
> 
> So Ada provides only two ways to access the managed object?
> 
> - returning it via value : function Get(Pointer : Smart_Pointer) return
> Object;
> 
> - returning it via access type : function Get(Pointer : Smart_Pointer)
> return Access_Object;
> 
> - returning a proxy which implements the functions applicable to the managed
> object
> 
> In the first case I can not manipulate the state of the managed object
> (setting some internal data).
> The second case is dangerous because the ownership 'constraint' can be
> violated -> other code can call Unchecked_Delete on the returned access
> type or assign it to another one.
> 
> How can case three be implemented in Ada if the smart ponter package is
> generic?

It is difficult, since there is no any delegation support in Ada.

I extensively used two approaches:

1. In Ada 95 I declare the proxy type tagged controlled. The handle is
created through a instantiation in the private part of the generic package
Handle:

   type X_Handle is new Ada.Finalization.Controlled with private;
      -- Repeat all operations of the target
   procedure Foo (Object : X_Handle);
   ... -- and so on
private
   package Handles is not Object.Handle (...); -- Instantiation
   type X_Handle is new Handles.Handle with null record;

The implementation of Foo goes as follows:

   procedure Foo (Object : X_Handle) is
   begin
       Foo (Ptr (Object).all);
   end Foo;

this is extremely tedious, but the best way I know.

Important, the target object is derived from Object.Entity. This is
necessary for reference counting. There is also another base for
persistence support.

2. In Ada 2005 things are slightly better. There I use interfaces. The
target object receives an interface:

   type X_Interface is ...;
   procedure Foo (Object : X_Interface) is abstract;

Then I define the object's implementation type derived from Object.Entity
as before, but also implementing X_Interface. Usually I do it privately,
though this requires some additional efforts in package hierarchy
structuring (the proxy must see the object implementation type at the
handle instantiation point).

Now the proxy type:

   type X_Handle is new Ada.Finalization.Controlled
      and X_Interface with private;

No more need to repeat everything.

The implementation is still needed:

   procedure Foo (Object : X_Handle) is
   begin
      Ptr (Object).Foo; -- Using prefix notation
   end Foo;

This is less error prone, but still endlessly boring. Two major problems
are:

1. Lack of delegation in order to automate generation of wrappers like Foo.
2. Lack of MI, because one base type is required and used further extension
of the target type becomes practically impossible.

There also are more difficult problem of building hierarchies of proxy
types corresponding to the hierarchy of the targets as well as container
types for proxies. But this is another story.

---------------
P.S. My implementation of proxy object can be found here

http://www.dmitry-kazakov.de/ada/components.htm#Objects_etc

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: smart pointer dangerous (no -> operator)
  2009-01-16 11:09 ` Dmitry A. Kazakov
@ 2009-01-16 11:42   ` Georg Bauhaus
  2009-01-16 12:43     ` Dmitry A. Kazakov
  2009-01-16 21:21     ` Maciej Sobczak
  2009-01-16 11:46   ` Oliver Kowalke
  2009-01-17  0:43   ` Brian Drummond
  2 siblings, 2 replies; 13+ messages in thread
From: Georg Bauhaus @ 2009-01-16 11:42 UTC (permalink / raw)


Dmitry A. Kazakov schrieb:
> On Fri, 16 Jan 2009 11:04:57 +0100, Oliver Kowalke wrote:
> 
>> Ada doesn't support the dereference operator -> as C++.
>>
>> So Ada provides only two ways to access the managed object?

> Two major problems
> are:
> 
> 1. Lack of delegation in order to automate generation of wrappers like Foo.
> 2. Lack of MI, because one base type is required and used further extension
> of the target type becomes practically impossible.

Frequently overlooked, I think, Ada has nested scopes, useful
in many ways here. (Except, perhaps, if reuse is only be
perceived to be possible with flat library level compiled
brick stone, and not by reusing source code and some
programming...)

1 - Ownership of objects can be strictly confined in scopes.

2 - Types can be declared where they are needed. See 1.

3 - Finalization is performed when a scope ends.

These may not directly address the original question,
but why design a program around C++ imported hindrances,
if you will?

with Ada.Finalization, Ada.Text_IO;

procedure Scoped_Pointer is

   procedure Local is
      -- architectural unit

      type T is new Ada.Finalization.Controlled with
         record
            Data: Character;
         end record;

      overriding procedure Finalize(Object: in out T) is
      begin
         Ada.Text_IO.Put_Line("F!");
      end Finalize;

      procedure Next(Item: in out T) is
         -- may raise
      begin
         Item.Data := Character'Val
           (Natural'Succ(Character'Pos(Item.Data)));
      end Next;

      -- Pointer whose objects do not exist outside Local:
      type T_Ptr is access T;
      X: T_Ptr;

   begin  -- Local
      X := new T'(Ada.Finalization.Controlled with Data => '*');
      loop
         Next(X.all);  -- passed by reference; raises
      end loop;
   end Local;

begin
   -- Local.T does not exist
   Local;
   -- Local.T does not exist, neither does Local.X.all,
   -- as it has been finalized automatically.
end Scoped_Pointer;




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

* Re: smart pointer dangerous (no -> operator)
  2009-01-16 11:09 ` Dmitry A. Kazakov
  2009-01-16 11:42   ` Georg Bauhaus
@ 2009-01-16 11:46   ` Oliver Kowalke
  2009-01-16 12:45     ` Dmitry A. Kazakov
  2009-01-17  0:43   ` Brian Drummond
  2 siblings, 1 reply; 13+ messages in thread
From: Oliver Kowalke @ 2009-01-16 11:46 UTC (permalink / raw)


Dmitry A. Kazakov wrote:

> On Fri, 16 Jan 2009 11:04:57 +0100, Oliver Kowalke wrote:
>> Ada doesn't support the dereference operator -> as C++.

> It is difficult, since there is no any delegation support in Ada.

What are the reasons not to introduce it into Ada2005?

> P.S. My implementation of proxy object can be found here
> http://www.dmitry-kazakov.de/ada/components.htm#Objects_etc

Thanks! I'll take a look into your library

regards,
Oliver



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

* Re: smart pointer dangerous (no -> operator)
  2009-01-16 11:42   ` Georg Bauhaus
@ 2009-01-16 12:43     ` Dmitry A. Kazakov
  2009-01-16 21:21     ` Maciej Sobczak
  1 sibling, 0 replies; 13+ messages in thread
From: Dmitry A. Kazakov @ 2009-01-16 12:43 UTC (permalink / raw)


On Fri, 16 Jan 2009 12:42:17 +0100, Georg Bauhaus wrote:

> Dmitry A. Kazakov schrieb:
>> On Fri, 16 Jan 2009 11:04:57 +0100, Oliver Kowalke wrote:
>> 
>>> Ada doesn't support the dereference operator -> as C++.
>>>
>>> So Ada provides only two ways to access the managed object?
> 
>> Two major problems are:
>> 
>> 1. Lack of delegation in order to automate generation of wrappers like Foo.
>> 2. Lack of MI, because one base type is required and used further extension
>> of the target type becomes practically impossible.
> 
> Frequently overlooked, I think, Ada has nested scopes, useful
> in many ways here.

This is an unrelated issue. Referential semantics is not necessarily about
object life time management. Surely a better support provided by Ada for
stack allocated objects eliminates some cases of smart pointers typical in
C++. But the problem of referential objects exists independently on that.

Yes, if Ada had abstract access types, the positions 1 and 2 would be not
needed in this concrete case, because built-in access types in Ada already
"delegate" to the target, when operation "." is used. But Ada lacks
abstract access types either.

See last month's discussion: "Run-time accessibility checks (was:
Construction initialization problem)"

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: smart pointer dangerous (no -> operator)
  2009-01-16 11:46   ` Oliver Kowalke
@ 2009-01-16 12:45     ` Dmitry A. Kazakov
  0 siblings, 0 replies; 13+ messages in thread
From: Dmitry A. Kazakov @ 2009-01-16 12:45 UTC (permalink / raw)


On Fri, 16 Jan 2009 12:46:50 +0100, Oliver Kowalke wrote:

> What are the reasons not to introduce it into Ada2005?

Obviously, because people who shaped Ada 2005 felt other things useful and
important.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: smart pointer dangerous (no -> operator)
  2009-01-16 10:04 smart pointer dangerous (no -> operator) Oliver Kowalke
  2009-01-16 11:09 ` Dmitry A. Kazakov
@ 2009-01-16 20:08 ` Tero Koskinen
  2009-01-16 21:16   ` Maciej Sobczak
  2009-01-18 12:21 ` Samuel Tardieu
  2 siblings, 1 reply; 13+ messages in thread
From: Tero Koskinen @ 2009-01-16 20:08 UTC (permalink / raw)


Hi,

On Fri, 16 Jan 2009 11:04:57 +0100 Oliver Kowalke wrote:
> Hello,
> Ada doesn't support the dereference operator -> as C++.
> 
> So Ada provides only two ways to access the managed object?
> 
> In the first case I can not manipulate the state of the managed object
> (setting some internal data).
> The second case is dangerous because the ownership 'constraint' can be
> violated -> other code can call Unchecked_Delete on the returned access
> type or assign it to another one.

If you want to manipulate the original object behind the smart pointer
without using access types, you can use generics (or access to procedure):

generic
   type Element (<>) is limited private;
   type Element_Access is access Element;
package Smart_Pointer is
   type Pointer is new Ada.Finalization.Controlled with private;
   function Create (Object : Element_Access) return Pointer;

   generic
      with procedure Action (Object : in out Element) is <>;
   procedure Update (Pointer_Object : Pointer);
...
end Smart_Pointer;

Usage would be something like this:
package body Test is
   type Integer_Access is access Integer;

   package Smart_Int is new Smart_Pointer (Integer, Integer_Access);

   procedure Test_Update is
      use Smart_Int;

      procedure Do_Update (I : in out Integer) is
      begin
         I := 2;
      end Do_Update;
      A_Int : Smart_Int.Pointer := Smart_Int.Create(new Integer'(1));

      procedure Int_Update is new Smart_Int.Update (Do_Update);
   begin
      Int_Update (A_Int);
   end Test_Update;
end Test;

-- 
Tero Koskinen - http://iki.fi/tero.koskinen/



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

* Re: smart pointer dangerous (no -> operator)
  2009-01-16 20:08 ` Tero Koskinen
@ 2009-01-16 21:16   ` Maciej Sobczak
  0 siblings, 0 replies; 13+ messages in thread
From: Maciej Sobczak @ 2009-01-16 21:16 UTC (permalink / raw)


On 16 Sty, 21:08, Tero Koskinen <tero.koski...@iki.fi> wrote:

> If you want to manipulate the original object behind the smart pointer
> without using access types, you can use generics (or access to procedure):
>
> generic
>    type Element (<>) is limited private;
>    type Element_Access is access Element;
> package Smart_Pointer is
>    type Pointer is new Ada.Finalization.Controlled with private;
>    function Create (Object : Element_Access) return Pointer;
>
>    generic
>       with procedure Action (Object : in out Element) is <>;
>    procedure Update (Pointer_Object : Pointer);

I was thinking rather about something similar in spirit to
Update_Element/Query_Element in Ada.Containers.Vector.
One generic less -> easier to use.

Actually, it all comes with analogy. There is no indexing operator in
Vector (I mean - nothing like operator[] in C++, which returns a
reference to contained element), which is exactly the same kind of
problem as the lack of dereference in smart pointers.
If the problems are similar, why not go for similar solutions?

This, however, brings important question: what about performance? Is
it common for the compilers to inline such constructs (calls with
callbacks to local subprograms)?

--
Maciej Sobczak * www.msobczak.com * www.inspirel.com

Database Access Library for Ada: www.inspirel.com/soci-ada



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

* Re: smart pointer dangerous (no -> operator)
  2009-01-16 11:42   ` Georg Bauhaus
  2009-01-16 12:43     ` Dmitry A. Kazakov
@ 2009-01-16 21:21     ` Maciej Sobczak
  2009-01-17 19:07       ` Georg Bauhaus
  1 sibling, 1 reply; 13+ messages in thread
From: Maciej Sobczak @ 2009-01-16 21:21 UTC (permalink / raw)


On 16 Sty, 12:42, Georg Bauhaus <rm.dash-bauh...@futureapps.de> wrote:

> Frequently overlooked, I think, Ada has nested scopes, useful
> in many ways here.
[...]

The problem is that in the majority of cases the whole point of
creating something dynamically (rather than on the stack) is to use it
in some other scope - outside of where it was created.

In your example the only motivation for allocating the object
dynamically would be to trade off stack space for heap space. Are
there any others?

--
Maciej Sobczak * www.msobczak.com * www.inspirel.com

Database Access Library for Ada: www.inspirel.com/soci-ada




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

* Re: smart pointer dangerous (no -> operator)
  2009-01-16 11:09 ` Dmitry A. Kazakov
  2009-01-16 11:42   ` Georg Bauhaus
  2009-01-16 11:46   ` Oliver Kowalke
@ 2009-01-17  0:43   ` Brian Drummond
  2009-01-17  9:28     ` Dmitry A. Kazakov
  2 siblings, 1 reply; 13+ messages in thread
From: Brian Drummond @ 2009-01-17  0:43 UTC (permalink / raw)


On Fri, 16 Jan 2009 12:09:16 +0100, "Dmitry A. Kazakov"
<mailbox@dmitry-kazakov.de> wrote:

>1. In Ada 95 I declare the proxy type tagged controlled. The handle is
>created through a instantiation in the private part of the generic package
>Handle:
>
>   type X_Handle is new Ada.Finalization.Controlled with private;
>      -- Repeat all operations of the target
>   procedure Foo (Object : X_Handle);
>   ... -- and so on
>private
>   package Handles is not Object.Handle (...); -- Instantiation
>   type X_Handle is new Handles.Handle with null record;
>
>The implementation of Foo goes as follows:
>
>   procedure Foo (Object : X_Handle) is
>   begin
>       Foo (Ptr (Object).all);
>   end Foo;
>
>this is extremely tedious, but the best way I know.

As an Ada newbie, I've probably lost track of something important, but
I can't see a reason not to create a generic procedure, and simply
instantiate it for each Foo, which would be slightly less boring if it
worked..

I suspect it can't be that simple, but I can't see why. You don't know
the type of Ptr (Object).all but can't it be private from the generic's
point of view, or is that where a generic falls apart? Whatever the
type, there must be a Foo() for it, or the above wouldn't compile.

-Brian




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

* Re: smart pointer dangerous (no -> operator)
  2009-01-17  0:43   ` Brian Drummond
@ 2009-01-17  9:28     ` Dmitry A. Kazakov
  0 siblings, 0 replies; 13+ messages in thread
From: Dmitry A. Kazakov @ 2009-01-17  9:28 UTC (permalink / raw)


On Sat, 17 Jan 2009 00:43:17 +0000, Brian Drummond wrote:

> On Fri, 16 Jan 2009 12:09:16 +0100, "Dmitry A. Kazakov"
> <mailbox@dmitry-kazakov.de> wrote:
> 
>>1. In Ada 95 I declare the proxy type tagged controlled. The handle is
>>created through a instantiation in the private part of the generic package
>>Handle:
>>
>>   type X_Handle is new Ada.Finalization.Controlled with private;
>>      -- Repeat all operations of the target
>>   procedure Foo (Object : X_Handle);
>>   ... -- and so on
>>private
>>   package Handles is not Object.Handle (...); -- Instantiation
>>   type X_Handle is new Handles.Handle with null record;
>>
>>The implementation of Foo goes as follows:
>>
>>   procedure Foo (Object : X_Handle) is
>>   begin
>>       Foo (Ptr (Object).all);
>>   end Foo;
>>
>>this is extremely tedious, but the best way I know.
> 
> As an Ada newbie, I've probably lost track of something important, but
> I can't see a reason not to create a generic procedure, and simply
> instantiate it for each Foo, which would be slightly less boring if it
> worked..

I am not sure what you mean. To have a generic subprogram in addition to
each primitive operation of the target type? That would be even more
boring.

The problem is:

You have a target type:

   type T is ...;
   procedure Foo (X : T);
   function Bar return T;
   procedure Baz (X : T; Y : in out T; Z : Float);
   ...

Now you want to create a type T_Handle which would in its implementation
Ada.Finalization.Controlled with a pointer to an instance of T and all
operations of T delegated to the pointer's target:

   type T_Handle is Ada.Finalization.Controlled with private;
   procedure Foo (X : T_Handle);
   function Bar return T_Handle;
   procedure Baz (X : T_Handle; Y : in out T_Handle; Z : Float);
   ...

Generics do not solve that. What does is delegation + interface inheritance
from concrete type (T). Ada has neither.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: smart pointer dangerous (no -> operator)
  2009-01-16 21:21     ` Maciej Sobczak
@ 2009-01-17 19:07       ` Georg Bauhaus
  0 siblings, 0 replies; 13+ messages in thread
From: Georg Bauhaus @ 2009-01-17 19:07 UTC (permalink / raw)


Maciej Sobczak wrote:
> On 16 Sty, 12:42, Georg Bauhaus <rm.dash-bauh...@futureapps.de> wrote:
> 
>> Frequently overlooked, I think, Ada has nested scopes, useful
>> in many ways here.
> [...]
> 
> The problem is that in the majority of cases the whole point of
> creating something dynamically (rather than on the stack) is to use it
> in some other scope - outside of where it was created.

Yes, local objects do not always work, even if the location
is spanning vast areas of the program. While you can have scopes
nested in scopes nested in scopes next to a neighboring nested scope,
nicely making types and objects local or global as
desired, this is not always a (satisfactory) solution.

> In your example the only motivation for allocating the object
> dynamically would be to trade off stack space for heap space. Are
> there any others?

None that I can think of. But being able to confine objects to
scopes and at the same time  control their lifetimes using scopes
can make some tricky pointer programming patterns unnecessary that
might otherwise be necessary, namely in case you use an allocator
of a library level pointer type. (GNAT makes us prefer allocators
for larger objects because of an implementation choice, AFAIK...)



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

* Re: smart pointer dangerous (no -> operator)
  2009-01-16 10:04 smart pointer dangerous (no -> operator) Oliver Kowalke
  2009-01-16 11:09 ` Dmitry A. Kazakov
  2009-01-16 20:08 ` Tero Koskinen
@ 2009-01-18 12:21 ` Samuel Tardieu
  2 siblings, 0 replies; 13+ messages in thread
From: Samuel Tardieu @ 2009-01-18 12:21 UTC (permalink / raw)


>>>>> "Oliver" == Oliver Kowalke <oliver.kowalke@gmx.de> writes:

Oliver> - returning it via access type :
Oliver>      function Get(Pointer : Smart_Pointer) return Access_Object;

Why not use "return access Object" to get an anonymous access type,
on which Unchecked_Deallocation cannot be called without jumping
through hoops?

  Sam
-- 
Samuel Tardieu -- sam@rfc1149.net -- http://www.rfc1149.net/



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

end of thread, other threads:[~2009-01-18 12:21 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-01-16 10:04 smart pointer dangerous (no -> operator) Oliver Kowalke
2009-01-16 11:09 ` Dmitry A. Kazakov
2009-01-16 11:42   ` Georg Bauhaus
2009-01-16 12:43     ` Dmitry A. Kazakov
2009-01-16 21:21     ` Maciej Sobczak
2009-01-17 19:07       ` Georg Bauhaus
2009-01-16 11:46   ` Oliver Kowalke
2009-01-16 12:45     ` Dmitry A. Kazakov
2009-01-17  0:43   ` Brian Drummond
2009-01-17  9:28     ` Dmitry A. Kazakov
2009-01-16 20:08 ` Tero Koskinen
2009-01-16 21:16   ` Maciej Sobczak
2009-01-18 12:21 ` Samuel Tardieu

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