Jump to page: 1 2
Thread overview
__traits getMember is context sensetive?
Jun 13, 2015
JDemler
Jun 13, 2015
Marc Schütz
Jun 13, 2015
JDemler
Jun 13, 2015
John Colvin
Jun 13, 2015
ketmar
Jun 13, 2015
JDemler
Jun 13, 2015
JDemler
Jun 14, 2015
ketmar
Jun 14, 2015
ketmar
Jun 14, 2015
JDemler
Jun 14, 2015
John Colvin
Jun 14, 2015
JDemler
Jun 14, 2015
John Colvin
Jun 14, 2015
ketmar
Jun 14, 2015
anonymous
Jun 14, 2015
JDemler
Jun 14, 2015
anonymous
Jun 14, 2015
ketmar
June 13, 2015
Hey,

i am trying to wrap my head around __traits.

One thing i just do not understand is following:

struct S{
   string member1;
   int member2;
}

void main(string[] args)
{
   foreach(typeStr; __traits(allMembers, S))
   {
     auto tp = __traits(getMember, S, typeStr);
     static if (__traits(isArithmetic, tp))
       writeln(typeStr ~ " is Arithmetic");
   }
}

Does not compile. "main.d(15): Error: need 'this' for 'member1'
of type 'string'"

But if the inner part of the foreach-loop is changed to:

static if (__traits(isArithmetic, __traits(getMember, S,
typeStr)))
       writeln(typeStr ~ " is Arithmetic");

it compiles and does exactly what i expect it to do.

