voile.refcnt

参照カウンタ
alias Allocator = CountedInstance* delegate() pure nothrow;
アロケータ
CountedDataを作成する際、メモリを割り当てる関数の型。
alias Deallocator = void delegate(CountedInstance* instance) pure nothrow @nogc;
デアロケータ
CountedDataを解放する際、メモリを解放する関数の型。
template isAllocator(T...) if (T.length > 0 && isCallable!(T[0]))
アロケータかどうかを判定する
Examples:
static assert(isAllocator!(CountedInstance* delegate() @safe pure nothrow));
static assert(isAllocator!(CountedInstance* delegate() @system pure nothrow const));
static assert(isAllocator!(CountedInstance* delegate() @system pure nothrow @nogc));
static assert(!isAllocator!(CountedInstance* delegate() @system nothrow @nogc));
static assert(!isAllocator!(CountedInstance* delegate() @system pure @nogc));

void aaa();
static assert(!isAllocator!aaa);
CountedInstance* bbb() pure nothrow;
static assert(isAllocator!bbb);
template isDeallocator(T...) if (T.length > 0 && isCallable!(T[0]))
デアロケータかどうかを判定する
Examples:
static assert(isDeallocator!(void delegate(CountedInstance*) @safe pure nothrow @nogc));
static assert(isDeallocator!(void delegate(CountedInstance*) @system pure nothrow @nogc));
static assert(isDeallocator!(void delegate(CountedInstance*) @system pure nothrow @nogc inout));
static assert(!isDeallocator!(void delegate(CountedInstance*) @system nothrow @nogc const));
static assert(!isDeallocator!(void delegate(CountedInstance*) @system pure @nogc));

void aaa();
static assert(!isDeallocator!aaa);
void bbb(CountedInstance*) pure nothrow @nogc;
static assert(isDeallocator!bbb);
pure nothrow @nogc @safe auto defaultAllocatorByMalloc(size_t bufSize = 0);
malloc/freeによるアロケータ
pure nothrow auto defaultAllocatorByGC(size_t bufSize = 0);
GCによるアロケータ
struct CountedInstance;
参照カウンタ用のデータを作成する
int counter;
参照カウンタ
ubyte[] rawData;
生データ(データのインスタンスと拡張領域を含むメモリ領域全体)
Deallocator deallocator;
解放時に呼び出すためのコールバック
template CountedImpl(T)
参照カウンタ用のデータを作成するmixinテンプレート
inout pure nothrow @nogc @property @safe inout(ubyte)[] buffer();
生データ(データのインスタンスと拡張領域を含むメモリ領域全体)
inout pure nothrow @nogc @property ref @trusted inout(T) data();
データのインスタンス
inout pure nothrow @nogc @property @safe inout(ubyte)[] extra();
拡張データ領域
pure @nogc @system void initializeCountedInstance(ubyte[] refbuf);
pure @nogc @system void initializeCountedInstance(size_t bufSize = T.sizeof);
void initializeCountedInstance(Alloc)(scope Alloc alloc);
初期化する
Note: 本関数を呼び出して初期化した場合、正しくrelease(CountedInstanceのdeallocatorの呼び出し)をしなければ メモリリークする
const pure nothrow @nogc @safe bool opCast(T)()
if(is(T == bool));
キャスト
pure @nogc @system int addRef();
参照カウンタ加算
pure @system int release();
参照カウンタ減算と解放
struct CountedData(T);
参照カウントを持つを形成する(RefCounted専用)
CountedImplを実装しており、isCountedData!Tでtrueを返す型です。 ただし、メソッドはすべてモジュール内ローカルとなっており、アクセスは許されません。 かならずRefCountedを通してアクセスしてください。
template isCountedData(T)
RefCountedにできるデータか検証する
RefCountedに直接対応可能な型は以下の特徴を備えています。
  • 参照型か、ポインタのサイズと同じサイズ
  • releaseまたはRelease関数を備えていて、それらは整数を返す
  • addRefまたはAddRef関数を備えていて、それらは整数を返す
上記に該当しない場合、RefCounted!Tとすると、自動的に上記特徴を備えたCountedData!Tが内部的に使用されます。
Parameters:
T 調べたい型
See Also: CountedData
Examples:
static struct S1 {}
static assert(!isCountedData!S1);
static assert(isCountedData!(CountedData!S1));
static struct S2 { size_t x; uint release() {return 0;} uint addRef(){return 0;} }
static assert(isCountedData!S2);
static assert(isCountedData!(CountedData!S2));

