11 hours ago
On Tue, Sep 09, 2025 at 02:26:20PM +1200, Richard (Rikki) Andrew Cattermole via Digitalmars-d-announce wrote:
> On 09/09/2025 3:39 AM, H. S. Teoh wrote:
[...]
> > I'm still a fan of old-school format strings, I've to admit. Having
> > to manually type `formatValue(x), formatValue(y), ...` is just way
> > too much boilerplate.
> 
> Agreed it is too much boilerplate.
> 
> If we have to add a runtime string option then we can, the machinery will all be there.
> 
> However it shouldn't be the option people should be reaching for.
> 
> How long have we been recommending the template parameter for formatting over the runtime one? A good 10+ years now right.


> > > Require the use of a string builder, rather than any old output range.
> > 
> > Too much boilerplate to use a string builder.
> 
> Nah.
> 
> We use appenders in place of the string builder today.

Oh you mean use a string building internally?  That makes sense. I misunderstood, I thought you meant for user code to use a string builder.


[...]
> > See, the thing is that the current implementation of std.format goes about things the wrong way.  It really should be just a thin wrapper template, the sole purpose of which is to unpack the incoming arguments and forward them to non-templated formatting functions. Or, at least, formatting functions templated only on a *single* argument type (like formatValue!int, formatValue!string, formatValue!float, ...), or perhaps just overload on various basic types, maybe plus a couple of templates for handling structs and classes, not on the entire `Args...` tuple of types.  The latter causes combinatorial explosion of template instances, which is both bloating and needless.  Only the top-level std.format.format needs to be templated on `Args...`.  This should be split up so that instead of O(n*m) template instantiations we have only O(n) template instantiations (or preferably, O(1) template instantiations if all the type-dependent stuff is handled at the top level, and all lower-level functions are isolated formatting functions that only do one job each).
> 
> Looks like its three template parameters: writer, format spec char and value type. Two of which I want gone.

Yeah, the writer should be type-erased to a delegate that receives string data, the format spec should not be templated on char type. Value type should pretty much be the only template parameter.


> Doesn't appear to have a central dispatcher, its leaving it to overloading which is not a good design IMO.

Yeah it's a mess. :-/  I did look at this code some years ago, hoping to find low-hanging fruits to improve it, but gave up after struggling with the tangled mess that it was in.


> We'd like it to be closer to mine (although for whatever reason I've still got the builder templated):
> 
> https://github.com/Project-Sidero/basic_memory/blob/main/source/sidero/base/text/format/rawwrite.d#L13

Standardizing to a delegate for the builder allows us to swap out different builders without incurring any template bloat. Not sure if that's necessary, but could be a nice escape hatch just in case an unusual case arises.

Templates are powerful but sometimes type erasure is called for.


> > Also, I dream of the day when we can pass compile-time format strings to std.format and it will *only* instantiate the formatting functions that you actually use. Float-formatting functions are particularly complex and bloaty; why should your program pay for that extra baggage if you never actually format a float?  The various formatting functions should be pulled in only when you actually use them.  Just because you call std.format with "%d" should not also pull in the whole shebang for formatting floats, structs, classes, BigInts, and who knows what else.
> 
> It already is.
> 
> https://github.com/dlang/phobos/blob/master/std/format/internal/write.d#L575

+1.


T

-- 
Talk is cheap. Whining is actually free. -- Lars Wirzenius
1 2
Next ›   Last »