If i understand it correctly __traits(getMember returns a
reference to that member, so i get why i shouldn't be able to use
it with the class instead of an instance of a class.

But why does it work if it is nested inside a __traits call?
June 13, 2015
On Saturday, 13 June 2015 at 10:01:45 UTC, JDemler wrote:
> Hey,
>
> i am trying to wrap my head around __traits.
>
> One thing i just do not understand is following:
>
> struct S{
>    string member1;
>    int member2;
> }
>
> void main(string[] args)
> {
>    foreach(typeStr; __traits(allMembers, S))
>    {
>      auto tp = __traits(getMember, S, typeStr);
>      static if (__traits(isArithmetic, tp))
>        writeln(typeStr ~ " is Arithmetic");
>    }
> }
>
> Does not compile. "main.d(15): Error: need 'this' for 'member1'
> of type 'string'"
>
> But if the inner part of the foreach-loop is changed to:
>
> static if (__traits(isArithmetic, __traits(getMember, S,
> typeStr)))
>        writeln(typeStr ~ " is Arithmetic");
>
> it compiles and does exactly what i expect it to do.
>
> If i understand it correctly __traits(getMember returns a
> reference to that member, so i get why i shouldn't be able to use
> it with the class instead of an instance of a class.
>
> But why does it work if it is nested inside a __traits call?

Try `alias` instead of `auto`:

    struct S{
       string member1;
       int member2;
    }

    alias I(Args...) = Args;

    void main(string[] args)
    {
       import std.stdio;
       foreach(typeStr; __traits(allMembers, S))
       {
         alias tp = I!(__traits(getMember, S, typeStr));
         static if (__traits(isArithmetic, tp))
           writeln(typeStr ~ " is Arithmetic");
       }
    }

`auto` declares a variable, which in this case will probably contain a delegate to that member.

The workaround with `I` is needed because of a syntactic limitation: `alias tp = __traits(...);` is currently not allowed by the grammar.
June 13, 2015
On Saturday, 13 June 2015 at 10:26:06 UTC, Marc Schütz wrote:
> On Saturday, 13 June 2015 at 10:01:45 UTC, JDemler wrote:
>> Hey,
>>
>> i am trying to wrap my head around __traits.
>>
>> One thing i just do not understand is following:
>>
>> struct S{
>>   string member1;
>>   int member2;
>> }
>>
>> void main(string[] args)
>> {
>>   foreach(typeStr; __traits(allMembers, S))
>>   {
>>     auto tp = __traits(getMember, S, typeStr);
>>     static if (__traits(isArithmetic, tp))
>>       writeln(typeStr ~ " is Arithmetic");
>>   }
>> }
>>
>> Does not compile. "main.d(15): Error: need 'this' for 'member1'
>> of type 'string'"
>>
>> But if the inner part of the foreach-loop is changed to:
>>
>> static if (__traits(isArithmetic, __traits(getMember, S,
>> typeStr)))
>>       writeln(typeStr ~ " is Arithmetic");
>>
>> it compiles and does exactly what i expect it to do.
>>
>> If i understand it correctly __traits(getMember returns a
>> reference to that member, so i get why i shouldn't be able to use
>> it with the class instead of an instance of a class.
>>
>> But why does it work if it is nested inside a __traits call?
>
> Try `alias` instead of `auto`:
>
>     struct S{
>        string member1;
>        int member2;
>     }
>
>     alias I(Args...) = Args;
>
>     void main(string[] args)
>     {
>        import std.stdio;
>        foreach(typeStr; __traits(allMembers, S))
>        {
>          alias tp = I!(__traits(getMember, S, typeStr));
>          static if (__traits(isArithmetic, tp))
>            writeln(typeStr ~ " is Arithmetic");
>        }
>     }
>
> `auto` declares a variable, which in this case will probably contain a delegate to that member.
>
> The workaround with `I` is needed because of a syntactic limitation: `alias tp = __traits(...);` is currently not allowed by the grammar.

Thank you. I tried alias but encountered the limitation mentioned.

June 13, 2015
On Saturday, 13 June 2015 at 10:01:45 UTC, JDemler wrote:
> Hey,
>
> i am trying to wrap my head around __traits.
>
> One thing i just do not understand is following:
>
> struct S{
>    string member1;
>    int member2;
> }
>
> void main(string[] args)
> {
>    foreach(typeStr; __traits(allMembers, S))
>    {
>      auto tp = __traits(getMember, S, typeStr);
>      static if (__traits(isArithmetic, tp))
>        writeln(typeStr ~ " is Arithmetic");
>    }
> }
>
> Does not compile. "main.d(15): Error: need 'this' for 'member1'
> of type 'string'"
>
> But if the inner part of the foreach-loop is changed to:
>
> static if (__traits(isArithmetic, __traits(getMember, S,
> typeStr)))
>        writeln(typeStr ~ " is Arithmetic");
>
> it compiles and does exactly what i expect it to do.
>
> If i understand it correctly __traits(getMember returns a
> reference to that member, so i get why i shouldn't be able to use
> it with the class instead of an instance of a class.
>
> But why does it work if it is nested inside a __traits call?

"auto tp" is treating a runtime variable, but you don't have an actual instance of S. What you want is an alias of the symbol without creating an instance.

Unfortunately alias tp = __traits(getMember, S, typeStr); doesn't work, but if you steal* Alias from std.typetuple(or std.meta, if you're running a very recent dmd build) then you can do

alias tp = Alias!(__traits(getMember, S, typeStr));

and then it will work for you.

*for some reason it's not public, but it's very short and simple:

template Alias(alias a)
{
    static if (__traits(compiles, { alias x = a; }))
        alias Alias = a;
    else static if (__traits(compiles, { enum x = a; }))
        enum Alias = a;
    else
        static assert(0, "Cannot alias " ~ a.stringof);
}
// types and tuples
template Alias(a...)
{
    alias Alias = a;
}

unittest
{
    enum abc = 1;
    static assert(__traits(compiles, { alias a = Alias!(123); }));
    static assert(__traits(compiles, { alias a = Alias!(abc); }));
    static assert(__traits(compiles, { alias a = Alias!(int); }));
    static assert(__traits(compiles, { alias a = Alias!(1,abc,int); }));
}
June 13, 2015
On Sat, 13 Jun 2015 10:36:39 +0000, John Colvin wrote:

> *for some reason it's not public, but it's very short and simple:

it's funny how many useful things are there in Phobos, carefully hidden from user. i believe each D book should include an advice like this: "to fully learn what you can do in D out-of-the-box, carefully read druntime and Phobos sources. you'll find many gems there. but tell noone about your discoveries, neophytes should not get the Secret Knowledge!" ;-)

June 13, 2015
I have another one :)

module test;

struct S{
  string member1;
  int member2;
}

string test(string[] a)
{
  const S s = { member1:"It is also important to go to Mars!"};
  const string y = a[0];
  return y;
}

void main()
{
  enum e = ["member1","member2"];
  pragma(msg, e[0]);
  pragma(msg, test(e));
}

Compiles, works fine while

module test;

struct S{
  string member1;
  int member2;
}

string test(string[] a)
{
  const S s = { member1:"It is also important to go to Mars!"};
  const string y = a[0];
  return __traits(getMember, s, y);
}

void main()
{
  enum e = ["member1","member2"];
  pragma(msg, e[0]);
  pragma(msg, test(e));
}

spits out following:

test.d(11): Error: variable a cannot be read at compile time
test.d(12):        while evaluating y.init
test.d(12): Error: string expected as second argument of __traits getMember instead of __error
test.d(12): Error: cannot implicitly convert expression (false) of type bool to string
member1
test.d(19): Error: CTFE failed because of previous errors in test
test.d(19):        while evaluating pragma(msg, test(["member1", "member2"]))

If i use "member1" directly it works.

What am I missing here?

Thanks for your help


June 13, 2015
On Saturday, 13 June 2015 at 23:51:32 UTC, JDemler wrote:
> I have another one :)
>
> module test;
>
> struct S{
>   string member1;
>   int member2;
> }
>
> string test(string[] a)
> {
>   const S s = { member1:"It is also important to go to Mars!"};
>   const string y = a[0];
>   return y;
> }
>
> void main()
> {
>   enum e = ["member1","member2"];
>   pragma(msg, e[0]);
>   pragma(msg, test(e));
> }
>
> Compiles, works fine while
>
> module test;
>
> struct S{
>   string member1;
>   int member2;
> }
>
> string test(string[] a)
> {
>   const S s = { member1:"It is also important to go to Mars!"};
>   const string y = a[0];
>   return __traits(getMember, s, y);
> }
>
> void main()
> {
>   enum e = ["member1","member2"];
>   pragma(msg, e[0]);
>   pragma(msg, test(e));
> }
>
> spits out following:
>
> test.d(11): Error: variable a cannot be read at compile time
> test.d(12):        while evaluating y.init
> test.d(12): Error: string expected as second argument of __traits getMember instead of __error
> test.d(12): Error: cannot implicitly convert expression (false) of type bool to string
> member1
> test.d(19): Error: CTFE failed because of previous errors in test
> test.d(19):        while evaluating pragma(msg, test(["member1", "member2"]))
>
> If i use "member1" directly it works.
>
> What am I missing here?
>
> Thanks for your help

After a bit of rethinking:

I guess the compiler goes through 2 loops:
the first resolves __traits, the second does ctfe.

That would explain this behavior. "a" is not present to the compiler while it tries to resolve my __traits call, but will be present in case of ctfe.

Is there a workaround for that?


June 14, 2015
On Sat, 13 Jun 2015 23:55:53 +0000, JDemler wrote:

> After a bit of rethinking:
> 
> I guess the compiler goes through 2 loops:
> the first resolves __traits, the second does ctfe.
> 
> That would explain this behavior. "a" is not present to the compiler while it tries to resolve my __traits call, but will be present in case of ctfe.

the thing is that `a` (and `s` for that matter) is a *runtime* variable. even if you can see that it will not change and effectively a constant, compiler doesn't see that and doesn't assume that it can use such constants in CTFE.

the difference is like this:

  enum s0 = "string0"; // compile time constant

  string s1 = "string1"; // runtime constant

compiler doesn't do data flow analysis to prove that s1 will never change and it can be used as compile time constant, it simply plays a crybaby here, complaining that it can't read runtime variable value in compile time.

here's what you can do instead:

  immutable S s = { member1:"It is also important to go to Mars!"};

  template test(string[] a) {
     enum y = a[0];
     enum test = __traits(getMember, s, y);
  }

  void main () {
     enum e = ["member1","member2"];
     pragma(msg, e[0]);
     pragma(msg, test!(e));
  }


June 14, 2015
oh, seems that i managed to make everything even less understandable...

June 14, 2015
On Sunday, 14 June 2015 at 05:52:00 UTC, ketmar wrote:
> oh, seems that i managed to make everything even less understandable...

Your code works perfectly and makes at least some sense to me. Thank you.

If i understand it correctly: __traits-time = templateinstatiation-time = pragma-time before ctfe-time?
« First   ‹ Prev
1 2