comp.lang.ada
 help / color / mirror / Atom feed
* Converting access values
@ 2005-01-05 22:31 Mark Lorenzen
  2005-01-05 23:32 ` Stephen Leake
                   ` (3 more replies)
  0 siblings, 4 replies; 20+ messages in thread
From: Mark Lorenzen @ 2005-01-05 22:31 UTC (permalink / raw)


Hello,

Imagine that we have a program that reads large amount of data from
(for example) a network connection into buffers. The data is just to
be seen as a sequence of octets.

We now want to copy parts ("slices") of the data to other tasks that
may do something interesting with these slices. The rate of data is
too high to simply copy the wanted slices, so instead we make "cheap
copies".

The idea is that these cheap copies simply point to slices within the
large buffer and this way all copies refer to a common buffer and no
real copying is going on.

We do not care about concurrent access to the buffers, allocation and
deallocation of the buffers. All this is taken care of by some safe
mechanism which would just clutter up the following little example.

I am able to actually create such a slice. It contains the correct
values, has the correct constraints and so on. The problem is simply
that my buffer has type access-to-unconstrained-array type and my copy
has an access-to-constrained-array type.

Question: How do i convert an access-to-constraint-array type to
access-to-unconstrained-array type in a (more or less) portable way?

As the example involves access types, adresses and conversions, I have
taken on my asbestos long-johns - so let the flaming begin!

Regards,
- Mark Lorenzen

type Buffer_Ptr is access Ada.Streams.Stream_Element_Array;

procedure Copy (Source : in Buffer_Ptr;
                Offset : in Ada.Streams.Stream_Element_Count;
                Length : in Ada.Streams.Stream_Element_Count;
                Target : out Buffer_Ptr) is
   use type Ada.Streams.Stream_Element_Offset;

   Slice_Begin : constant Ada.Streams.Stream_Element_Offset :=
     Source'First + Offset;
   Slice_End   : constant Ada.Streams.Stream_Element_Offset :=
     Slice_Begin + Length - 1;

   -- Subtype defining the desired constraints (ie. the slice) in
   --  the source buffer.
   subtype Constrained_Buffer is
     Ada.Streams.Stream_Element_Array (Slice_Begin .. Slice_End);

   package Conversions is
      new System.Address_To_Access_Conversions (Constrained_Buffer);

   Slice : Conversions.Object_Pointer;

   function To_Buffer_Ptr is
      new Ada.Unchecked_Conversion (Conversions.Object_Pointer, Buffer_Ptr);
