Another way to do it would be an alternative to std.Io.Writer.print that still has comptime format string, and still takes a tuple, but is inline and lowers to a call to a non-generic, type-erased function.
Since this has identical function signature, it might even make sense to offer this as an application configuration option in the standard library
@kristoff I don’t mean runtime strings, I mean a type-erased interface over a (comptime string, args) pair. The same way Thread.Pool makes a type-erased interface over a pair of (comptime function, args).
@andrewrk I’m not convinced that would solve it. I probably didn’t explain it well, but in the snippet I was trying to show that doThing shouldn’t have any knowledge of the format args.
If I passed the differently-typed arg tuples all the way down to finalThing and print (even with an alternative Writer.print), every function in the call stack between main and finalThing would have to be generic. I want to avoid that.
I had actually started something like that but it was on 0.13 and a lot of stuff broke in the meantime, including the comptime logic.
My idea was to have single fn format(tape: []const Fmt , args: []const u8) the caller would pass a pointer to the args tuple, and the function would interpret the bytes based on the “tape” metadata. The “tape” was created at comptime.
Did you have another idea in mind?
At the time I also estimated that 15% of Zig compiler binary was generated fmt functions.
I think runtime formatting is definitely something worth investigating but not what the original post is about as far as I can tell.
Perhaps something like this?
Oh, I just realised that that will definitely fail when closing over other values, that’d need to be considered and put into DelayedFormat as well, should be pretty simple.
Edit: “Should be pretty simple,” I say. Right…
Main challenge there is coming up with a way to not include, for example, f128 printing code if such thing is not needed.
I can think of a way to do it, but unsure if it’s in poor taste. One idea is to call through a weak extern function that does the rare, expensive printing. This is initialized to null but in the logic that processes the comptime string, if such printing is encountered, it would reference an exported matching function definition.
I guess it might make sense to specialize this to popular types like ints, but there’s going to be some dynamic dispatch somewhere to handle {f}, and f128 could go through that machinery?