www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - __traits getMember is context sensetive?

reply "JDemler" <jakob.demler stud-mail.uni-wuerzburg.de> writes:
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?
Jun 13 2015
next sibling parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
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.
Jun 13 2015
parent "JDemler" <jakob.demler stud-mail.uni-wuerzburg.de> writes:
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.
Jun 13 2015
prev sibling parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
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); })); }
Jun 13 2015
parent reply ketmar <ketmar ketmar.no-ip.org> writes:
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=20 from user. i believe each D book should include an advice like this: "to=20 fully learn what you can do in D out-of-the-box, carefully read druntime=20 and Phobos sources. you'll find many gems there. but tell noone about=20 your discoveries, neophytes should not get the Secret Knowledge!" ;-)=
Jun 13 2015
parent reply "JDemler" <jakob.demler stud-mail.uni-wuerzburg.de> writes:
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
Jun 13 2015
parent reply "JDemler" <jakob.demler stud-mail.uni-wuerzburg.de> writes:
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?
Jun 13 2015
next sibling parent ketmar <ketmar ketmar.no-ip.org> writes:
On Sat, 13 Jun 2015 23:55:53 +0000, JDemler wrote:

 After a bit of rethinking:
=20
 I guess the compiler goes through 2 loops:
 the first resolves __traits, the second does ctfe.
=20
 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.=20 even if you can see that it will not change and effectively a constant,=20 compiler doesn't see that and doesn't assume that it can use such=20 constants in CTFE. the difference is like this: enum s0 =3D "string0"; // compile time constant string s1 =3D "string1"; // runtime constant compiler doesn't do data flow analysis to prove that s1 will never change=20 and it can be used as compile time constant, it simply plays a crybaby=20 here, complaining that it can't read runtime variable value in compile=20 time. here's what you can do instead: immutable S s =3D { member1:"It is also important to go to Mars!"}; template test(string[] a) { enum y =3D a[0]; enum test =3D __traits(getMember, s, y); } void main () { enum e =3D ["member1","member2"]; pragma(msg, e[0]); pragma(msg, test!(e)); } =
Jun 13 2015
prev sibling parent reply ketmar <ketmar ketmar.no-ip.org> writes:
oh, seems that i managed to make everything even less understandable...=
Jun 13 2015
parent reply "JDemler" <jakob.demler stud-mail.uni-wuerzburg.de> writes:
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?
Jun 14 2015
next sibling parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Sunday, 14 June 2015 at 09:46:56 UTC, JDemler wrote:
 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?
Not really. These things are just (conceptually) done as/when they are required. If a template instantiation needs to do ctfe to calculate it's result, it does it. If some code running in ctfe needs the result of a template, it instantiates it. The only distinction you have to think about is compile-time vs run-time, with the caveat that ctfe code is run as if it were run-time, but the result can be used at compile-time. If you imagine ctfe as being "compiling the function, running it, getting the answer and copying it in to your code" then even that distinction goes away and you just have ct vs rt.
Jun 14 2015
parent reply "JDemler" <jakob.demler stud-mail.uni-wuerzburg.de> writes:
On Sunday, 14 June 2015 at 10:04:35 UTC, John Colvin wrote:
 On Sunday, 14 June 2015 at 09:46:56 UTC, JDemler wrote:
 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?
Not really. These things are just (conceptually) done as/when they are required. If a template instantiation needs to do ctfe to calculate it's result, it does it. If some code running in ctfe needs the result of a template, it instantiates it. The only distinction you have to think about is compile-time vs run-time, with the caveat that ctfe code is run as if it were run-time, but the result can be used at compile-time. If you imagine ctfe as being "compiling the function, running it, getting the answer and copying it in to your code" then even that distinction goes away and you just have ct vs rt.
If that is the case then i really do not get why my first example compiles and my second does not. The compiler sees the pragma(msg, test(e)) and runs test(e). If test uses __traits it does not work if it does not it works. If __traits is just another ctfe function i dont see the difference.
Jun 14 2015
parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Sunday, 14 June 2015 at 10:16:24 UTC, JDemler wrote:
 On Sunday, 14 June 2015 at 10:04:35 UTC, John Colvin wrote:
 [...]
If that is the case then i really do not get why my first example compiles and my second does not. The compiler sees the pragma(msg, test(e)) and runs test(e). If test uses __traits it does not work if it does not it works. If __traits is just another ctfe function i dont see the difference.
An easy to remember rule: If it won't work at runtime, it won't work in ctfe.
Jun 14 2015
prev sibling parent reply ketmar <ketmar ketmar.no-ip.org> writes:
On Sun, 14 Jun 2015 09:46:54 +0000, JDemler wrote:

 On Sunday, 14 June 2015 at 05:52:00 UTC, ketmar wrote:
 oh, seems that i managed to make everything even less understandable...
