Reference counting example
cc
cc at nevernet.com
Tue Apr 26 22:16:01 UTC 2022
On Tuesday, 26 April 2022 at 06:55:34 UTC, Alain De Vos wrote:
> Can someone provide a simple/very simple reference counting or
> refcounted example i can understand. Thanks.
I've been playing around with the automem[1] library's RefCounted
feature as we speak, it seems to fit my needs more than
std.typecons which doesn't quite do what I want. I did have to
make some changes to the library though to allow for inheritance
and manually releasing (below). It's pretty fun so far so I'm
looking forward to trying it in some other projects like a non-GC
XML library.
[1] https://github.com/atilaneves/automem
Test application:
```d
import std.stdio;
import core.memory;
import util.array; // ARRAY Custom wrapper for std.container.array
// The vector/array library provided with automem does NOT
properly destroy array elements
// so we'll use std.container.array instead
import std.experimental.allocator.mallocator;
import automem;
alias RC(T) = RefCounted!(T, Mallocator);
// Optional default constructor workaround
auto RCREATE(T, Args...)(auto ref Args args) {
return RC!T.create(args);
}
class Farm {
ARRAY!(RC!Cow) animals;
//this() {}
this(int) { writeln("[Farm]"); }
~this() {
writeln("[~Farm]");
animals.clear();
writeln("[/Farm]");
}
void pet(RC!Animal animal) {
writefln("Farm: The %s says...", animal);
animal.speak;
}
}
class Animal {
void speak() {
writeln("Animal: ???");
}
}
class Cow : Animal {
ARRAY!(RC!Animal) friends; // Amazingly, this works, as long as
the array elem type is NOT the same as RC!(this class)
// otherwise we get a forwarding error
int x;
this() { writefln("[Cow]"); }
this(int x) { this.x = x; writefln("[Cow %s]", x); }
~this() { writefln("[/Cow %s]", x); }
override void speak() {
writefln("Cow#%s: Moo.", x);
}
}
void main() {
auto used = GC.stats.usedSize;
scope(exit) assert(GC.stats.usedSize == used); // GC is not
touched!
{
assert(RCREATE!Cow.x == 0);
assert(RCREATE!Cow(99).x == 99);
}
RC!Animal other;
auto farm = RC!Farm(1);
{
auto cow = RC!Cow(1);
farm.animals ~= cow;
farm.animals ~= RC!Cow(2);
other = farm.animals[1];
auto cowGoesOutOfScope = RC!Cow(70);
}
writeln("out, should have seen Cow#70's dtor");
farm.animals[0] = farm.animals[1];
writeln("animals[0] (Cow#1) just got overwritten so we should
have seen its dtor");
farm.animals ~= RC!Cow(3);
farm.pet(other);
other = null;
farm = null;
writeln("done");
}
```
Output:
```
[Cow]
[/Cow 0]
[Cow 99]
[/Cow 99]
[Farm]
[Cow 1]
[Cow 2]
[Cow 70]
[/Cow 70]
out, should have seen Cow#70's dtor
[/Cow 1]
animals[0] (Cow#1) just got overwritten so we should have seen
its dtor
[Cow 3]
Farm: The memtest.Cow says...
Cow#2: Moo.
[~Farm]
[/Cow 2]
[/Cow 3]
[/Farm]
done
```
I added the following functions to automem `ref_counted.d`:
```d
// static .create method to allow use of class's default
constructor if desired
static if (isGlobal && is(Type == class) &&
__traits(compiles, new Type())) {
static auto create(Args...)(auto ref Args args) {
typeof(this) obj;
obj.makeObject!args();
return obj;
}
}
// allow instantiation or assignment from derived classes if
the Allocator is the same
this(U)(ref RefCounted!(U,Allocator) rhs) if (is(U == class)
&& !is(U == Type)) {
_impl = cast(typeof(_impl)) rhs._impl;
if(_impl !is null) inc;
}
void opAssign(U : Type)(ref RefCounted!(U,Allocator) other)
if (is(U == class) && !is(U == Type)) {
// if (_impl == other._impl) return;
if (_impl._rawMemory.ptr == other._impl._rawMemory.ptr)
return;
if(_impl !is null) release;
static if(!isGlobal)
_allocator = other._allocator;
_impl = cast(typeof(_impl)) other._impl;
if(_impl !is null) inc;
}
// Allow assigning null to manually release payload
void opAssign(typeof(null)) {
if(_impl !is null) release;
_impl = null;
}
```
More information about the Digitalmars-d-learn
mailing list