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!

Reply via email to