www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Referring to alias parameters in a mixin template

reply "aldanor" <i.s.smirnov gmail.com> writes:
Would something like this be possible at all? A hypothetical 
mixin template

     mixin template makeProperty(T, string name, alias func) {
         ...
     }

that could be called like this:

     makeProperty!(int, "foo", f)

and would generate code like

     int  property foo() { return f(); }

This is a very simplified example of what I'm trying to do, but 
I'm a bit stuck at this point -- if I'm generating the code as a 
string, how do I know how to refer to "func" alias (traits 
identifier / fullyQualifiedName just don't cut it for a lot of 
cases)? For one, thing, it could be an anonymous delegate like { 
return 0; } or symbol from another module or anything else. This 
is obviously doable if "func" is a string that gets mixed in, but 
what if it is an alias?

Without the "name" part, one could sure use a simple template:

     template makeUnnamedProperty(T, alias func) {
         T makeUnnamedProperty()  property { return func(); }
     }

and then this works...

     alias foo = makeUnnamedProperty!(int, f);

However, how does the one go about templating this  when "foo" is 
a (compile-time) string?

Wonder if I'm missing something...

Thanks.
Dec 16 2014
parent reply "aldanor" <i.s.smirnov gmail.com> writes:
A partial solution would be something like this:

     mixin template makeProperty(T, string name, alias func) {
         enum p = makeUnnamedProperty!(T, func);
         mixin("enum %s = p;".format(name)); // or alias
     }

however now the parent namespace is polluted with "p", is there 
any way to hide it away and/or avoid it?

I may be wrong, but I guess the whole thing boils down to a 
question whether it's possible to have a mixin template with 
signature 'bind(string name, alias symbol)' which generates 'enum 
foo = bar;' when called as 'mixin bind!("foo", bar)'?
Dec 16 2014
parent reply "anonymous" <anonymous example.com> writes:
On Wednesday, 17 December 2014 at 01:14:36 UTC, aldanor wrote:
 A partial solution would be something like this:

     mixin template makeProperty(T, string name, alias func) {
         enum p = makeUnnamedProperty!(T, func);
         mixin("enum %s = p;".format(name)); // or alias
     }

 however now the parent namespace is polluted with "p", is there 
 any way to hide it away and/or avoid it?
The polution isn't too bad. Mixed-in symbols are second class. A symbol not from a mixin would win, and multiple mixed-in `p`s would only conflict on use. But if you want to avoid `p`, just do the substitution: mixin template makeProperty(T, string name, alias func) { mixin("enum %s = makeUnnamedProperty!(T, func);".format(name)); // or alias }
Dec 16 2014
parent reply "aldanor" <i.s.smirnov gmail.com> writes:
On Wednesday, 17 December 2014 at 01:39:07 UTC, anonymous wrote:
 But if you want to avoid `p`, just do the substitution:

      mixin template makeProperty(T, string name, alias func) {
          mixin("enum %s = makeUnnamedProperty!(T,
 func);".format(name)); // or alias
      }
Thanks, that looks exactly like what I need -- I figured something like this would compile, but I guess it's slightly counterintuitive that you can access "T" and 'func" this way. Wonder if this is doable within a single mixin template without using makeUnnamedProperty?
Dec 16 2014
parent reply "anonymous" <anonymous example.com> writes:
On Wednesday, 17 December 2014 at 01:49:14 UTC, aldanor wrote:
 Wonder if this is doable within a single mixin template without 
 using makeUnnamedProperty?
Sure, straight forward: mixin template makeProperty(T, string name, alias func) { mixin("T %s() property { return func(); }".format(name)); }
Dec 16 2014
parent reply "aldanor" <i.s.smirnov gmail.com> writes:
On Wednesday, 17 December 2014 at 02:12:52 UTC, anonymous wrote:
 Sure, straight forward:

       mixin template makeProperty(T, string name, alias func) {
           mixin("T %s()  property { return func();
 }".format(name));
       }
Indeed... thanks! Just one thing that I find confusing here -- how exactly do T and func resolve when this template is mixed in? What if there was another local symbol named "func" in the target scope?
Dec 16 2014
next sibling parent ketmar via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
On Wed, 17 Dec 2014 02:34:15 +0000
aldanor via Digitalmars-d-learn <digitalmars-d-learn puremagic.com>
wrote:

 On Wednesday, 17 December 2014 at 02:12:52 UTC, anonymous wrote:
 Sure, straight forward:

       mixin template makeProperty(T, string name, alias func) {
           mixin("T %s()  property { return func();
 }".format(name));
       }
