digitalmars.D - stringof and mixins when the types aren't available
- Adam D. Ruppe (43/43) Jun 16 2013 Consider the following:
- David Nadlinger (10/18) Jun 16 2013 The fix is simple: Replace `mixin(T.stringof ~ ".init")` by
- David Nadlinger (8/13) Jun 16 2013 To expand on that a bit: In the case of default parameters,
- Adam D. Ruppe (28/30) Jun 16 2013 The thing here is I'm not really generating the string, this
- David Nadlinger (4/10) Jun 16 2013 It is (since a few releases ago), see
- Adam D. Ruppe (4/6) Jun 16 2013 Well, don't I look silly now! Very cool, and the technique they
- Adam D. Ruppe (52/54) Jun 16 2013 Eh that doesn't seem to work in a more complex case... here's
- David Nadlinger (5/12) Jun 16 2013 Yes, it is. Seems like you managed to immediately hit a DMD bug –
- Adam D. Ruppe (9/13) Jun 16 2013 OK cool. Thanks, I never thought of doing it this way before!
- bearophile (4/7) Jun 16 2013 Is this in bugzilla?
- Dicebot (3/3) Jun 17 2013 Similar problem is solved in vibe.d REST generator by using
- David Nadlinger (4/7) Jun 17 2013 Doesn't help you if the types in question are private, Voldemort
- Dicebot (3/6) Jun 17 2013 As it should be, shouldn't it? That is the point of private. Any
- David Nadlinger (11/13) Jun 17 2013 Nah, I don't think so. For example let's assume you have a module
Consider the following: module a; T getInit(T)() { return mixin(T.stringof ~ ".init"); } module b; struct S {} void main() { import a; auto s = getInit!S(); } If we compile, we'll get this: testmixin1.d(3): Error: undefined identifier S, did you mean module a? testmixin2.d(6): Error: template instance a.getInit!(S) error instantiating Now, in this case, the solution is obvious, ditch the mixin. But what about more complex cases, like parsing stringof to get default arguments or generating a whole class in a string mixin? One technique I've used is instead of generating the whole class, just generate the innards and wrap it in a mixin template. Then, in the other module, write: class Foo { mixin MakeFoo!(); } and things will work since the mixin template will use this scope. But I haven't solved the default arguments out of .stringof yet, which is similar to the dummy code at the top of this post. If you'd like to see my current code, search my web.d for parameterDefaultOf. https://github.com/adamdruppe/misc-stuff-including-D-programming-language-web-stuff/blob/master/web.d That code is so hideous btw, I think if I was redoing it today from scratch it would be a lot simpler, this was my first attempt at doing this kind of reflection thing. But somehow, it works. Anyway, I get the default by parsing stringof, and when it is time to use it, I mix it in to get the value. This works for a lot of things, strings, ints, etc., built in types basically. But it fails for enums or structs (except those defined in web.d itself) because they aren't in that scope. The type is semi-known though, it assigns it via a ParameterTypeTuple, but the actual name isn't here. Any ideas to solve this, or do you have any techniques for string mixins you'd like to share for general knowledge?
Jun 16 2013
On Monday, 17 June 2013 at 00:10:58 UTC, Adam D. Ruppe wrote:Consider the following: module a; T getInit(T)() { return mixin(T.stringof ~ ".init"); } […] Any ideas to solve this, or do you have any techniques for string mixins you'd like to share for general knowledge?The fix is simple: Replace `mixin(T.stringof ~ ".init")` by `return mixin("T.init")`. I am not mocking you here, using .stringof for codegen is amost always a bug, and it is usually possible to rewrite the code in such a way that just template-local names are used. I've done quite a bit of this kind of code for Thrift, so if you post a more concrete example that is giving you trouble, I might be able to help. David
Jun 16 2013
On Monday, 17 June 2013 at 00:27:07 UTC, David Nadlinger wrote:The fix is simple: Replace `mixin(T.stringof ~ ".init")` by `return mixin("T.init")`. I am not mocking you here, using .stringof for codegen is amost always a bug, and it is usually possible to rewrite the code in such a way that just template-local names are used.To expand on that a bit: In the case of default parameters, instead of trying to directly generate a string representation for them, you would instead design your (possibly complex) string mixin logic such that the result string contains appropriate instantiations of ParameterDefaultValueTuple to be resolved at the point where you actually mix in the string. David
Jun 16 2013
On Monday, 17 June 2013 at 00:36:40 UTC, David Nadlinger wrote:To expand on that a bit: In the case of default parameters, instead of trying to directly generate a string representationThe thing here is I'm not really generating the string, this comes out of the compiler and I don't think it is available any other way. void foo(int a, int b = 10) {} pragma(msg, typeof(foo).stringof); gives: void(int a, int b = 10) And from that, I read out the parameter names and the default values, but they are all as strings. So, to get it back to a value, I mixin(that slice of string) The short version of what web.d tries to do is: void callFunction(alias fun)(string[string] arguments) { ParameterTypeTuple!fun args; foreach(i, arg; args) { string arg_name = ParameterNames!(fun)[i] ; if(arg_name in arguments) args[i] = to!arg(arguments[arg_name]); else if(arg has default) args[i] = mixin(ParameterDefaults!(fun)[i]); else throw new Exception ("argument " ~ arg_name ~ " is missing"); } fun(args); } that obviously won't compile cuz of the pseudocode, but that's more or less what I'm trying to do.
Jun 16 2013
On Monday, 17 June 2013 at 01:09:25 UTC, Adam D. Ruppe wrote:On Monday, 17 June 2013 at 00:36:40 UTC, David Nadlinger wrote:It is (since a few releases ago), see std.traits.ParameterDefaultValueTuple. DavidTo expand on that a bit: In the case of default parameters, instead of trying to directly generate a string representationThe thing here is I'm not really generating the string, this comes out of the compiler and I don't think it is available any other way.
Jun 16 2013
On Monday, 17 June 2013 at 01:15:18 UTC, David Nadlinger wrote:It is (since a few releases ago), see std.traits.ParameterDefaultValueTuple.Well, don't I look silly now! Very cool, and the technique they used in there is nice too. Outstanding, thanks again.
Jun 16 2013
On Monday, 17 June 2013 at 00:27:07 UTC, David Nadlinger wrote:The fix is simple: Replace `mixin(T.stringof ~ ".init")` by `return mixin("T.init")`.Eh that doesn't seem to work in a more complex case... here's something that tries to implement an interface with stubs. The hard part is the arguments of the members: ==== module a; auto magic(Interface)() { import std.traits; template passthrough(T) { alias passthrough = T; } string makeCode() { string code = "class Class : Interface {"; foreach(memIdx, member; __traits(allMembers, Interface)) { import std.conv; string args; foreach(idx, arg; ParameterTypeTuple!(__traits(getMember, Interface, member))) { if(idx) args ~= ","; //args ~= arg.stringof ~ " arg" ~ to!string(idx); // trying to use local names args ~= "passthrough!(ParameterTypeTuple!(__traits(getMember, Interface, \""~member~"\"))[" ~ to!string(idx) ~ "]) arg" ~ to! string(idx); } code ~= "override ReturnType!(__traits(getMember, Interface, \""~member~"\")) " ~ member ~ "(" ~ args ~ ") {}"; } code ~= "}"; return code; } pragma(msg, makeCode()); mixin(makeCode()); return new Class(); } ===== module b; struct S{}; interface Test { void foo(S s); } void main() { import a; auto s = magic!Test(); } === The stringof doesn't work because the name is unknown. But the longer one, currently uncommented, doesn't seem to work either. dmd just dumps a whole lot of trash errors referencing phobos and nothing specifically about this code... eyeballing it, I don't think I screwed it up though. Is this the right principle at least?
Jun 16 2013
On Monday, 17 June 2013 at 00:56:09 UTC, Adam D. Ruppe wrote:[…] The stringof doesn't work because the name is unknown. But the longer one, currently uncommented, doesn't seem to work either. dmd just dumps a whole lot of trash errors referencing phobos and nothing specifically about this code... eyeballing it, I don't think I screwed it up though. Is this the right principle at least?Yes, it is. Seems like you managed to immediately hit a DMD bug – "dmd b.d a.d" works using DMD 2.063.1, but reversing the module order produces a stream of arbitrary error messages. David
Jun 16 2013
On Monday, 17 June 2013 at 01:05:26 UTC, David Nadlinger wrote:Yes, it is.OK cool. Thanks, I never thought of doing it this way before! Should solve a lot of problems, though see my last note on the parameter default which is still outstanding. (Now the whole parsing stringof is a massive hack in the first place, I'd prefer to have a __traits or something to get the names and defaults directly, but still gotta work with what we have.)Seems like you managed to immediately hit a DMD bug – "dmd b.d a.d" works using DMD 2.063.1, but reversing the module order produces a stream of arbitrary error messages.Yeah, same with the 2.063 I have now that I try reversing it. How bizarre.
Jun 16 2013
David Nadlinger:Yes, it is. Seems like you managed to immediately hit a DMD bug – "dmd b.d a.d" works using DMD 2.063.1, but reversing the module order produces a stream of arbitrary error messages.Is this in bugzilla? Bye, bearophile
Jun 16 2013
Similar problem is solved in vibe.d REST generator by using fullyQualifiedName and utility function that provides tuple of local imports needed for parameter type.
Jun 17 2013
On Monday, 17 June 2013 at 07:21:58 UTC, Dicebot wrote:Similar problem is solved in vibe.d REST generator by using fullyQualifiedName and utility function that provides tuple of local imports needed for parameter type.Doesn't help you if the types in question are private, Voldemort types, etc. David
Jun 17 2013
On Monday, 17 June 2013 at 07:42:54 UTC, David Nadlinger wrote:Doesn't help you if the types in question are private, Voldemort types, etc. DavidAs it should be, shouldn't it? That is the point of private. Any workarounds can be very misleading.
Jun 17 2013
On Monday, 17 June 2013 at 20:37:39 UTC, Dicebot wrote:As it should be, shouldn't it? That is the point of private. Any workarounds can be very misleading.Nah, I don't think so. For example let's assume you have a module A with a private struct MySuperSecretType. Now A wants to write instances of MySuperSecretType to disk, and it would be natural for a serialization library to be in another module B. But calling B.serialize(mySuperSecretInstance) won't work if serialize(T)(T t) tries to somehow access MySuperSecretType from outside instead of simply referring to its template parameter. Trying to use the string identifier of symbols in mixin codegen is the "misleading workaround" here. David
Jun 17 2013