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;
    }
```

Reply via email to