Thread overview | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
October 31, 2018 expanding variadic into format | ||||
---|---|---|---|---|
| ||||
I have a routine that was happily printing ASCII strings and values using opengl, however I want to improve it so it can be used in the same manner as some other languages printf function... void printValue(Font fnt,float x, float y, string frmt, ...) { /* matrix math and other stuff removed for readability */ string message = format(frmt, _arguments); no surprise this naive attempt causes a runtime error as its trying to format a range using the first format specifier in frmt am I going to have to chop frmt into descrete chunks that have just one % symbol in them and then build up the formatted message string like that? is there some way to somehow transfer my input variadic into formats variadic ? |
November 01, 2018 Re: expanding variadic into format | ||||
---|---|---|---|---|
| ||||
Posted in reply to Codifies | On 01/11/2018 12:53 AM, Codifies wrote:
> I have a routine that was happily printing ASCII strings and values using opengl, however I want to improve it so it can be used in the same manner as some other languages printf function...
>
> void printValue(Font fnt,float x, float y, string frmt, ...)
> {
> /* matrix math and other stuff removed for readability */
> string message = format(frmt, _arguments);
>
>
> no surprise this naive attempt causes a runtime error as its trying to format a range using the first format specifier in frmt
>
> am I going to have to chop frmt into descrete chunks that have just one % symbol in them and then build up the formatted message string like that?
>
> is there some way to somehow transfer my input variadic into formats variadic ?
Just to confirm, format there is std.format:format right?
Because that isn't using C variadics, its using template variadics.
|
October 31, 2018 Re: expanding variadic into format | ||||
---|---|---|---|---|
| ||||
Posted in reply to rikki cattermole | On Wednesday, 31 October 2018 at 11:56:31 UTC, rikki cattermole wrote:
> On 01/11/2018 12:53 AM, Codifies wrote:
>> [...]
>
> Just to confirm, format there is std.format:format right?
>
> Because that isn't using C variadics, its using template variadics.
thought I was using core.vararg and std.format both using templates ??
|
October 31, 2018 Re: expanding variadic into format | ||||
---|---|---|---|---|
| ||||
Posted in reply to Codifies | On Wednesday, 31 October 2018 at 11:53:52 UTC, Codifies wrote: > void printValue(Font fnt,float x, float y, string frmt, ...) > { > /* matrix math and other stuff removed for readability */ > string message = format(frmt, _arguments); > > is there some way to somehow transfer my input variadic into formats variadic ? >> Just to confirm, format there is std.format:format right? >> Because that isn't using C variadics, its using template variadics. ...as in: ``` void printValue(Args...)(Font fnt, float x, float y, string frmt, auto ref Args args) { // ... import std.functional : forward; string message = format(frmt, forward!args); // ... } ``` |
November 01, 2018 Re: expanding variadic into format | ||||
---|---|---|---|---|
| ||||
Posted in reply to Codifies | On 01/11/2018 1:08 AM, Codifies wrote:
> On Wednesday, 31 October 2018 at 11:56:31 UTC, rikki cattermole wrote:
>> On 01/11/2018 12:53 AM, Codifies wrote:
>>> [...]
>>
>> Just to confirm, format there is std.format:format right?
>>
>> Because that isn't using C variadics, its using template variadics.
>
> thought I was using core.vararg and std.format both using templates ??
No. They use different variadics.
Template variadics happen on the template parameter side:
void func(T...)(T args) {
pragma(msg, T.length);
pragma(msg, T.stringof);
}
C variadics happen on the function parameters side and are highly unsafe:
void func(...) {
}
If you use core.vararg you're talking with C.
If you use std.format you're talking D's template variadics.
|
October 31, 2018 Re: expanding variadic into format | ||||
---|---|---|---|---|
| ||||
Posted in reply to Stanislav Blinov | On Wednesday, 31 October 2018 at 12:09:04 UTC, Stanislav Blinov wrote:
> On Wednesday, 31 October 2018 at 11:53:52 UTC, Codifies wrote:
>
>> void printValue(Font fnt,float x, float y, string frmt, ...)
>> {
>> /* matrix math and other stuff removed for readability */
>> string message = format(frmt, _arguments);
>
>>
>> is there some way to somehow transfer my input variadic into formats variadic ?
>
>>> Just to confirm, format there is std.format:format right?
>>> Because that isn't using C variadics, its using template variadics.
>
> ...as in:
>
> ```
> void printValue(Args...)(Font fnt, float x, float y, string frmt, auto ref Args args) {
> // ...
> import std.functional : forward;
> string message = format(frmt, forward!args);
> // ...
> }
> ```
thats fantastic thanks so much, can you explain a little more about whats going on here ?
|
October 31, 2018 Re: expanding variadic into format | ||||
---|---|---|---|---|
| ||||
Posted in reply to Codifies | On Wednesday, 31 October 2018 at 12:13:57 UTC, Codifies wrote: > On Wednesday, 31 October 2018 at 12:09:04 UTC, Stanislav Blinov wrote: >> ``` >> void printValue(Args...)(Font fnt, float x, float y, string frmt, auto ref Args args) { >> // ... >> import std.functional : forward; >> string message = format(frmt, forward!args); >> // ... >> } >> ``` > > thats fantastic thanks so much, can you explain a little more about whats going on here ? As rikki already explained, std.format is a variadic template, which gets expanded into argument list at compile time. That's why it can't be used with C-syle variadics: when you passed _arguments, the expansion treated that as a single argument instead of a tuple. Therefore, to forward arguments to std.format, your `printValue` must also be a variadic. There are, broadly speaking, two ways to pass arguments to functions: by value and by reference. When you make a template like this: void foo(T)(T value) { /* ... */ } it will take the argument by value, making copies when necessary: struct S { /* ... */ } S s; foo(S.init); // calls foo without copying, argument is constructed directly foo(s); // copies `s` and passes that copy to `foo` If that template is defined like this: void foo(T)(ref T value) { /* ... */ } then it will *only* take argument by reference: foo(S.init); // error, 'ref' cannot bind to rvalue foo(s); // no copy is made, `foo` takes `s` by reference There are more subtleties, especially when taking `const ref` arguments, but I won't get into those. There's a special syntax for template functions: `auto ref` arguments. Those are deduced to be by-value or by-reference at compile time (see https://dlang.org/spec/template.html#auto-ref-parameters): void foo(T)(auto ref T value) { /* ... */ } foo(S.init); // works, compiles as foo(S); foo(s); // works, compiles as foo(ref S); But, because inside of function definition all arguments are lvalues, you lose this additional information if you pass them to another function directly. To preserve that information, there's a `forward` template in std.functional. D doesn't have rvalue references, so that template will still copy the bits of non-`ref` arguments, but it will not call postblits, etc (it `move`s them using std.algorithm.mutation.move). So, there are two possible ways to implement your print: // Take all Args by value, i.e. copy everything first time void printValue(Args...)(Font fnt, float x, float y, string frmt, Args args) { // make copies of every argument in `args` (again) and pass those to `format` auto message = format(frmt, args); } or // Infer whether each argument is an lvalue or not void printValue(Args...)(Font fnt, float x, float y, string frmt, auto ref Args args) { import std.functional : forward; // preserve lvalue/rvalue string message = format(frmt, forward!args); } Both allow you to accomplish your goal, but the second one only copies the argument bits when necessary. Getting into finer implementation nuances, conceptually this allows a function to even take and pass around non-copyable types as arguments. Sadly, this is not widely adopted by Phobos, which likes to make unnecessary copies. I.e. the `format` function itself takes Args by value, even though it probably should take advantage of this specific language feature. But at least calling it via `forward`, you only make necessary copies once, instead of twice. |
October 31, 2018 Re: expanding variadic into format | ||||
---|---|---|---|---|
| ||||
Posted in reply to Stanislav Blinov | On Wednesday, 31 October 2018 at 12:54:52 UTC, Stanislav Blinov wrote:
> On Wednesday, 31 October 2018 at 12:13:57 UTC, Codifies wrote:
>> [...]
>
>> [...]
>
> As rikki already explained, std.format is a variadic template, which gets expanded into argument list at compile time. That's why it can't be used with C-syle variadics: when you passed _arguments, the expansion treated that as a single argument instead of a tuple.
> Therefore, to forward arguments to std.format, your `printValue` must also be a variadic.
>
> [...]
thanks for this makes it a lot clearer, I'm guessing there is a mailing list backing this web forum, as I replied missing some other responses.
|
Copyright © 1999-2021 by the D Language Foundation