Jump to page: 1 2
Thread overview
Compile time data structure
Sep 15, 2013
Marek Janukowicz
Sep 16, 2013
Ali Çehreli
Sep 16, 2013
Marek Janukowicz
Sep 18, 2013
Ali Çehreli
Sep 18, 2013
John Colvin
Sep 18, 2013
H. S. Teoh
Sep 19, 2013
Ali Çehreli
Sep 19, 2013
Dicebot
Sep 20, 2013
Ali Çehreli
Sep 20, 2013
Dicebot
Sep 19, 2013
H. S. Teoh
Sep 20, 2013
Ali Çehreli
Sep 19, 2013
Marek Janukowicz
Sep 19, 2013
H. S. Teoh
Sep 19, 2013
Ali Çehreli
September 15, 2013
I need to gather some data in compile time basing on UDA.

struct Attr {
  string name;
}

mixin template Model() {

  static string[string] columns () {
    string[string] cols;
    alias type = typeof(this);
     // Basically - get all members with @Attr UDA and build AA out of those
    foreach( field; __traits(derivedMembers, type)) {
      static if( is(typeof(__traits(getAttributes, __traits(getMember, type,
field))) blah)) {
        foreach( uda; __traits(getAttributes, __traits(getMember, type,
field))) {
          static if (is (typeof(uda) == Attr)) {
            static if (uda.name == "") cols[field] = field;
            else cols[field] = uda.name;
          }
        }
      }
    }
    return cols;
  }
}

class ExampleModel {

  @Attr() int id;
  @Attr( "field_id" ) int fieldId;

  mixin Model;
}

This works and the result of columns() method is as expected. However, if I try to use it in another compile time fuction, eg:

    foreach( attr, col; columns ) {
      __traits(getMember, obj, attr) = 1;
    }

it fails saying "attr" cannot be read at compile time.

I suspect I can't just build AA during compile time, but is there any other way for columns() method to return a structure that could be further evaluated at compile time?

-- 
Marek Janukowicz
September 16, 2013
Could you please provide complete code.

Thank you,
Ali

September 16, 2013
Ali Çehreli wrote:
> Could you please provide complete code.

Sure. This is of course stripped down just for demonstration purposes:

struct Attr {
  string name;
}

mixin template Model() {

  static string[string] columns () {
    string[string] cols;
    alias type = typeof(this);
     // Basically - get all members with @Attr UDA and build AA out of those
    foreach( field; __traits(derivedMembers, type)) {
      static if( is(typeof(__traits(getAttributes, __traits(getMember, type,
field))) blah)) {
        foreach( uda; __traits(getAttributes, __traits(getMember, type,
field))) {
          static if (is (typeof(uda) == Attr)) {
            static if (uda.name == "") cols[field] = field;
            else cols[field] = uda.name;
          }
        }
      }
    }
    return cols;
  }

  alias typeof(this) Me;

  static Me initialize () {
    Me me = new Me();
    foreach( attr, col; columns() ) {
      __traits(getMember, me, attr) = typeof(__traits(getMember, me,
attr)).init;
    }
    return me;
  }

}

class ExampleModel {

  @Attr() int id;
  @Attr( "field_id" ) int fieldId;

  mixin Model;
}


void main () {
  ExampleModel ex = ExampleModel.initialize();
}

-- 
Marek Janukowicz
September 18, 2013
On 09/16/2013 01:24 PM, Marek Janukowicz wrote:

>    static string[string] columns () {
// ...
>    }

Although the function itself is static, it returns a dynamic value.

>      foreach( attr, col; columns() ) {
>        __traits(getMember, me, attr) = typeof(__traits(getMember, me,
> attr)).init;
>      }

That foreach is a run-time foreach because columns()'s return value is a run-time value.

As far as I know, static foreach is only for tuples (or TypeTuples). If you can generate the AA as a tuple, then the foreach will be evaluated at compile time.

Ali

September 18, 2013
On Wednesday, 18 September 2013 at 01:24:37 UTC, Ali Çehreli wrote:
> As far as I know, static foreach is only for tuples (or TypeTuples)

TypeTuples are tuples. Sortof. We really need to get that whole situation sorted....
September 18, 2013
On Wed, Sep 18, 2013 at 04:12:01AM +0200, John Colvin wrote:
> On Wednesday, 18 September 2013 at 01:24:37 UTC, Ali Çehreli wrote:
> >As far as I know, static foreach is only for tuples (or TypeTuples)
> 
> TypeTuples are tuples. Sortof. We really need to get that whole situation sorted....

