On Tuesday, 4 February 2014 at 23:51:35 UTC, Andrei Alexandrescu
wrote:
Consider we add a library slice type called RCSlice!T. It would
have the same primitives as T[] but would use reference
counting through and through. When the last reference count is
gone, the buffer underlying the slice is freed. The underlying
allocator will be the GC allocator.
Now, what if someone doesn't care about the whole RC thing and
aims at convenience? There would be a method .toGC that just
detaches the slice and disables the reference counter (e.g. by
setting it to uint.max/2 or whatever).
Then people who want reference counting say
auto x = fun();
and those who don't care say:
auto x = fun().toGC();
Destroy.
Andrei
I think I'd rather have a higher-level solution than this. I
would worry about having to reason about code that's littered
with toGC() or toARC() calls: my peanut butter (application
logic) is now mixed in with my chocolate (allocation logic),
making both harder to understand.
I thought there was some discussion about arenas and custom
allocators a while back, and that seemed like a sensible way to
address these issues. I had imagined (naively?) that I would have
been able to write something like:
auto someFunction(T)(T params)
{
ComplicatedResult result;
{
// set up allocator at start of scope
auto arena = GrowableArena();
push_allocator(arena); // thread-local allocator
scope(exit) pop_allocator();
// call as many expensive functions as you like...
auto tmp = allocation_intensive_computation(params);
// transitive-move result from arena, at the scope's tail
result = tmp.toGC();
} // arena goes out of scope and is deallocated
return result;
}
So, allocation and lifetime issues are handled at the boundaries
of scopes that have custom allocators associated with them.
Um... destroy?
Graham