www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - string mixin only works once per block?

reply "Vlad Levenfeld" <vlevenfeld gmail.com> writes:
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

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

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.

Is this a bug or am I doing it wrong?
Apr 11 2014
next sibling parent reply "Chris" <wendlec tcd.ie> writes:
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

 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

 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.

 Is this a bug or am I doing it wrong?
Just of the top of my head, have you tried mixin template Link { const string link() { // ... } } or something like this?
Apr 11 2014
parent reply "Dicebot" <public dicebot.lv> writes:
On Friday, 11 April 2014 at 14:31:47 UTC, Chris wrote:
 Just of the top of my head, have you tried

 mixin template Link {
   const string link() {
   // ...
   }
 }

 or something like this?
You can't use template mixins as statements, those are only for declarations.
Apr 11 2014
next sibling parent "Vlad Levenfeld" <vlevenfeld gmail.com> writes:
I changed link to this:

template link (alias variable)
{
	const string link ()
	{
		const string name = __traits (identifier, variable);
		return name~`= glGetUniformLocation (program, "`~name~`");`;
	}
}

but the results are the same - however
I've realized that the first mixin doesn't necessarily work - I 
think what is actually happening is that, since the variables 
never get written to, they retain their initial values, and this 
corresponds to the first uniform location in the shader, and so 
this location receives the variable when I upload the uniforms. 
(But then I would expect the last uniform I sent to assume the 
color but this isn't what happens, so I'm not sure).
Apr 11 2014
prev sibling parent "Chris" <wendlec tcd.ie> writes:
On Friday, 11 April 2014 at 14:37:32 UTC, Dicebot wrote:
 On Friday, 11 April 2014 at 14:31:47 UTC, Chris wrote:
 Just of the top of my head, have you tried

 mixin template Link {
  const string link() {
  // ...
  }
 }

 or something like this?
You can't use template mixins as statements, those are only for declarations.
Ah yes, I see. Was only off the top of my head.
Apr 11 2014
prev sibling next sibling parent reply "Dicebot" <public dicebot.lv> writes:
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 }
Apr 11 2014
parent reply "Vlad Levenfeld" <vlevenfeld gmail.com> writes:
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!
Apr 11 2014
next sibling parent "Vlad Levenfeld" <vlevenfeld gmail.com> writes:
actually I just realized there is a bug, in the 2nd case of 
collect_fields:

else static if (members[0] == "this")
   return [];

Should be:

else static if (members[0] == "this")
   return collect_fields!(T, attribute, members[1..$]);

Otherwise the method craps out on the constructor and returns an 
incomplete list.
Apr 11 2014
prev sibling parent reply "TheFlyingFiddle" <theflyingfiddle gmail.com> writes:
On Friday, 11 April 2014 at 18:46:12 UTC, Vlad Levenfeld wrote:
 Corrections and suggestions welcome!
I would not recommend using string attributes since it's easy to misspell them, it would be better to use a struct tag or simply make a uniform type since the compiler will help you out if you miss something. //Struct tag struct Uniform { } Uniform GLint start_color; instead of ("uniform") GLint start_color; // Even better using uniform as a type struct Uniform(T) { T value; GLint location; } Uniform!vec4 start_color; For a next step i would recommend that you do some validation as part of the linking process. After you have found the location of the uniform simply use the opengl query functions to get the type of the uniform and do type-checking agains the client uniform type. If you want to take it even further you could automate the opengl setup step. (Templates are freeking awesome) //U == Uniform Type V == vertex type (used for validation). struct ShaderProgram(U,V) { GLint[U.tupleof.length] uniformLocations; //Locations of all the uniforms in the same order as they appear in the U struct. U uniforms; //All the uniforms that exists in the shader. GLint glName; //gl id this(string geometrySource, string vertSource, string fragSource) { //Compile shaders link program. //Find uniforms locations by looking at fileds in U and do some validation //on these. //Find all vertex input attributes and do validation against fields in V } void use() { //Call glUseProgram(glName); //Go through all the fields in //uniformValues and set them with //glUniform* //Locations are stored in locations array. } //More methods for validation or other convinience things. } An example useage of this system. (I assume some sort of particle system) struct Particle { vec2 velocity; vec2 position; float spawnTime; } struct ParticleUniforms { mat4 projection; Color startColor; Color endColor; vec2 startSize; vec2 endSize; float particleLifeTime; float currentTime; } ShaderProgram!(Particle, ParticleUniforms) program; void setup() { //Initialize the program with the given shaders //If we get passed this line we can be sure that the //program is actually functional! program = ShaderProgram!(Particle, ParticleUniforms) (gShader, vShader, fShader); //Set initial values for all the uniforms in the program. program.uniforms.startColor = Color.red; program.uniforms.endColor = Color.green; etc... } void render(Time time) { //Update interesting uniforms program.uniform.currentTime += time.delta; program.use(); //glUseProgram //Now we have a program that is usable and ensured to be valid. //(If it works like intended is subject to the shaders ^^) } I am currently using something similar to this and it has made compability debugging virtually nonexistant. It also leaves the amount of boilerplate to a minimum. (It can be extended even further to validate correct setup of various buffers, vertex array objects and most other opengl related stuff) One last thing: The following will make debugging opengl easier. struct gl { static auto ref opDispatch(string name, Args...)(Args args) { enum glName = "gl" ~ name[0].toUpper.to!string ~ name[1 .. $]; debug scope(exit) checkGLError(name, args) mixin("return " ~ glName ~ "(args);"); } } Now instead of calling opengl methods like this: glUniform* glBindTexture* etc Call them like this. gl.uniform* gl.bindTexture* etc This will automagically add a glGetError() after every call in debugmode. No more adding glGetError manually! I feel i went of topic. I hope this was usefull to you.
Apr 11 2014
parent "Vlad Levenfeld" <vlevenfeld gmail.com> writes:
These are excellent, thanks! I went ahead and swapped out the 
tags and I think I am going to implement something like that 
opDispatch method for all outside libraries now. Very useful!
That type validation is particularly cool. You could be rendering 
almost arbitrary data in like 2 steps...
Apr 12 2014
prev sibling parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
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

 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

 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.

 Is this a bug or am I doing it wrong?
I'm not sure exactly what's going on here, but here's a few bits of info you might find useful. mixin mixinTemplate!Params; is for template mixins http://dlang.org/template-mixin.html mixin(someString); is for string mixins http://dlang.org/mixin.html template mixins can only insert declarations/definitions, string mixins can inject arbitrary code i.e. anything. When working with string mixins, pragma(msg allows you to print the result at compile-time to check what code is being generated. http://dlang.org/pragma.html
Apr 11 2014
parent "Vlad Levenfeld" <vlevenfeld gmail.com> writes:
Yes, I changed to mixin (link!"variable") and everything works 
now. Thanks!

I gotta say, writing openGL in D is easily the least painful 
openGL programming I've experienced thanks to these string mixins.
Apr 11 2014