Jump to page: 1 2
Thread overview
stringof and mixins when the types aren't available
Jun 17, 2013
Adam D. Ruppe
Jun 17, 2013
David Nadlinger
Jun 17, 2013
David Nadlinger
Jun 17, 2013
Adam D. Ruppe
Jun 17, 2013
David Nadlinger
Jun 17, 2013
Adam D. Ruppe
Jun 17, 2013
Adam D. Ruppe
Jun 17, 2013
David Nadlinger
Jun 17, 2013
Adam D. Ruppe
Jun 17, 2013
bearophile
Jun 17, 2013
Dicebot
Jun 17, 2013
David Nadlinger
Jun 17, 2013
Dicebot
Jun 17, 2013
David Nadlinger
June 17, 2013
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?
June 17, 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
June 17, 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
June 17, 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?
June 17, 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
June 17, 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 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.
June 17, 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.
June 17, 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:
>> 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
June 17, 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.
June 17, 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
« First   ‹ Prev
1 2