Presenting the Turducken Type Technique: Certified Evil™

FeepingCreature feepingcreature at gmail.de
Wed Jul 11 18:36:24 UTC 2018


// 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!



More information about the Digitalmars-d mailing list