Thread overview
Manually calling postblots recursively
Jun 18, 2017
Johannes Loher
Jun 18, 2017
Basile B.
Jun 22, 2017
Johannes Loher
June 18, 2017
Hey, I'm trying to work on https://issues.dlang.org/show_bug.cgi?id=15708 so I decided it might be interesting to find a way to (recursively) call all postblits that belong to certain struct or static array. This is what I came up with so far:

import std.traits;

void callPostblits(S)(ref S s)
{
    static if (isStaticArray!S && S.length)
    {
        foreach (ref elem; s)
            callPostblits(elem);
    }
    else static if (is(S == struct))
    {
        foreach (field; FieldNameTuple!S)
        {
            callPostblits(__traits(getMember, s, field));
        }

        static if (hasMember!(S, "__postblit"))
        {
            s.__postblit();
        }
    }
}

@safe unittest
{

    struct AnotherTestStruct
    {
        int b = 0;

        this(this)
        {
            b = 1;
        }
    }

    struct TestStruct
    {
        int a = 0;

        this(this)
        {
            a = 1;
        }

        AnotherTestStruct anotherTestStruct;
    }


    TestStruct[2] testStructs;

    assert(testStructs[0].a == 0 && testStructs[0].anotherTestStruct.b == 0);
    assert(testStructs[1].a == 0 && testStructs[1].anotherTestStruct.b == 0);

    callPostblits(testStructs);

    assert(testStructs[0].a == 1 && testStructs[0].anotherTestStruct.b == 1);
    assert(testStructs[1].a == 1 && testStructs[1].anotherTestStruct.b == 1);
}

Any suggestions for improvement or cases where this fails?
June 18, 2017
On Sunday, 18 June 2017 at 09:41:01 UTC, Johannes Loher wrote:
> Hey, I'm trying to work on https://issues.dlang.org/show_bug.cgi?id=15708 so I decided it might be interesting to find a way to (recursively) call all postblits that belong to certain struct or static array. This is what I came up with so far:
>
> [...]

"@disable" postblits are detected as valid postblits.
I think that you have to AndAnd the detection with std.traits.isCopyable
June 22, 2017
On Sunday, 18 June 2017 at 14:16:03 UTC, Basile B. wrote:
> On Sunday, 18 June 2017 at 09:41:01 UTC, Johannes Loher wrote:
>> Hey, I'm trying to work on https://issues.dlang.org/show_bug.cgi?id=15708 so I decided it might be interesting to find a way to (recursively) call all postblits that belong to certain struct or static array. This is what I came up with so far:
>>
>> [...]
>
> "@disable" postblits are detected as valid postblits.
> I think that you have to AndAnd the detection with std.traits.isCopyable

Here is my new version. Additionally to taking care of @disable this(this); I added compile time checks if the underlying fields have an "elaborate copy constructor" so that I only call callPostblits on them if needed. Is this reasonable? It increases compiletime, but could possibly decrease runtime a little. On the other hand, the empty function calls should probably just get optimized away...?

While doing all that I realized that hasElaborateCopyConstructor is true for structs with @disable this(this). Is that intended? It looks a bit weird to me...



import std.traits;

void callPostblits(S)(ref S s)
{
    static if (isStaticArray!S && S.length && hasElaborateCopyConstructor!(ElementType!S))
    {
        foreach (ref elem; s)
            callPostblits(elem);
    }
    else static if (is(S == struct))
    {
        foreach (field; FieldNameTuple!S)
        {
            static if (hasElaborateCopyConstructor!(typeof(__traits(getMember, s, field))))
            {
                callPostblits(__traits(getMember, s, field));
            }
        }

        static if (hasMember!(S, "__postblit") && isCopyable!S)
        {
            s.__postblit();
        }
    }
}

unittest
{

    struct AnotherTestStruct
    {
        int b = 0;

        this(this)
        {
            b = 1;
        }
    }

    struct TestStruct
    {
        int a = 0;

        this(this)
        {
            a = 1;
        }

        AnotherTestStruct anotherTestStruct;
    }


    TestStruct[2] testStructs;

    assert(testStructs[0].a == 0 && testStructs[0].anotherTestStruct.b == 0);
    assert(testStructs[1].a == 0 && testStructs[1].anotherTestStruct.b == 0);

    callPostblits(testStructs);

    assert(testStructs[0].a == 1 && testStructs[0].anotherTestStruct.b == 1);
    assert(testStructs[1].a == 1 && testStructs[1].anotherTestStruct.b == 1);

    struct YetAnotherTestStruct
    {
        @disable this(this);
    }

    YetAnotherTestStruct yetAnotherTestStruct;
    callPostblits(yetAnotherTestStruct); // This will also compile
}