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