Mixin and function template?

Frits van Bommel fvbommel at REMwOVExCAPSs.nl
Wed Feb 28 16:56:20 PST 2007


renoX wrote:
> Frits van Bommel a écrit :
>> renoX wrote:
>>>         mixin(`fwritef(fd,`~Fmt!(A)~`);`);   
>>
>> This line is equivalent to:
>>         fwritef(fd, Fmt!(A));
>  > no mixin() required.
> 
> In theory yes, in practice when I tried  fwritef(fd, Fmt!(A)); it didn't 
>  work..

Oh, sorry. I should have seen that Fmt!() generates the arguments as a 
string, not a tuple. So you'd still need the mixin wrapper here.

>>>         std.c.stdio.fclose(fd);
>>>         auto res = cast(char[])read("tmp_file");
>>
>> So basically, you're creating a temporary file, write some formatted 
>> output to it, then open it and put the contents in an allocated buffer?
>> Why not just:
>>         auto res = std.string.format(Fmt!(A));
> 
> Because I didn't know this function, thanks for the suggestion.
> When I tried it didn't work though, unfortunately..

This should be equivalent to your original[1]:
	auto res = mixin("std.string.format(" ~ Fmt!(A) ~ ")");


[1]: Of course, my previous suggestion *should* have been equivalent as 
well. Unfortunately it wasn't ;).

>>> At the call site, I have to do the following:
>>>     mixin sputf!("%d{x}") P1;
>>>     res = P1.call();
>>> which is quite ugly, so I'd like to convert the code in a 'function 
>>> template' but when I do this, I don't manage to call the function 
>>> without failure..
>>> Does someone knows how to do it?
>>
>> Well, presumably the function needs to access (local?) variable 'x'. 
>> That means you can't avoid using some kind of mixin if you want to do 
>> this.
> 
> It's not the mixin that I want to get rid of (I can't) but what I'd like 
>  is to rename the function call sputf and to have just one function 
> call, this works for templates, but not for template function, weird..

You could have a compile-time function generate a string that calls the 
appropriate code.

Here's what I came up with:
---
import std.stdio;
import std.string;

/** dummy Fmt!() for testing. This example assumes it accepts a
  *  single string and returns format() parameters in string form.
  */
template Fmt(char[] A) {
	const char[] Fmt = `"%d", x`;
}

/** Ensures escape codes and quotes in strings are escaped themselves.
  *  Not thoroughly checked, but the idea is that the following should
  *  hold for all strings:
  *  ---
  *      string == mixin(escape(string));
  *  ---
  */
char[] escape(char[] string) {
	char[] result = "\"";
	foreach (c; string) {
		if (c == '\\')
			result ~= "\\\\";
		else if (c == '"')
			result ~= "\\\"";
		else
			result ~= c;
	}
	result ~= "\"";
	return result;
}

/** The input must be a format string with embedded "%d{var}"-style
  *  formatting commands
  */
char[] sputf(char[] string) {
	return "mixin(`std.string.format(` ~ Fmt!(" ~ escape(string) ~ ") ~ `)`)";
}

void main(char[][] args) {
	char[] ret;
	int x = 2007;
	
	ret = mixin(sputf("%d{x}"));
	
	writefln("%s", ret);
}
---

As short as it is, it took me a while to get it that way.
This is playing with meta-levels. It mixes in a string containing a 
mixin expression. It used to be even worse though:
At first the sputf() function was just a wrapper around an adapted 
sputf!() template, but all the call() function did was return a mixed-in 
expression, which was then wrapped into a string that - when mixed in - 
assigned the second-level mixed-in result to a variable passed by name.
So it mixed in a string, that mixed in a template, that mixed in an 
expression -- a three-level mixin. (Careful: multiple meta-levels like 
that can get really confusing really quick :P)

Then I figured, that template mixin (sputfImpl!(), specifically the 
call() member) must be inlinable ;).

After adding an escape() function around the format string, the result 
is what you see above: short but sweet.

I hope you agree that
---
	ret = mixin(sputf("%d{x}"));
---
is a pretty good syntax :).


More information about the Digitalmars-d-learn mailing list