www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - stringof and mixins when the types aren't available

reply "Adam D. Ruppe" <destructionator gmail.com> writes:
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
next sibling parent "David Nadlinger" <code klickverbot.at> writes:
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
prev sibling next sibling parent "David Nadlinger" <code klickverbot.at> writes:
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
prev sibling next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
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
prev sibling next sibling parent "David Nadlinger" <code klickverbot.at> writes:
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
prev sibling next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
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 representation

The 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
prev sibling next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
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
prev sibling next sibling parent "David Nadlinger" <code klickverbot.at> writes:
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:
 To expand on that a bit: In the case of default parameters, 
 instead of trying to directly generate a string representation

The 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.

It is (since a few releases ago), see std.traits.ParameterDefaultValueTuple. David
Jun 16 2013
prev sibling next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
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
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
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
prev sibling next sibling parent "Dicebot" <public dicebot.lv> writes:
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
prev sibling next sibling parent "David Nadlinger" <code klickverbot.at> writes:
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
prev sibling next sibling parent "Dicebot" <public dicebot.lv> writes:
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.

 David

As it should be, shouldn't it? That is the point of private. Any workarounds can be very misleading.
Jun 17 2013
prev sibling parent "David Nadlinger" <code klickverbot.at> writes:
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