Feedback Wanted on Homegrown @nogc WriteLn Alternative
H. S. Teoh via Digitalmars-d
digitalmars-d at puremagic.com
Thu Oct 2 16:30:33 PDT 2014
On Sat, Sep 27, 2014 at 01:06:24PM +0200, Andrej Mitrovic via Digitalmars-d wrote:
> On 9/27/14, H. S. Teoh via Digitalmars-d <digitalmars-d at puremagic.com> wrote:
> > writefln!"...format string here"(... /* arguments here */);
>
> Mmm, I like this. It would be one of those killer little features to
> show in a talk. Slide 1:
>
> // oops, forgot an %s
> writefln("%s %s", 1, 2, 3);
>
> Slide 2:
>
> // Programmer error caught at compile-time!
> writefln!("%s %s")(1, 2, 3);
Alright, today I drafted up the following proof of concept:
private size_t numSpecs(string fmt)
{
size_t count = 0;
for (size_t i=0; i < fmt.length; i++)
{
if (fmt[i] == '%')
{
i++;
if (i < fmt.length && fmt[i] == '%')
continue;
count++;
}
}
return count;
}
void writef(string fmt="", Args...)(Args args)
{
import std.format : formattedWrite;
import std.stdio : stdout;
static if (fmt == "")
{
// fmt == "" means we're using a runtime-specified format string.
static if (args.length >= 1)
{
static if (is(typeof(args[0]) : string))
stdout.lockingTextWriter().formattedWrite(args[0], args[1..$]);
else
static assert(0, "Expecting format string as first argument");
}
}
else
{
// Compile-time specified format string: we can run sanity checks on
// it at compile-time.
enum count = numSpecs(fmt);
static assert(args.length == count,
"Format string requires " ~ count.stringof ~
" arguments but " ~ args.length.stringof ~
" are supplied");
// For now, we just forward it to the runtime format implementation.
// The eventual goal is to only depend on formatting functions that
// the format string actually uses, so that we can be @safe, nothrow,
// @nogc, pure, etc., as long as the dependent parts don't break any of
// those attributes.
stdout.lockingTextWriter().formattedWrite(fmt, args);
}
}
void writefln(string fmt="", Args...)(Args args)
{
writef!(fmt)(args);
writef!"\n";
}
void main()
{
//writefln!"Number: %d Tag: %s"(123, "mytag", 1);
writefln!"Number: %d Tag: %s"(123, "mytag");
}
If you uncomment the first line in main(), you'll get a nice
compile-time error telling you that the number of format specifiers and
the number of actual arguments passed don't match.
This is just a proof-of-concept, mind you; it doesn't actually parse the
format string correctly (it's just counting the number of unescaped %'s,
but that doesn't necessarily correspond with the number of arguments
needed, e.g., if you use "%*s" or "%(...%)").
But it *does* prove that it's possible to achieve compatibility with the
current way of invoking writefln, that is, if you write:
writefln("format %s string", "abc");
it will actually compile as before, and work as expected.
So this new syntax can be implemented alongside the existing syntax and
people can gradually migrate over from purely-runtime format strings to
compile-time, statically-checked format strings.
T
--
"Uhh, I'm still not here." -- KD, while "away" on ICQ.
More information about the Digitalmars-d
mailing list