1 2 3 4 5 6 7 8 9 10 11 12 | #define QUOTE(...) #__VA_ARGS__ void foo() { char shader[] = QUOTE( void main() { gl_FragmentColor = vec4(1.0, 0.0, 0.0, 0.0); } ); // shader contains: // "void main() { gl_FragmentColor = vec4(1.0, 0.0, 0.0, 0.0); }" } |
Most editors will sort of work as far as indentation and syntax-highlighting is concerned inside the embedded block.
Of course, we're dealing with C++ so everything comes at a cost. In this case, we lose all newlines and indentation in the string at runtime and we lose the ability to embed preprocessor directives.
We can recover newlines with a poor man's formatter, and also add some magic for embedded preprocessor directives while we're at it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | void cleanup(char* text) { for (; text[0]; ++text) { switch (text[0]) { case '$': { if (text[1] == ' ') { text[0] = ' '; text[1] = '\n'; } else { text[0] = '#'; } break; } case '{': case '}': case ';': { if (text[1] == ' ') { text[1] = '\n'; } break; } } } } |
Now we can do stuff like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #define QUOTE(...) #__VA_ARGS__ void foo() { char shader[] = QUOTE( $version 420$ void main() { gl_FragmentColor = vec4(1.0, 0.0, 0.0, 0.0); } ); cleanup(shader); // shader contains: // "#version 420 \n" // "void main() {\n" // "gl_FragmentColor = vec4(1.0, 0.0, 0.0, 0.0);\n" // "}" } |
If you can't get enough of ugly preprocessor tricks you can go even further by doing quasiquotation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #define QUOTE(...) #__VA_ARGS__ #define QUASIQUOTE(...) QUOTE(__VA_ARGS__) struct mat4 { float m[4][4]; }; // shared parameter value #define TWEAK_PARAM 0.5 // shared code structure #define TRANSFORM_PARAMS(kind) \ kind TransformParams {\ mat4 world; \ mat4 projection; \ } TRANSFORM_PARAMS(struct); void foo() { char shader[] = QUASIQUOTE( $version 420$ TRANSFORM_PARAMS(layout(std140) uniform) params; void main() { // params.world, params.projection are available for use here gl_FragmentColor = vec4(1.0, TWEAK_PARAM, 0.0, 0.0); } ); cleanup(shader); // shader contains: // "#version 420 \n" // "layout(std140) uniform TransformParams {\n" // "mat4 world;\n" // "mat4 projection;\n" // "}\n" // "params;\n" // "void main() {\n" // "gl_FragmentColor = vec4(1.0, 0.5, 0.0, 0.0);\n" // "}\n" TransformParams params; // fill params with data, upload as uniform block, etc, etc (be careful with padding though) } |
This allows us to share macros between the host and the embedded code. Whether that's a good idea or not we'll never know.