www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - need help with CTFE

reply "Dmitri Makarov" <dmakarv gmail.com> writes:
I'm compiling the following application (2 d modules)

// file c/tool.d
module c.tool;

struct Tool
{
   string name;

   auto generate()
   {
     version (DOES_NOT_WORK)
     {
       import std.traits : hasMember;
       if (hasMember!(Tool, name))
         return `writeln("this is a ` ~ mixin ("this." ~ name ~ 
"()") ~ `.");`;
     }
     else
     {
       if (name == "hammer")
         return `writeln("this is a ` ~ this.hammer() ~ `.");`;
     }
     return `writeln("unknown tool ` ~ name ~ `.");`;
   }

   auto hammer()
   {
     return "screwdriver";
   }
}

string code(string input)
{
   auto tool = Tool(input);
   return tool.generate();
}
-----------------
// file x/app.d
class Application
{
   void run()
   {
     import c.tool;
     import std.stdio;
     mixin (code("hammer"));
     mixin (code("transmogrifier"));
   }
}

int main(string[] args)
{
   auto app = new Application();
   app.run();
   return 0;
}

When I compile version DOES_NOT_WORK, I get the following error:
c/tool.d(13): Error: variable name cannot be read at compile time
c/tool.d(13):        while looking for match for hasMember!(Tool, 
name)

However, the other version ('else' case) compiles, runs, and 
outputs (as expected):
this is a screwdriver.
unknown tool transmogrifier.

What's the fundamental difference that makes the variable 'name' 
readable in one version and unreadable in another?  Should the 
version DOES_NOT_WORK not be compilable in principle or is it 
only a limitation of the current CTFE implementation in the 
front-end?

Thanks.
Mar 26 2015
parent reply "anonymous" <anonymous example.com> writes:
On Thursday, 26 March 2015 at 16:19:17 UTC, Dmitri Makarov wrote:
 When I compile version DOES_NOT_WORK, I get the following error:
 c/tool.d(13): Error: variable name cannot be read at compile 
 time
 c/tool.d(13):        while looking for match for 
 hasMember!(Tool, name)

 However, the other version ('else' case) compiles, runs, and 
 outputs (as expected):
 this is a screwdriver.
 unknown tool transmogrifier.

 What's the fundamental difference that makes the variable 
 'name' readable in one version and unreadable in another?

 Should the version DOES_NOT_WORK not be compilable in principle 
 or is it only a limitation of the current CTFE implementation 
 in the front-end?
In DOES_NOT_WORK you're trying to pass `name` in a template value parameter. You cannot do that, because `name` is a "dynamic value" but you can only pass a "static value" there. (There may be better terms than dynamic/static value.) You may think: But it all happens in CTFE, so all values are "compile time values" or "static values", aren't they? They aren't. The semantics during CTFE are the same as for run time. `name` is still a dynamic value. If it doesn't fly for run time execution, it doesn't fly in CTFE. To solve the problem at hand, here's one solution that's similar to what you tried: string generate() { import std.traits : isCallable; foreach(memberName; __traits(allMembers, Tool)) { if(memberName == name) { alias M = typeof(mixin("this." ~ memberName)); static if(isCallable!M) { return `writeln("this is a ` ~ mixin("this." ~ memberName ~ "()") ~ `.");`; } } } return `writeln("unknown tool ` ~ name ~ `.");`; } The foreach is (implicitly) a 'static' one, because __traits(allMembers, ...) results in a static/type/expression tuple (I don't know what's the best name to set it apart from other kinds of tuples). That means, `memberName` is a static value. And so it can be used in mixin, whereas `name` cannot be used there.
Mar 26 2015
parent "Dmitri Makarov" <dmakarv gmail.com> writes:
On Thursday, 26 March 2015 at 17:30:40 UTC, anonymous wrote:
 value parameter. You cannot do that, because `name` is a 
 "dynamic value" but you can only pass a "static value" there. 
 (There may be better terms than dynamic/static value.)
Thank you, anonymous. It makes sense. I guess rather than "static/dynamic", I would think of "known/unknown at compile-time". In this case, 'if (name == "hammer")' seems no more static or less dynamic than 'hasMember!(Tool, name)'. Nevertheless, I appreciate your help and will use the solution that you offered.
Mar 26 2015