comp.lang.ada
 help / color / mirror / Atom feed
From: Felix Krause <contact@flyx.org>
Subject: Re: Smart Pointers and Tagged Type Hierarchies
Date: Thu, 27 Jul 2017 21:30:59 +0200
Date: 2017-07-27T21:30:59+02:00	[thread overview]
Message-ID: <2017072721305951845-contact@flyx.org> (raw)
In-Reply-To: ol5j8i$10s8$1@gioia.aioe.org

On 2017-07-24 19:53:54 +0000, Dmitry A. Kazakov said:

> On 2017-07-24 17:41, Felix Krause wrote:
> 
>> Now I am wondering what others think of these approaches. Are there 
>> alternatives? Which one would be better from a user perspective?
> 
> I am using generic pointer (Handle), which can be instantiated later 
> with any derived type from the base reference counting type. See Simple 
> Components:
> 
>     http://www.dmitry-kazakov.de/ada/components.htm#Objects_etc
> 
> This is your second approach but with object pointer type passed as a 
> second generic parameter.
> 
> generic
>     type Object_Type (<>) is abstract new Entity with private;
>     type Object_Type_Ptr is access Object_Type'Class;
> package Object.Handle is
>     type Handle is new Ada.Finalization.Controlled with private;
> 
> Users of smart pointer need not to be generic. I don't know why you 
> think it is necessary.

Using your types, let's say I have

    type Entity_Access is access Entity'Class;
    package Entity_Handle is new Object.Handle (Entity, Entity_Access);

    type A_Type is new Entity with private;
    type A_Access is access A_Type'Class;

    package A_Handle is new Object.Handle (A_Type, A_Access);

Now some subroutine wants to take an Entitiy as parameter, or more precisely, a
Handle to an Entity. If I specify this:

    procedure Do_Something (E : Entity_Handle.Handle);

I cannot call this procedure with an A_Handle.Handle. So I'd need to do:

    generic
       with package Actual_Handle is new Object.Handle (<>);
    procedure Do_Something (E : Actual_Handle.Handle);

I don't see how I can prevent this using your approach.

> Regarding hierarchy of types and additional operations and the problem 
> of exposing the implementation type. You can use delegation:
> 
> E.g. if you have your File_Stream_Object and File_Stream_Reference, you 
> define a File_Stream_Interface:
> 
>     type File_Stream_Interface is interface;
>     procedure Foo (Stream : in out File_Stream_Interface) is abstract;
> 
> Then both object and reference implement the interface:
> 
>     type File_Stream_Object is
>        new Instance and File_Stream_Interface ...
>     overriding procedure Foo (Stream : in out File_Stream_Reference);
> 
>     type File_Stream_Reference is
>        new Reference and File_Stream_Interface ...
>     overriding procedure Foo (Stream : in out File_Stream_Reference);
> 
>  From second Foo you call the first after dereferencing. Now you can 
> hide File_Stream_Object and expose only File_Stream_Interface and 
> File_Stream_Reference. You can also derive from File_Stream_Object and 
> File_Stream_Reference adding new interfaces. The latter has a drawback 
> that you will have to convert pointer to a more specific class.
> 
> ------------------------------------
> It is quite tedious because:
> 
> 1. Delegation cannot be automated in Ada;
> 2. Pointer are not promoted upon inheritance.
> 
> Otherwise this schema works well.

Thanks for this. It is probably not the approach I will use, but not a 
bad idea.

Another question using generics: If I do use a generic package for the 
smart pointers, is there a good way to put the instance of that package 
inside the package that defines the derived type?

Example (starts with the generic code in my original post):

    package Stream is
       type Instance is abstract limited tagged private;

       --  fetches an event from the stream
       procedure Fetch (Object : in out Instance; Ret : out Event) is abstract;
    private
       type Instance is abstract tagged limited record
          Refcount : Natural := 1;
       end record;
    end Stream;

    generic
       type Implementation is new Stream.Instance with private;
    package Stream.Smart is
       type Reference is new Ada.Finalization.Controlled with private;

       --  reference-counting implementation
       overriding procedure Adjust (Object : in out Reference);
       overriding procedure Finalize (Object : in out Reference);
    private
       type Implementation_Access is access all Implementation'Class;

       type Reference is new Ada.Finalization.Controlled with record
          Data : access Implementation_Access;
       end record;
    end Stream.Smart;

    package File_Stream is
       type Instance is new Stream.Instance with private;

       package Smart is new Stream.Smart (Instance);
    private
       type Instance is new Stream.Instance with record
          File : Ada.Text_IO.File_Access;
       end record;
    end File_Stream;

This does not compile because I cannot instantiate Stream.Smart with 
the incomplete File_Stream.Instance type. What would work is to have 
instead a child package:

    package File_Stream.Smart is new Stream.Smart (File_Stream.Instance);

But with this, I am not able to define any subroutines in the 
File_Stream package that take a smart pointer.

-- 
Regards,
Felix Krause


  reply	other threads:[~2017-07-27 19:30 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-07-24 15:41 Smart Pointers and Tagged Type Hierarchies Felix Krause
2017-07-24 19:53 ` Dmitry A. Kazakov
2017-07-27 19:30   ` Felix Krause [this message]
2017-07-27 20:42     ` Dmitry A. Kazakov
2017-07-24 21:24 ` Chris Moore
2017-07-27 19:38   ` Felix Krause
2017-08-01  4:07     ` Randy Brukardt
2017-07-26 17:53 ` Ivan Levashev
2017-07-28  9:21 ` AdaMagica
2017-07-30 19:45   ` briot.emmanuel
2017-08-01  3:43 ` Randy Brukardt
2017-08-01  7:36   ` Dmitry A. Kazakov
2017-08-01 22:41     ` Randy Brukardt
2017-08-02  6:28       ` Dmitry A. Kazakov
2017-08-02 19:26         ` Randy Brukardt
replies disabled

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