D-Programming-Language/phobos — pull request #922 by monarchdodra (2012-11-03 10:05:26+00:00)
Title: improvements

Third time's a charm.

Contains the ".this" fix. Regarding the context pointer thing, I realized that a struct can embed other struct that can embed static arrays...

Ergo, a recursive approach to "deinitializing" has to be taken.

I added an undocumented trait: `hasContextPointer`

```D
template hasContextPointer(S)
{
static if (is(S == struct) || is(S == class))
{
static if (S.tupleof.length > 0)
{
static if (S.tupleof[$-1].stringof.endsWith(".this"))
enum hasContextPointer = true;
else
enum hasContextPointer = anySatisfy!(.hasContextPointer, typeof(S.tupleof));
}
else
enum hasContextPointer = false;
}
else static if(isStaticArray!S && S.length)
{
enum hasContextPointer = hasContextPointer!(typeof(S[0]));
}
else
{
enum hasContextPointer = false;
}
}
```

The new move dispatches to `deinitialize` to copy the init value over the source, without squishing any context pointers:

```D

/*
Like move, but assumes that source does not contain meaningful content
*/
private void uninitializedMove(T)(ref T source, ref T target)
{
//memcpy *always* works great!
memcpy(&target, &source, T.sizeof);

// If the source defines a destructor or a postblit hook, we must obliterate the
// object in order to avoid double freeing and undue aliasing
static if (is(T == struct) || isStaticArray!T)
static if (hasElaborateDestructor!T || hasElaborateCopyConstructor!T)
{
static T empty = T.init;
deinitialize(source, empty);
}
}

/*
copies $(D source) over $(D target), while keeping any context pointers intact;
*/
void deinitialize(T)(ref T target, ref T source)
{
static if (isStaticArray!T && hasContextPointer!T)
{
foreach(i; 0 .. T.length)
deinitialize(target[i], source[i]);
}
else static if (is(T == struct) && hasContextPointer!T)
{
//Our T has a context pointer. Is it here, or lower?
static if (T.tupleof[$-1].stringof.endsWith(".this"))
{
//It's here, but are there any *other*, lower?
static if (anySatisfy!(.hasContextPointer, typeof(T.tupleof[$-1])))
//yes, deinitialize each inidividually
foreach(i; 0 .. T.tupleof.length - 1)
deinitialize(target.tupleof[i], source.tupleof[i]);
else
//nope, shortcut.
memcpy(&target, &source, T.sizeof - (void*).sizeof);
}
else
{
//It's not this, so it must be lower, deinitialize each inidividually
foreach(i; 0 .. T.tupleof.length)
deinitialize(target.tupleof[i], source.tupleof[i]);
}
}
else
memcpy(&target, &source, T.sizeof);
}
```

--------
I tried to write a more complete unit test with nested context pointers, but I'm subject to issues #8951 and #8952 ...

http://d.puremagic.com/issues/show_bug.cgi?id=8951
http://d.puremagic.com/issues/show_bug.cgi?id=8952

I have a doubt: It *is* legal to nest context pointers... right?