=20 Your code works perfectly and makes at least some sense to me. Thank you. =20 If i understand it correctly: __traits-time =3D templateinstatiation-time =3D pragma-time before ctfe-time?
not quite right. there is no dedicated "CTFE time". compiler triggers CTFE=20 when it needs to, i.e. when it sees something like this: enum myval =3D myfunction(); // or mytemplate!(...) i.e. when it need a value in compile time. the interpreter is invoked, it=20 evaluates (interprets) the given code (function or template=20 instantiation), and then it returns result (or raises an error). so CTFE can occur at any stage of semantic analysis. to be clear: compiler goes (roughly) thru this stages: 1. parsing and building AST. 2. semantic analysis. 3. generating code. at the stage (1) only syntax is checked, and source is converted to=20 internal representation (AST). at the state (2) compiler tries to actually make sense of your code. it=20 deducing types, checking type correctness and such. if it need to=20 instantiate some template, it does that here. it does semantic for each=20 function (note: not for templates or eponymous templates!), one by one. and here compiler may step on something that needs CTFE. it invokes=20 interpreter on this stage. so there is no dedicated "CTFE time", compiler=20 does it while it busy with semantic analysis, on demand. this way it=20 avoids evaluating CTFE values that never used, which is good for compile=20 speed. ;-) pragmas and traits are executed by the very same CTFE engine, on demand=20 too. i.e. when compiler hits pragma or traits during semantic analysis,=20 compiler executes that. overal semantic stage is more complex, of course, but i hope you get the=20 idea.=
Jun 14 2015
parent reply "anonymous" <anonymous example.com> writes:
On Sunday, 14 June 2015 at 10:10:51 UTC, ketmar wrote:
 i.e. when it need a value in compile time. the interpreter is 
 invoked, it evaluates (interprets) the given code (function or 
 template instantiation), and then it returns result (or raises 
 an error).
One important thing I didn't see stated clearly by anyone in here: CTFE may run at compile time but it follows the same rules as run time evaluation (plus some restrictions). This means, you can't use dynamic values (e.g. function parameters) in static contexts (e.g. __traits). Example: ---- int f(int x) { if (__ctfe) { enum e = x; /* nope, not even during CTFE */ alias t = some_template!x; /* nope */ pragma(msg, x); /* nope */ } return x; } enum result = f(1); /* CTFE-ing f */ ----
Jun 14 2015
next sibling parent reply "JDemler" <jakob.demler stud-mail.uni-wuerzburg.de> writes:
On Sunday, 14 June 2015 at 10:29:09 UTC, anonymous wrote:
 On Sunday, 14 June 2015 at 10:10:51 UTC, ketmar wrote:
 i.e. when it need a value in compile time. the interpreter is 
 invoked, it evaluates (interprets) the given code (function or 
 template instantiation), and then it returns result (or raises 
 an error).
One important thing I didn't see stated clearly by anyone in here: CTFE may run at compile time but it follows the same rules as run time evaluation (plus some restrictions). This means, you can't use dynamic values (e.g. function parameters) in static contexts (e.g. __traits). Example: ---- int f(int x) { if (__ctfe) { enum e = x; /* nope, not even during CTFE */ alias t = some_template!x; /* nope */ pragma(msg, x); /* nope */ } return x; } enum result = f(1); /* CTFE-ing f */ ----
So if i want to use parameters in a static context at compile time i have to pass them as template parameters? That would explain my problems. Thanks
Jun 14 2015
parent "anonymous" <anonymous example.com> writes:
On Sunday, 14 June 2015 at 10:41:24 UTC, JDemler wrote:
 So if i want to use parameters in a static context at compile 
 time i have to pass them as template parameters?
Yes, template parameters are fine.
Jun 14 2015
prev sibling parent ketmar <ketmar ketmar.no-ip.org> writes:
On Sun, 14 Jun 2015 10:29:08 +0000, anonymous wrote:

 One important thing I didn't see stated clearly by anyone in here:
=20
 CTFE may run at compile time but it follows the same rules as run time
 evaluation (plus some restrictions).
=20
 This means, you can't use dynamic values (e.g. function parameters) in
 static contexts (e.g. __traits).
yes, i managed to write a wall of text escaping to talk about the main=20 issue of the thread. ;-)=
Jun 14 2015