On Thursday, 29 October 2015 at 02:43:59 UTC, Shammah Chancellor wrote:
Currently D has some very very powerful compile time features, but they can get unwieldily quickly when doing compile time reflection with mildly complicated logic. It's pretty disappointing. I'd like to start a discussion around some of the problems I've ran into using these features. If there aren't bugzilla reports, I can open them but I'm not entirely sure how to file these appropriately.

1) There seems to be many cases where __traits(allMembers, ...) over a package produces symbol strings which cannot be converted to a symbol using __traits(getMember). Also __traits is not useable in reasonable contexts( alias foo = __traits(..)). E.g.

This will fail due to weird symbols being returned from allMembers.
```
import std.stdio;

enum hack(A...) = A;

int main() {
        foreach(member; __traits(allMembers, std.stdio)) {
                pragma(msg, member);
                alias sym = hack!(__traits(getMember, std.stdio, member))[0];
        }
        return 0;
}
```

2) There are oddities around getting the types of functions which are properties:

e.g. (Note the __compiles, and addressing the member):

```
//Gets the member type, even for properties.
private template MemberType(C, string memberName)
{
alias member = TypeTuple!(__traits (getMember, C, memberName))[0];

    static if (__traits (compiles, typeof(&member)))
    {
        static if (isSomeFunction!(typeof(&member)))
        {
            alias MemberType = typeof(&member);
        }
        else
        {
            alias MemberType = typeof(member);
        }
    } else {
        alias MemberType = typeof(member);
    }
}
```

3) Additionally, any kind of iteration becomes very unwieldy. This kind of code becomes necessary in common cases. (E.g. pass in a list of packages and reduce to a AliasSeq of type symbols)


```
import std.stdio : writeln;
import std.typetuple;

// This won't work:
/+
private template stuff(Args...) {
        /+ static +/ foreach(Arg; Args) {
                ...
        }
}
+/

// Must do this instead:
private template expand(Args...)
{
        static if (Args.length > 0)
        {
                static if (Args.length > 1)
                {
enum stuff = AliasSeq!(repeat!(Args[0], Args.length), stuff!(Args[1..$]));
                }
                else static if (Args.length == 1)
                {
                        enum stuff = AliasSeq!(repeat!(Args[0], Args.length));
                }
        }
}

private template repeat(alias T, int times) {
        static if ( times > 1 ) {
                enum repeat = AliasSeq!(T, repeat!(T, times-1));
        } else static if (times == 1) {
                enum repeat = AliasSeq!(T);
        }
}

enum things = expand!("A","B","C");

void main() {
        pragma(msg, things);
        writeln("Hello World");
}
```

I agree with you on an aspect: writing code with __traits() often leads to a cascade of "unfriendly", "cryptic", "undigest", static if and loops.

However your examples are discutables:

1/ The symbol is not always a type, so to test it there is `if(is()){}`:

---
import std.stdio;

enum hack(A...) = A;

int main() {
        foreach(member; __traits(allMembers, std.stdio)) {
                pragma(msg, member);
        static if (is(member))
alias sym = hack!(__traits(getMember, std.stdio, member))[0];
        }
}
---

2/ with only one way to define the alias it works, whatever is the function type (property or not)

---
import std.traits, std.stdio, std.typetuple;

private template MemberType(C, string memberName)
{
alias member = TypeTuple!(__traits (getMember, C, memberName))[0];
    static if (isSomeFunction!(typeof(&member)))
        alias MemberType = typeof(&member);
    else static assert(0, "not handled here !");
}

struct Hop
{
    void foo(uint a){}
    @property uint bar(){return 0;}
    uint baz(){return 0;}
    @property void bla(uint h){}
}

void main()
{
    MemberType!(Hop, "foo").stringof.writeln;
    MemberType!(Hop, "bar").stringof.writeln;
    MemberType!(Hop, "baz").stringof.writeln;
    MemberType!(Hop, "bla").stringof.writeln;
}
---

3/ Your example doesn't compile


Reply via email to