std.container and classes

foobar foo at bar.com
Tue Dec 20 23:07:23 PST 2011


On Wednesday, 21 December 2011 at 05:08:27 UTC, Jonathan M Davis 
wrote:
> On Monday, December 19, 2011 08:54:00 Jacob Carlborg wrote:
>> The Tango runtime has added a new method in Object, "dispose". 
>> This
>> method is called when "scope" is used or "delete" is 
>> explicitly called.
>> I don't know if that would help.
>
> That sounds a lot like using clear, though clear doesn't free 
> memory unless the finalizer does (and I'm not sure that 
> managing memory with finalizers actually works right now; there 
> were issues with that previously - but it might have only been 
> GC memory, so perhaps malloc and free would still work). The 
> issue with them is escaping references. You can easily end up 
> with references to data which has been destroyed. It also makes 
> them harder to pass around unless you essentially wrap the 
> container in ref-counted struct.
>
> The lifetime of the container must be managed if you really 
> want to be able to use custom allocators or have the container 
> be as efficient as possible with regards to its memory usage in 
> the general case. With a struct, it manages itself. It can do 
> whatever it wants to with memory internally, and the 
> combination of its constructor, postblit constructor, and 
> destructor allows it to take care of it itself and clean up its 
> memory when it's destroyed. You also don't have issues of stuff 
> referring to a container which doesn't exist anymore (unless 
> you add pointers to the container into the mix and then move or 
> destroy the container).
>
> With classes, if they're owned by the GC, then it's the GC that 
> manages their lifetime. It destroys them when they're no longer 
> referenced and it runs a collection cycle. You have no control 
> over its lifetime, and even if it tries to manage its memory 
> internally on some level, that memory won't be cleaned up until 
> the GC collects the container itself. So, if you want to manage 
> the lifetime of the class, you can't use a naked reference 
> anymore. You need a way to get the GC to collect it, or you 
> need to have it somewhere else other than the GC heap.
>
> If you use something like Scoped, then its lifetime will be 
> tied to the scope that it's in, but you risk references to it 
> escaping, and limiting the container to a particular scope 
> could be far too limiting anyway. That being the case, if you 
> want to use an allocator other than the GC for the class 
> itself, then you're probably going to have to stick it in a 
> struct. That struct could then manage the container's lifetime 
> on some level. It would probably have to use ref-counting and 
> then call clear on the container when the ref-count hits zero. 
> That should then allow the container to at least clean up its 
> internal memory, assuming that that's not on the GC heap, but 
> unless you use emplace on non-GC heap memory, the container 
> itself still can't be collected until the GC collects it (since 
> clear destroys it but doesn't free its memory). Not to mention, 
> if you're using a ref-counted struct to hold a class in order 
> to manage that class' lifetime, why on earth did you make it a 
> class in the first place?
>
> If what you're suggesting with dispose is that it would act 
> like clear except that it would actually free the container, 
> well that pretty much goes against the direction that we've 
> been trying to go with the GC, which is to not have the 
> programmer explicitly freeing anything on the GC heap (which is 
> why delete is being removed from the language).
>
> I really think that if we want deterministic destruction of 
> containers, they need to be structs. And if we want to use 
> custom allocators with containers, I don't see how we could 
> reasonably expect them to be of much use unless we're expecting 
> that programmers will explicitly call clear on them when 
> they're done with them. So, while the fact that containers need 
> to be reference types does imply that classes are the way to 
> go, I think that we're going to have to go with structs if we 
> want their memory management to be more efficient than simply 
> letting the GC handle it.
>
> - Jonathan M Davis

I disagree with the above conclusion. you conflate two issues 
that are orthogonal:
a. value vs. ref semantics which is already seemed to be decided 
in favor of the latter and hence classes. b. memory and lifetime 
management

The containers should allow for (disregard the specifics of the 
syntax):

Container a = new(SharedMemAllocator) LinkedList();
Container b = new(MallocAllocator) LinkedList();
Container c = new(GC) LinkedList();

When adding an item to the above containers the relevant 
allocator will enact its policy about intermixing with other 
allocators - by default the item will be copied if it comes from 
a separate allocator. I don't see anything here that forces the 
use of structs instead of classes.



More information about the Digitalmars-d mailing list