class C1 {}
static assert(!isCountedData!C1);
class C2 { uint release(){return 0;} uint addRef(){return 0;}}
static assert( isCountedData!C2);

static assert(!isCountedData!int);
static assert(!isCountedData!(int*));
static assert(isCountedData!(CountedData!int));
struct RefCounted(T);
参照カウンタのあるデータを管理する
isCountedDataなデータの参照カウントをコピーの発生や寿命の終了で自動的に増減させる。
Parameters:
T isCountedDataなclass/interface/structのポインタ/size_tと同サイズのstruct、およびCountedDataのインスタンス
See Also: isCountedData
Examples:
string[] msg;
static class C
{
	int cnt;
	string[]* msg;
	this(int x, string[]* m) {cnt = x; msg = m; }
	~this() @trusted {*msg ~= "dtor"; }
	int release() @trusted { cnt--; return cnt; }
	int addRef(){  cnt++; return cnt;}
}

{
	// コンストラクタを使うとカウント値が増える
	RefCounted!C dat1 = new C(0, (() @trusted => &msg)());
	assert(dat1.cnt == 1);
}
// スコープを抜けてRefCountedのインスタンスの寿命が終わると、デストラクタが呼ばれる
assert(msg == ["dtor"]);
@trusted this(U)(U newRefData)
if(is(U : RefType!T));
参照カウンタを保持してアタッチ&加算する
void opAssign(U)(U newRefData)
if(is(U : RefType!T));
参照カウンタを保持してアタッチ&加算する
ref auto opUnary(string op)()
if(op == "*");
参照外し
enum auto isRefCounted(T);
参照カウンタかどうか確認します
Examples:
static assert(isRefCounted!(RefCounted!int));
static assert(!isRefCounted!(int*));
template RefCountedTypeOf(T) if (isRefCounted!T)
参照カウンタの元データの型を得ます
Examples:
static assert(is(RefCountedTypeOf!(RefCounted!int) == int));
bool isInitialized(T)(const ref RefCounted!T dat);
bool isEmpty(T)(const ref RefCounted!T dat);
alias isNull = isEmpty(T)(const ref RefCounted!T dat);
参照が初期化されているか確認する
Examples:
RefCounted!int x;
assert(!x.isInitialized());
assert(x.isEmpty());
assert(x.isNull());
x = createRefCounted!int(1);
assert(x.isInitialized());
assert(!x.isEmpty());
assert(!x.isNull());
pure nothrow @property void attach(T, U)(ref RefCounted!T rc, auto ref U newRefData)
if(is(U : RefType!T));
参照カウンタ加算せずにアタッチする
Examples:
static class C
{
	int cnt;
	this(int x) {cnt = x;}
	int release() @trusted { cnt--; return cnt; }
	int addRef(){  cnt++; return cnt;}
}

// コンストラクタを使うとカウント値が増える
RefCounted!C dat1 = new C(1);
assert(dat1.cnt == 2);
dat1.release();

// attachを使うとカウント値が増えない
RefCounted!C dat2;
dat2.attach(new C(1));
assert(dat2.cnt == 1);
pure nothrow ref inout(T) deref(T)(inout ref RefCounted!T rc);
参照外し
Examples:
auto x = createRefCounted!int(1);
static assert (is(typeof(x.deref) == int));
x.deref = 2;
assert(x == 2);
pure nothrow inout(T)* ptr(T)(inout ref RefCounted!T rc);
ポインタを得る
Examples:
auto x = createRefCounted!int(1);
static assert (is(typeof(x.ptr) == int*));
*x = 2;
assert(x == 2);
(() @trusted => *(x.ptr) = 3)();
assert(x == 3);
@trusted RefCounted!T createRefCounted(T, Args...)(auto ref Args args)
if(isRefCounted!T);
@trusted RefCounted!T createRefCounted(T, Args...)(auto ref Args args)
if(is(T == class) && isCountedData!T);
@trusted RefCounted!T createRefCounted(T, Args...)(auto ref Args args)
if(!isPointer!T && (is(T == struct) || is(T == union)) && isCountedData!T);
@trusted RefCounted!T createRefCounted(T, Args...)(auto ref Args args)
if(isPointer!T && (is(PointerTarget!T == struct) || is(PointerTarget!T == union)) && isCountedData!T);
@trusted RefCounted!T createRefCounted(T, Args...)(auto ref Args args)
if(!isCountedData!T);
@trusted RefCounted!T createRefCounted(T, alias allocator, Args...)(auto ref Args args)
if(!isCountedData!T && isAllocator!allocator);
参照カウンタを生成
Examples:
enum short initCnt = 1;
// class
static class C
{
	short cnt;
	this(int x) {cnt = 1;}
	int release(){ cnt--; return cnt; }
	int addRef(){  cnt++; return cnt;}
}
auto c = createRefCounted!C(1);
auto c2 = c;
assert(c.cnt == 2);

