digitalmars.D - Aggregates & associations
- Bruce Adams (81/81) Dec 18 2007 Hi,
- Daniel Keep (43/43) Dec 18 2007 You seem to be getting confused as to how D classes work. In D, classes
- Bruce Adams (78/122) Dec 18 2007 es
-
Jarrett Billingsley
(15/15)
Dec 18 2007
"Bruce Adams"
wrote in message - Bruce Adams (19/38) Dec 18 2007 That's an explanation for struct versus class but not the more general c...
- =?ISO-8859-1?Q?Jari-Matti_M=E4kel=E4?= (3/5) Dec 23 2007 And OE doesn't specify the message encoding. Well luckily there's rarely...
- Sean Kelly (13/13) Dec 18 2007 A long time ago Walter mentioned that the "scope" attribute may be
- Steven Schveighoffer (7/19) Dec 19 2007 I believe this type of behavior is not necessary. Whether a member is a...
- Jason House (2/23) Dec 19 2007 It has more value that that. It's easy for me to imagine a scope class ...
- Bruce Adams (14/47) Dec 19 2007 Also failing early can be a good thing. Attempting to use an associated ...
- Sean Kelly (9/60) Dec 19 2007 Interestingly, if objects are constructed inside the memory reserved for...
- Bruce Adams (7/66) Dec 19 2007 Except that at present there's no way to declare that an object is
- Sean Kelly (4/20) Dec 19 2007 Why? It works this way for class variables declared elsewhere. As a
- Bruce Adams (17/36) Dec 20 2007 Sorry I should have qualified that. Scope as I understand it, declares
- Jarrett Billingsley (16/27) Dec 20 2007 See, it's an optimization. Guess who should be doing optimizations? Hi...
- Bruce Adams (9/43) Dec 20 2007 Its not quite as simple as that. What needs to be optimised depends on h...
- Sean Kelly (12/33) Dec 19 2007 You're right that it's not necessary. The GC is supposed to take care
- Steven Schveighoffer (4/36) Dec 20 2007 I didn't quite understand your original post, thanks for explaining it
- Christopher Wright (7/30) Dec 19 2007 I think the desired behavior is this:
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?
Dec 18 2007
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
Dec 18 2007
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, class=eswork 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 o=fFoo, 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 =3D=3D &b.foo =3D=3D &b. =If b isdeclared as a function local, it takes up 12 bytes on the stack; if it='sdeclared 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 represen=taggregates as structs and associations with classes. Note that the on=lyone 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 sa= me 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 thing= s. = 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 ha= ve = 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 =3D new foo; versus Foo& foo =3D 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 th= e = 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 i=salso 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. -- DanielIt 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" vers= us "this is a reference" as a qualifier on the type. In c++ value was the norm and reference was &. So if reference is the no= rm = 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/
Dec 18 2007
"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...)
Dec 18 2007
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)
Dec 18 2007
On Tue, 18 Dec 2007, Jarrett Billingsley wrote:(side note: absolutely _everyone_ who uses Opera as their newsreader, their posts don't quote correctly in OE...)And OE doesn't specify the message encoding. Well luckily there's rarely non-ASCII discussion here.
Dec 23 2007
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
Dec 18 2007
"Sean Kelly" wroteA 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
Dec 19 2007
Steven Schveighoffer Wrote:"Sean Kelly" wroteIt 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)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.
Dec 19 2007
On Wed, 19 Dec 2007 15:11:46 -0000, Jason House <jason.james.house gmail.com> wrote:Steven Schveighoffer Wrote: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."Sean Kelly" wroteIt 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)A long time ago Walter mentioned that the "scope" attribute may beextendedto indicate aggregation: class Foo { Bar valA; scope Baz val2; } With the above, the size of Foo would be expanded to contain aninstanceof Baz and val2 would be initialized to reference this memory, verymuchlike "scope" works in functions now. But I didn't see any mention ofthisin 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.
Dec 19 2007
Bruce Adams wrote:On Wed, 19 Dec 2007 15:11:46 -0000, Jason House <jason.james.house gmail.com> wrote:Interestingly, if objects are constructed inside the memory reserved for other objects, then if one is collected by the GC it must be true that all other objects in the same block are free as well. Also, since they share memory, all the objects can safely refer to one another in their dtors so long as they are careful to avoid using member data that may already have been released. As you say, this promises to offer some interesting possibilities for composite objects. SeanSteven Schveighoffer Wrote: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."Sean Kelly" wroteIt 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)A long time ago Walter mentioned that the "scope" attribute may beextendedto indicate aggregation: class Foo { Bar valA; scope Baz val2; } With the above, the size of Foo would be expanded to contain aninstanceof Baz and val2 would be initialized to reference this memory, verymuchlike "scope" works in functions now. But I didn't see any mentionof thisin 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.
Dec 19 2007
On Wed, 19 Dec 2007 20:41:58 -0000, Sean Kelly <sean f4.ca> wrote:Bruce Adams wrote:Except that at present there's no way to declare that an object is contained in another object except as a struct and a struct is not an object. I don't think scope would do this and if it did it would be ahem beyond its scope to do so.On Wed, 19 Dec 2007 15:11:46 -0000, Jason House <jason.james.house gmail.com> wrote:Interestingly, if objects are constructed inside the memory reserved for other objects, then if one is collected by the GC it must be true that all other objects in the same block are free as well. Also, since they share memory, all the objects can safely refer to one another in their dtors so long as they are careful to avoid using member data that may already have been released. As you say, this promises to offer some interesting possibilities for composite objects. SeanSteven Schveighoffer Wrote: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."Sean Kelly" wroteIt 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)A long time ago Walter mentioned that the "scope" attribute may beextendedto indicate aggregation: class Foo { Bar valA; scope Baz val2; } With the above, the size of Foo would be expanded to contain aninstanceof Baz and val2 would be initialized to reference this memory, verymuchlike "scope" works in functions now. But I didn't see any mentionof thisin 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.
Dec 19 2007
Bruce Adams wrote:On Wed, 19 Dec 2007 20:41:58 -0000, Sean Kelly <sean f4.ca> wrote:Why? It works this way for class variables declared elsewhere. As a QOI feature, but still... SeanInterestingly, if objects are constructed inside the memory reserved for other objects, then if one is collected by the GC it must be true that all other objects in the same block are free as well. Also, since they share memory, all the objects can safely refer to one another in their dtors so long as they are careful to avoid using member data that may already have been released. As you say, this promises to offer some interesting possibilities for composite objects.Except that at present there's no way to declare that an object is contained in another object except as a struct and a struct is not an object. I don't think scope would do this and if it did it would be ahem beyond its scope to do so.
Dec 19 2007
On Thu, 20 Dec 2007 00:49:05 -0000, Sean Kelly <sean f4.ca> wrote:Bruce Adams wrote:Sorry I should have qualified that. Scope as I understand it, declares whether a variable should be deleted or it. i.e. whether it is owned by the current scope, which in the proposed extension would mean class scope. It does not say anything about the memory layout. Whether the object is a value or reference is still up for grabs. It doesn't usually matter in functions but in a class it does, at least as an optimisation. I think something like the "val" syntax would make this kind of optimisation possible as well as solving the pass by value, pass by reference optimisation problem. Bruce.On Wed, 19 Dec 2007 20:41:58 -0000, Sean Kelly <sean f4.ca> wrote:Why? It works this way for class variables declared elsewhere. As a QOI feature, but still... SeanInterestingly, if objects are constructed inside the memory reserved for other objects, then if one is collected by the GC it must be true that all other objects in the same block are free as well. Also, since they share memory, all the objects can safely refer to one another in their dtors so long as they are careful to avoid using member data that may already have been released. As you say, this promises to offer some interesting possibilities for composite objects.Except that at present there's no way to declare that an object is contained in another object except as a struct and a struct is not an object. I don't think scope would do this and if it did it would be ahem beyond its scope to do so.
Dec 20 2007
"Bruce Adams" <tortoise_74 yeah.who.co.uk> wrote in message news:op.t3mssesyxikks4 lionheart.cybernetics...Sorry I should have qualified that. Scope as I understand it, declares whether a variable should be deleted or it. i.e. whether it is owned by the current scope, which in the proposed extension would mean class scope. It does not say anything about the memory layout. Whether the object is a value or reference is still up for grabs. It doesn't usually matter in functions but in a class it does, at least as an optimisation.See, it's an optimization. Guess who should be doing optimizations? Hint: it's not the programmer. I'd place manual specification of whether a scope class is allocated inline the body of its owning class or function in the same category as "inline" and "register" -- ultimately meaningless hints to the compiler to do something that it can figure out better than you can. Chances are if we got scope class references in classes they _would_ be inlined into the owning class's body, if for no other reason than the principle of least surprise -- it already happens in functions -- and also because, well, it's a good optimization. This is why C++ is complex. It gives you all these possibilities and features, but leaves it up to you to make something useful out of them. D tries to give you useful _semantic_ features whose semantics leave room for optimization, freeing you from dealing with the low-level crap that C++ makes you deal with. I don't want D to go to the way C++ does it.
Dec 20 2007
On Thu, 20 Dec 2007 14:38:40 -0000, Jarrett Billingsley <kb3ctd2 yahoo.com> wrote:"Bruce Adams" <tortoise_74 yeah.who.co.uk> wrote in message news:op.t3mssesyxikks4 lionheart.cybernetics...Its not quite as simple as that. What needs to be optimised depends on how the program is used. The compiler cannot know that unless you add some pretty strong AI (or at least a way to feedback profiling results to your compiler). This is why inlining is still occasionally useful in C++, not because of bad language design.Sorry I should have qualified that. Scope as I understand it, declares whether a variable should be deleted or it. i.e. whether it is owned by the current scope, which in the proposed extension would mean class scope. It does not say anything about the memory layout. Whether the object is a value or reference is still up for grabs. It doesn't usually matter in functions but in a class it does, at least as an optimisation.See, it's an optimization. Guess who should be doing optimizations? Hint: it's not the programmer. I'd place manual specification of whether a scope class is allocated inline the body of its owning class or function in the same category as "inline" and "register" -- ultimately meaningless hints to the compiler to do something that it can figure out better than you can. Chances are if we got scope class references in classes they _would_ be inlined into the owning class's body, if for no other reason than the principle of least surprise -- it already happens in functions -- and also because, well, it's a good optimization. This is why C++ is complex. It gives you all these possibilities and features, but leaves it up to you to make something useful out of them. D tries to give you useful _semantic_ features whose semantics leave room for optimization, freeing you from dealing with the low-level crap that C++ makes you deal with. I don't want D to go to the way C++ does it.
Dec 20 2007
Steven Schveighoffer wrote:"Sean Kelly" wroteYou'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. SeanA 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.
Dec 19 2007
"Sean Kelly" wroteSteven Schveighoffer wrote:I didn't quite understand your original post, thanks for explaining it further. -Steve"Sean Kelly" wroteYou'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.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.
Dec 20 2007
Steven Schveighoffer wrote:"Sean Kelly" wroteI think the desired behavior is this: 1. Any object can have zero or one owners. 2. Any object that has an owner is collected and has its destructor called after its owner is collected and has its destructor called. 3. As an exception to (2), if an object's owner has no reference to it, the object can be collected.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
Dec 19 2007