begin
   -- Use the address of element Source(Offset) and convert it into
   --  an access-to-constrained-array type.

   Slice := Conversions.To_Pointer (Source(Slice_Begin)'Address);

   -- Now Slice actually points to a constrained array with the correct
   --  'First, 'Last and 'Length attributes. It's location in the storage
   --  also coincides with Source (Slice_Begin .. Slice_End). Fine!

   -- Problem: Slice is of type Conversions.Object_Pointer, but we
   --  really want a value of type Buffer_Ptr. How do we convert an
   --  access-to-constrained-array value to an
   --  access-to-unconstrained-array value in a (more or less)
   --  portable way?

   Target := ????
end Copy;



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

* Re: Converting access values
  2005-01-05 22:31 Converting access values Mark Lorenzen
@ 2005-01-05 23:32 ` Stephen Leake
  2005-01-05 23:51   ` Mark Lorenzen
  2005-01-06  0:18 ` Jeffrey Carter
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 20+ messages in thread
From: Stephen Leake @ 2005-01-05 23:32 UTC (permalink / raw)
  To: comp.lang.ada

Mark Lorenzen <mark.lorenzen@ofir.dk> writes:

> The problem is simply
> that my buffer has type access-to-unconstrained-array type and my copy
> has an access-to-constrained-array type.

Why? Why not use the right type?

> Question: How do i convert an access-to-constraint-array type to
> access-to-unconstrained-array type in a (more or less) portable way?

By copying the data :). I don't think there is a portable way to do that.

-- 
-- Stephe




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

* Re: Converting access values
  2005-01-05 23:32 ` Stephen Leake
@ 2005-01-05 23:51   ` Mark Lorenzen
  0 siblings, 0 replies; 20+ messages in thread
From: Mark Lorenzen @ 2005-01-05 23:51 UTC (permalink / raw)


Stephen Leake <stephen_leake@acm.org> writes:

> Mark Lorenzen <mark.lorenzen@ofir.dk> writes:
> 
> > The problem is simply
> > that my buffer has type access-to-unconstrained-array type and my copy
> > has an access-to-constrained-array type.
> 
> Why? Why not use the right type?

The example is a part of a package where I "export" the type
Buffer_Ptr in the public part of the package specification. The
access-to-unconstrained-array is local to the Copy function as it is
defined in the instantiation from Ada.Address_To_Access_Conversions.

> 
> > Question: How do i convert an access-to-constraint-array type to
> > access-to-unconstrained-array type in a (more or less) portable way?
> 
> By copying the data :). I don't think there is a portable way to do that.

Shame.

> 
> -- 
> -- Stephe

- Mark Lorenzen



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

* Re: Converting access values
  2005-01-05 22:31 Converting access values Mark Lorenzen
  2005-01-05 23:32 ` Stephen Leake
@ 2005-01-06  0:18 ` Jeffrey Carter
  2005-01-06  0:28   ` Mark Lorenzen
  2005-01-06 10:52 ` Dmitry A. Kazakov
  2005-01-06 11:02 ` Duncan Sands
  3 siblings, 1 reply; 20+ messages in thread
From: Jeffrey Carter @ 2005-01-06  0:18 UTC (permalink / raw)


Mark Lorenzen wrote:

> Hello,
> 
> Imagine that we have a program that reads large amount of data from
> (for example) a network connection into buffers. The data is just to
> be seen as a sequence of octets.
> 
> We now want to copy parts ("slices") of the data to other tasks that
> may do something interesting with these slices. The rate of data is
> too high to simply copy the wanted slices, so instead we make "cheap
> copies".
> 
> The idea is that these cheap copies simply point to slices within the
> large buffer and this way all copies refer to a common buffer and no
> real copying is going on.

The answer, as is often the case with Ada, is that you don't need to use 
access types to do what you want. Assuming you have a big Storage_Array 
and want to pass a slice of it somewhere, just pass the slice:

Buf : Stream_Element_Array (Really_Big);
...
procedure Do_Something (Data : in [out] Stream_Element_Array);
...
Do_Something (Data => Buf (Low .. High) );

I don't know of any compiler that will pass Data by copy; if yours does, 
you should seriously consider changing compilers. What will be passed is 
a slice descriptor, which may be bigger than an access value but will 
still be very small. Actual access to the buffer will be by reference.

-- 
Jeff Carter
"It's all right, Taggart. Just a man and a horse being hung out there."
Blazing Saddles
34



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

* Re: Converting access values
  2005-01-06  0:18 ` Jeffrey Carter
@ 2005-01-06  0:28   ` Mark Lorenzen
  2005-01-07 16:55     ` Nick Roberts
  0 siblings, 1 reply; 20+ messages in thread
From: Mark Lorenzen @ 2005-01-06  0:28 UTC (permalink / raw)


Jeffrey Carter <spam@spam.com> writes:

> Mark Lorenzen wrote:
> 
> > Hello,
> > Imagine that we have a program that reads large amount of data from
> > (for example) a network connection into buffers. The data is just to
> > be seen as a sequence of octets.
> > We now want to copy parts ("slices") of the data to other tasks that
> > may do something interesting with these slices. The rate of data is
> > too high to simply copy the wanted slices, so instead we make "cheap
> > copies".
> > The idea is that these cheap copies simply point to slices within the
> > large buffer and this way all copies refer to a common buffer and no
> > real copying is going on.
> 
> The answer, as is often the case with Ada, is that you don't need to
> use access types to do what you want. Assuming you have a big
> Storage_Array and want to pass a slice of it somewhere, just pass the
> slice:
> 
> Buf : Stream_Element_Array (Really_Big);
> ...
> procedure Do_Something (Data : in [out] Stream_Element_Array);
> ...
> Do_Something (Data => Buf (Low .. High) );
> 
> I don't know of any compiler that will pass Data by copy; if yours
> does, you should seriously consider changing compilers. What will be
> passed is a slice descriptor, which may be bigger than an access value
> but will still be very small. Actual access to the buffer will be by
> reference.

My problem is not passing a slice as an actual parameter to a
subprogram. I have a task which receives Buf, slices it up and then
passes the slices to other tasks using queues implemented as protected
types. When passing the slice to a queue, it will probably not be done
by copy, but the queue puts the slice into a data structure (list,
array etc.) and there we definately have a real assignment and copy.
 
> 
> -- 
> Jeff Carter
> "It's all right, Taggart. Just a man and a horse being hung out there."
> Blazing Saddles
> 34

- Mark Lorenzen



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

* Re: Converting access values
  2005-01-05 22:31 Converting access values Mark Lorenzen
  2005-01-05 23:32 ` Stephen Leake
  2005-01-06  0:18 ` Jeffrey Carter
@ 2005-01-06 10:52 ` Dmitry A. Kazakov
  2005-01-06 11:02 ` Duncan Sands
  3 siblings, 0 replies; 20+ messages in thread
From: Dmitry A. Kazakov @ 2005-01-06 10:52 UTC (permalink / raw)


On 05 Jan 2005 23:31:09 +0100, Mark Lorenzen wrote:

> Imagine that we have a program that reads large amount of data from
> (for example) a network connection into buffers. The data is just to
> be seen as a sequence of octets.
> 
> We now want to copy parts ("slices") of the data to other tasks that
> may do something interesting with these slices. The rate of data is
> too high to simply copy the wanted slices, so instead we make "cheap
> copies".
> 
> The idea is that these cheap copies simply point to slices within the
> large buffer and this way all copies refer to a common buffer and no
> real copying is going on.
> 
> We do not care about concurrent access to the buffers, allocation and
> deallocation of the buffers. All this is taken care of by some safe
> mechanism which would just clutter up the following little example.
> 
> I am able to actually create such a slice. It contains the correct
> values, has the correct constraints and so on. The problem is simply
> that my buffer has type access-to-unconstrained-array type and my copy
> has an access-to-constrained-array type.
> 
> Question: How do i convert an access-to-constraint-array type to
> access-to-unconstrained-array type in a (more or less) portable way?

No way, Ada has no abstract array interfaces. Your example shows where
better ADT might be useful. Your problem is that constrained and
unconstrained arrays may have different implementations which you are
unable to control. You cannot substitute one for another "as-is", it is not
pointer conversion. If there were array interfaces you could make an array
type of your own, which would keep dope and a reference to the vector (the
slice). Well, you can do it, but that would not be an array for Ada. So
the only way is to use non-array types and rework all the consumers of the
slices.

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



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

* Re: Converting access values
  2005-01-05 22:31 Converting access values Mark Lorenzen
                   ` (2 preceding siblings ...)
  2005-01-06 10:52 ` Dmitry A. Kazakov
@ 2005-01-06 11:02 ` Duncan Sands
  2005-01-06 12:17   ` Martin Dowie
  2005-01-06 19:30   ` Mark Lorenzen
  3 siblings, 2 replies; 20+ messages in thread
From: Duncan Sands @ 2005-01-06 11:02 UTC (permalink / raw)
  To: comp.lang.ada; +Cc: Mark Lorenzen

Hi Mark,

> We now want to copy parts ("slices") of the data to other tasks that
> may do something interesting with these slices. The rate of data is
> too high to simply copy the wanted slices, so instead we make "cheap
> copies".

if I understand right, you have a bunch of bytes that you would like the
rest of your program to see as an array of an unconstrained array type.
The problem here is: where do you put the info about the array bounds?
I had a similar problem where my bunch of bytes was passed to me from
a C library, and I didn't want to make a copy of it (too big), but wanted
to treat it as an unconstrained array.  This email I got from Steve Adams
may interest you:



Re: C array to Ada pointer to unconstrained array without copying memory
To: comp.lang.ada@ada-france.org
 
Duncan,
  I have had to do this for my project. What I do is not portable really but
in general is OK if no other solution is available.

Gnat does indeed use Fat pointers. *By Default*
You can make it use thin pointers though:

type var_arr is array (positive range <>) of T;
type var_arr_ptr is access var_arr;
for var_arr_ptr'size use DWORD; -- from memory, probably want a 'size of int
or something

The var_arr_ptr will only be a standard platform pointer size, 32 bits for
ease in the rest of discussion.
The pointer in *GNAT* points to the data (satisfies LRM that arr'access
points to data) and immediately before this are
the bounds, two ints every case i have for 1D arrays, lower then upper ->

[l bound][u bound][data....]

You need to do some pointer manipulations in C when first allocating them to
set the bounds, but Gnat just treats the arrays as normal.
Gnat is good like this since it means that C pointer is handled the same as
Ada pointer.

ObjectAda however has the access value pointing to the bounds, this gives me
quite a few headaches in my work, but gnat has a problem with the calling
convention I need and doesn't handle discriminants properly with the
convention.

For function calls the array can be passed as:
f( pa : in var_arr_ptr) -- reference, NOTE that it is NOT in out, since that
would be a pointer to a pointer
f( pa : in out var_arr) -- behind the scenes reference passed because of
access parameters.

Usual caveats apply that Gnat might change this scheme, however its unlikly.

For infomration the Fat pointer is a struct with two pointers, one to the
bounds and one to the data. Surprise is that the layout of the bounds and
data in memory is the same, so they are allocated in a single call, this
means thin and fat are the same at the basest level, you just have more
overhead for fats.

before language lawyers attack, my project uses intermixed Ada, Fortran,
C/C++ and Pascal. You end up sacrificing portability if you want it to work,
in C this isn't a problem, a single code base manipulated via the
preprocessor is easy, as does fortran. Ada doesn't which makes life hard. I
wish theyed make preprocessing a feature.

Steve A


> Greetings.  Given a C pointer Ptr to a type T (which points to a block of memory
> containing a C array of T's) and the length of the array Length, there are various
> tricks for getting an Ada array of the right size (defined on the stack - important!)
> that uses the block of memory to hold the array data, i.e. no copying required.
>
> For example,
>
> type Ada_Array is array (Positive range <>) of T;
>
> ...
>
> [have Length and Ptr]
>
> subtype Array2 is Ada_Array (1 .. Length);
>
> X : Array2;
> for X'Address use Ptr; -- ok, Ptr should be of type System'Address, but hey!
>
> And then you can make use of X.  However being defined on the stack, it
> can be quite awkward to use.  It would be nice to have the same thing but
> with X dynamically allocated.  For example,
>
> type Ada_Array_Pointer is access Ada_Array;
>
> ...
>
> [have Length and Ptr]
>
> X_Ptr : Ada_Array_Pointer;
>
> [do some clever stuff to set up X_Ptr to point to an array where the
> data is given by Ptr, and the bounds are somewhere else, but where? :)]
>
> [Pass X_Ptr around and dereference it etc.]
>
> The problem is the bounds of course.  For example, GNAT usually uses
> fat pointers, consisting of two normal pointers where one points to the
> data, and the other to the bounds.  So the "clever stuff" will need to
> allocate some memory to hold the bounds and set up the fat pointer
> appropriately.
>
> Does anyone know a good way to do this?  The solution only needs to
> work with GNAT.  I appreciate that deallocating the pointer may need
> to be handled specially.
>
> All the best,
>
> Duncan.



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

* Re: Converting access values
  2005-01-06 11:02 ` Duncan Sands
@ 2005-01-06 12:17   ` Martin Dowie
  2005-01-06 19:30   ` Mark Lorenzen
  1 sibling, 0 replies; 20+ messages in thread
From: Martin Dowie @ 2005-01-06 12:17 UTC (permalink / raw)


> before language lawyers attack, my project uses intermixed Ada,
> Fortran, C/C++ and Pascal. You end up sacrificing portability if you
> want it to work, in C this isn't a problem, a single code base
> manipulated via the preprocessor is easy, as does fortran. Ada
> doesn't which makes life hard. I wish theyed make preprocessing a
> feature.

There is nothing to stop you running GNATPrep on source before passing it to
any Ada compiler - could this help?

Cheers

-- Martin






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

* Re: Converting access values
  2005-01-06 11:02 ` Duncan Sands
  2005-01-06 12:17   ` Martin Dowie
@ 2005-01-06 19:30   ` Mark Lorenzen
  2005-01-06 20:40     ` Randy Brukardt
  1 sibling, 1 reply; 20+ messages in thread
From: Mark Lorenzen @ 2005-01-06 19:30 UTC (permalink / raw)


Duncan Sands <baldrick@free.fr> writes:

> Hi Mark,
> 
> > We now want to copy parts ("slices") of the data to other tasks that
> > may do something interesting with these slices. The rate of data is
> > too high to simply copy the wanted slices, so instead we make "cheap
> > copies".
> 
> if I understand right, you have a bunch of bytes that you would like the
> rest of your program to see as an array of an unconstrained array type.

Correct.

> The problem here is: where do you put the info about the array bounds?

Also correct. I have an access type *without* dope, but need an access
type *without* dope.

> I had a similar problem where my bunch of bytes was passed to me from
> a C library, and I didn't want to make a copy of it (too big), but wanted
> to treat it as an unconstrained array.  This email I got from Steve Adams
> may interest you:
> 
> 
> 
> Re: C array to Ada pointer to unconstrained array without copying memory
> To: comp.lang.ada@ada-france.org
>  
> Duncan,
>   I have had to do this for my project. What I do is not portable really but
> in general is OK if no other solution is available.
> 
> Gnat does indeed use Fat pointers. *By Default*
> You can make it use thin pointers though:
> 
> type var_arr is array (positive range <>) of T;
> type var_arr_ptr is access var_arr;
> for var_arr_ptr'size use DWORD; -- from memory, probably want a 'size of int
> or something
> 
> The var_arr_ptr will only be a standard platform pointer size, 32 bits for
> ease in the rest of discussion.
> The pointer in *GNAT* points to the data (satisfies LRM that arr'access
> points to data) and immediately before this are
> the bounds, two ints every case i have for 1D arrays, lower then upper ->
> 
> [l bound][u bound][data....]
> 
> You need to do some pointer manipulations in C when first allocating them to
> set the bounds, but Gnat just treats the arrays as normal.
> Gnat is good like this since it means that C pointer is handled the same as
> Ada pointer.

Unfortunately this does not work in my case. I have to "copy" a slice
from the middle of an access, and the [l bound][u bound] values would
just overwrite some of the values in the array located before the
slice.

[cut]

I think I have to go for a buffer type like this:

type Buffer_Slice is
   record
     Data  : Buffer_Ptr;
     First : Ada.Streams.Stream_element_Offset;
     Last  : Ada.Streams.Stream_element_Offset;
   end record;

Where First .. Last defines the slice constraints. It is much more the
Ada-way, but it puts more responsibility on the user of the buffer
type, since he/she could easily overwrite parts of the array which is
outside the slice. It looks like my other idea is going to be very
nasty to implement.

- Mark



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

* Re: Converting access values
  2005-01-06 19:30   ` Mark Lorenzen
@ 2005-01-06 20:40     ` Randy Brukardt
  0 siblings, 0 replies; 20+ messages in thread
From: Randy Brukardt @ 2005-01-06 20:40 UTC (permalink / raw)


"Mark Lorenzen" <mark.lorenzen@ofir.dk> wrote in message
news:m3oeg28iek.fsf@0x53586c58.boanxx18.adsl-dhcp.tele.dk...
> Duncan Sands <baldrick@free.fr> writes:
>
> > Hi Mark,
> >
> > > We now want to copy parts ("slices") of the data to other tasks that
> > > may do something interesting with these slices. The rate of data is
> > > too high to simply copy the wanted slices, so instead we make "cheap
> > > copies".
> >
> > if I understand right, you have a bunch of bytes that you would like the
> > rest of your program to see as an array of an unconstrained array type.
>
> Correct.

I think you are trying too hard to hide the information about the slices.
Forget that and simply pass the original buffer pointer and slice bounds to
the queuing tasks. (Package it up in a record to clean it up a bit and make
it clear that these are logically one entity). As someone else mentioned,
creating a slice doesn't copy anything. Neither does a type conversion from
one array subtype to another. So, in the queuing task, just create the slice
using the bounds passed, then type convert the slice to the appropriate type
for use (probably to use an input to Unchecked_Conversion).

                      Randy Brukardt







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

* Re: Converting access values
  2005-01-06  0:28   ` Mark Lorenzen
@ 2005-01-07 16:55     ` Nick Roberts
  2005-01-07 19:49       ` Mark Lorenzen
  0 siblings, 1 reply; 20+ messages in thread
From: Nick Roberts @ 2005-01-07 16:55 UTC (permalink / raw)


Mark Lorenzen <mark.lorenzen@ofir.dk> wrote:

> ...
> My problem is not passing a slice as an actual parameter to a subprogram.
> I have a task which receives Buf, slices it up and then passes the slices
> to other tasks using queues implemented as protected types. When passing
> the slice to a queue, it will probably not be done by copy, but the queue
> puts the slice into a data structure (list, array etc.) and there we
> definately have a real assignment and copy.

Mark, you are a silly sausage! All you do is pass the /bounds/ to the tasks.
When a task finally comes to actually process a slice, it uses the bounds
passed to it to obtain the actual slice of the buffer. Since there is only
one buffer, it can be a global, and parts of it do not need to be passed as
a parameter anywhere. Would this not work for you?

I can do a lot of guessing and suggest a skeleton:

   Buffer: Stream_Element_Array(0..4095);

   type Bounds is record First, Last: Stream_Element_Offset; end record;

   procedure Flabbergnosticate (Slice: in Bounds) is
      ...
   begin
      for i in Slice.First .. Slice'Last loop
         Flabber( Buffer(i), Temp );
         Gnosticate( Temp, Buffer(Slice.First..Slice'Last) );
      end loop;
   end;

   task Allocator is
      ...
   end;

   task Processor is
      ...
      entry Allocate (Slice: in Bounds);
      entry Retire_Slice;
      ...
   end;

   type Processor_ID is mod 200;

   Procs: array (Processor_ID) of Processor;

   task body Processor is
      ...
      Slice: Bounds;
      Busy:  Boolean := False;
   begin
      loop
         select
            ...
         or when not Busy =>
            accept Allocate (Slice: in Bounds) do
               Processor.Slice := Slice;
               Processor.Busy  := True;
            end;
            Flabbergnosticate( Slice );
         or when Busy =>
            accept Retire_Slice do
               Busy := False;
            end;
         end select;
      end loop
      ...
   end;

The Allocator task, here, is responsible for ensuring that no slice of the
buffer is refilled with data before it has been processed and retired. This
could be quite complicated (I've not attempted to show a body for this
task). I think perhaps the use of protected types is not appropriate in this
case, therefore (but I have done a lot of guessing).

Does this actually solve your problem, or am I missing something?

-- 
Nick Roberts



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

* Re: Converting access values
  2005-01-07 16:55     ` Nick Roberts
@ 2005-01-07 19:49       ` Mark Lorenzen
  2005-01-07 20:23         ` Nick Roberts
  2005-01-07 21:17         ` Randy Brukardt
  0 siblings, 2 replies; 20+ messages in thread
From: Mark Lorenzen @ 2005-01-07 19:49 UTC (permalink / raw)


Nick Roberts <nick.roberts@acm.org> writes:

> Mark Lorenzen <mark.lorenzen@ofir.dk> wrote:
> 
> > ...
> > My problem is not passing a slice as an actual parameter to a subprogram.
> > I have a task which receives Buf, slices it up and then passes the slices
> > to other tasks using queues implemented as protected types. When passing
> > the slice to a queue, it will probably not be done by copy, but the queue
> > puts the slice into a data structure (list, array etc.) and there we
> > definately have a real assignment and copy.
> 
> Mark, you are a silly sausage! All you do is pass the /bounds/ to the tasks.
> When a task finally comes to actually process a slice, it uses the bounds
> passed to it to obtain the actual slice of the buffer. Since there is only
> one buffer, it can be a global, and parts of it do not need to be passed as
> a parameter anywhere. Would this not work for you?

As I said in another posting, I am going for a design like this:

type Buffer_Slice is
   record
     Data  : Buffer_Ptr;
     First : Ada.Streams.Stream_element_Offset;
     Last  : Ada.Streams.Stream_element_Offset;
   end record;

There will be several buffers allocated/deallocated from a pool, so
the reference is a part of the Buffer_Slice type.

[cut]

> 
> 
> Does this actually solve your problem, or am I missing something?

It solves the problem, but exposes a bit more information to the
clients than necessary. It is very easy for a client to address a
slice outside the bounds:

Buffer.Data (Buffer.First - 10 .. Buffer.Last + 15)

But there is of course also the risk of over-engineering the buffer,
so I will settle with the above design.

> 
> -- 
> Nick Roberts

- Mark Lorenzen



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

* Re: Converting access values
  2005-01-07 19:49       ` Mark Lorenzen
@ 2005-01-07 20:23         ` Nick Roberts
  2005-01-07 21:23           ` Robert A Duff
  2005-01-07 21:17         ` Randy Brukardt
  1 sibling, 1 reply; 20+ messages in thread
From: Nick Roberts @ 2005-01-07 20:23 UTC (permalink / raw)


Mark Lorenzen <mark.lorenzen@ofir.dk> wrote:

> As I said in another posting, I am going for a design like this:
> 
> type Buffer_Slice is
>    record
>      Data  : Buffer_Ptr;
>      First : Ada.Streams.Stream_element_Offset;
>      Last  : Ada.Streams.Stream_element_Offset;
>    end record;
> ...
> It solves the problem, but exposes a bit more information to the clients
> than necessary. It is very easy for a client to address a slice outside
> the bounds:
> ...

Since you put it that way, I see your point. I suppose it is an area where
Ada fails to provide a level of abstraction that perhaps it should. It's
almost as if we want a new kind of access type, the 'slice access' type,
that can point to a slice of an array, e.g.:

   type Buffer_Slice is access range Stream_Element_Array;

objects of which, when dereferenced, can be used anywhere a
Stream_Element_Array object can, and have the same attributes (First and
Last and so on).

-- 
Nick Roberts



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

* Re: Converting access values
  2005-01-07 19:49       ` Mark Lorenzen
  2005-01-07 20:23         ` Nick Roberts
@ 2005-01-07 21:17         ` Randy Brukardt
  2005-01-07 22:15           ` Robert A Duff
  1 sibling, 1 reply; 20+ messages in thread
From: Randy Brukardt @ 2005-01-07 21:17 UTC (permalink / raw)


"Mark Lorenzen" <mark.lorenzen@ofir.dk> wrote in message
news:m31xcxdnoz.fsf@0x53586c58.boanxx18.adsl-dhcp.tele.dk...
...
...
> It solves the problem, but exposes a bit more information to the
> clients than necessary. It is very easy for a client to address a
> slice outside the bounds:
>
> Buffer.Data (Buffer.First - 10 .. Buffer.Last + 15)
>
> But there is of course also the risk of over-engineering the buffer,
> so I will settle with the above design.

I understand, but you had said that performance was critically important
here. When that's the case (and remember that it is very rare that it is),
you have to toss abstraction and OOP and all of that other good stuff that
eats ups a bit of performance in favor of getting the job done. So I
wouldn't worry too much about that small safety hole. (It's perfectly
reasonable to look for a solution that doesn't give up that stuff, but silly
to obsess about it.) The important thing is that the code with the guts
hanging out be limited to a small, critical part of the system, and it seems
that you are doing that.

                            Randy.







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

* Re: Converting access values
  2005-01-07 20:23         ` Nick Roberts
@ 2005-01-07 21:23           ` Robert A Duff
  2005-01-11 17:02             ` Upkeep
  0 siblings, 1 reply; 20+ messages in thread
From: Robert A Duff @ 2005-01-07 21:23 UTC (permalink / raw)


Nick Roberts <nick.roberts@acm.org> writes:

> Mark Lorenzen <mark.lorenzen@ofir.dk> wrote:
> 
> > As I said in another posting, I am going for a design like this:
> > 
> > type Buffer_Slice is
> >    record
> >      Data  : Buffer_Ptr;
> >      First : Ada.Streams.Stream_element_Offset;
> >      Last  : Ada.Streams.Stream_element_Offset;
> >    end record;
> > ...
> > It solves the problem, but exposes a bit more information to the clients
> > than necessary. It is very easy for a client to address a slice outside
> > the bounds:
> > ...

To avoid that exposure, you can make the Buffer_Slice type private,
and provide the appropriate operations as subprograms.
The Buffer_Slices package can easily enforce the desired bounds checking.
Unfortunately, you then lose all the nice notations for dealing
with arrays -- you have to say "Set_Nth_Element(X, I, Blah)"
instead of "X(I) := Blah", or whatever.

> Since you put it that way, I see your point. I suppose it is an area where
> Ada fails to provide a level of abstraction that perhaps it should. It's
> almost as if we want a new kind of access type, the 'slice access' type,
> that can point to a slice of an array, e.g.:
> 
>    type Buffer_Slice is access range Stream_Element_Array;
> 
> objects of which, when dereferenced, can be used anywhere a
> Stream_Element_Array object can, and have the same attributes (First and
> Last and so on).

I don't agree with that approach.  The underlying problem here is that
private types cannot be made to mimic the predefined syntax.  I can
think of dozens of abstractions that are like arrays, where one would
like to use array-like syntax.  The Buffer_Slice thing that Mark
Lorenzen wants is just one example.  Sure, you could add that to the
language directly, but then you haven't solved the general problem.
The "right" solution, in my opinion, is to extend the "user-defined
operators" idea to allow user-defined array-indexing, literals,
aggregates, &c.

A similar issue: Ada does not support arbitrary-range integers.
Of course you can define a package to do it.  And you can define "+",
"*", &c to do the right thing.  But then you lose constrained subtypes,
case statements, integer literals, &c., all of which work fine on the
*predefined* integers.

- Bob



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

* Re: Converting access values
  2005-01-07 21:17         ` Randy Brukardt
@ 2005-01-07 22:15           ` Robert A Duff
  0 siblings, 0 replies; 20+ messages in thread
From: Robert A Duff @ 2005-01-07 22:15 UTC (permalink / raw)


"Randy Brukardt" <randy@rrsoftware.com> writes:

> "Mark Lorenzen" <mark.lorenzen@ofir.dk> wrote in message
> news:m31xcxdnoz.fsf@0x53586c58.boanxx18.adsl-dhcp.tele.dk...
> ...
> ...
> > It solves the problem, but exposes a bit more information to the
> > clients than necessary. It is very easy for a client to address a
> > slice outside the bounds:
> >
> > Buffer.Data (Buffer.First - 10 .. Buffer.Last + 15)
> >
> > But there is of course also the risk of over-engineering the buffer,
> > so I will settle with the above design.
> 
> I understand, but you had said that performance was critically important
> here. When that's the case (and remember that it is very rare that it is),
> you have to toss abstraction and OOP and all of that other good stuff that
> eats ups a bit of performance in favor of getting the job done. So I
> wouldn't worry too much about that small safety hole. (It's perfectly
> reasonable to look for a solution that doesn't give up that stuff, but silly
> to obsess about it.) The important thing is that the code with the guts
> hanging out be limited to a small, critical part of the system, and it seems
> that you are doing that.

Right.  And one can ameliorate the problem with a coding convention:
At the start of each procedure that messes with these things,
declare:

    Buf: ... renames Buffer.Data(Buffer.First..Buffer.Last);

and then refer only to Buf in there.

- Bob



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

* Re: Converting access values
  2005-01-07 21:23           ` Robert A Duff
@ 2005-01-11 17:02             ` Upkeep
  2005-01-11 21:37               ` Robert A Duff
  0 siblings, 1 reply; 20+ messages in thread
From: Upkeep @ 2005-01-11 17:02 UTC (permalink / raw)


Robert A Duff wrote:

> ... The underlying problem here is that
> private types cannot be made to mimic the predefined syntax. I can
> think of dozens of abstractions that are like arrays, where one would
> like to use array-like syntax. The Buffer_Slice thing that Mark
> Lorenzen wants is just one example. Sure, you could add that to the
> language directly, but then you haven't solved the general problem.
> The "right" solution, in my opinion, is to extend the "user-defined
> operators" idea to allow user-defined array-indexing, literals,
> aggregates, &c.
>
> A similar issue: Ada does not support arbitrary-range integers.
> Of course you can define a package to do it. And you can define "+",
> "*", &c to do the right thing. But then you lose constrained
subtypes,
> case statements, integer literals, &c., all of which work fine on the
> *predefined* integers.

I think that this problem can be solved by introduction of appropriate
attributes. Something like this:

1) for literals:

for T'Literal_Conversion use F;

where F takes a literal (integer, string, etc) and returns T.

2) for array-indexing, constrained subtypes and case statements:

