digitalmars.D.learn - string mixin only works once per block?
- Vlad Levenfeld (33/33) Apr 11 2014 I'm trying to cut down on some boilerplate with this function:
- Chris (8/41) Apr 11 2014 Just of the top of my head, have you tried
- Dicebot (3/10) Apr 11 2014 You can't use template mixins as statements, those are only for
- Vlad Levenfeld (17/17) Apr 11 2014 I changed link to this:
- Chris (2/14) Apr 11 2014 Ah yes, I see. Was only off the top of my head.
- Dicebot (34/66) Apr 11 2014 This syntax is only supported by template mixins and you generate
- Vlad Levenfeld (67/142) Apr 11 2014 This is a cool trick. It made me wonder if I could somehow
- Vlad Levenfeld (9/9) Apr 11 2014 actually I just realized there is a bug, in the 2nd case of
- TheFlyingFiddle (119/120) Apr 11 2014 I would not recommend using string attributes since it's easy to
- Vlad Levenfeld (5/5) Apr 12 2014 These are excellent, thanks! I went ahead and swapped out the
- John Colvin (12/45) Apr 11 2014 I'm not sure exactly what's going on here, but here's a few bits
- Vlad Levenfeld (4/4) Apr 11 2014 Yes, I changed to mixin (link!"variable") and everything works
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
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
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
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
On Friday, 11 April 2014 at 14:37:32 UTC, Dicebot wrote:On Friday, 11 April 2014 at 14:31:47 UTC, Chris wrote:Ah yes, I see. Was only off the top of my head.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
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 templateThis 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 timeThis 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
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: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!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 templateThis 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 timeThis 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
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
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
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
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
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