May 16, 2013
Hello! I've written a few functions as a way to learn about D metaprogramming and CTFE. Maybe they'll be useful to someone else too. I'd also love it if I could get some feedback on how the code uses various D idioms, and if there's something that could be done a better way. The syntax the template language uses is the same as embedded ruby, so any Rails/PHP developers will feel right at home.

Source + documenting unittests here: https://gist.github.com/dymk/bbf6c87e92d5aca3c737

Templates are compiled into a single anonymous D function at compile time, and the anon function uses an Appender for its internal buffer to maximize speed. It was designed so there is minimal overhead at runtime, but I'm sure it could be speed up somehow.

An overview of how to use the template functions:

Anything between <% %> is D code
Anything between <%= %> is D code which is evaluated and pushed onto the buffer
Anything else is raw text that is pushed onto the buffer

Loops, conditional logic, and declarations all work as expected. Because the function is generated in the context of the call point, you can pass any arbitrary private object in scope to the render function.

// Some examples:
// Most basic: Raw text
const render = mixin(EmbeddedDFunc(`foo`));
writeln(render()); // foo

// Bit more: D literals
const render = mixin(EmbeddedDFunc(`<%= "foo" %> <%= 22 %>`));
writeln(render()); // foo 22

// Passing in a context to the template
// Static/member variables/methods all work
struct Ctx {
  static static_var = "foo";
  auto my_variable = 5;
  int my_method() {
  	return 10;
  }
}
const render = mixin(EmbeddedDFunc(`<%= static_var %> <%= my_variable %> <%= my_method() %>`);
writeln(render(Ctx())); // 5 foo 10

// Loops (it'd have newlines, but I'm ignoring that for simplicity)
const render = mixin(EmbeddedDFunc(`
	<% foreach(i; 0..4 { %>
		<%= i %>
	<% } %>`));
writeln(render()) // 0123

Thank you,
--Dylan Knutson