Thread overview
MemberDefaults trait
Oct 10, 2016
Ali Çehreli
Oct 11, 2016
Ali Çehreli
Mar 15, 2017
Ali Çehreli
October 10, 2016
Could you please review the following template to see whether it makes sense. It produces an AliasSeq type consisting of the default values of the members of a struct. It should and does support members that are initialized with '= void'. I could not achieve this with std.traits or __traits.

However, I'm not that happy with the use of __traits(compiles) below. Can this be improved?

Thank you,
Ali


import std.meta;

/** Get as an expression tuple the default values of members of a struct. */
template MemberDefaults(T) {
    static if (!is(T == struct)) {
        static assert(T.stringof ~ " is not a struct type");
    }

    import std.traits : FieldNameTuple;
    enum t = T.init;
    alias fields = FieldNameTuple!T;

    template get(size_t i) {
        static if (__traits(compiles, { enum _ = t.tupleof[i]; })) {
            enum get = t.tupleof[i];
        }
        else {
            alias get = void;
        }
    }

    template Impl(size_t i = 0) {
        import std.meta : AliasSeq;
        static if (i == fields.length) {
            alias Impl = AliasSeq!();
        } else {
            alias Impl = AliasSeq!(get!i, Impl!(i+1));
        }
    }

    alias MemberDefaults = Impl!();
}

unittest {
    struct S {
        int i = 42;
        string s = "hello";
        char c = 'c';
    }

    static assert(MemberDefaults!S == AliasSeq!(42, "hello", 'c'));
}

unittest {
    struct S {
        int i = 42;
        int j = void;
    }

    static assert(MemberDefaults!S[0] == 42);
    static assert(is(MemberDefaults!S[1] == void));
}

void main() {
}
October 10, 2016
On 10/10/2016 04:50 PM, Ali Çehreli wrote:

>     static if (!is(T == struct)) {
>         static assert(T.stringof ~ " is not a struct type");
>     }

Wow! That's a nice brain fart on my part. Ok, I can fix that one... :)

Ali

March 14, 2017
On 10/10/2016 04:50 PM, Ali Çehreli wrote:
> Could you please review the following template to see whether it makes
> sense. It produces an AliasSeq type consisting of the default values of
> the members of a struct. It should and does support members that are
> initialized with '= void'. I could not achieve this with std.traits or
> __traits.

Reviving this thread because I've realized that the template does not work for members that are structs themselves.

/** Get as an expression tuple the default values of members of a struct. */
template MemberDefaults(T) {
    static assert (is(T == struct), T.stringof ~ " is not a struct type");

    import std.traits : FieldNameTuple;
    enum t = T.init;
    alias fields = FieldNameTuple!T;

    template get(size_t i) {
        static if (__traits(compiles, { enum get = t.tupleof[i]; })) {
            enum get = t.tupleof[i];
        }
        else {
            alias get = void;
        }
    }

    template Impl(size_t i = 0) {
        import std.meta : AliasSeq;
        static if (i == fields.length) {
            alias Impl = AliasSeq!();
        } else {
            alias Impl = AliasSeq!(get!i, Impl!(i+1));
        }
    }

    alias MemberDefaults = Impl!();
}

struct Foo {
    string s;
    int i;
}

unittest {
    struct S {
        int i = 42;
        string s = "hello";
        char c = 'c';
        int j = void;
        Foo foo0;
        Foo fooVoid = void;
    }

    alias result = MemberDefaults!S;

    import std.meta : AliasSeq;
    if (!is(result == AliasSeq!(42, "hello", 'c', void, Foo, void))) {
        pragma(msg, "Did not match: ", result);
        static assert(false);
    }
}

void main() {
}

Here is the output of a -unittest compilation:

  Did not match: tuple(42, "hello", 'c', (void), Foo(null, 0), Foo(, ))

Notice how the j member worked as expected: Its default value appeared as (void) in the result. (Aside: It's strange that pragma(msg) parenthesizes it.)

The problem is, the default value of the fooVoid member does not appear as void but as "Foo(, )". This seems to be a compiler bug to me. One might parse that string as a workaround but then it wouldn't work for empty structs. (Although, empty structs could be excluded by this template. Hmmm... That might work.)

Can you make this work? i.e. Can you make the result be

  AliasSeq!(42, "hello", 'c', void, Foo, void)

Is it possible to detect members that are void-initialized at all?

Ali