| Thread overview | |||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
December 19, 2007 Aggregates & associations | ||||
|---|---|---|---|---|
| ||||
Hi,
A lot of the recent debate surrounding garbage collection and to a lesser extent const has got me wondering about
aggregation, association and attribution in (garbage collected) languages like D and Java.
In object oriented modelling there is a clear distinction between association and aggregation. An aggregate is
part of the object. If the object is destroyed it should be destroyed as well. An association is just a reference to
another object. How are these two concepts distinguished in D or Java?
Even in C++ you can't express them directly. I find myself labelling pointers as "owned" or "not-owned".
In java:
class Foo;
class Bar
{
Foo aggregate;
Foo association;
};
In C++:
class Foo;
class Bar
{
Foo aggregate;
Foo* association
};
or
class Bar2
{
~bar2()
{
delete aggregate;
}
// owned
Foo* aggregate;
// not owned
Foo* association
};
It seems to me that in D the correct idiom is:
class Bar
{
Foo aggregate;
Foo* association;
}
and specifically that:
class Bar
{
Foo association;
}
should be considered an error.
How do you label associations and aggregations? Is there any modern language that lets you represent these distinct concepts directly?
Also I have just realised I have a fundermental gap in my understanding of very basic D.
When I declare:
class Foo
{
int x;
int y;
}
class Bar
{
Foo aggregate;
}
What is the memory layout of the object I am actually declaring?
Is it the C++ equivalent of:
class Bar
{
Foo aggregate; // i.e. { int x, int y }
};
or
class Bar
{
Foo* aggregate;
};
If I was being more low-level than I need to be most of time these days the memory layout matters. How do you choose
which kind of aggregation (has-a versus part-of) is implemented?
I'm amazed I never noticed this before but semantically its not a problem because an aggregate is an aggregate.
Could someone please enlighten me - my brain isn't screwed in today?
| ||||
December 19, 2007 Re: Aggregates & associations | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Bruce Adams |
You seem to be getting confused as to how D classes work. In D, classes work like they do in Java: they are *always* references. That is,
class Foo {}
Foo a;
Here, a is *effectively* a pointer; it is a reference to an instance of Foo, just like in Java.
If you want to control the layout of memory, you can use a struct.
struct Foo
{
int a, b;
}
struct Bar
{
Foo foo;
float x;
}
Bar b;
Here, b is exactly 12 bytes long. &b.foo.a == &b.foo == &b. If b is declared as a function local, it takes up 12 bytes on the stack; if it's declared as part of an aggregate type (like a struct, class or union,) then it takes up 12 bytes within an instance of that type.
So, to get back to your original question, you could probably represent aggregates as structs and associations with classes. Note that the only one that can represent *both* would be a struct:
struct Baz
{
Foo aggregate;
Foo* association;
}
Currently, this is all a bit of a mess. However, I believe that Walter intends to (eventually) introduce scoped class members like so:
class Foo {}
class Bar
{
scope Foo aggregate;
Foo association;
}
So that when an instance of Bar is destroyed, its 'aggregate' member is also destroyed (currently, this is impossible to guarantee if 'aggregate' is GC-allocated.)
But that's not really useful at this point.
Hope that helps.
-- Daniel
| |||
December 19, 2007 Re: Aggregates & associations | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Bruce Adams | A long time ago Walter mentioned that the "scope" attribute may be extended to indicate aggregation:
class Foo
{
Bar valA;
scope Baz val2;
}
With the above, the size of Foo would be expanded to contain an instance of Baz and val2 would be initialized to reference this memory, very much like "scope" works in functions now. But I didn't see any mention of this in the D 2.0 writeup so the idea may have been either discarded or forgotten.
Sean
| |||
December 19, 2007 Re: Aggregates & associations | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Daniel Keep | On Wed, 19 Dec 2007 00:53:43 -0000, Daniel Keep <daniel.keep.lists@gmail.com> wrote: > > You seem to be getting confused as to how D classes work. In D, classes > work like they do in Java: they are *always* references. That is, > > class Foo {} > Foo a; > > Here, a is *effectively* a pointer; it is a reference to an instance of > Foo, just like in Java. > Right. I got that. > If you want to control the layout of memory, you can use a struct. > > struct Foo > { > int a, b; > } > > struct Bar > { > Foo foo; > float x; > } > > Bar b; > > Here, b is exactly 12 bytes long. &b.foo.a == &b.foo == &b. If b is > declared as a function local, it takes up 12 bytes on the stack; if it's > declared as part of an aggregate type (like a struct, class or union,) > then it takes up 12 bytes within an instance of that type. > > So, to get back to your original question, you could probably represent > aggregates as structs and associations with classes. Note that the only > one that can represent *both* would be a struct: > > struct Baz > { > Foo aggregate; > Foo* association; > } > > Currently, this is all a bit of a mess. Yes it is isn't it. Why on Earth would any sensible refactoring of C++ include making the same declaration "Foo aggregate" context dependent on whether its inside a struct or a class. Why did Walter do this when his other refactorings were so sane? (Lose two guru points and make a sanity roll) This is not want I would want as classes and structs are different things. An instance of a class ISA object. A instance of a struct is not an object. This is also no good because structs have value semantics and classes have reference semantics. Is this why we I see a "ref" keyword popping up occasionally? Is it the main way of making a struct behave like a class? Is this all for the sake of eliminating -> and &? I'm not sure it was worth it. I can see there is a trade off between: Foo foo = new foo; versus Foo& foo = new Foo; //this is not D but it could have been The reference form is the most common usage so you don't want to type the extra &. This makes sense with: scope Foo foo; being on (e.g.) the stack. Though it doesn't obviously mean "I am a value type". > However, I believe that Walter > intends to (eventually) introduce scoped class members like so: > > class Foo {} > > class Bar > { > scope Foo aggregate; > Foo association; > } > > So that when an instance of Bar is destroyed, its 'aggregate' member is > also destroyed (currently, this is impossible to guarantee if > 'aggregate' is GC-allocated.) > > But that's not really useful at this point. > This is useful for defining ownership though it a stretch for the definition of scope. I suppose its better than introducing a new keyword like 'owned' though. > Hope that helps. > > -- Daniel It does but it makes me want to run away from D. What is really needed is a sensible way of saying "this is a value" versus "this is a reference" as a qualifier on the type. In c++ value was the norm and reference was &. So if reference is the norm we need a qualifier that says I am a value. Its not obvious what the best choice would be. Perhaps * because it is conventionally understood to C/C++ emigrees to mean dereference. So. class Bar { *Foo aggregateValue; Foo association; } I think the above syntax is particularly vile and would go for something like "val" as the antinym of "ref". i.e. class Bar { val Foo aggregateValue; Foo association; } The same keyword could solve the pass by value problem on functions too. I like this it means that for every type there is always either a ref or val qualifier. The ref qualifier is just hidden because its the default. Have I missed anything? -- Using Opera's revolutionary [NOT!] e-mail client: http://www.opera.com/mail/ | |||
December 19, 2007 Re: Aggregates & associations | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Bruce Adams | "Bruce Adams" <tortoise_74@yeah.who.co.uk> wrote in message news:op.t3j6ntq1xikks4@lionheart.cybernetics... On Wed, 19 Dec 2007 00:53:43 -0000, Daniel Keep <daniel.keep.lists@gmail.com> wrote: ------------------ What is really needed is a sensible way of saying "this is a value" versus "this is a reference" as a qualifier on the type. ------------------ I'm just going to tell you what Walter would (and has, several times): in C++, 95% of the time you design a class to be _either_ a value type _or_ a reference type, not both, but of course there's no way to restrict the use thereof to one kind or the other. Don't shoot the messenger. (side note: absolutely _everyone_ who uses Opera as their newsreader, their posts don't quote correctly in OE...) | |||
December 19, 2007 Re: Aggregates & associations | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Jarrett Billingsley | On Wed, 19 Dec 2007 01:54:56 -0000, Jarrett Billingsley <kb3ctd2@yahoo.com> wrote: > "Bruce Adams" <tortoise_74@yeah.who.co.uk> wrote in message > news:op.t3j6ntq1xikks4@lionheart.cybernetics... > On Wed, 19 Dec 2007 00:53:43 -0000, Daniel Keep > <daniel.keep.lists@gmail.com> wrote: > > ------------------ > What is really needed is a sensible way of saying "this is a value" versus > "this is a reference" as a qualifier on the type. > ------------------ > > I'm just going to tell you what Walter would (and has, several times): in > C++, 95% of the time you design a class to be _either_ a value type _or_ a > reference type, not both, but of course there's no way to restrict the use > thereof to one kind or the other. > That's an explanation for struct versus class but not the more general case including memory layout and calling conventions. > Don't shoot the messenger. [bang!] Whether you use values or references 95% of the time isn't an issue if you focus on semantics (of program behaviour). Most of the time is going to be about optimisation in terms of both speed and memory layout. However, I dislike the idea of having that 5% inaccessible to the language. I mean 95% of the time I don't need floating point numbers would it be okay to not support them? > > (side note: absolutely _everyone_ who uses Opera as their newsreader, their > posts don't quote correctly in OE...) > Presumably because outlook doesn't follow RFCs properly. Your voting buttons don't show up too well on my linux box either, :). (or your wacky decision to have reply to all reply to yourself as well) | |||
December 19, 2007 Re: Aggregates & associations | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Sean Kelly | "Sean Kelly" wrote
>A long time ago Walter mentioned that the "scope" attribute may be extended to indicate aggregation:
>
> class Foo
> {
> Bar valA;
> scope Baz val2;
> }
>
> With the above, the size of Foo would be expanded to contain an instance of Baz and val2 would be initialized to reference this memory, very much like "scope" works in functions now. But I didn't see any mention of this in the D 2.0 writeup so the idea may have been either discarded or forgotten.
I believe this type of behavior is not necessary. Whether a member is an aggregate or an association, it will be deleted when nothing references it anymore, which is when it should be deleted. The only thing this proposed behavior can do is cause segfaults when something that still has a reference to a scoped member tries to use it.
-Steve
| |||
December 19, 2007 Re: Aggregates & associations | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | Steven Schveighoffer Wrote:
> "Sean Kelly" wrote
> >A long time ago Walter mentioned that the "scope" attribute may be extended to indicate aggregation:
> >
> > class Foo
> > {
> > Bar valA;
> > scope Baz val2;
> > }
> >
> > With the above, the size of Foo would be expanded to contain an instance of Baz and val2 would be initialized to reference this memory, very much like "scope" works in functions now. But I didn't see any mention of this in the D 2.0 writeup so the idea may have been either discarded or forgotten.
>
> I believe this type of behavior is not necessary. Whether a member is an aggregate or an association, it will be deleted when nothing references it anymore, which is when it should be deleted. The only thing this proposed behavior can do is cause segfaults when something that still has a reference to a scoped member tries to use it.
It has more value that that. It's easy for me to imagine a scope class that includes one or more scope classes (such as a wrapper around an RAII object)
| |||
December 19, 2007 Re: Aggregates & associations | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Jason House | On Wed, 19 Dec 2007 15:11:46 -0000, Jason House <jason.james.house@gmail.com> wrote:
> Steven Schveighoffer Wrote:
>
>> "Sean Kelly" wrote
>> >A long time ago Walter mentioned that the "scope" attribute may be
>> extended
>> >to indicate aggregation:
>> >
>> > class Foo
>> > {
>> > Bar valA;
>> > scope Baz val2;
>> > }
>> >
>> > With the above, the size of Foo would be expanded to contain an
>> instance
>> > of Baz and val2 would be initialized to reference this memory, very
>> much
>> > like "scope" works in functions now. But I didn't see any mention of
>> this
>> > in the D 2.0 writeup so the idea may have been either discarded or
>> > forgotten.
>>
>> I believe this type of behavior is not necessary. Whether a member is an
>> aggregate or an association, it will be deleted when nothing references it
>> anymore, which is when it should be deleted. The only thing this proposed
>> behavior can do is cause segfaults when something that still has a reference
>> to a scoped member tries to use it.
>
> It has more value that that. It's easy for me to imagine a scope class that includes one or more scope classes (such as a wrapper around an RAII object)
Also failing early can be a good thing. Attempting to use an associated object that
is not valid is an error. Just because you have not released the memory for it
does not make it valid. You program could give the illusion of working for a bit
and then die mysteriously.
The ability to delete deterministically as an optimisation could make programs more
efficient too. You might even be able to simplify collection sweeps or switch them off
temporarily without compromising performance.
| |||
December 19, 2007 Re: Aggregates & associations | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | Steven Schveighoffer wrote:
> "Sean Kelly" wrote
>> A long time ago Walter mentioned that the "scope" attribute may be extended to indicate aggregation:
>>
>> class Foo
>> {
>> Bar valA;
>> scope Baz val2;
>> }
>>
>> With the above, the size of Foo would be expanded to contain an instance of Baz and val2 would be initialized to reference this memory, very much like "scope" works in functions now. But I didn't see any mention of this in the D 2.0 writeup so the idea may have been either discarded or forgotten.
>
> I believe this type of behavior is not necessary. Whether a member is an aggregate or an association, it will be deleted when nothing references it anymore, which is when it should be deleted. The only thing this proposed behavior can do is cause segfaults when something that still has a reference to a scoped member tries to use it.
You're right that it's not necessary. The GC is supposed to take care of this for you. But constructing complex aggregates can be quite expensive in terms of GC use, as a separate allocation must be made for every object. This can also reduce locality of the objects are of different sizes, which may affect performance by incurring cache misses. Now, it is possible in D 2.0 to get the size of an object and reserve space for it using a static array, but this requires the use of placement new, which requires yet more fancy template code to bolt onto existing classes, etc. So this is certainly possible, but far messier than in C++. And it's really a fairly common design requirement.
Sean
| |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply