Thread overview
expanding variadic into format
Oct 31, 2018
Codifies
Oct 31, 2018
rikki cattermole
Oct 31, 2018
Codifies
Oct 31, 2018
rikki cattermole
Oct 31, 2018
Stanislav Blinov
Oct 31, 2018
Codifies
Oct 31, 2018
Stanislav Blinov
Oct 31, 2018
Codifies
October 31, 2018
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
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
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
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
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
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
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
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.