digitalmars.D - Transferring 32 bits
- Salih Dincer (33/33) Aug 25 2022 Hi All,
- Salih Dincer (11/13) Aug 25 2022 ```d
- Adam D Ruppe (8/10) Aug 25 2022 The `scope` keyword means you promise not to let any references
- Adam D Ruppe (21/35) Aug 25 2022 This code is completely wrong. You're improperly casting mutable
- Paul Backus (11/14) Aug 25 2022 Rather than using a delegate sink, I believe the current
- Salih Dincer (43/59) Aug 25 2022 Yes, but the code works withthe new operator. I just wanted to go
Hi All, Transferring 32 bits (4 characters) of data from a data source, for example to the S structure takes place... Everything is fine with the constructor but I can't keep the object alive with the foo() convenience function! My sample code is like this: ```d import std.stdio; struct S { char[size] bytes; alias toString this; string toString() const { scope res = [ cast(string) bytes ]; return res[0]; } } string foo(char[size] bytes) { scope res = S(bytes); return res.toString(); // problem here } enum size = 4; void main() { char[size] text = "abcd".dup; auto m = S(text); // test ok assert(m == text); foo(text).writefln!"(%s)"; // (¿) } ``` SDB 79
Aug 25 2022
On Thursday, 25 August 2022 at 12:18:19 UTC, Salih Dincer wrote:Everything is fine with the constructor but I can't keep the object alive with the foo() convenience function!```d string foo(char[size] bytes) { scope res = new S(bytes); return res.toString(); // problem here } ``` **One more thing:** It's ok when I construct S with the new operator! SDB 79
Aug 25 2022
On Thursday, 25 August 2022 at 12:22:56 UTC, Salih Dincer wrote:scope res = new S(bytes);The `scope` keyword means you promise not to let any references escape this scope. Your toString does do that, so the compiler might optimize this `new` away; the scope and new keywords here cancel each other out, leaving you with the same problem.It's ok when I construct S with the new operator!Still wrong code, but it'd work here because the bytes then are GC managed instead of stack temporaries so you still cast to immutable but at least it isn't use-after-free.
Aug 25 2022
On Thursday, 25 August 2022 at 12:18:19 UTC, Salih Dincer wrote:char[size] bytes; alias toString this; string toString() const { scope res = [ cast(string) bytes ]; return res[0]; }This code is completely wrong. You're improperly casting mutable and limited lifetime data to string, then returning it. And that scope res has no reason to exist; the code is identical to just `return cast(string) bytes;` which the compiler will slice for you - taking a pointer of it - then you return that slice with a new type. So this is returning a pointer to a local variable which is liable to cease to exist as soon as the function calling it returns...string foo(char[size] bytes) { scope res = S(bytes); return res.toString(); // problem here }...which is exactly what you're doing here. Those bytes inside S live on the stack of this foo function. You return a pointer to them, then the foo function returns and those bytes on the stack cease to exist (actually reused memory but same thing conceptually) yet you keep the reference.auto m = S(text); // test ok assert(m == text);this only works because m still exists at the point of the assert, once there's another function involved the stack memory is gone. What you want to do instead is either `.idup` the bytes or make your toString do a `void delegate(in char[]) sink` and you pass the bytes to the sink casting to char[].
Aug 25 2022
On Thursday, 25 August 2022 at 12:40:57 UTC, Adam D Ruppe wrote:What you want to do instead is either `.idup` the bytes or make your toString do a `void delegate(in char[]) sink` and you pass the bytes to the sink casting to char[].Rather than using a delegate sink, I believe the current recommended approach [1] is to make toString a template that accepts an output range: import std.range; void toString(Sink)(ref Sink sink) if (isOutputRange!(Sink, char)) { put(sink, cast(char[]) bytes); } [1] https://dlang.org/phobos/std_format_write.html
Aug 25 2022
On Thursday, 25 August 2022 at 12:40:57 UTC, Adam D Ruppe wrote:This code is completely wrong. You're improperly casting mutable and limited lifetime data to string, then returning it. And that scope res has no reason to exist; the code is identical to just `return cast(string) bytes;` which the compiler will slice for you - taking a pointer of it - then you return that slice with a new type.Yes, but the code works withthe new operator. I just wanted to go to the limits D allowed; to get some understanding of reference types and scope... On Thursday, 25 August 2022 at 14:17:16 UTC, Paul Backus wrote:...to make toString a template that accepts an output range: ```d import std.range; void toString(Sink)(ref Sink sink) if (isOutputRange!(Sink, char)) { put(sink, cast(char[]) bytes); } ```I tried using outputrange but without success. But I implemented like this and was able to return a living string perfectly: ```d import std.format, std.stdio; struct S(int len) { char[len] bytes; void toString(void delegate(const(char)[]) sink) const { auto res = cast(char[]) bytes; typeid(res).writeln(" toString()=>"); sink.formattedWrite("%s", res); } } char[] foo(char[9] bytes) { auto res = cast(char[]) format("%s", S!9(bytes)); scope(exit) typeid(res).writeln(" foo()=>"); return res; } void main() { char[9] text = " Hello D".dup; auto test = S!9(text); test.writeln; // ok foo(text).writeln; // ok } /* Output: char[] toString()=> Hello D char[] toString()=> char[] foo()=> Hello D */ ``` Thank you all, it was helpful... SDB 79
Aug 25 2022