天天看點

d重綁定不變

// Nullable,如何更好工作?技術上是`安全`的,但真不是.
module turducken;

import std.algorithm;
import std.datetime;

// 如何在`邪惡類型`上,早期解除綁定,并再後期綁定
// Turducken是答案.

// 先,設定舞台,值構類型
struct S
{
    // 有一個引用不變資料的不變值字段
    immutable int[] i;

    // SysTime:過去和我沖突
    SysTime st;

    // 無預設
    @disable this();

    // 違反其不變量的.init
    bool properlyInitialized = false;

    invariant { assert(properlyInitialized); }

    // 重載複制指派,可禁止複制指派.
    void opAssign(S) { assert(false); }

    // 為确認每個構造函數調用都比對析構函數調用,它計算引用
    int *refs;

    this(int* refs)
    pure @safe @nogc
    {
        this.properlyInitialized = true;
        this.refs = refs;
        (*refs)++;
    }

    this(this)
    pure @safe @nogc
    { (*refs)++; }

    // 既然定義了析構函數,我們肯定會,斷定已破壞`.init`.
    ~this()
    pure @safe @nogc
    in(refs)
    out(; *refs >= 0)
    do { (*refs)--; }
}

// 挑戰!
//函數為:
pure // pure,
@safe // safe,
@nogc // 及nogc:
unittest
{
    // 幹後期綁定和早期解除綁定

    // 準備

    // (驗證)
    int refs;

    // (欺騙)
    int* refsp = () @trusted { return &refs; }();

    {
        // 從預設初化變量開始
        Turducken!S magic;
        // 綁定到已構造值
        magic.bind(S(refsp));

        // 隻一份
        assert(refs == 1);

        // 為了開心,綁定它
        magic.bind(S(refsp));

        // 仍然隻有一個副本
        assert(refs == 1);

        // 包含的值一切正常
        assert(magic.value.properlyInitialized);

        // 域結束前解除綁定
        magic.unbind;

        // S離開了
        assert(refs == 0);
    }
    // 隻析構一次,正确.
    assert(refs == 0);
}

// 如何完成
// Turducken!
struct Turducken(T)
{
    // 美味中心
    alias Chicken = T;

    //Union確定不調用T析構函數
    union Duck
    {
        Chicken chicken; // 
    }

    //確定可用moveEmplace及神奇(違反常)的memcpy的構
    struct Turkey
    {
        Duck duck;
    }

    Turkey store = Turkey.init; // 火雞店

    bool set = false;

    // 插入吸管,進入中心
    @property ref T value() in(set) { return store.duck.chicken; }

    // 特制的醬汁
    void bind(T value)
    {
        // 為防幾秒後回來,清理
        unbind;

        //制作析構器保護的副本來粘貼在我們商店中
        Turkey wrapper = Turkey(Duck(value));

        // 忽略常,周遊資料.
        () @trusted { moveEmplace(wrapper, store); }();

        set = true;
    }

    // 調味品
    void unbind()
    {
        if (set)
        {
            static if (is(T == struct)) {
                destroy(value);
            }
            set = false;
        }
    }

    // 因為已經避免了值受D的監視,
    // 必須手動清理
    ~this()
    {
        unbind;
    }
}