From: xorque on
Hello.

I'm trying to write an archive/directory abstraction in a similar vein
to PhysicsFS
but am at a bit of a loss as to how to design the archiver interface:

with Path;

package Archiver is

type Archiver_t is abstract tagged limited private;
type Archiver_Class_Access_t is access Archiver_t'Class;

procedure Init
(Archiver : out Archiver_t) is abstract;

function Can_Mount
(Archiver : in Archiver_t;
Path : in Path.Real_t) return Boolean is abstract;

type File_t is abstract tagged limited private;
type File_Class_Access_t is access File_t'Class;

procedure Open
(Archiver : in Archiver_t;
Path : in Path.Virtual_t;
File : out File_t) is abstract;

procedure Close
(File : in out File_t) is abstract;

private

type Archiver_t is abstract tagged limited null record;

type File_t is abstract tagged limited null record;

end Archiver;

The idea of the above is that the main part of the library only deals
with
archivers and "files" (which might only really be pointers to entries
in Zip
files, for example) by 'Class.

The problem with the above is that:

archiver.ads:18:13: operation can be dispatching in only one type

Hopefully someone here knows a better way to handle this.
From: Dmitry A. Kazakov on
On Fri, 13 Nov 2009 12:12:18 -0800 (PST), xorque wrote:

> I'm trying to write an archive/directory abstraction in a similar vein
> to PhysicsFS
> but am at a bit of a loss as to how to design the archiver interface:
>
> with Path;
>
> package Archiver is
>
> type Archiver_t is abstract tagged limited private;
> type Archiver_Class_Access_t is access Archiver_t'Class;
>
> procedure Init
> (Archiver : out Archiver_t) is abstract;
>
> function Can_Mount
> (Archiver : in Archiver_t;
> Path : in Path.Real_t) return Boolean is abstract;
>
> type File_t is abstract tagged limited private;
> type File_Class_Access_t is access File_t'Class;
>
> procedure Open
> (Archiver : in Archiver_t;
> Path : in Path.Virtual_t;
> File : out File_t) is abstract;
>
> procedure Close
> (File : in out File_t) is abstract;
>
> private
>
> type Archiver_t is abstract tagged limited null record;
>
> type File_t is abstract tagged limited null record;
>
> end Archiver;
>
> The idea of the above is that the main part of the library only deals with
> archivers and "files" (which might only really be pointers to entries in Zip
> files, for example) by 'Class.
>
> The problem with the above is that:
>
> archiver.ads:18:13: operation can be dispatching in only one type
>
> Hopefully someone here knows a better way to handle this.

Ada does not support multiple dispatch of this form. For simplicity
consider it does not support MD at all.

According to your code Open is doubly dispatching in Archiver_t and File_t
arguments. That does not work (unfortunately). You have to choose one and
make another class-wide. E.g.

procedure Open
(Archiver : Archiver_t'Class; -- Class-wide
Path : Virtual_t;
File : in out File_t) is abstract;

This would be a primitive operation of File_t only.

P.S. If you indeed have to do MD, you should emulate it manually, by
dispatching within Open to some primitive operation of Archiver_t.

P.P.S. Do not use access types unless you need them.

P.P.P.S. Replace tagged limited with Ada.Finalization.Limited_Controlled,
sooner or later you almost certainly will need finalization and
initialization.

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
From: xorque on
Thanks for the response.

It may be that I don't want to design the interface like the above at
all.

The only real requirement is that:

Archivers use a common protocol: Code just sees operations
on Archiver_t'Class and doesn't know if it's reading from a Zip, RAR,
directory, etc.

Presumably, then, when I open a file using an operation from an
archiver, I can't know about the implementation of the actual file
as it's private to the archiver.

Hence, I've ended up with two private tagged types. Is there a better
way (I've not ignored your other suggestions, I'm just exploring other
possibilities first)?
From: xorque on
On Nov 13, 8:34 pm, "Dmitry A. Kazakov" <mail...(a)dmitry-kazakov.de>
wrote:
..>
> P.P.P.S. Replace tagged limited with Ada.Finalization.Limited_Controlled,
> sooner or later you almost certainly will need finalization and
> initialization.

Agreed. It was always intended, just not implemented there for
simplicity's
sake.
From: Dmitry A. Kazakov on
On Fri, 13 Nov 2009 12:43:42 -0800 (PST), xorque wrote:

> The only real requirement is that:
>
> Archivers use a common protocol: Code just sees operations
> on Archiver_t'Class and doesn't know if it's reading from a Zip, RAR,
> directory, etc.
>
> Presumably, then, when I open a file using an operation from an
> archiver, I can't know about the implementation of the actual file
> as it's private to the archiver.
>
> Hence, I've ended up with two private tagged types. Is there a better
> way (I've not ignored your other suggestions, I'm just exploring other
> possibilities first)?

MD is customary replaced my mix-in:

1. Standard mix-in:

type File_Type is
abstract new Ada.Finalization.Limited_Controlled with null record;
procedure Open
( File : in out File_Type;
Path : String
) is abstract;
procedure Close (File : in out File_Type) is abstract;
procedure Read ...;
procedure Write ...;

Files know nothing about archives. Archive has a file as a mixin:

type Archiver_Type (Transport : not null access File_Type'Class) is
new Ada.Finalization.Limited_Controlled with null record;
procedure Open (Archiver : in out Archiver_Type; Path : String);

Open of Archive_Type calls to Open of Archive.Transport. Same with other
operations. They all are defined in terms of the operations of File_Type.
Class through Archive.Transport are dispatching, because it is class-wide.

2. Generic mix-in.

You can put Archive in the hierarchy of File_Type. The implementation of
Archive_Type can be generic taking a descendant of File_Type and then
extending it in the generic body.

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