=20 Indeed... thanks! Just one thing that I find confusing here -- how exactly do T and func resolve when this template is mixed in? What if there was another local symbol named "func" in the target scope?
this is mixed inside the `makeProperty` template itself, not where you instantiated it. i.e. when compiler sees mixin, it not postponing it. so what you actually got is `makeProperty` template with `%s` substituted with `name`, and only then `makeProperty` is mixed at the place of it's instatiation.
Dec 17 2014
prev sibling parent reply "anonymous" <anonymous example.com> writes:
On Wednesday, 17 December 2014 at 02:34:16 UTC, aldanor wrote:
 On Wednesday, 17 December 2014 at 02:12:52 UTC, anonymous wrote:
 Sure, straight forward:

      mixin template makeProperty(T, string name, alias func) {
          mixin("T %s()  property { return func();
 }".format(name));
      }
Indeed... thanks! Just one thing that I find confusing here -- how exactly do T and func resolve when this template is mixed in? What if there was another local symbol named "func" in the target scope?
As far as I understand, the string mixin is resolved first, and then the template mixin takes place. So the progression is somewhat like this (pseudo code): ---- mixin makeProperty!(int, "foo", f); /* Replace "makeProperty" with its definition. */ mixin (T, name, func){mixin("T %s() property { return func();}".format(name));}!(int, "foo", f); /* First round, substitute arguments for parameters. `T` and `func` are not replaced, because they're just string contents at this point. */ mixin (T, name, func){mixin("T %s() property { return func();}".format("foo"));}!(int, "foo", f); /* Evaluate `format` and do the string mixin. */ mixin (T, name, func){T foo() property { return func();}}!(int, "foo", f); /* Second round, substitute arguments for parameters. This time, `T` and `func` are replaced. */ mixin (T, name, func){int foo() property { return f();}}!(int, "foo", f); /* Didn't do any string mixins in the second round, so there's no need for a third round. Get rid of template parameters and arguments. */ mixin {int foo() property { return f();}}; /* Finally, do the template mixin. */ int foo() property { return f();} ---- Not sure if that helps or maybe it just adds to the confusion. As to if there were `T` or `func` in the target scope, they'd be shadowed by the template parameters, no matter if there's a string mixin or not.
Dec 17 2014
parent reply "aldanor" <i.s.smirnov gmail.com> writes:
On Wednesday, 17 December 2014 at 12:49:10 UTC, anonymous wrote:
 As far as I understand, the string mixin is resolved first, and
 then the template mixin takes place. So the progression is
 somewhat like this (pseudo code):

 ----
 mixin makeProperty!(int, "foo", f);

 /* Replace "makeProperty" with its definition. */
 mixin (T, name, func){mixin("T %s()  property { return
 func();}".format(name));}!(int, "foo", f);

 /* First round, substitute arguments for parameters. `T` and
 `func` are not replaced, because they're just string contents at
 this point. */
 mixin (T, name, func){mixin("T %s()  property { return
 func();}".format("foo"));}!(int, "foo", f);

 /* Evaluate `format` and do the string mixin. */
 mixin (T, name, func){T foo()  property { return func();}}!(int,
 "foo", f);

 /* Second round, substitute arguments for parameters. This time,
 `T` and `func` are replaced. */
 mixin (T, name, func){int foo()  property { return f();}}!(int,
 "foo", f);

 /* Didn't do any string mixins in the second round, so there's 
 no
 need for a third round. Get rid of template parameters and
 arguments. */
 mixin {int foo()  property { return f();}};

 /* Finally, do the template mixin. */
 int foo()  property { return f();}
 ----

 Not sure if that helps or maybe it just adds to the confusion.

 As to if there were `T` or `func` in the target scope, they'd be
 shadowed by the template parameters, no matter if there's a
 string mixin or not.
That makes sense. So if I understand correctly, basically after each string mixin it has to check if any new symbols were leaked into the current scope and then try to resolve them, if any, with template parameters taking precedence over local variables? (This obviously has to repeat in case of nested mixins) Thanks again!
Dec 17 2014
parent "anonymous" <anonymous example.com> writes:
On Wednesday, 17 December 2014 at 15:40:25 UTC, aldanor wrote:
 On Wednesday, 17 December 2014 at 12:49:10 UTC, anonymous wrote:
[...]
 As to if there were `T` or `func` in the target scope, they'd 
 be
 shadowed by the template parameters, no matter if there's a
 string mixin or not.
That makes sense. So if I understand correctly, basically after each string mixin it has to check if any new symbols were leaked into the current scope and then try to resolve them, if any,
Well, every mixed-in string necessarily goes through a full compiler pass. I don't think there's anything special going on with regards to new symbols.
 with template parameters taking precedence over local 
 variables? (This obviously has to repeat in case of nested 
 mixins)
My wording was bad. There's no shadowing. When a template is mixed in, the substitution of parameters happens before the injection. That means, when the parameters are substituted there are no local symbols yet. And when the code is injected there are no parameters any more. So local symbols and parameters are never considered at the same time. Obviously, parameters take precedence, simply because they're considered earlier. But there's no shadowing. Disclaimer: I'm not a compiler dev. I don't know if/how all this is specified (or I'd quote/link it). I'm just describing current compiler behaviour and how it makes sense to me.
Dec 17 2014