for T'As_Discrete use F;

where F takes an object of type T and returns a value of some discrete
type.

All that means that a feature of this kind (literals, indexing etc.) is
provided for a particular private type if and only if the corresponding
attribute is defined by appropriate "for ... use" definition for that
type.

Naturally, there arises the question of static (that is, compile-time)
evaluation of those attributes (although they are sensible and useful
even without this opportunity).  Well, there may be some restrictions
for the functions that implement those attributes - that permit (or
facilitate static evaluation of these functions. And if those
restrictions aren't satisfied then compiler can issue a warning,
telling programmer that all relevant checks/transformations will be
done dynamically (this warning may be suppressed by appropriate
pragma).

I'm not sure about wishes for aggregates, but I think that if all
objects of the type T have the same size and this size is statically
defined then the only problem may be that this size just is not known
in right place because of visibility rules. But if it is not visible
there then the use of access is essentially forced, because any other
solution will violate (directly or indirectly) encapsulation of the
private type to some degree.





Alexander Kopilovich                      aek@vib.usr.pu.ru
Saint-Petersburg
Russia




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

* Re: Converting access values
  2005-01-11 17:02             ` Upkeep
@ 2005-01-11 21:37               ` Robert A Duff
  2005-01-12  4:56                 ` Alexander E. Kopilovich
  2005-01-12 10:48                 ` Dmitry A. Kazakov
  0 siblings, 2 replies; 20+ messages in thread
From: Robert A Duff @ 2005-01-11 21:37 UTC (permalink / raw)


"Upkeep" <aek@vib.usr.pu.ru> writes:

> 1) for literals:
> 
> for T'Literal_Conversion use F;
> 
> where F takes a literal (integer, string, etc) and returns T.