Not to mention there are also "parameter tuples" that behave sorta like TypeTuples but not quite.


T

-- 
In a world without fences, who needs Windows and Gates? -- Christian Surchi
September 19, 2013
On 09/17/2013 07:26 PM, H. S. Teoh wrote:

> On Wed, Sep 18, 2013 at 04:12:01AM +0200, John Colvin wrote:
>> On Wednesday, 18 September 2013 at 01:24:37 UTC, Ali Çehreli wrote:
>>> As far as I know, static foreach is only for tuples (or TypeTuples)
>>
>> TypeTuples are tuples. Sortof. We really need to get that whole
>> situation sorted....
>
> Not to mention there are also "parameter tuples" that behave sorta like
> TypeTuples but not quite.

I have just published two chapters that present tuples and TypeTuple sufficiently-completely and sufficiently-consistently (of course, according to me ;) ):

Tuples:

  http://ddili.org/ders/d.en/tuples.html

More Templates:

  http://ddili.org/ders/d.en/templates_more.html

I haven't officially announced those yet. I appreciate any review. (I am sure there are lots of English grammar and syntax issues as well. :-/ Those mature over time organically. :) )

Thank you,
Ali

September 19, 2013
On Thursday, 19 September 2013 at 07:46:41 UTC, Ali Çehreli wrote:
> I have just published two chapters that present tuples and TypeTuple sufficiently-completely and sufficiently-consistently (of course, according to me ;) ):
>
> Tuples:
>
>   http://ddili.org/ders/d.en/tuples.html
>
> More Templates:
>
>   http://ddili.org/ders/d.en/templates_more.html
>
> I haven't officially announced those yet. I appreciate any review. (I am sure there are lots of English grammar and syntax issues as well. :-/ Those mature over time organically. :) )
>
> Thank you,
> Ali

Some obvious catches:

http://ddili.org/ders/d.en/tuples.html : `S.tupleof[i].stringof` value was recently changed.

`object.tupleof` part does not mention important fact that tuple does not contain values of object members but actual members as if it is a reference.

There are few imprecise wordings but I am afraid being 100% precise when speaking about tuples will result in "WTF, what dark magic this is?" :(

http://ddili.org/ders/d.en/templates_more.html : "The following code instantiates the template for int and dchar" - `double` is actually used in snippet instead of dchar.

Variadic template arg chapter should probably mention "variadic args of length 1" idiom used to have parameter accepting types, values and aliases at once.
September 19, 2013
Ali Çehreli wrote:

> On 09/16/2013 01:24 PM, Marek Janukowicz wrote:
> 
>  >    static string[string] columns () {
> // ...
>  >    }
> 
> Although the function itself is static, it returns a dynamic value.
> 
>  >      foreach( attr, col; columns() ) {
>  >        __traits(getMember, me, attr) = typeof(__traits(getMember, me,
>  > attr)).init;
>  >      }
> 
> That foreach is a run-time foreach because columns()'s return value is a
> run-time value.
> 
> As far as I know, static foreach is only for tuples (or TypeTuples). If you can generate the AA as a tuple, then the foreach will be evaluated at compile time.

I read your articles about tuples and templates and it all now makes more sense (why is your book not linked on dlang.org? It is much better stuff for beginners than official D docs). However, there is still one thing I struggle with: how do I create a tuple at compile time if I'm getting information I want to put into it in a foreach? All the examples I found create a tuple with all it's attributes available, while I get mine in an iterative manner...

-- 
Marek Janukowicz
September 19, 2013
On Thu, Sep 19, 2013 at 12:46:39AM -0700, Ali Çehreli wrote:
> On 09/17/2013 07:26 PM, H. S. Teoh wrote:
> 
> > On Wed, Sep 18, 2013 at 04:12:01AM +0200, John Colvin wrote:
> >> On Wednesday, 18 September 2013 at 01:24:37 UTC, Ali Çehreli wrote:
> >>> As far as I know, static foreach is only for tuples (or TypeTuples)
> >>
> >> TypeTuples are tuples. Sortof. We really need to get that whole situation sorted....
> >
> > Not to mention there are also "parameter tuples" that behave sorta like TypeTuples but not quite.
> 
> I have just published two chapters that present tuples and TypeTuple sufficiently-completely and sufficiently-consistently (of course, according to me ;) ):
> 
> Tuples:
> 
>   http://ddili.org/ders/d.en/tuples.html
> 
> More Templates:
> 
>   http://ddili.org/ders/d.en/templates_more.html
> 
> I haven't officially announced those yet. I appreciate any review. (I am sure there are lots of English grammar and syntax issues as well. :-/ Those mature over time organically. :) )
[...]

I didn't read everything in detail but skimming over it, I think the chapters look OK.

But they didn't describe parameter tuples -- and I'm not sure if you want to, because it's very dark arcane magic. But if you *really* want to know:

A parameter tuple is the type of Params in the following code:

	import std.stdio;

	// Function to analyze
	int func(string x, int y=123, float z=1.618) {
		return 0;
	}

	// Prints out types, names, and default arguments of func.
	void main() {
		static if (is(typeof(func) Params == __parameters)) {
			foreach (i, param; Params) {
				writefln("[%d]:", i);
				writeln("\tType: ", param.stringof);
				writeln("\tName: ", __traits(identifier, Params[i..i+1]));

				auto getDefArg(int i)(Params[i..i+1] args) {
					return args[0];
				}
				static if (is(typeof(getDefArg!i())))
					writeln("\tDefault value: ", getDefArg!i());
			}
		}
	}

This code looks pretty innocent until you start looking at it closer.

For starters, the first line in main() must be written *exactly* like that, because the magic keyword __parameters *only* works in this exact invocation of is(), and doesn't work anywhere else in the language. This must be one of the darkest corners of is() syntax, because there's basically no consistent rationalization of what each element in `is(X Y == Z)` actually means. Each different Z changes the interpretation of X and Y in unpredictable ways (cf. the language spec on dlang.org, under Expressions -> isExpression -> 6).

Then there's the main question of what exactly Params is. It's what the docs call a "parameter tuple", but what is that? Well, first of all, contrary to what one might expect, calling foreach over Params does NOT actually iterate over its actual elements. Rather, it only iterates over the *types* of each func's parameters (i.e., string, int, float).

This is why in the next line, `param.stringof` prints only the type name of the parameter.

So how does one get the name of the parameter? Well, a first thought might be, since foreach doesn't give us actual elements in Params, maybe array-indexing notation (i.e., Params[i]) might? Wrong! Params[i] also returns only the *type* of the parameter. :)  It turns out that the only way to actually get an element of Params that isn't reduced to just a type is to take a 1-element slice of it. That's why we have to write Params[i..i+1] in the code. Are you confused yet? :) Once we have it, though, how do we get the parameter name? Well, it turns out that Params[i..i+1].stringof returns strings of the form "(int y = 123)" which we'd have to manually parse. To avoid needing manual string parsing, we have to use __traits(identifier...) to get the parameter name. (Of course! Isn't that obvious!?)

Finally, what about default arguments? We *could* in theory parse Params[i..i+1].stringof to extract it as a string, and then use mixin() to get at its actual value... but I don't feel very confident that my string parsing code would actually be correct in all possible cases, so I'd rather have the compiler tell me what the default value is directly. Except that there is no way to directly get the default value at all! The only way (and this I learned from Phobos, since it's pretty much impossible for anybody to guess on their own) is to declare a function using Params[i..i+1] -- which, being a parameter type tuple, obviously can be used to declare function parameters -- that returns the value of the default parameter.

The additional (int i) compile-time argument was added to appease the compiler, because otherwise each iteration of the foreach loop would declare a different function body under the same name "getDefArg", which wouldn't work. So we templatize the function with the loop index in order to generate unique names for its various incarnations.

Now, how do we know if the parameter has a default value? Well, it has a default value if getDefArg!i can be called with no arguments, you see, since writing getDefArg(int i)(Params[i..i+1] args) is equivalent to writing getDefArg(int i)(int y=123) when Params[i..i+1] represents the parameter `int y=123`. So if the default parameter is present, getDefArg can be called with no arguments. So that's what the static if checks for.

But here, `args` binds not to the parameter itself, but to the 1-element tuple representing the argument list, so to get the actual default value, we have to return args[0], not just args. (Of course! Isn't that obvious??!)


And that concludes today's lesson on parameter tuples. I hope you're thoroughly confused and utterly perplexed by now, because next class, we're going to talk about how to extract parameter qualifiers (like ref, scope, etc.) from a parameter tuple when the language actually has no API to directly fetch this information. :-P

(On a more serious note, though: all of the above probably should *not* be any D textbook; readers should instead be directed to std.traits where all of this arcane black magic is wrapped by a nicer user-facing API that isn't insanity-inducing.)


T

-- 
Маленькие детки - маленькие бедки.
« First   ‹ Prev
1 2