天天看点

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