Thread overview
Format
May 21, 2021
newbie
May 21, 2021
drug
May 21, 2021
newbie
May 21, 2021
cc
May 21, 2021
drug
May 22, 2021
cc
May 22, 2021
cc
May 22, 2021
cc
May 22, 2021
drug
May 21, 2021

I am following https://wiki.dlang.org/Defining_custom_print_format_specifiers, why sink and formatValue are not @safe? What are the best practice for toString in safe code? Thank you

May 21, 2021
21.05.2021 16:45, newbie пишет:
> I am following https://wiki.dlang.org/Defining_custom_print_format_specifiers, why sink and formatValue are not @safe? What are the best practice for toString in safe code? Thank you

sink is obsolete now, use W(riter)

```D
import std.range : isOutputRange;

void toString(W)(ref W writer) const
	if (isOutputRange!(W, char))
{
	// your stuff
}
```
writer can be @safe, @nogc, nothrow etc like you want
May 21, 2021
On Friday, 21 May 2021 at 13:59:13 UTC, drug wrote:
> 21.05.2021 16:45, newbie пишет:
>> I am following https://wiki.dlang.org/Defining_custom_print_format_specifiers, why sink and formatValue are not @safe? What are the best practice for toString in safe code? Thank you
>
> sink is obsolete now, use W(riter)
>
> ```D
> import std.range : isOutputRange;
>
> void toString(W)(ref W writer) const
> 	if (isOutputRange!(W, char))
> {
> 	// your stuff
> }
> ```
> writer can be @safe, @nogc, nothrow etc like you want

Thank you, and formatValue?
May 21, 2021

On Friday, 21 May 2021 at 14:19:03 UTC, newbie wrote:

>

Thank you, and formatValue?

formattedWrite should handle this.

@safe struct Foo {
	int x = 3;
	void toString(W)(ref W writer) if (isOutputRange!(W, char)) {
		writer.formattedWrite("Foo(%s)", x);
	}
}
Foo foo;
writeln(foo);

Oddly enough this form of toString works from @safe code even if it's not marked @safe, or even marked @system...

May 21, 2021
21.05.2021 18:28, cc пишет:
> On Friday, 21 May 2021 at 14:19:03 UTC, newbie wrote:
>> Thank you, and formatValue?
> 
> formattedWrite should handle this.
> 
> ```d
> @safe struct Foo {
>      int x = 3;
>      void toString(W)(ref W writer) if (isOutputRange!(W, char)) {
>          writer.formattedWrite("Foo(%s)", x);
>      }
> }
> Foo foo;
> writeln(foo);
> ```
> 
> Oddly enough this form of toString works from @safe code even if it's not marked @safe, or even marked @system...

I guess that because it is a template so the compiler is able to deduce its attributes. What about the case when it is marked @system - in this case compiler ignore that method due to the fact that it is @system and generate the default one. Because your implementation of the method is equal to the default implementation you didn't see the difference but it exists. Try to make your implementation of `toString` different and you'll see.
May 22, 2021

On Friday, 21 May 2021 at 16:53:48 UTC, drug wrote:

>

21.05.2021 18:28, cc пишет:

>

On Friday, 21 May 2021 at 14:19:03 UTC, newbie wrote:

>

Thank you, and formatValue?

formattedWrite should handle this.

@safe struct Foo {
     int x = 3;
     void toString(W)(ref W writer) if (isOutputRange!(W, char)) {
         writer.formattedWrite("Foo(%s)", x);
     }
}
Foo foo;
writeln(foo);

Oddly enough this form of toString works from @safe code even if it's not marked @safe, or even marked @system...

I guess that because it is a template so the compiler is able to deduce its attributes. What about the case when it is marked @system - in this case compiler ignore that method due to the fact that it is @system and generate the default one. Because your implementation of the method is equal to the default implementation you didn't see the difference but it exists. Try to make your implementation of toString different and you'll see.

Ahh, in that case it would appear formattedWrite isn't @safe at all. Looks like you have to stick with put()?

@safe void toString(W)(ref W writer) if (isOutputRange!(W, char)) {
	//writer.formattedWrite!("FOO:%s", x); // fails
	import std.conv;
	put(writer, "FOO:");
	put(writer, x.to!string);
}
May 22, 2021

On Saturday, 22 May 2021 at 03:07:10 UTC, cc wrote:

>

Ahh, in that case it would appear formattedWrite isn't @safe at all. Looks like you have to stick with put()?

@safe void toString(W)(ref W writer) if (isOutputRange!(W, char)) {
	//writer.formattedWrite!("FOO:%s", x); // fails
	import std.conv;
	put(writer, "FOO:");
	put(writer, x.to!string);
}

Oops, disregard this. I had an error in my imports.😓
It does in fact work in @safe.

May 22, 2021

On Saturday, 22 May 2021 at 03:14:35 UTC, cc wrote:

>

Oops, disregard this. I had an error in my imports.😓
It does in fact work in @safe.

I should add as an aside then that there is an issue of errors from the body of a toString template not being displayed, and instead the template being silently skipped, e.g.:

@safe void toString(W)(ref W writer) if (isOutputRange!(W, char)) {
	writer.formattedWrite("FOO:%s", x);
	syntactically correct; // Program will compile without error, but this function will now never be called
}

I ran into a similar challenge with opDispatch some time ago: https://forum.dlang.org/post/axgwhzzawncbpcvqqrur@forum.dlang.org
In this case I assume it's some consequence of NOT wanting to emit a warning or error for every single class or struct that doesn't have a templated toString when the various output writers go looking for one.

Something like this can help reveal errors, not sure if there's an easier way:

unittest {
	struct DebugWriter {
		void put(C)(C c) {}
	}
	DebugWriter wr;
	Foo foo;
	foo.toString(wr);
}

Error: undefined identifier 'syntactically'
Error: template instance 'amemfailuretest.Foo.toString!(DebugWriter)' error instantiating

May 22, 2021
22.05.2021 06:07, cc пишет:
> Looks like you have to stick with put()?

yes, OutputRange assumes put(). If put() does not support string (accepts char only) I use std.algorithm.copy.

22.05.2021 07:58, cc пишет:
> On Saturday, 22 May 2021 at 03:14:35 UTC, cc wrote:
>> Oops, disregard this.  I had an error in my imports.😓
>> It does in fact work in @safe.
> 
> I should add as an aside then that there is an issue of errors from the body of a toString template not being displayed, and instead the template being silently skipped, e.g.:

yes, it's really annoying because you need to write additional code to reveal the error like you do:

> 
> Something like this can help reveal errors, not sure if there's an easier way:
> ```d
> unittest {
>      struct DebugWriter {
>          void put(C)(C c) {}
>      }
>      DebugWriter wr;
>      Foo foo;
>      foo.toString(wr);
> }
> ```
> `Error: undefined identifier 'syntactically'`
> `Error: template instance 'amemfailuretest.Foo.toString!(DebugWriter)' error instantiating`