digitalmars.D.learn - Stop writeln from calling object destructor
- data pulverizer (40/40) Oct 02 2022 I've noticed that `writeln` calls the destructor of a struct
- Paul Backus (42/45) Oct 02 2022 It's because `writeln` is copying the object, and each of the
- data pulverizer (8/12) Oct 02 2022 I thought something like this could be happening in my original
- data pulverizer (3/4) Oct 02 2022 Sorry I'll need to implement all the overloaded copy constructors
- =?UTF-8?Q?Ali_=c3=87ehreli?= (27/31) Oct 02 2022 What I noticed first in your original code was that it would be
- data pulverizer (10/15) Oct 02 2022 Thanks for the advice, for a while now I didn't know what was
- data pulverizer (19/21) Oct 02 2022 I've got it, something weird happened to my copy constructor.
- =?UTF-8?Q?Ali_=c3=87ehreli?= (18/38) Oct 02 2022 I've just tested. That is used only for explicit constructor syntax:
- data pulverizer (2/4) Oct 02 2022 Many thanks. Knowledgeable as always!
- Paul Backus (4/30) Oct 02 2022 It's a bug in the documentation.
- Steven Schveighoffer (7/11) Oct 03 2022 I know you already solved the problem, but just for future reference, if...
- data pulverizer (4/16) Oct 04 2022 Just seen this. Nice one. The docs link:
I've noticed that `writeln` calls the destructor of a struct multiple times and would like to know how to stop this from happening. It has become a very serious problem when working with objects that have memory management external to D. Here is a repeatable example, where the destructor appears to have been called 4 times with one call of `writeln` before the object actually goes out of scope: Code: ``` import std.stdio: writeln; struct MyObject { int id; this(int id) nogc { this.id = id; } ~this() { writeln("Object destructor ..."); } } void main() { auto obj = MyObject(42); writeln("MyObject: ", obj); writeln("Goodbye:\n"); } ``` Output: ``` $ rdmd gc.d MyObject: MyObject(42)Object destructor ... Object destructor ... Object destructor ... Object destructor ... Goodbye: Object destructor ... ``` Thank you
Oct 02 2022
On Sunday, 2 October 2022 at 16:21:47 UTC, data pulverizer wrote:I've noticed that `writeln` calls the destructor of a struct multiple times and would like to know how to stop this from happening.It's because `writeln` is copying the object, and each of the copies is being destroyed. If you add a copy constructor to your example, you can see it happening: ```d import std.stdio: writeln; struct MyObject { int id; this(int id) nogc { this.id = id; } this(inout ref MyObject) inout { writeln("Object copy constructor..."); } ~this() { writeln("Object destructor ..."); } } void main() { auto obj = MyObject(42); writeln(obj); writeln("Goodbye:\n"); } ``` Output: ```d Object copy constructor... Object copy constructor... Object copy constructor... Object copy constructor... MyObject(0)Object destructor ... Object destructor ... Object destructor ... Object destructor ... Goodbye: Object destructor ... ```
Oct 02 2022
On Sunday, 2 October 2022 at 16:44:25 UTC, Paul Backus wrote:It's because `writeln` is copying the object, and each of the copies is being destroyed. If you add a copy constructor to your example, you can see it happening: ...I thought something like this could be happening in my original implementation and tried implementing a copy constructor using this reference https://dlang.org/spec/struct.html#struct-copy-constructor but it did not work. Both your's and the manual's suggestion works for my baby example but not for my actual code. Any reason why this could be?
Oct 02 2022
On Sunday, 2 October 2022 at 17:19:55 UTC, data pulverizer wrote:Any reason why this could be?Sorry I'll need to implement all the overloaded copy constructors and see if that fixes it.
Oct 02 2022
On 10/2/22 10:28, data pulverizer wrote:On Sunday, 2 October 2022 at 17:19:55 UTC, data pulverizer wrote:What I noticed first in your original code was that it would be considered buggy because it was not considering copying. Every struct that does something in its destructor should either have post-blit (or copy constructor) defined or simpler, disallow copying altogether. That's what I did here: https://github.com/acehreli/alid/blob/main/cached/alid/cached.d#L178 disable this(this); I think disabling copy constructor was unnecessary but I did that as well: disable this(ref const(typeof(this))); The issue remains and bothers me as well. I think writeln copies objects because D disallows references to rvalue. We couldn't print rvalues if writeln insisted on 'ref'. Or, rvalues would be copied anyway if we used 'auto ref'. Hence the status quo...Any reason why this could be?Sorry I'll need to implement all the overloaded copy constructors and see if that fixes it.The best solution I know is to disable copying and printing not the object but an explicit string representation of it: Added: disable this(this); Added (there are better ways of doing the same e.g. using a 'sink' parameter): string toString() const { import std.format : format; return format!"id: %s"(id); } Called toString: writeln("MyObject: ", obj.toString); Ali
Oct 02 2022
On Sunday, 2 October 2022 at 17:51:59 UTC, Ali Çehreli wrote:What I noticed first in your original code was that it would be considered buggy because it was not considering copying. Every struct that does something in its destructor should either have post-blit (or copy constructor) defined or simpler, disallow copying altogether.Thanks for the advice, for a while now I didn't know what was creating the issue. The code I'm running is my D connector to the R API and for ages I didn't know where the multiple destructor calls to allow an object to be garbage collected by the R API was coming from, and it was breaking the whole thing. I think I'll have to play it by ear whether to disable the copy constructor altogether or to use it now it is working. Thanks both of you.
Oct 02 2022
On Sunday, 2 October 2022 at 17:28:51 UTC, data pulverizer wrote:Sorry I'll need to implement all the overloaded copy constructors and see if that fixes it.I've got it, something weird happened to my copy constructor. This was my original attempt and was ignored (didn't run in the copy constructor): ``` this(T)(ref return scope T original) if(is(T == RVector!(Type))) { //... code ... } ``` But this now works: ``` this(ref return scope RVector!(Type) original) { //... code ... } ``` No idea why. `Type` is a template parameter of the object.
Oct 02 2022
On 10/2/22 10:55, data pulverizer wrote:On Sunday, 2 October 2022 at 17:28:51 UTC, data pulverizer wrote:I've just tested. That is used only for explicit constructor syntax: auto b = RVector!int(a); // templatizedSorry I'll need to implement all the overloaded copy constructors and see if that fixes it.I've got it, something weird happened to my copy constructor. This was my original attempt and was ignored (didn't run in the copy constructor): ``` this(T)(ref return scope T original) if(is(T == RVector!(Type))) { //... code ... } ```But this now works: ``` this(ref return scope RVector!(Type) original) { //... code ... } ```That one works for both syntaxes: auto b = RVector!int(a); // templatized auto c = a; // non-templatized Certainly confusing and potentially a bug... :/No idea why. `Type` is a template parameter of the object.Minor convenience: You can replace all RVector!(Type) with just RVector in the implementation of RVector because the name of the struct template *is* that specific instantiation of it: struct RVector(Type) { // RVector below means RVector!Type: this(ref return scope RVector original) { // ... } } Ali
Oct 02 2022
On Sunday, 2 October 2022 at 18:24:51 UTC, Ali Çehreli wrote:I've just tested. That is used only for explicit constructor syntax ...Many thanks. Knowledgeable as always!
Oct 02 2022
On Sunday, 2 October 2022 at 18:24:51 UTC, Ali Çehreli wrote:On 10/2/22 10:55, data pulverizer wrote:It's a bug in the documentation. https://issues.dlang.org/show_bug.cgi?id=23382 https://github.com/dlang/dlang.org/pull/3427``` this(T)(ref return scope T original) if(is(T == RVector!(Type))) { //... code ... } ```I've just tested. That is used only for explicit constructor syntax: auto b = RVector!int(a); // templatizedBut this now works: ``` this(ref return scope RVector!(Type) original) { //... code ... } ```That one works for both syntaxes: auto b = RVector!int(a); // templatized auto c = a; // non-templatized Certainly confusing and potentially a bug... :/
Oct 02 2022
On 10/2/22 12:21 PM, data pulverizer wrote:I've noticed that `writeln` calls the destructor of a struct multiple times and would like to know how to stop this from happening. It has become a very serious problem when working with objects that have memory management external to D.I know you already solved the problem, but just for future reference, if you use something like `RefCounted`, you can avoid copying and destruction until everyone is done with the object. This is how my io library works, the IO objects are non-copyable, and you wrap them in `RefCounted` if you want to copy them. -Steve
Oct 03 2022
On Monday, 3 October 2022 at 11:08:00 UTC, Steven Schveighoffer wrote:On 10/2/22 12:21 PM, data pulverizer wrote:Just seen this. Nice one. The docs link: https://dlang.org/library/std/typecons/ref_counted.htmlI've noticed that `writeln` calls the destructor of a struct multiple times and would like to know how to stop this from happening. It has become a very serious problem when working with objects that have memory management external to D.I know you already solved the problem, but just for future reference, if you use something like `RefCounted`, you can avoid copying and destruction until everyone is done with the object. This is how my io library works, the IO objects are non-copyable, and you wrap them in `RefCounted` if you want to copy them. -Steve
Oct 04 2022