digitalmars.D.learn - Alias template param - Undefined Identifier
- Chris Williams (50/50) Mar 18 2014 I have a series of functions that each needs to change directory,
- anonymous (46/66) Mar 18 2014 This is just a string. The occurence of "path" in it has no
- Chris Williams (33/44) Mar 18 2014 Your memory seems to be correct:
- anonymous (7/32) Mar 18 2014 I feel like there might be a misunderstanding. It's not that the
- bearophile (4/7) Mar 18 2014 I suggest to keep the code simpler.
- Chris Williams (9/16) Mar 18 2014 I could. I could simply allow the current working directory to
- bearophile (4/7) Mar 18 2014 This is an antipattern :-)
- Chris Williams (19/26) Mar 18 2014 As are goto, pointers, and conditional compilation. That doesn't
- Chris Williams (78/84) Mar 21 2014 Here's a simple (unsafe and inflexible) implementation, should
I have a series of functions that each needs to change directory, perform an action, and then revert to the original working directory. I could put a wrapper around the calls to these functions that performs this action, but I figured a little macro would be sufficient for my needs (it's just a little script.) import fs = std.file; import std.stdio; template gotoPath(alias path) { enum gotoPath = q{ string currPath = fs.getcwd(); scope (exit) fs.chdir(currPath); fs.chdir(path); }; } void doStuff(string targetDirectory) { mixin(gotoPath!(targetDirectory)); writefln("b %s", fs.getcwd()); } void main() { writefln("a %s", fs.getcwd()); doStuff("/etc"); writefln("c %s", fs.getcwd()); } When I try to compile (DMD 2.064), I get the following error: test.d(16): Error: undefined identifier path I can fix the issue by removing the alias parameter and hardcoding the name of the variable in the template: import fs = std.file; import std.stdio; template gotoPath() { enum gotoPath = q{ string currPath = fs.getcwd(); scope (exit) fs.chdir(currPath); fs.chdir(targetDirectory); }; } void doStuff(string targetDirectory) { mixin(gotoPath!()); writefln("b %s", fs.getcwd()); } void main() { writefln("a %s", fs.getcwd()); doStuff("/etc"); writefln("c %s", fs.getcwd()); } However, it seems like that shouldn't be necessary. The alias parameter should create a local alias for the external variable which can be utilized by the generated code. Is there something wrong with my syntax?
Mar 18 2014
On Tuesday, 18 March 2014 at 21:56:32 UTC, Chris Williams wrote:import fs = std.file; import std.stdio; template gotoPath(alias path) { enum gotoPath = q{ string currPath = fs.getcwd(); scope (exit) fs.chdir(currPath); fs.chdir(path); };This is just a string. The occurence of "path" in it has no relation to the template parameter path. "path" is mixed in literally. The template parameter is unused.} void doStuff(string targetDirectory) { mixin(gotoPath!(targetDirectory)); writefln("b %s", fs.getcwd()); } void main() { writefln("a %s", fs.getcwd()); doStuff("/etc"); writefln("c %s", fs.getcwd()); } When I try to compile (DMD 2.064), I get the following error: test.d(16): Error: undefined identifier pathYou can pass the variable name as a string: import std.string: format; template gotoPath(string pathVar) { enum gotoPath = format(q{ ... fs.chdir(%s); }, pathVar); } ... mixin(gotoPath!"targetDirectory"); Or you can get the variable name from the alias parameter: import std.string: format; template gotoPath(alias path) { enum gotoPath = format(q{ ... fs.chdir(%s); }, path.stringof); } I think I've read that .stringof shouldn't be used for code generation, but I can't think of anything better. Then there's the struct destructor version: struct GotoPath { private string currPath; this(string path) { currPath = fs.getcwd(); fs.chdir(path); } ~this() { fs.chdir(currPath); } } Ideally, it would be used like this: with(GotoPath(targetDirectory)) writefln("b %s", fs.getcwd()); which would be beautiful, if you ask me. But a compiler bug [1] ruins that. You can still use it with a dummy variable: auto dummy = GotoPath(targetDirectory); writefln("b %s", fs.getcwd()); [1] https://d.puremagic.com/issues/show_bug.cgi?id=8269
Mar 18 2014
On Tuesday, 18 March 2014 at 22:42:20 UTC, anonymous wrote:You can pass the variable name as a string: Or you can get the variable name from the alias parameter: import std.string: format; template gotoPath(alias path) { enum gotoPath = format(q{ ... fs.chdir(%s); }, path.stringof); } I think I've read that .stringof shouldn't be used for code generation, but I can't think of anything better.Your memory seems to be correct: http://forum.dlang.org/thread/bug-11036-3 http.d.puremagic.com/issues/ It looks like the issue is that .stringOf is something generated according to AST information and is thus intrinsically compiler-specific. The ticket it links to suggest using __traits(identifier, X) or 'one of the Phobos helper functions such as "fullyQualifiedName".' Annoying that formatting the string is required. I think you're right that it's ignoring my alias parameter because I'm not using it. Formatting doesn't accomplish anything except to use the parameter. But in exchange, you have to relate your format arguments to the %s entries, instead of the q{} contents just being code. import fs = std.file; import std.stdio; import std.string: format; template gotoPath(alias path) { enum gotoPath = format(q{ string currPath = fs.getcwd(); scope (exit) fs.chdir(currPath); fs.chdir(%s); }, __traits(identifier, path)); } void doStuff(string targetDirectory) { mixin(gotoPath!(targetDirectory)); writefln("b %s", fs.getcwd()); } void main() { writefln("a %s", fs.getcwd()); doStuff("/etc"); writefln("c %s", fs.getcwd()); }
Mar 18 2014
On Tuesday, 18 March 2014 at 23:10:05 UTC, Chris Williams wrote:Annoying that formatting the string is required. I think you're right that it's ignoring my alias parameter because I'm not using it. Formatting doesn't accomplish anything except to use the parameter. But in exchange, you have to relate your format arguments to the %s entries, instead of the q{} contents just being code. import fs = std.file; import std.stdio; import std.string: format; template gotoPath(alias path) { enum gotoPath = format(q{ string currPath = fs.getcwd(); scope (exit) fs.chdir(currPath); fs.chdir(%s); }, __traits(identifier, path)); } void doStuff(string targetDirectory) { mixin(gotoPath!(targetDirectory)); writefln("b %s", fs.getcwd()); } void main() { writefln("a %s", fs.getcwd()); doStuff("/etc"); writefln("c %s", fs.getcwd()); }I feel like there might be a misunderstanding. It's not that the parameter was ignored because it wasn't used. You're not giving the compiler a little extra help. You're building a string from the parameter. __traits(identifier, path) isn't "path", it's "targetDirectory" (in this specific instantiation).
Mar 18 2014
Chris Williams:I could put a wrapper around the calls to these functions that performs this action, but I figured a little macro would be sufficient for my needs (it's just a little script.)I suggest to keep the code simpler. Bye, bearophile
Mar 18 2014
On Tuesday, 18 March 2014 at 23:05:51 UTC, bearophile wrote:Chris Williams:I could. I could simply allow the current working directory to drift as the application performs operations, but I prefer the consistency of knowing that methods I call restore state before I continue to the next method. I sense that in the long run it will make life easier. But I also like to know how most-effectively to write a C-style macro in D, so it seemed worth checking what the state of the art is.I could put a wrapper around the calls to these functions that performs this action, but I figured a little macro would be sufficient for my needs (it's just a little script.)I suggest to keep the code simpler. Bye, bearophile
Mar 18 2014
Chris Williams:But I also like to know how most-effectively to write a C-style macro in D, so it seemed worth checking what the state of the art is.This is an antipattern :-) Bye, bearophile
Mar 18 2014
On Tuesday, 18 March 2014 at 23:33:12 UTC, bearophile wrote:Chris Williams:As are goto, pointers, and conditional compilation. That doesn't mean that there aren't cases where those are the best solution, because other solutions would be longer and more convoluted or too inefficient. In Python, I could write a wrapping function that I could apply (using decorators) to each function with this behavior, based on the presence of a UDA. I could roughly build something like that for D, but the whole framework for it would be far more convoluted and harder to maintain than a simple three line macro. ...probably something along the lines of making all of my functions a static function in a struct, which I then pass into a template which processes UDAs to generate functions at the top with the same names as the originals in the struct, which call the struct variants. It's also a longer to write and debug. Unfortunately, at the moment, I don't believe that there's any way to use UDAs to process a file and generate interceptors -- which would be the sort of pattern that we'd like.But I also like to know how most-effectively to write a C-style macro in D, so it seemed worth checking what the state of the art is.This is an antipattern :-) Bye, bearophile
Mar 18 2014
On Wednesday, 19 March 2014 at 00:07:04 UTC, Chris Williams wrote:...probably something along the lines of making all of my functions a static function in a struct, which I then pass into a template which processes UDAs to generate functions at the top with the same names as the originals in the struct, which call the struct variants. It's also a longer to write and debug.Here's a simple (unsafe and inflexible) implementation, should anyone want to build it out into something that can accept multiple arguments and return types. import std.stdio; string wrap(string wrapperName, string structName, string innerName) { return "void " ~ innerName ~ "() {" ~wrapperName~ "( &"~structName~"."~innerName~" );}"; } template decorate(T) { template ForeachMember(Mbr...) { static if (__traits(getAttributes, __traits(getMember, T, Mbr[0])).length > 0) { static if (Mbr.length > 1) { enum ForeachMember = wrap( __traits(identifier, __traits(getAttributes, __traits(getMember, T, Mbr[0]))[0]), __traits(identifier, T), Mbr[0] ) ~ ForeachMember!(Mbr[1..$]) ; } else { enum ForeachMember = wrap( __traits(identifier, __traits(getAttributes, __traits(getMember, T, Mbr[0]))[0]), __traits(identifier, T), Mbr[0] ) ; } } else { static if (Mbr.length > 1) { enum ForeachMember = "" ~ ForeachMember!(Mbr[1..$]); } else { enum ForeachMember = ""; } } } enum decorate = ForeachMember!(__traits(allMembers, T)); } void BeforeAfter(void function() fun) { writeln("Before"); write("\t"); fun(); writeln("After"); } void Llama(void function() fun) { writeln("Llama"); write("\t"); fun(); writeln("Llama"); } struct Foo { BeforeAfter static void hello() { writeln("Hello"); } Llama static void and() { writeln("and"); } BeforeAfter static void goodbye() { writeln("Goodbye"); } } mixin(decorate!(Foo)); void main() { hello(); and(); goodbye(); }
Mar 21 2014