// struct
static struct S
{
	short cnt;
	int release(){ cnt--; return cnt; }
	int addRef(){  cnt++; return cnt;}
}
// struct - for pointer
auto s1 = createRefCounted!(S*)(initCnt);
assert(s1.cnt == 1);
static assert(isCountedData!(S*));
// struct - for CountedData
auto s2 = createRefCounted!S(initCnt);
auto s3 = s1;
assert(s1.cnt == 2);
auto s4 = s2;
// s2, s4はCountedDataなので、インスタンスの中身ではなく
// CountedDataのcounterが増加する
assert(s2.cnt == 1);
assert(s2.counter == 2);

// for union
static union U
{
	short cnt;
	int release(){ cnt--; return cnt; }
	int addRef(){  cnt++; return cnt;}
}
auto u1 = createRefCounted!(U*)(initCnt);
assert(u1.cnt == 1);
static assert(isCountedData!(U*));
auto u2 = createRefCounted!U(initCnt);
assert(u2.cnt == 1);
auto u3 = u1;
assert(u1.cnt == 2);
auto u4 = u2;
// u2, u4はCountedDataなので、インスタンスの中身ではなく
// CountedDataのcounterが増加する
assert(u2.cnt == 1);
assert(u2.counter == 2);

// for scalar data
auto int1 = createRefCounted!int(1);

// with allocator
auto long1 = createRefCounted!(long, defaultAllocatorByGC(long.sizeof))(1);
RefCounted!T attachRefCounted(T)(T newRefData)
if(isCountedData!T);
RefCounted!T attachRefCounted(T)(RefType!T newRefData)
if(!isCountedData!T);
参照カウンタを追加せずにRefCountedを得る
pure nothrow @nogc @property @system inout(ubyte)[] buffer(T)(inout ref RefCounted!T rc)
if(!isCountedData!T || isInstanceOf!(CountedData, T));
バッファ領域へアクセス
バッファ領域は、Tのインスタンスと拡張領域のメモリ領域全体を指す。
Note: release後に触れた場合、ダングリングポインタへのアクセスの可能性がある。 また、書き換えた場合、本来RefCountedで管理しているデータが損失する可能性がある。
Examples:
auto dat = createRefCounted!int(1);
assert(dat.buffer.length == 4);
assert(dat == *cast(int*)dat.buffer.ptr);
pure nothrow @nogc @property @system inout(ubyte)[] extraBuffer(T)(inout ref RefCounted!T rc)
if(!isCountedData!T || isInstanceOf!(CountedData, T));
拡張データ領域へアクセス
初期化時にTのインスタンスより大きなバッファを与えることで、拡張データ領域を持つことができる。 この関数は拡張データ領域へのアクセス手段を提供する。
Note: release後に触れた場合、ダングリングポインタへのアクセスの可能性がある。 また、書き換えた場合、本来RefCountedで管理しているデータが損失する可能性がある。
Examples:
auto dat = createRefCounted!(int, defaultAllocatorByMalloc(int.sizeof + 4))(1);
assert(dat.buffer.length == 8);
assert(dat.extraBuffer.length == 4);
pure nothrow @nogc @property @safe int counter(T)(const ref RefCounted!T rc)
if(!isCountedData!T || isInstanceOf!(CountedData, T));
カウント値を得る
Examples:
auto dat1 = createRefCounted!int(1);
assert(dat1.counter == 1);
{
	auto dat2 = dat1;
	assert(dat1.counter == 2);
}
assert(dat1.counter == 1);