* Getting the index for an element in mutually referencing containers
@ 2017-03-09 13:45 Mart van de Wege
2017-03-09 15:25 ` Egil H H
[not found] ` <ly7f3xedp4.fsf@pushface.org>
0 siblings, 2 replies; 33+ messages in thread
From: Mart van de Wege @ 2017-03-09 13:45 UTC (permalink / raw)
I am missing another thing: I managed to boil down the problem to the
PoC code below. I want to have two objects, each with a container as a
record attribute, referring to each other, and then merely get the index
value of one of the two containers. Yet if I do, I get a Storage_Error
exception.
Code:
with Ada.Containers.Indefinite_Vectors;
with Ada.Containers.Indefinite_Doubly_Linked_Lists;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Test_Container is
type Abstract_Person is abstract tagged null record;
package Offspring is new Ada.Containers.Indefinite_Vectors
(Index_Type => Positive,
Element_Type => Abstract_Person'Class);
package Group is new Ada.Containers.Indefinite_Doubly_Linked_Lists
(Element_Type => Abstract_Person'Class);
type Person is new Abstract_Person with record
Children : Offspring.Vector := Offspring.To_Vector(4);
Parents : Group.List;
end record;
Father : Person;
Child : Person;
begin
Child.Parents.Prepend(Father);
Father.Children.Append(Child);
Put(Integer(Father.Children.Find_Index(Child)));
end Test_Container;
This compiles OK, but on runtime throws an exception:
raised STORAGE_ERROR : stack overflow or erroneous memory access
Now, cyclic dependencies are tricky, so I am sure I am doing something
wrong, but I can't for the life of me see what.
Mart
--
"We will need a longer wall when the revolution comes."
--- AJS, quoting an uncertain source.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-09 13:45 Getting the index for an element in mutually referencing containers Mart van de Wege
@ 2017-03-09 15:25 ` Egil H H
2017-03-09 15:45 ` Mart van de Wege
[not found] ` <ly7f3xedp4.fsf@pushface.org>
1 sibling, 1 reply; 33+ messages in thread
From: Egil H H @ 2017-03-09 15:25 UTC (permalink / raw)
On Thursday, March 9, 2017 at 2:50:03 PM UTC+1, Mart van de Wege wrote:
>
> Children : Offspring.Vector := Offspring.To_Vector(4);
>
> Put(Integer(Father.Children.Find_Index(Child)));
To_Vector adds empty elements to your list of Children, so Find_Index fails (probably dereferencing null or other erroneouse memory access) when trying to compare them to the Child. Removing the call to To_Vector seems to work as expected...
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-09 15:25 ` Egil H H
@ 2017-03-09 15:45 ` Mart van de Wege
2017-03-09 16:02 ` Mart van de Wege
2017-03-09 16:11 ` Egil H H
0 siblings, 2 replies; 33+ messages in thread
From: Mart van de Wege @ 2017-03-09 15:45 UTC (permalink / raw)
Egil H H <ehh.public@gmail.com> writes:
> On Thursday, March 9, 2017 at 2:50:03 PM UTC+1, Mart van de Wege wrote:
>>
>> Children : Offspring.Vector := Offspring.To_Vector(4);
>>
>> Put(Integer(Father.Children.Find_Index(Child)));
>
>
> To_Vector adds empty elements to your list of Children, so Find_Index
> fails (probably dereferencing null or other erroneouse memory access)
> when trying to compare them to the Child. Removing the call to
> To_Vector seems to work as expected...
Aargh. So Find and Find_Index don't just skip empty elements, but die on
them? *That* was not clear from my reading of the spec.
And yes, in actual practice I expect to have sparse Vectors with a few
gaps not holding an Element. So how do I deal with this?
Mart
--
"We will need a longer wall when the revolution comes."
--- AJS, quoting an uncertain source.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-09 15:45 ` Mart van de Wege
@ 2017-03-09 16:02 ` Mart van de Wege
2017-03-09 16:11 ` Egil H H
1 sibling, 0 replies; 33+ messages in thread
From: Mart van de Wege @ 2017-03-09 16:02 UTC (permalink / raw)
Mart van de Wege <mvdwege@gmail.com> writes:
> Egil H H <ehh.public@gmail.com> writes:
>
>> On Thursday, March 9, 2017 at 2:50:03 PM UTC+1, Mart van de Wege wrote:
>>>
>>> Children : Offspring.Vector := Offspring.To_Vector(4);
>>>
>>> Put(Integer(Father.Children.Find_Index(Child)));
>>
>>
>> To_Vector adds empty elements to your list of Children, so Find_Index
>> fails (probably dereferencing null or other erroneouse memory access)
>> when trying to compare them to the Child. Removing the call to
>> To_Vector seems to work as expected...
>
> Aargh. So Find and Find_Index don't just skip empty elements, but die on
> them? *That* was not clear from my reading of the spec.
>
> And yes, in actual practice I expect to have sparse Vectors with a few
> gaps not holding an Element. So how do I deal with this?
>
Hmm. Given that Person objects and their derivatives aren't very large,
I could just prefill the Vector with Person objects. As long as I don't
set any of their parameters, they will represent *potential* siblings.
This feels like a hack though.
Mart
--
"We will need a longer wall when the revolution comes."
--- AJS, quoting an uncertain source.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-09 15:45 ` Mart van de Wege
2017-03-09 16:02 ` Mart van de Wege
@ 2017-03-09 16:11 ` Egil H H
1 sibling, 0 replies; 33+ messages in thread
From: Egil H H @ 2017-03-09 16:11 UTC (permalink / raw)
On Thursday, March 9, 2017 at 4:50:04 PM UTC+1, Mart van de Wege wrote:
>
> Aargh. So Find and Find_Index don't just skip empty elements, but die on
> them? *That* was not clear from my reading of the spec.
>
Because it's a bug... And it seems to be fixed in (at least) Gnat Pro 17.0
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
[not found] ` <o9vcbp$t0t$1@franka.jacob-sparre.dk>
@ 2017-03-11 6:45 ` Mart van de Wege
2017-03-11 8:40 ` Simon Wright
1 sibling, 0 replies; 33+ messages in thread
From: Mart van de Wege @ 2017-03-11 6:45 UTC (permalink / raw)
"Randy Brukardt" <randy@rrsoftware.com> writes:
> "Mart van de Wege" <mvdwege@gmail.com> wrote in message
> news:86wpbxneuz.fsf@gaheris.avalon.lan...
>> Simon Wright <simon@pushface.org> writes:
> ...
>>> But, I really think you're going to need to use access types here. A
>>> Person is a unique object, and should only exist once in your data
>>> structures. A Child should hold references to its Parent(s) and vice
>>> versa; if a Child holds *copies* of its Parents, what happens when you
>>> change the Parent's data in one place? how are you going to find all the
>>> copies and change all of them?
>>>
>> That was what I was afraid of. I like the way Ada usually takes care of
>> this for you, but I've been trying to work it out during downtime today
>> at work, and I'm afraid it's indeed time to grab the access types out of
>> the toolbox.
>
> The trick in such cases is to have a global "person" vector, which contains
> all of the actual Person objects. Then use the cursors into that array in
> your other data structures. That's gives the needed level of indirection
> without involving any actual access types (meaning that you let Ada do the
> storage management, and depending on your container implementation, also
> reduce/eliminate the danger of dangling access types).
>
Hmm. Given that any roleplaying *campaign*, let alone a single session,
will not have a lot of characters to make that a memory issue, and that
characters are global to the campaign anyway, that makes a good deal of
sense.
Mart
--
"We will need a longer wall when the revolution comes."
--- AJS, quoting an uncertain source.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
[not found] ` <o9vcbp$t0t$1@franka.jacob-sparre.dk>
2017-03-11 6:45 ` Mart van de Wege
@ 2017-03-11 8:40 ` Simon Wright
2017-03-11 8:58 ` Dmitry A. Kazakov
2017-03-12 1:36 ` Randy Brukardt
1 sibling, 2 replies; 33+ messages in thread
From: Simon Wright @ 2017-03-11 8:40 UTC (permalink / raw)
"Randy Brukardt" <randy@rrsoftware.com> writes:
> "Mart van de Wege" <mvdwege@gmail.com> wrote in message
> news:86wpbxneuz.fsf@gaheris.avalon.lan...
>> Simon Wright <simon@pushface.org> writes:
> ...
>>> But, I really think you're going to need to use access types here. A
>>> Person is a unique object, and should only exist once in your data
>>> structures. A Child should hold references to its Parent(s) and vice
>>> versa; if a Child holds *copies* of its Parents, what happens when
>>> you change the Parent's data in one place? how are you going to find
>>> all the copies and change all of them?
>>>
>> That was what I was afraid of. I like the way Ada usually takes care
>> of this for you, but I've been trying to work it out during downtime
>> today at work, and I'm afraid it's indeed time to grab the access
>> types out of the toolbox.
>
> The trick in such cases is to have a global "person" vector, which
> contains all of the actual Person objects. Then use the cursors into
> that array in your other data structures. That's gives the needed
> level of indirection without involving any actual access types
> (meaning that you let Ada do the storage management, and depending on
> your container implementation, also reduce/eliminate the danger of
> dangling access types).
In the general case, you'd need to cope with deleting (the equivalent
of) Person objects.
And wouldn't even appending a new Person risk invalidating any existing
cursors? AARM A18(5.k) says "Cursors *generally* remain valid as long as
the container exists and the element referenced is not deleted",
emphasis added. AdaCore's Vector implementation would be OK with
inserting elements _after_ the Cursor concerned, since the Cursor is
based on an index into an array. See discussion at AARM A18.2(240) ff.
I quite like the idea of using an indefinite container to hold the
Person objects, but I think I'd use a Map with the key an integer
(Natural?), with the next key held in a private global, incremented as
each new Person is created. The reference to a Person would be the
associated key.
One disadvantage to this design is that Containers hold non-limited
types. Your users run the risk of writing code that gets hold of a copy
of the object and modifies that, rather than modifying the actual
object.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-11 8:40 ` Simon Wright
@ 2017-03-11 8:58 ` Dmitry A. Kazakov
2017-03-11 11:21 ` Simon Wright
2017-03-12 1:36 ` Randy Brukardt
1 sibling, 1 reply; 33+ messages in thread
From: Dmitry A. Kazakov @ 2017-03-11 8:58 UTC (permalink / raw)
On 2017-03-11 09:40, Simon Wright wrote:
> "Randy Brukardt" <randy@rrsoftware.com> writes:
>
>> "Mart van de Wege" <mvdwege@gmail.com> wrote in message
>> news:86wpbxneuz.fsf@gaheris.avalon.lan...
>>> Simon Wright <simon@pushface.org> writes:
>> ...
>>>> But, I really think you're going to need to use access types here. A
>>>> Person is a unique object, and should only exist once in your data
>>>> structures. A Child should hold references to its Parent(s) and vice
>>>> versa; if a Child holds *copies* of its Parents, what happens when
>>>> you change the Parent's data in one place? how are you going to find
>>>> all the copies and change all of them?
>>>>
>>> That was what I was afraid of. I like the way Ada usually takes care
>>> of this for you, but I've been trying to work it out during downtime
>>> today at work, and I'm afraid it's indeed time to grab the access
>>> types out of the toolbox.
>>
>> The trick in such cases is to have a global "person" vector, which
>> contains all of the actual Person objects. Then use the cursors into
>> that array in your other data structures. That's gives the needed
>> level of indirection without involving any actual access types
>> (meaning that you let Ada do the storage management, and depending on
>> your container implementation, also reduce/eliminate the danger of
>> dangling access types).
>
> In the general case, you'd need to cope with deleting (the equivalent
> of) Person objects.
>
> And wouldn't even appending a new Person risk invalidating any existing
> cursors? AARM A18(5.k) says "Cursors *generally* remain valid as long as
> the container exists and the element referenced is not deleted",
> emphasis added. AdaCore's Vector implementation would be OK with
> inserting elements _after_ the Cursor concerned, since the Cursor is
> based on an index into an array. See discussion at AARM A18.2(240) ff.
>
> I quite like the idea of using an indefinite container to hold the
> Person objects, but I think I'd use a Map with the key an integer
> (Natural?), with the next key held in a private global, incremented as
> each new Person is created. The reference to a Person would be the
> associated key.
This looks like a clear case for reference counted objects. A handle to
Person to be put into containers (for search) and into other objects for
referencing. No problem with limited types. Weak handles are for
backward referencing and so on.
I often start with a container only to be forced to switch to reference
counting later, especially when the objects are mutable. There is no
good containers for mutable types except for built-in arrays.
> One disadvantage to this design is that Containers hold non-limited
> types. Your users run the risk of writing code that gets hold of a copy
> of the object and modifies that, rather than modifying the actual
> object.
A transaction sort of schema can be implemented on top of reference
counting. E.g. if the count > 1 you clone the object you want to modify.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-11 8:58 ` Dmitry A. Kazakov
@ 2017-03-11 11:21 ` Simon Wright
2017-03-11 14:18 ` Dmitry A. Kazakov
0 siblings, 1 reply; 33+ messages in thread
From: Simon Wright @ 2017-03-11 11:21 UTC (permalink / raw)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> This looks like a clear case for reference counted objects. A handle
> to Person to be put into containers (for search) and into other
> objects for referencing. No problem with limited types. Weak handles
> are for backward referencing and so on.
Does that mean reference-counted _handles_? If you have multiple handles
to a particular Person object, which gets deleted, you'd want all those
handles to be invalidated.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-11 11:21 ` Simon Wright
@ 2017-03-11 14:18 ` Dmitry A. Kazakov
2017-03-11 20:05 ` Simon Wright
0 siblings, 1 reply; 33+ messages in thread
From: Dmitry A. Kazakov @ 2017-03-11 14:18 UTC (permalink / raw)
On 2017-03-11 12:21, Simon Wright wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>
>> This looks like a clear case for reference counted objects. A handle
>> to Person to be put into containers (for search) and into other
>> objects for referencing. No problem with limited types. Weak handles
>> are for backward referencing and so on.
>
> Does that mean reference-counted _handles_?
Objects are reference-counted. Handles are not.
> If you have multiple handles
> to a particular Person object, which gets deleted, you'd want all those
> handles to be invalidated.
It is in essence the difference between weak and strong references. If
you want a reference invalidated when the object vanishes that would be
a weak reference. In order to access object through weak reference you
first elevate it to a strong one. You drop the strong reference once you
used the object.
The semantics of "object deleted" is in question. Usually it is merely
throwing it out of some list (container) and thus decreasing object's
reference count. The object stays alive until nobody else uses it =
nobody holds a strong reference to it.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-11 14:18 ` Dmitry A. Kazakov
@ 2017-03-11 20:05 ` Simon Wright
2017-03-11 20:52 ` Dmitry A. Kazakov
0 siblings, 1 reply; 33+ messages in thread
From: Simon Wright @ 2017-03-11 20:05 UTC (permalink / raw)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> On 2017-03-11 12:21, Simon Wright wrote:
>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>>
>>> This looks like a clear case for reference counted objects. A handle
>>> to Person to be put into containers (for search) and into other
>>> objects for referencing. No problem with limited types. Weak handles
>>> are for backward referencing and so on.
>>
>> Does that mean reference-counted _handles_?
>
> Objects are reference-counted. Handles are not.
>
>> If you have multiple handles
>> to a particular Person object, which gets deleted, you'd want all those
>> handles to be invalidated.
>
> It is in essence the difference between weak and strong references. If
> you want a reference invalidated when the object vanishes that would
> be a weak reference. In order to access object through weak reference
> you first elevate it to a strong one. You drop the strong reference
> once you used the object.
>
> The semantics of "object deleted" is in question. Usually it is merely
> throwing it out of some list (container) and thus decreasing object's
> reference count. The object stays alive until nobody else uses it =
> nobody holds a strong reference to it.
That seems to be a very computer-oriented viewpoint. If your objects
represent real-world objects (or roles, or events - OOA view) then you
wouldn't want them to hang around! If you decide to abort an engagement
and therefore delete the Missile object, whose finalization sends a
self-destruct command to the corresponding missile in flight, you want
it to happen Right Now.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-11 20:05 ` Simon Wright
@ 2017-03-11 20:52 ` Dmitry A. Kazakov
2017-03-11 21:46 ` Simon Wright
0 siblings, 1 reply; 33+ messages in thread
From: Dmitry A. Kazakov @ 2017-03-11 20:52 UTC (permalink / raw)
On 2017-03-11 21:05, Simon Wright wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>
>> On 2017-03-11 12:21, Simon Wright wrote:
>>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>>>
>>>> This looks like a clear case for reference counted objects. A handle
>>>> to Person to be put into containers (for search) and into other
>>>> objects for referencing. No problem with limited types. Weak handles
>>>> are for backward referencing and so on.
>>>
>>> Does that mean reference-counted _handles_?
>>
>> Objects are reference-counted. Handles are not.
>>
>>> If you have multiple handles
>>> to a particular Person object, which gets deleted, you'd want all those
>>> handles to be invalidated.
>>
>> It is in essence the difference between weak and strong references. If
>> you want a reference invalidated when the object vanishes that would
>> be a weak reference. In order to access object through weak reference
>> you first elevate it to a strong one. You drop the strong reference
>> once you used the object.
>>
>> The semantics of "object deleted" is in question. Usually it is merely
>> throwing it out of some list (container) and thus decreasing object's
>> reference count. The object stays alive until nobody else uses it =
>> nobody holds a strong reference to it.
>
> That seems to be a very computer-oriented viewpoint. If your objects
> represent real-world objects (or roles, or events - OOA view) then you
> wouldn't want them to hang around! If you decide to abort an engagement
> and therefore delete the Missile object, whose finalization sends a
> self-destruct command to the corresponding missile in flight, you want
> it to happen Right Now.
If you have control over it. In the real world, which includes real
programs, you might have none and there is no such thing as "now" anyway.
Then I would say that rather your idea is computer-oriented. Objects are
given us in their public operations. Once you cannot apply any to the
object, or in other semantics, if Object.Exists yields False, there is
no object. In terms of missiles, when you see a cloud of explosion, the
work is done. You drop the last your reference and don't care if the
object transited into another dimension or some object paradise. If you
don't see it by any programmatic means, then there is none.
P.S. The idea that the world consists of objects is the symbol of faith
of the Church of Object-Orientation...
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-11 20:52 ` Dmitry A. Kazakov
@ 2017-03-11 21:46 ` Simon Wright
2017-03-11 22:37 ` Niklas Holsti
2017-03-12 8:20 ` Dmitry A. Kazakov
0 siblings, 2 replies; 33+ messages in thread
From: Simon Wright @ 2017-03-11 21:46 UTC (permalink / raw)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> On 2017-03-11 21:05, Simon Wright wrote:
>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>>
>>> On 2017-03-11 12:21, Simon Wright wrote:
>>>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>>> The semantics of "object deleted" is in question. Usually it is merely
>>> throwing it out of some list (container) and thus decreasing object's
>>> reference count. The object stays alive until nobody else uses it =
>>> nobody holds a strong reference to it.
>>
>> That seems to be a very computer-oriented viewpoint. If your objects
>> represent real-world objects (or roles, or events - OOA view) then you
>> wouldn't want them to hang around! If you decide to abort an engagement
>> and therefore delete the Missile object, whose finalization sends a
>> self-destruct command to the corresponding missile in flight, you want
>> it to happen Right Now.
>
> If you have control over it. In the real world, which includes real
> programs, you might have none and there is no such thing as "now"
> anyway.
Of course not. But there is such a thing as avoiding the possibility
of references preventing deletion when deletion needs to happen as soon
as possible.
> Then I would say that rather your idea is computer-oriented. Objects
> are given us in their public operations. Once you cannot apply any to
> the object, or in other semantics, if Object.Exists yields False,
> there is no object. In terms of missiles, when you see a cloud of
> explosion, the work is done. You drop the last your reference and
> don't care if the object transited into another dimension or some
> object paradise. If you don't see it by any programmatic means, then
> there is none.
I (a software Missile object) have been told to delete myself. In my
finalization, I must send a command to my real-world equivalent telling
it to destroy itself. I only need to send it once, because
communications aren't my responsibility (and if comms are down nothing
can be done anyway).
Basically, I (Simon, now) am having trouble thinking of an application
where reference counting would be an appropriate solution.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-11 21:46 ` Simon Wright
@ 2017-03-11 22:37 ` Niklas Holsti
2017-03-12 8:22 ` Simon Wright
2017-03-12 8:20 ` Dmitry A. Kazakov
1 sibling, 1 reply; 33+ messages in thread
From: Niklas Holsti @ 2017-03-11 22:37 UTC (permalink / raw)
On 17-03-11 23:46 , Simon Wright wrote:
> Basically, I (Simon, now) am having trouble thinking of an application
> where reference counting would be an appropriate solution.
How about this one: the SW generates messages (telemetry packets)
reporting various sorts of data and events.
Depending on the kind of the message, the same message may have to be
distributed within the SW to several destinations: a radio link, a log
file (mass memory), a message-monitoring service (for possible
autonomous action), etc.
Each destination has a queue of incoming messages, and the queueing and
processing time for a given message varies accordingly. Rather than copy
the (possibly long) message into each destination's queue, the queues
hold references to a single, shared instance of the message, dynamically
allocated in a memory pool. These references are counted. When, finally,
all destinations have processed the message, the message's reference
count reaches zero, and the message can be deallocated.
This use of reference counting is a typical design in satellite on-board SW.
--
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
. @ .
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-11 8:40 ` Simon Wright
2017-03-11 8:58 ` Dmitry A. Kazakov
@ 2017-03-12 1:36 ` Randy Brukardt
1 sibling, 0 replies; 33+ messages in thread
From: Randy Brukardt @ 2017-03-12 1:36 UTC (permalink / raw)
"Simon Wright" <simon@pushface.org> wrote in message
news:ly37ekcikw.fsf@pushface.org...
> "Randy Brukardt" <randy@rrsoftware.com> writes:
>
>> "Mart van de Wege" <mvdwege@gmail.com> wrote in message
>> news:86wpbxneuz.fsf@gaheris.avalon.lan...
>>> Simon Wright <simon@pushface.org> writes:
>> ...
>>>> But, I really think you're going to need to use access types here. A
>>>> Person is a unique object, and should only exist once in your data
>>>> structures. A Child should hold references to its Parent(s) and vice
>>>> versa; if a Child holds *copies* of its Parents, what happens when
>>>> you change the Parent's data in one place? how are you going to find
>>>> all the copies and change all of them?
>>>>
>>> That was what I was afraid of. I like the way Ada usually takes care
>>> of this for you, but I've been trying to work it out during downtime
>>> today at work, and I'm afraid it's indeed time to grab the access
>>> types out of the toolbox.
>>
>> The trick in such cases is to have a global "person" vector, which
>> contains all of the actual Person objects. Then use the cursors into
>> that array in your other data structures. That's gives the needed
>> level of indirection without involving any actual access types
>> (meaning that you let Ada do the storage management, and depending on
>> your container implementation, also reduce/eliminate the danger of
>> dangling access types).
>
> In the general case, you'd need to cope with deleting (the equivalent
> of) Person objects.
True, but that's the dangling cursor problem, and you're certainly no worse
off with a dangling cursor than a dangling access type. (And the container
implementation might have detection for dangling cursors, that's not likely
with access types.)
> And wouldn't even appending a new Person risk invalidating any existing
> cursors? AARM A18(5.k) says "Cursors *generally* remain valid as long as
> the container exists and the element referenced is not deleted",
> emphasis added. AdaCore's Vector implementation would be OK with
> inserting elements _after_ the Cursor concerned, since the Cursor is
> based on an index into an array. See discussion at AARM A18.2(240) ff.
With the exception of the Vector container, cursors always stay valid
through operations on the container. They continue to point at the same
element no matter what insertions/deletions occur, until of course the
container itself is destroyed. (Generally, one can't reliably detect cursors
that point into a destroyed container [since all of the data structures have
been recycled], but that is a lot less likely problem than a cursor that
points at a deleted element of a still-existing container.)
The Vector container allows "sliding" of elements, which may (or may not)
cause a cursor to become invalid. But that can only happen with an
insertion/deletion *in front* of the cursor -- never with Append. There's a
bunch of special rules in the RM for that case.
If keeping the cursor stable is a priority, I'd use a list or map container
rather than a vector. For "Person", a map seems likely (you'd want to be
able to look them up by name, I'd think).
> I quite like the idea of using an indefinite container to hold the
> Person objects, but I think I'd use a Map with the key an integer
> (Natural?), with the next key held in a private global, incremented as
> each new Person is created. The reference to a Person would be the
> associated key.
That would work but would probably be extra work for no real reason. The
only real advantage that I can see is that you'd get real dangling reference
detection with that (without depending upon the container implementation).
YMMV, of course.
> One disadvantage to this design is that Containers hold non-limited
> types. Your users run the risk of writing code that gets hold of a copy
> of the object and modifies that, rather than modifying the actual
> object.
Right. Not much that can be done about that, unfortunately. Limited types
seem to defy reusable containerization. I've tried to design such a
container, but there's always some reason that it won't work without
introducing access types.
The one thing I've done with Claw programs is to declare the type
non-limited controlled and override Adjust to raise Program_Error. [Claw
uses non-limited types, so if you have a window extension that you don't
want copied, you have to take some action.] But that won't work with the
containers, as the initial copy would raise Program_Error. If you can live
with only having default initialization, then you could modify the elements
in place in such a scenario. But that limits one to definite types.
Randy.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-11 21:46 ` Simon Wright
2017-03-11 22:37 ` Niklas Holsti
@ 2017-03-12 8:20 ` Dmitry A. Kazakov
2017-03-12 11:30 ` Simon Wright
1 sibling, 1 reply; 33+ messages in thread
From: Dmitry A. Kazakov @ 2017-03-12 8:20 UTC (permalink / raw)
On 2017-03-11 22:46, Simon Wright wrote:
> I (a software Missile object) have been told to delete myself. In my
> finalization, I must send a command to my real-world equivalent telling
> it to destroy itself.
That is OK, but you (as software designer) should also answer the
question what happens with the missile production log records. Should
the sad end of the missile erase it from the production log? Tracking
system?
As a software designer there is only one question. What is the contract
of Delete? Usually the contract is silent about when the memory is freed
or Finalization called. If you tried to put finalization into the
contract you might find very difficult to implement it without breaking
other contracts. The implementation of Ada container library serves as a
perfect illustration to this.
> Basically, I (Simon, now) am having trouble thinking of an application
> where reference counting would be an appropriate solution.
As I said, in practice it is all cases when you would have a container
of mutable elements. It is largely a language problem. But even without
that, there are cases when you would need multiple references to the
same element or resource.
E.g. in OS, there is a file object and handles to the file. When you
tell Delete to a file you do that to the handle. The file is not deleted
at once. Rather it will be "unnamed" (not instantly ether) and
physically deleted only when the last handle is closed. And even then it
could be kept for a while in the recycle bin.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-11 22:37 ` Niklas Holsti
@ 2017-03-12 8:22 ` Simon Wright
2017-03-12 9:38 ` G.B.
2017-03-13 10:29 ` Alejandro R. Mosteo
0 siblings, 2 replies; 33+ messages in thread
From: Simon Wright @ 2017-03-12 8:22 UTC (permalink / raw)
Niklas Holsti <niklas.holsti@tidorum.invalid> writes:
> On 17-03-11 23:46 , Simon Wright wrote:
>
>> Basically, I (Simon, now) am having trouble thinking of an
>> application where reference counting would be an appropriate
>> solution.
>
> How about this one: the SW generates messages (telemetry packets)
> reporting various sorts of data and events.
>
[...]
>
> Each destination has a queue of incoming messages, and the queueing
> and processing time for a given message varies accordingly. Rather
> than copy the (possibly long) message into each destination's queue,
> the queues hold references to a single, shared instance of the
> message, dynamically allocated in a memory pool. These references are
> counted. When, finally, all destinations have processed the message,
> the message's reference count reaches zero, and the message can be
> deallocated.
>
> This use of reference counting is a typical design in satellite
> on-board SW.
Thanks for the example!
I still have a feeling that this is an in-computer software technique
which resolves a software problem, rather than a necessary response to
an application-domain problem.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-12 8:22 ` Simon Wright
@ 2017-03-12 9:38 ` G.B.
2017-03-12 11:21 ` Simon Wright
2017-03-13 10:29 ` Alejandro R. Mosteo
1 sibling, 1 reply; 33+ messages in thread
From: G.B. @ 2017-03-12 9:38 UTC (permalink / raw)
On 12.03.17 09:22, Simon Wright wrote:
> Niklas Holsti <niklas.holsti@tidorum.invalid> writes:
>
>> On 17-03-11 23:46 , Simon Wright wrote:
>>
>>> Basically, I (Simon, now) am having trouble thinking of an
>>> application where reference counting would be an appropriate
>>> solution.
>>
>> How about this one: the SW generates messages (telemetry packets)
>> reporting various sorts of data and events.
>>
> [...]
>>
>> Each destination has a queue of incoming messages, and the queueing
>> and processing time for a given message varies accordingly. Rather
>> than copy the (possibly long) message into each destination's queue,
>> the queues hold references to a single, shared instance of the
>> message, dynamically allocated in a memory pool. These references are
>> counted. When, finally, all destinations have processed the message,
>> the message's reference count reaches zero, and the message can be
>> deallocated.
>>
>> This use of reference counting is a typical design in satellite
>> on-board SW.
>
> Thanks for the example!
A good one that should have a place in Ada Docs on Stackoverflow,
I think.
> I still have a feeling that this is an in-computer software technique
> which resolves a software problem, rather than a necessary response to
> an application-domain problem.
The radio operators, log inspectors, or message-monitoring staff
might feel overlooked after being changed into a software problem! ;-)
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-12 9:38 ` G.B.
@ 2017-03-12 11:21 ` Simon Wright
0 siblings, 0 replies; 33+ messages in thread
From: Simon Wright @ 2017-03-12 11:21 UTC (permalink / raw)
"G.B." <bauhaus@notmyhomepage.invalid> writes:
> On 12.03.17 09:22, Simon Wright wrote:
>> Niklas Holsti <niklas.holsti@tidorum.invalid> writes:
>>> How about this one: the SW generates messages (telemetry packets)
>>> reporting various sorts of data and events.
>>> This use of reference counting is a typical design in satellite
>>> on-board SW.
>> I still have a feeling that this is an in-computer software technique
>> which resolves a software problem, rather than a necessary response
>> to an application-domain problem.
>
> The radio operators, log inspectors, or message-monitoring staff might
> feel overlooked after being changed into a software problem! ;-)
Yes, but you could solve that by sending a copy of each message to seach
recipient, rather than a reference to a reference-counted
message. Reference counting is a solution to an internal (memory,
possibly time) resource problem.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-12 8:20 ` Dmitry A. Kazakov
@ 2017-03-12 11:30 ` Simon Wright
2017-03-12 11:55 ` Dmitry A. Kazakov
0 siblings, 1 reply; 33+ messages in thread
From: Simon Wright @ 2017-03-12 11:30 UTC (permalink / raw)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> On 2017-03-11 22:46, Simon Wright wrote:
>> Basically, I (Simon, now) am having trouble thinking of an application
>> where reference counting would be an appropriate solution.
>
> As I said, in practice it is all cases when you would have a container
> of mutable elements.
I had some trouble understanding your point the first time, and skipped
over it (sorry).
What's the opposite of "mutable"? constant? because, if so, why wouldn't
I use Update_Element? (aside from it being very clumsy; more likely to
use a reference, Person (P).Health := Unwell; )
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-12 11:30 ` Simon Wright
@ 2017-03-12 11:55 ` Dmitry A. Kazakov
2017-03-12 16:44 ` Simon Wright
0 siblings, 1 reply; 33+ messages in thread
From: Dmitry A. Kazakov @ 2017-03-12 11:55 UTC (permalink / raw)
On 2017-03-12 12:30, Simon Wright wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>
>> On 2017-03-11 22:46, Simon Wright wrote:
>
>>> Basically, I (Simon, now) am having trouble thinking of an application
>>> where reference counting would be an appropriate solution.
>>
>> As I said, in practice it is all cases when you would have a container
>> of mutable elements.
>
> I had some trouble understanding your point the first time, and skipped
> over it (sorry).
>
> What's the opposite of "mutable"? constant? because, if so, why wouldn't
> I use Update_Element?
Yes, But it is almost never element update. Usually what you want is to
call some mutable operations on the element. The language does not offer
user-defined by-reference or copy-out-copy-in methods to element access.
Thus it becomes an access type which opens the whole can of worms.
Reference counting is merely a method to deal with access types and to
prevent excessive copying, e.g. when inserting elements.
> (aside from it being very clumsy; more likely to
> use a reference, Person (P).Health := Unwell; )
An access type, you mean. References are only for built-in containers:
records and arrays.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-12 11:55 ` Dmitry A. Kazakov
@ 2017-03-12 16:44 ` Simon Wright
2017-03-12 17:42 ` Dmitry A. Kazakov
0 siblings, 1 reply; 33+ messages in thread
From: Simon Wright @ 2017-03-12 16:44 UTC (permalink / raw)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> On 2017-03-12 12:30, Simon Wright wrote:
>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>>
>>> On 2017-03-11 22:46, Simon Wright wrote:
>>
>>>> Basically, I (Simon, now) am having trouble thinking of an
>>>> application where reference counting would be an appropriate
>>>> solution.
>>>
>>> As I said, in practice it is all cases when you would have a
>>> container of mutable elements.
>>
>> I had some trouble understanding your point the first time, and
>> skipped over it (sorry).
>>
>> What's the opposite of "mutable"? constant? because, if so, why
>> wouldn't I use Update_Element?
>
> Yes, But it is almost never element update. Usually what you want is
> to call some mutable operations on the element. The language does not
> offer user-defined by-reference or copy-out-copy-in methods to element
> access.
with Ada.Containers.Vectors;
package Mutable_Elements is
type Element is record
Numeral : Integer := 0;
end record;
procedure Update (E : in out Element);
package Element_Vectors is new Ada.Containers.Vectors
(Index_Type => Positive,
Element_Type => Element);
Elements : Element_Vectors.Vector;
end Mutable_Elements;
package body Mutable_Elements is
procedure Update (E : in out Element) is
begin
E.Numeral := E.Numeral + 1;
end Update;
end Mutable_Elements;
with Ada.Text_IO;
with Mutable_Elements;
procedure Mutable_Elements_Test is
use Mutable_Elements;
begin
for J in 1 .. 5 loop
Elements.Append (Element'(Numeral => J));
end loop;
Elements (4).Numeral := Elements (4).Numeral + 1;
Ada.Text_IO.Put_Line ("Elements (4).Numeral: "
& Elements (4).Numeral'Img);
Update (Elements (4));
Ada.Text_IO.Put_Line ("Elements (4).Numeral: "
& Elements (4).Numeral'Img);
end Mutable_Elements_Test;
$ ./mutable_elements_test
Elements (4).Numeral: 5
Elements (4).Numeral: 6
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-12 16:44 ` Simon Wright
@ 2017-03-12 17:42 ` Dmitry A. Kazakov
2017-03-13 19:55 ` Randy Brukardt
2017-03-13 23:25 ` Simon Wright
0 siblings, 2 replies; 33+ messages in thread
From: Dmitry A. Kazakov @ 2017-03-12 17:42 UTC (permalink / raw)
On 2017-03-12 17:44, Simon Wright wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>
>> On 2017-03-12 12:30, Simon Wright wrote:
>>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>>>
>>>> On 2017-03-11 22:46, Simon Wright wrote:
>>>
>>>>> Basically, I (Simon, now) am having trouble thinking of an
>>>>> application where reference counting would be an appropriate
>>>>> solution.
>>>>
>>>> As I said, in practice it is all cases when you would have a
>>>> container of mutable elements.
>>>
>>> I had some trouble understanding your point the first time, and
>>> skipped over it (sorry).
>>>
>>> What's the opposite of "mutable"? constant? because, if so, why
>>> wouldn't I use Update_Element?
>>
>> Yes, But it is almost never element update. Usually what you want is
>> to call some mutable operations on the element. The language does not
>> offer user-defined by-reference or copy-out-copy-in methods to element
>> access.
>
> with Ada.Containers.Vectors;
> package Mutable_Elements is
> type Element is record
[...]
Your example uses access type in Reference_Type which was the point.
Issues to consider are:
1. Two useless Reference_Type type where there should be none, if Ada
had references.
2. The case of long living multiple references for which Reference_Type
is no solution.
The case #1 is compiler-managed implicit references or copies. The case
#2 is user-managed referential helper types. Reference-counted objects
serve both #1 and #2, being really necessary only for #2.
P.S. It is a pity that Implicit_Dereference type does not have proper
interface to work with *any* type instead of discriminated records. This
is design fault when the use case #2 was taken for #1.
P.P.S. Issues with inheritance upon derivation from the container type,
from the element type, and from both not even mentioned.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-12 8:22 ` Simon Wright
2017-03-12 9:38 ` G.B.
@ 2017-03-13 10:29 ` Alejandro R. Mosteo
1 sibling, 0 replies; 33+ messages in thread
From: Alejandro R. Mosteo @ 2017-03-13 10:29 UTC (permalink / raw)
On 12/03/17 09:22, Simon Wright wrote:
> Niklas Holsti <niklas.holsti@tidorum.invalid> writes:
>
>> On 17-03-11 23:46 , Simon Wright wrote:
>>
>>> Basically, I (Simon, now) am having trouble thinking of an
>>> application where reference counting would be an appropriate
>>> solution.
>>
>> How about this one: the SW generates messages (telemetry packets)
>> reporting various sorts of data and events.
>>
> [...]
>>
>> Each destination has a queue of incoming messages, and the queueing
>> and processing time for a given message varies accordingly. Rather
>> than copy the (possibly long) message into each destination's queue,
>> the queues hold references to a single, shared instance of the
>> message, dynamically allocated in a memory pool. These references are
>> counted. When, finally, all destinations have processed the message,
>> the message's reference count reaches zero, and the message can be
>> deallocated.
>>
>> This use of reference counting is a typical design in satellite
>> on-board SW.
>
> Thanks for the example!
>
> I still have a feeling that this is an in-computer software technique
> which resolves a software problem, rather than a necessary response to
> an application-domain problem.
This is how (most? I can only vouch for Java) garbage-collecting
languages work. Thus is a common way of thinking for many programmers (I
would say most think first of ref-counted ptrs before weak refs when
faced with the problem being discussed, but that's just my impression).
And the source of a kind of memory leak, which is the problem you get in
exchange for no dangling pointers...
Not surprisingly, C++ has standardized the whole shebang, with both
semantics being discussed here covered:
http://en.cppreference.com/w/cpp/memory
Alex.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-12 17:42 ` Dmitry A. Kazakov
@ 2017-03-13 19:55 ` Randy Brukardt
2017-03-13 20:53 ` Dmitry A. Kazakov
2017-03-13 23:25 ` Simon Wright
1 sibling, 1 reply; 33+ messages in thread
From: Randy Brukardt @ 2017-03-13 19:55 UTC (permalink / raw)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:oa41a4$1ko5$1@gioia.aioe.org...
> On 2017-03-12 17:44, Simon Wright wrote:
...
> P.S. It is a pity that Implicit_Dereference type does not have proper
> interface to work with *any* type instead of discriminated records. This
> is design fault when the use case #2 was taken for #1.
It works with any type, since the real target type is the thing designated
by the discriminant. The discriminant is the key, because it is the only way
in Ada to get an access type with a controlled lifetime. The discriminated
type is just a helper, not the end result.
Yes, we could have done that some other way, and indeed we started with a
separate construct, but the semantics ended up identical to that of an
access discriminant of a controlled type -- so why build another construct
(with all of the additional chances of getting it wrong) when an existing
one will do? Syntax sugar covers up the mess in almost all uses - and that's
a lot easier to define correctly.
Randy.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-13 19:55 ` Randy Brukardt
@ 2017-03-13 20:53 ` Dmitry A. Kazakov
2017-03-14 20:40 ` Randy Brukardt
0 siblings, 1 reply; 33+ messages in thread
From: Dmitry A. Kazakov @ 2017-03-13 20:53 UTC (permalink / raw)
On 2017-03-13 20:55, Randy Brukardt wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
> news:oa41a4$1ko5$1@gioia.aioe.org...
>> On 2017-03-12 17:44, Simon Wright wrote:
> ...
>> P.S. It is a pity that Implicit_Dereference type does not have proper
>> interface to work with *any* type instead of discriminated records. This
>> is design fault when the use case #2 was taken for #1.
>
> It works with any type, since the real target type is the thing designated
> by the discriminant. The discriminant is the key, because it is the only way
> in Ada to get an access type with a controlled lifetime. The discriminated
> type is just a helper, not the end result.
That would be the case #1 with *no* helper type whatsoever:
type Element ...
type Index ...
type Container ...
function Get (X : Container; I : Index) return Element;
for Container'Get_Element use Get;
function Get (X : Container; I : Index) return access Element;
for Container'Access_Element use Get;
etc
> Yes, we could have done that some other way,
There is no other way, there is another use case #2, when a reference
object is required to live longer. That should be *any* type:
type Reference_Type is private;
private
function Get (X : Reference_Type) return access Element;
for Reference_Type'Implicit_Conversion (Element) use Get;
of course it should be inheritance from Element in a better world:
type Reference_Type is new Element;
private
function To_Element (X : Reference_Type) return Element;
for Reference_Type'Upcast_Conversion use To_Element;
by reference:
type Reference_Type is new access Element;
private
function To_Element (X : Reference_Type) return access Element;
for Reference_Type'Upcast_Conversion use To_Element;
> and indeed we started with a
> separate construct, but the semantics ended up identical to that of an
> access discriminant of a controlled type -- so why build another construct
> (with all of the additional chances of getting it wrong) when an existing
> one will do?
Because it gets everything wrong in the major use case #2. You need
invalidated reference types. A pointer when explicit cannot be
discriminant and furthermore there might be a pool-specific pointer or
no pointer at all. And nobody wants to expose pointers under no
circumstances anyway. When pointers are exposed any shred of safety is gone.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-12 17:42 ` Dmitry A. Kazakov
2017-03-13 19:55 ` Randy Brukardt
@ 2017-03-13 23:25 ` Simon Wright
2017-03-14 8:25 ` Dmitry A. Kazakov
1 sibling, 1 reply; 33+ messages in thread
From: Simon Wright @ 2017-03-13 23:25 UTC (permalink / raw)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> On 2017-03-12 17:44, Simon Wright wrote:
>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>>
>>> On 2017-03-12 12:30, Simon Wright wrote:
>>>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>>>>
>>>>> On 2017-03-11 22:46, Simon Wright wrote:
>>>>
>>>>>> Basically, I (Simon, now) am having trouble thinking of an
>>>>>> application where reference counting would be an appropriate
>>>>>> solution.
>>>>>
>>>>> As I said, in practice it is all cases when you would have a
>>>>> container of mutable elements.
>>>>
>>>> I had some trouble understanding your point the first time, and
>>>> skipped over it (sorry).
>>>>
>>>> What's the opposite of "mutable"? constant? because, if so, why
>>>> wouldn't I use Update_Element?
>>>
>>> Yes, But it is almost never element update. Usually what you want is
>>> to call some mutable operations on the element. The language does not
>>> offer user-defined by-reference or copy-out-copy-in methods to element
>>> access.
>>
>> with Ada.Containers.Vectors;
>> package Mutable_Elements is
>> type Element is record
>
> [...]
>
> Your example uses access type in Reference_Type which was the point.
There may be pointers under the hood, just like in assembler there are
registers which denote addresses in store, but there are no pointers
explicitly in the code I wrote; so your assertion (that I couldn't call
operations that change a container element) was wrong.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-13 23:25 ` Simon Wright
@ 2017-03-14 8:25 ` Dmitry A. Kazakov
0 siblings, 0 replies; 33+ messages in thread
From: Dmitry A. Kazakov @ 2017-03-14 8:25 UTC (permalink / raw)
On 14/03/2017 00:25, Simon Wright wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>
>> On 2017-03-12 17:44, Simon Wright wrote:
>>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>>>
>>>> On 2017-03-12 12:30, Simon Wright wrote:
>>>>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>>>>>
>>>>>> On 2017-03-11 22:46, Simon Wright wrote:
>>>>>
>>>>>>> Basically, I (Simon, now) am having trouble thinking of an
>>>>>>> application where reference counting would be an appropriate
>>>>>>> solution.
>>>>>>
>>>>>> As I said, in practice it is all cases when you would have a
>>>>>> container of mutable elements.
>>>>>
>>>>> I had some trouble understanding your point the first time, and
>>>>> skipped over it (sorry).
>>>>>
>>>>> What's the opposite of "mutable"? constant? because, if so, why
>>>>> wouldn't I use Update_Element?
>>>>
>>>> Yes, But it is almost never element update. Usually what you want is
>>>> to call some mutable operations on the element. The language does not
>>>> offer user-defined by-reference or copy-out-copy-in methods to element
>>>> access.
>>>
>>> with Ada.Containers.Vectors;
>>> package Mutable_Elements is
>>> type Element is record
>>
>> [...]
>>
>> Your example uses access type in Reference_Type which was the point.
>
> There may be pointers under the hood,
Under a straw hat with huge holes meant for ventilation alas exposing
ugly nasty lack of hair and warts. Pointers are right in the public part
of Ada.Containers.Vectors.
Even if Reference_Type were an opaque type with no visible access
discriminant, it would still be a pointer in its semantics with all
strings attached. Thus if it must be there [*], it must better be a
smart pointer, e.g. to a reference-counted object.
BTW, reference-counted design gives means to resolve problems with
at-cursor object deletion and aliased updates in one or another manner.
------------------------
* There should be no helper types needed to access container elements
"in place".
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-13 20:53 ` Dmitry A. Kazakov
@ 2017-03-14 20:40 ` Randy Brukardt
2017-03-15 8:44 ` Dmitry A. Kazakov
0 siblings, 1 reply; 33+ messages in thread
From: Randy Brukardt @ 2017-03-14 20:40 UTC (permalink / raw)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:oa70rj$12td$1@gioia.aioe.org...
> On 2017-03-13 20:55, Randy Brukardt wrote:
>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
>> news:oa41a4$1ko5$1@gioia.aioe.org...
>>> On 2017-03-12 17:44, Simon Wright wrote:
>> ...
>>> P.S. It is a pity that Implicit_Dereference type does not have proper
>>> interface to work with *any* type instead of discriminated records. This
>>> is design fault when the use case #2 was taken for #1.
>>
>> It works with any type, since the real target type is the thing
>> designated
>> by the discriminant. The discriminant is the key, because it is the only
>> way
>> in Ada to get an access type with a controlled lifetime. The
>> discriminated
>> type is just a helper, not the end result.
>
> That would be the case #1 with *no* helper type whatsoever:
>
> type Element ...
> type Index ...
> type Container ...
> function Get (X : Container; I : Index) return Element;
> for Container'Get_Element use Get;
>
> function Get (X : Container; I : Index) return access Element;
> for Container'Access_Element use Get;
>
> etc
The second case here doesn't limit the lifetime of the return access type,
and provides no notification when the caller is finally finished with the
accesss value (which is critical). Don't see how that solves anything for
"case 1".
>> Yes, we could have done that some other way,
>
> There is no other way, there is another use case #2, when a reference
> object is required to live longer.
We were not trying to solve that problem, it's relatively easy to solve with
the existing features (as your reference counted library shows). Ada is
about building blocks, not necessarily polished solutions.
...>> and indeed we started with a
>> separate construct, but the semantics ended up identical to that of an
>> access discriminant of a controlled type -- so why build another
>> construct
>> (with all of the additional chances of getting it wrong) when an existing
>> one will do?
>
> Because it gets everything wrong in the major use case #2.
Which it was not intended to solve. The whole "strong/weak reference"
business is not fundemental to solving problems -- if anything, it gets in
the way. We were only trying to solve the problem of having a reference that
is still safe (in the sense that the underlying data can be protected from
destruction while the reference exists). That requires two things: having a
reference that can't be copied, and having a call-back possibility when the
reference goes away. The Ada 2012 reference mechanism provides both.
We looked at more complex schemes and they mostly added confusion and
complexity rather than any benefit. If you want strong and weak references,
it's easy enough to build them from the parts available.
Randy.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-14 20:40 ` Randy Brukardt
@ 2017-03-15 8:44 ` Dmitry A. Kazakov
2017-03-15 20:12 ` Randy Brukardt
0 siblings, 1 reply; 33+ messages in thread
From: Dmitry A. Kazakov @ 2017-03-15 8:44 UTC (permalink / raw)
On 2017-03-14 21:40, Randy Brukardt wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
> news:oa70rj$12td$1@gioia.aioe.org...
>> On 2017-03-13 20:55, Randy Brukardt wrote:
>>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
>>> news:oa41a4$1ko5$1@gioia.aioe.org...
>>>> On 2017-03-12 17:44, Simon Wright wrote:
>>> ...
>>>> P.S. It is a pity that Implicit_Dereference type does not have proper
>>>> interface to work with *any* type instead of discriminated records. This
>>>> is design fault when the use case #2 was taken for #1.
>>>
>>> It works with any type, since the real target type is the thing
>>> designated
>>> by the discriminant. The discriminant is the key, because it is the only
>>> way
>>> in Ada to get an access type with a controlled lifetime. The
>>> discriminated
>>> type is just a helper, not the end result.
>>
>> That would be the case #1 with *no* helper type whatsoever:
>>
>> type Element ...
>> type Index ...
>> type Container ...
>> function Get (X : Container; I : Index) return Element;
>> for Container'Get_Element use Get;
>>
>> function Get (X : Container; I : Index) return access Element;
>> for Container'Access_Element use Get;
>>
>> etc
>
> The second case here doesn't limit the lifetime of the return access type,
> and provides no notification when the caller is finally finished with the
> accesss value (which is critical). Don't see how that solves anything for
> "case 1".
It is no problem because the operations are not to be called explicitly.
They must be private.
I was never a fun of aspects or representation clauses, the proper
notation should be this:
function "()" (X : Container; I : Index) return Element;
procedure "()" (X : in out Container; I : Index; V : Element);
This is for user copy-out / copy-in semantics. And this
function "()" (X : Container; I : Index)
return not null access Element;
procedure "()" (X : in out Container; I : Index; V : not null access
Element);
For by-reference semantics. The second operation is called to commit
element transaction. E.g.
X (I).Member := Foo;
Will be equivalent to
declare
Reference : not null access Element := <()> (X, I);
begin
Reference.Member := Foo;
<()> (X, I, Reference);
exception
when others =>
<()> (X, I, Reference);
raise;
end;
In particular, the first "()" could take a lock and the second would
release it.
>>> Yes, we could have done that some other way,
>>
>> There is no other way, there is another use case #2, when a reference
>> object is required to live longer.
>
> We were not trying to solve that problem,
Yes, that is my problem with the design. The use case is #1, the
solution is #2.
> it's relatively easy to solve with
> the existing features (as your reference counted library shows). Ada is
> about building blocks, not necessarily polished solutions.
It is far from being easy. In practice it leads to a geometric explosion
of generic instances when a types hierarchy is involved. Believe me, it
extreme even catastrophic.
The problem is that the smart pointer type must be anonymous (ad-hoc)
like access T is. Otherwise, you get the problem of parallel type
hierarchies of the targets and the pointers. And the pointer type must
have the interface of the target AKA implicit dereference.
> ...>> and indeed we started with a
>>> separate construct, but the semantics ended up identical to that of an
>>> access discriminant of a controlled type -- so why build another
>>> construct
>>> (with all of the additional chances of getting it wrong) when an existing
>>> one will do?
>>
>> Because it gets everything wrong in the major use case #2.
>
> Which it was not intended to solve. The whole "strong/weak reference"
> business is not fundemental to solving problems -- if anything, it gets in
> the way.
Theoretically it is not fundamental, in practice it is. Same as in-out
parameters. They are not fundamental, any program can theoretically be
immutable, if you could clone everything including the Universe
producing program inputs.
> We were only trying to solve the problem of having a reference that
> is still safe (in the sense that the underlying data can be protected from
> destruction while the reference exists). That requires two things: having a
> reference that can't be copied, and having a call-back possibility when the
> reference goes away. The Ada 2012 reference mechanism provides both.
Except that it is not a reference (compiler-managed view of an object),
it is an object of a different type that plays a role of a reference.
And this falls right into the use case you didn't want to solve in the
first place.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-15 8:44 ` Dmitry A. Kazakov
@ 2017-03-15 20:12 ` Randy Brukardt
2017-03-16 2:59 ` Paul Rubin
2017-03-16 9:04 ` Dmitry A. Kazakov
0 siblings, 2 replies; 33+ messages in thread
From: Randy Brukardt @ 2017-03-15 20:12 UTC (permalink / raw)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:oaausp$ddn$1@gioia.aioe.org...
> On 2017-03-14 21:40, Randy Brukardt wrote:
...
> The problem is that the smart pointer type must be anonymous (ad-hoc) like
> access T is. Otherwise, you get the problem of parallel type hierarchies
> of the targets and the pointers. And the pointer type must have the
> interface of the target AKA implicit dereference.
The problem of parallel type hierarchies is one that MUST be solved if one
is going to program with hierarchies. It IS fundamental to strong typing,
unlike most of these other issues. We really need a solution to it (I've
tried some and intend to try more).
I don't recall ever needing both "strong" or "weak" references to any data
structure. Most structures only need short-lived references, and the ones
that need long-lived ones are almost always permanent (only changed between
runs). Maybe that's because when the only tool you have is an access type,
every reference looks unchecked and dangerous. :-) But I doubt that many
problems need or care about the both possibilities.
Randy.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-15 20:12 ` Randy Brukardt
@ 2017-03-16 2:59 ` Paul Rubin
2017-03-16 9:04 ` Dmitry A. Kazakov
1 sibling, 0 replies; 33+ messages in thread
From: Paul Rubin @ 2017-03-16 2:59 UTC (permalink / raw)
"Randy Brukardt" <randy@rrsoftware.com> writes:
> I don't recall ever needing both "strong" or "weak" references to any data
> structure.
I've only heard of weakrefs in connection with garbage collected
systems, but they are useful there, for dealing with potentially
circular references between long-lived objects.
> Most structures only need short-lived references, and the ones
> that need long-lived ones are almost always permanent
That's partly a matter of how the application is organized, which in
turn is influenced by the language features. In particular, with GC
available, it suddenly becomes less important to carefully manage object
lifetimes and so you get a mixture. Of course then it's hard to prove
impossibility of OOM crashes, so this may not be a great approach for
critical realtime systems. But for lots of other tasks it makes
programming easier at the now-cheap cost of a few more computer
resources.
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: Getting the index for an element in mutually referencing containers
2017-03-15 20:12 ` Randy Brukardt
2017-03-16 2:59 ` Paul Rubin
@ 2017-03-16 9:04 ` Dmitry A. Kazakov
1 sibling, 0 replies; 33+ messages in thread
From: Dmitry A. Kazakov @ 2017-03-16 9:04 UTC (permalink / raw)
On 2017-03-15 21:12, Randy Brukardt wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
> news:oaausp$ddn$1@gioia.aioe.org...
>> On 2017-03-14 21:40, Randy Brukardt wrote:
> ...
>> The problem is that the smart pointer type must be anonymous (ad-hoc) like
>> access T is. Otherwise, you get the problem of parallel type hierarchies
>> of the targets and the pointers. And the pointer type must have the
>> interface of the target AKA implicit dereference.
>
> The problem of parallel type hierarchies is one that MUST be solved if one
> is going to program with hierarchies. It IS fundamental to strong typing,
> unlike most of these other issues. We really need a solution to it (I've
> tried some and intend to try more).
I hope you will find something.
> I don't recall ever needing both "strong" or "weak" references to any data
> structure. Most structures only need short-lived references, and the ones
> that need long-lived ones are almost always permanent (only changed between
> runs). Maybe that's because when the only tool you have is an access type,
> every reference looks unchecked and dangerous. :-) But I doubt that many
> problems need or care about the both possibilities.
1. Say you have a tree. A parent holds references to its children. Each
child holds a reference to its parent. One of these references must be
weak or you get a circular lock.
2. Another example. Let you attach a monitoring object to the object
that emits some events/callbacks. Neither may hold another. The monitor
can take its leave so the object can. In this case both references are weak.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 33+ messages in thread
end of thread, other threads:[~2017-03-16 9:04 UTC | newest]
Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-09 13:45 Getting the index for an element in mutually referencing containers Mart van de Wege
2017-03-09 15:25 ` Egil H H
2017-03-09 15:45 ` Mart van de Wege
2017-03-09 16:02 ` Mart van de Wege
2017-03-09 16:11 ` Egil H H
[not found] ` <ly7f3xedp4.fsf@pushface.org>
[not found] ` <86k27xpikd.fsf@gaheris.avalon.lan>
[not found] ` <lywpbxc9my.fsf@pushface.org>
[not found] ` <86wpbxneuz.fsf@gaheris.avalon.lan>
[not found] ` <o9vcbp$t0t$1@franka.jacob-sparre.dk>
2017-03-11 6:45 ` Mart van de Wege
2017-03-11 8:40 ` Simon Wright
2017-03-11 8:58 ` Dmitry A. Kazakov
2017-03-11 11:21 ` Simon Wright
2017-03-11 14:18 ` Dmitry A. Kazakov
2017-03-11 20:05 ` Simon Wright
2017-03-11 20:52 ` Dmitry A. Kazakov
2017-03-11 21:46 ` Simon Wright
2017-03-11 22:37 ` Niklas Holsti
2017-03-12 8:22 ` Simon Wright
2017-03-12 9:38 ` G.B.
2017-03-12 11:21 ` Simon Wright
2017-03-13 10:29 ` Alejandro R. Mosteo
2017-03-12 8:20 ` Dmitry A. Kazakov
2017-03-12 11:30 ` Simon Wright
2017-03-12 11:55 ` Dmitry A. Kazakov
2017-03-12 16:44 ` Simon Wright
2017-03-12 17:42 ` Dmitry A. Kazakov
2017-03-13 19:55 ` Randy Brukardt
2017-03-13 20:53 ` Dmitry A. Kazakov
2017-03-14 20:40 ` Randy Brukardt
2017-03-15 8:44 ` Dmitry A. Kazakov
2017-03-15 20:12 ` Randy Brukardt
2017-03-16 2:59 ` Paul Rubin
2017-03-16 9:04 ` Dmitry A. Kazakov
2017-03-13 23:25 ` Simon Wright
2017-03-14 8:25 ` Dmitry A. Kazakov
2017-03-12 1:36 ` Randy Brukardt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox