string mixin only works once per block?

Vlad Levenfeld vlevenfeld at gmail.com
Fri Apr 11 11:46:11 PDT 2014


On Friday, 11 April 2014 at 14:49:49 UTC, Dicebot wrote:
> On Friday, 11 April 2014 at 14:05:17 UTC, Vlad Levenfeld wrote:
>> I'm trying to cut down on some boilerplate with this function:
>>
>> const string link (alias variable) ()
>> {
>> 	const string name = __traits (identifier, variable);
>> 	return name~`= glGetUniformLocation (program, "`~name~`");`;
>> }
>>
>> Applied in this kind of context:
>> 		
>> this ()
>> {
>> 	super ("default.vert", "gradient.frag");
>> 	mixin link!start_color;
>> 	mixin link!final_color;
>> 	mixin link!center_pos;
>> 	mixin link!lerp_vec;
>> 	mixin link!lerp_range;
>> }
>> 					
>>
>> And the first mixin in every such function works fine, but the 
>> rest don't. Specifically, I get this error:
>>
>> source/main.d(483): Error: mixin link!final_color link isn't a 
>> template
>
> This syntax is only supported by template mixins and you 
> generate actual code as string. Template mixins can't be used 
> inside functions and intended only for injecting declarations.
>
>> for each mixin after the first. If I put the argument to mixin 
>> in parenthesis:
>>
>> mixin (link!start_color);
>>
>> I get:
>>
>> source/main.d(483): Error: value of 'this' is not known at 
>> compile time
>
> This is unfortunate flaw of how class members get passed as an 
> alias parameters. compiler tries to pass it together with 
> context pointer (this) which can't happen because there is no 
> valid context pointer at code generation point.
>
>> Finally, removing the parenthesis and putting each mixin 
>> statement in its own block:
>>
>> {mixin link!start_color;}
>>
>> compiles, but the mixed-in string has no apparent effect.
>
> This does not mixin generated code but function itself, so it 
> has no effect as expected.
>
> To workaround context pointer limitation you can use tupleof + 
> index trick:
>
> const string link (T, size_t index) ()
> 	if (is(T == class) || is(T == struct))
> {
> 	const string name = __traits (identifier, T.tupleof[index]);
> 	return name ~ `= 42;`;
> }
>
> class X
> {	
> 	int a, b;
> 	
> 	this ()
> 	{
> 		mixin (link!(X, 0));
> 		mixin (link!(X, 1));
> 	}
> }
>
> void main()
> {
> 	auto x = new X();
> 	import std.stdio;
> 	writeln(x.a, " ", x.b); // 42 42
> }

This is a cool trick. It made me wonder if I could somehow 
automate the entire uniform linking process, and I wound up with 
the following solution:

/* checks for the presence of an attribute for a symbol belonging 
to T */
template has_attribute (T, string symbol, string attribute)
{
   const bool has_attribute ()
   {
     mixin (
       `foreach (label; __traits (getAttributes, T.`~symbol~`))
	  static if (label == attribute)
             return true;
     `);
     return false;
   }
}

/* returns a tuple or an array or something?? of all fields of T 
which have a given attribute */
template collect_fields (T, string attribute)
{
   const string[] collect_fields ()
   {
     const string[] collect_fields (T, string attribute, 
members...)()
     {
       static if (members.length == 0)
         return [];
       else static if (members[0] == "this")
         return [];
       else static if (has_attribute!(T, members[0], attribute))
         return collect_fields!(T, attribute, members[1..$]) ~ 
members[0];
       else return collect_fields!(T, attribute, members[1..$]);
     }
     return collect_fields!(T, attribute, __traits (allMembers, 
T));
   }
}

/* returns a mixin-able string to get all uniform locations for a 
shader, provided that the variable names are identical on both 
client and server */
const string link_uniforms (T_shader) ()
{
   string command;
   foreach (uniform; collect_fields!(T_shader, "uniform"))
     command ~= uniform~` = glGetUniformLocation (program, 
"`~uniform~`"); `;
   return command;
}

And now all of my Shader subclass constructors look like this:

this ()
{
   super ("default.vert", "default.frag");
   mixin (link_uniforms!(typeof (this)));
}

Which works like a charm as long as my uniform handles look like 
this:

@("uniform") GLint start_color;
@("uniform") GLint start_color;
... etc

Which also strikes me as a really neat way to visually separate 
the client-side shader variables from the uniform handles.

And I am now officially 100% sold on D, haha. Who would have 
thought metaprogramming could be so straightforward...

Corrections and suggestions welcome!


More information about the Digitalmars-d-learn mailing list