Feedback Wanted on Homegrown @nogc WriteLn Alternative
monarch_dodra via Digitalmars-d
digitalmars-d at puremagic.com
Fri Oct 3 10:21:14 PDT 2014
On Friday, 3 October 2014 at 17:01:46 UTC, H. S. Teoh via
Digitalmars-d wrote:
> On Fri, Oct 03, 2014 at 11:15:28AM +0000, monarch_dodra via
> Digitalmars-d wrote:
>> On Thursday, 2 October 2014 at 23:32:32 UTC, H. S. Teoh via
>> Digitalmars-d
>> wrote:
>> >Alright, today I drafted up the following proof of concept:
>> >
>> >[...]
>> >
>> >writefln!"Number: %d Tag: %s"(123, "mytag");
>>
>> I had (amongst with others) thought about the possibility of
>> "ct-write".
>>
>> I think an even more powerful concept, would rather having a
>> "ct-fmt"
>> object dirctly. Indeed writefln!"string" requires the actual
>> format at
>> compile time, and for the write to be done.
>>
>> It can't just validate that *any* arbitrary (but pre-defined)
>> string
>> can be used with a certain set of write arguments.
>>
>> I'm thinking:
>>
>> //----
>> //Define several format strings.
>> auto english = ctFmt!"Today is %1$s %2$s";
>> auto french = ctFmt!"Nous sommes le %2$s %1$s";
>>
>> //Verify homogeneity.
>> static assert(is(typeof(english) == typeof(french)));
>>
>> //Chose your format.
>> auto myFmt = doEnglish ? english : french;
>>
>> //Benefit.
>> writfln(myFmt, Month.oct, 3);
>> //----
>>
>> I think this is particularly relevant in that it is these
>> kinds of cases
>> that are particularly tricky and easy to get wrong.
>
> So ctFmt would have to be a static type that contains static
> information
> about the number and types of formatting items it expects?
> Because
> otherwise, we won't be able to do checks like verifying at
> compile-time
> that the passed arguments match the given format.
>
> But if we're going to go in this direction, I'd also introduce
> named parameters instead of positional parameters, which would
> make
> translators' jobs easier. For example:
>
> ctFmt!"Today is %`day`s %`month`s"
>
> is far easier to translate correctly than:
>
> ctFmt!"Today is %1$s %2$s"
>
> where the translator may have no idea what %1$s and %2$s are
> supposed to
> refer to. For all they know, %1%s could be "our" and %2$s could
> be
> "anniversary".
Right, but that would also require named parameter passing, which
we don't really have.
>> For "basic" usage, you'd just use:
>> writefln(ctFmt!"Number: %d Tag: %s", 123, "mytag");
>>
>> The hard part is finding the sweet spot in runtime/compile
>> time data,
>> to make those format strings runtime-type compatible. But it
>> should be
>> fairly doable.
>
> Personally, I prefer the shorter syntax for the most usual
> cases where
> the format string doesn't change:
>
> writefln!"Number: %d Tag: %s"(123, "mytag");
>
> But ctFmt could also fit under this scheme when more
> flexibility is
> desired: we could pass it as a first parameter and leave the
> default CT
> parameter as "" (meaning, read args[0] for format string). So
> if args[0]
> is an instance of ctFmt, then we can do (more limited)
> compile-time
> checking, and if it's a runtime string, then fallback to the
> current
> behaviour.
Well, we could also simply have
writeln!str(args) => writefln(ctFmt!str, args)
> For a compile-string that's statically fixed (i.e.,
> writefln!"..."(...)), we can do a lot more than what ctFmt
> does. For
> example, we can parse the format at compile-time to extract
> individual
> formatting specifiers and intervening string fragments, and
> thereby
> transform the entire writefln call into a series of puts() and
> formattedWrite() calls.
>
> With ctFmt, you can't extract the intervening string fragments
> beforehand, and you'll need runtime binding of formatting
> specifiers to
> arguments, because the exact format string chosen may vary at
> runtime,
> though they *can* be statically checked to be compatible at
> compile-time
> (so "X %1$s Y %2$s Z" is compatible with "P %2$s Q %1$s R", but
> "%d %d
> %d" is not compatible with "%f %(%s%)" because they expect a
> different
> number of arguments and argument types). So I see ctFmt as an
> object
> that encapsulates the expected argument types, but leaves the
> actual
> format string details to runtime, whereas passing in a string
> in the CT
> argument of writefln will figure out the format string details
> at
> compile-time, leaving only the actual formatting to be done at
> runtime.
>
>
> T
Well, technically, `ctFmt` could still do some formatting. It can
still cut up the format into an alternative series of strings and
"to format objects". ctFmt would still know how many string
fragments there are, and so would writeln. Writeln would still be
able to generate nothing more than "puts", the only difference is
that the actual string token is runtime defined, but I don't
think that makes any change.
Eg: ct!"hello %s World" becomes the type:
struct
{
//Actual contents run-time defined,
//but possibly pre-calculated during ctfe.
string[2] fixedStrings;
//Completely statically know.
enum string[1] fmt = [%s];
}
In particular, what "ctFmt" doesn't know, could still be
interpreted by writeln. For example, while "ctFmt" doesn't know
what "%s" binds to, it still statically knows it's "%s", and
writeln can statically extract that information.
The compile restriction we'd place on ctFmt would be that:
-The amount of "format objects" must be the same
-Each format objects must be the same at the same place, bar some
"positional shuffling".
So as I said (IMO), if we correctly define what ctformat string
are type-compatible, we can have pretty good run-time
possibilities, but still have 100% of the capabilities that
writeln!str(args) would give us.
More information about the Digitalmars-d
mailing list