* 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-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 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: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
* 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 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-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
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