|  | 
  | Posted by FeepingCreature |  Permalink Reply |  
  | 
FeepingCreature  
 
 
  | // So I was working on Nullable,
// and I thought: how could this be made to work better?
// The following code runs on DMD master,
// and it is, technically, `safe`;
// but it REALLY REALLY shouldn't be.
module turducken;
import std.algorithm;
import std.datetime;
// The Turducken Type Technique is the answer to a challenge.
// How do we perform late binding and early unbinding
// on a type that's really, really evil?
// First of all, let's set the stage.
// We're going to construct the Type from Hell.
// It's a struct value type, giving us the
// greatest number of avenues to be horrible.
struct Satan
{
    // It has an immutable value field, referencing immutable data.
    immutable int[] i;
    // It has a SysTime: it's caused issues for
    // me in the past and I hold a grudge.
    SysTime st;
    // It has no default constructor.
    @disable this();
    // It has an .init that violates its invariant.
    bool properlyInitialized = false;
    invariant { assert(properlyInitialized); }
    // It has a copy assignment overload,
    // specifically so we can forbid copy assignment.
    void opAssign(Satan) { assert(false); }
    // To confirm that every constructor call matches one destructor call,
    // it counts references.
    int *refs;
    this(int* refs)
    pure @safe @nogc
    {
        this.properlyInitialized = true;
        this.refs = refs;
        (*refs)++;
    }
    this(this)
    pure @safe @nogc
    { (*refs)++; }
    // Since a destructor is defined, we will definitely
    // assert out if the .init value is ever destructed.
    ~this()
    pure @safe @nogc
    in(refs)
    out(; *refs >= 0)
    do { (*refs)--; }
}
// Now, this is the challenge.
// In a function that is
pure // pure,
@safe // safe,
@nogc // and nogc:
unittest
{
    // perform late binding and early unbinding of the Type from Hell.
    // Let's do some prep.
    // (for validation)
    int refs;
    // (cheat a bit)
    int* refsp = () @trusted { return &refs; }();
    {
        // We start with a default initialized variable.
        Turducken!Satan magic;
        // we bind it to a constructed value
        magic.bind(Satan(refsp));
        // There's now exactly one copy of Satan in the world:
        assert(refs == 1);
        // Just for laughs, bind over it:
        magic.bind(Satan(refsp));
        // Still only one copy around.
        assert(refs == 1);
        // all is well with the contained value
        assert(magic.value.properlyInitialized);
        // Now we unbind it before scope end
        magic.unbind;
        // Satan is gone now.
        assert(refs == 0);
    }
    // And he was destroyed exactly once, as is proper.
    assert(refs == 0);
}
// How did we do this?
// Turducken!
struct Turducken(T)
{
    // The delicious center.
    alias Chicken = T;
    // Union ensures that the T destructor is not called.
    union Duck
    {
        Chicken chicken; // chicken chicken? chicken!
    }
    // Struct ensures that moveEmplace and its magic
    // constness "tolerant" (violating) memcpy can be used.
    struct Turkey
    {
        Duck duck;
    }
    Turkey store = Turkey.init; // take you to the turkey store
    bool set = false;
    // Stick a straw in it to get at the delicious gooey center.
    @property ref T value() in(set) { return store.duck.chicken; }
    // And the special sauce:
    void bind(T value)
    {
        // clean up in case we're back for seconds
        unbind;
        // Make a destructor-protected copy to stick in our store
        Turkey wrapper = Turkey(Duck(value));
        // Plow over the existing data in total disregard of constness
        () @trusted { moveEmplace(wrapper, store); }();
        set = true;
    }
    // Various condiments:
    void unbind()
    {
        if (set)
        {
            static if (is(T == struct)) {
                destroy(value);
            }
            set = false;
        }
    }
    // Since we have shielded our value from D's watchful eyes,
    // we have to remember to clean up manually
    // Clean your dishes!
    ~this()
    {
        unbind;
    }
}
// And that's the Turducken Type Technique. Deliciously evil!
 |