Yeah, something like that is what I had in mind.

> 2) for array-indexing, constrained subtypes and case statements:
> 
> for T'As_Discrete use F;
> 
> where F takes an object of type T and returns a value of some discrete
> type.

I don't think that quite works.  For example, I don't see how it allows
me to write a case statement on an expression of type
Arbitrary_Range_Integer -- I can't merely convert it to Standard.Integer
and do the case on that.

> Naturally, there arises the question of static (that is, compile-time)
> evaluation of those attributes (although they are sensible and useful
> even without this opportunity).  Well, there may be some restrictions
> for the functions that implement those attributes - that permit (or
> facilitate static evaluation of these functions. And if those
> restrictions aren't satisfied then compiler can issue a warning,
> telling programmer that all relevant checks/transformations will be
> done dynamically (this warning may be suppressed by appropriate
> pragma).

If I were designing such a language, I think I would place restrictions
on the T'Literal_Conversion thing so that it *can* be evaluated at
compile time.  Not a suppressable warning, but a normal legality rule.

That's because I don't want an innocuous looking thing like:

    123

to be able to raise an exception at run time.  If there's something
wrong with that literal, I want a compile-time error.

> I'm not sure about wishes for aggregates, but I think that if all
> objects of the type T have the same size and this size is statically
> defined then the only problem may be that this size just is not known
> in right place because of visibility rules. But if it is not visible
> there then the use of access is essentially forced, because any other
> solution will violate (directly or indirectly) encapsulation of the
> private type to some degree.

I've lost you there.  I don't see why it has anything to do with size.
I was thinking of something like:

    for T'Aggregate use F;

where F takes whatever parameters it likes, and does whatever it likes,
and returns an object of type T.  Then:

    X := (A, B, C);

(I'd prefer [A, B, C], actually) would simply be a shorthand for:

    X := F(A, B, C);

- Bob



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

* Re: Converting access values
  2005-01-11 21:37               ` Robert A Duff
@ 2005-01-12  4:56                 ` Alexander E. Kopilovich
  2005-01-12 10:48                 ` Dmitry A. Kazakov
  1 sibling, 0 replies; 20+ messages in thread
From: Alexander E. Kopilovich @ 2005-01-12  4:56 UTC (permalink / raw)
  To: comp.lang.ada

Robert A Duff wrote:

> > 2) for array-indexing, constrained subtypes and case statements:
> > 
> > for T'As_Discrete use F;
> > 
> > where F takes an object of type T and returns a value of some discrete
> > type.
>
> I don't think that quite works.  For example, I don't see how it allows
> me to write a case statement on an expression of type
> Arbitrary_Range_Integer -- I can't merely convert it to Standard.Integer
> and do the case on that.

Well. I thought about the case where the expression at the top is of some
discrete type, and only particular branches are related to the private type.

But if the expression at the top is of the private type then there can be
ambiguity - generally there can be several different reductions of the
private type to different discrete types (that is, with separate "for...use"
statement for each). Therefore a particular reduction must be chosen for
the expression at the top - the corresponding discrete type must be given
there. I think that syntactically usual type conversion notation may be the
best choice there.

> > Naturally, there arises the question of static (that is, compile-time)
> > evaluation of those attributes (although they are sensible and useful
> > even without this opportunity).  Well, there may be some restrictions
> > for the functions that implement those attributes - that permit (or
> > facilitate static evaluation of these functions. And if those
> > restrictions aren't satisfied then compiler can issue a warning,
> > telling programmer that all relevant checks/transformations will be
> > done dynamically (this warning may be suppressed by appropriate
> > pragma).
>
> If I were designing such a language, I think I would place restrictions
> on the T'Literal_Conversion thing so that it *can* be evaluated at
> compile time.  Not a suppressable warning, but a normal legality rule.
>
> That's because I don't want an innocuous looking thing like:
>
>    123
>
> to be able to raise an exception at run time.  If there's something
> wrong with that literal, I want a compile-time error.

Well, I'm not sure about that, maybe you are right. But anyway, I think
that it will be useful to annotate this feature of a function - that it can
be evaluated in compile-time - explicitly, by use of 'constant' keyword:
 
  ... return constant T;

> > I'm not sure about wishes for aggregates, but I think that if all
> > objects of the type T have the same size and this size is statically
> > defined then the only problem may be that this size just is not known
> > in right place because of visibility rules. But if it is not visible
> > there then the use of access is essentially forced, because any other
> > solution will violate (directly or indirectly) encapsulation of the
> > private type to some degree.
>
> I've lost you there.  I don't see why it has anything to do with size.

Well, as I said, I was unsure about the wishes for the aggregates; I guessed
that you meant using an object of the private type as a member of a record,
and apparently this my guess was wrong - your "aggregate" was a verb, not
a noun -;) 

> I was thinking of something like:
>
>    for T'Aggregate use F;
>
> where F takes whatever parameters it likes, and does whatever it likes,
> and returns an object of type T.  Then:
>
>    X := (A, B, C);
>
> (I'd prefer [A, B, C], actually) would simply be a shorthand for:
>
>    X := F(A, B, C);

Well, no problem with it, except one: if F can do anything than why we are
thinking of it as of an "aggregation"? It is more like "computation" rather
than "aggregation".

I think that there should be some severe restrictions for F there, which
will justify use of the term and the notation. For example, there may be
following restriction:

   T is a record and the parameters of F correspond to the members of this
   record

But this seems, perhaps, too severe restriction... maybe it should be somehow
relaxed. It depends on intended uses, and I don't see them clear enough.




Alexander Kopilovich                      aek@vib.usr.pu.ru
Saint-Petersburg
Russia





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

* Re: Converting access values
  2005-01-11 21:37               ` Robert A Duff
  2005-01-12  4:56                 ` Alexander E. Kopilovich
@ 2005-01-12 10:48                 ` Dmitry A. Kazakov
  1 sibling, 0 replies; 20+ messages in thread
From: Dmitry A. Kazakov @ 2005-01-12 10:48 UTC (permalink / raw)


On 11 Jan 2005 16:37:18 -0500, Robert A Duff wrote:

> I was thinking of something like:
> 
>     for T'Aggregate use F;
> 
> where F takes whatever parameters it likes, and does whatever it likes,
> and returns an object of type T.  Then:
> 
>     X := (A, B, C);
> 
> (I'd prefer [A, B, C], actually) would simply be a shorthand for:
> 
>     X := F(A, B, C);

And what to do with array aggregates? I think the issue is not that simple.
I would first introduce a class of index types to make ranges, "..", "in"
and "not in" first-class citizens. Then I would use an index type in the
aggregate declaration instead of a subroutine name. That would cover
array-like aggregates when index is numeric and function-like aggregates
when index is an enumeration.

But how to deal with that in-place? Array-like aggregates would require
triple copying in an object initialization. That's much too much.

Then what about class-wide aggregates vs. specific aggregates, dispatching
aggregates and all that stuff? Consider:

type Foo is tagged ...;
type Baz is tagged ...;
function F (X : Baz) return Foo'Class;
for Foo'Class'Aggregate use F; -- What should it mean? (:-))
type Bar is new Foo with private; -- Has Bar or Bar'Class any aggregates?

There is a lot to discuss to get it right. [But it should be done, of
course]

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



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

end of thread, other threads:[~2005-01-12 10:48 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-01-05 22:31 Converting access values Mark Lorenzen
2005-01-05 23:32 ` Stephen Leake
2005-01-05 23:51   ` Mark Lorenzen
2005-01-06  0:18 ` Jeffrey Carter
2005-01-06  0:28   ` Mark Lorenzen
2005-01-07 16:55     ` Nick Roberts
2005-01-07 19:49       ` Mark Lorenzen
2005-01-07 20:23         ` Nick Roberts
2005-01-07 21:23           ` Robert A Duff
2005-01-11 17:02             ` Upkeep
2005-01-11 21:37               ` Robert A Duff
2005-01-12  4:56                 ` Alexander E. Kopilovich
2005-01-12 10:48                 ` Dmitry A. Kazakov
2005-01-07 21:17         ` Randy Brukardt
2005-01-07 22:15           ` Robert A Duff
2005-01-06 10:52 ` Dmitry A. Kazakov
2005-01-06 11:02 ` Duncan Sands
2005-01-06 12:17   ` Martin Dowie
2005-01-06 19:30   ` Mark Lorenzen
2005-01-06 20:40     ` Randy Brukardt

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