Jump to page: 1 2
Thread overview
Stop writeln from calling object destructor
Oct 02, 2022
data pulverizer
Oct 02, 2022
Paul Backus
Oct 02, 2022
data pulverizer
Oct 02, 2022
data pulverizer
Oct 02, 2022
Ali Çehreli
Oct 02, 2022
data pulverizer
Oct 02, 2022
data pulverizer
Oct 02, 2022
Ali Çehreli
Oct 02, 2022
data pulverizer
Oct 02, 2022
Paul Backus
Oct 04, 2022
data pulverizer
October 02, 2022

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

October 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:

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:

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 ...
October 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?

October 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.

October 02, 2022
On 10/2/22 10:28, data pulverizer wrote:
> On Sunday, 2 October 2022 at 17:19:55 UTC, data pulverizer wrote:
>> Any reason why this could be?
>

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...

> 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


October 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.

October 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.

October 02, 2022
On 10/2/22 10:55, data pulverizer wrote:
> 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 ...
> }
> ```

I've just tested. That is used only for explicit constructor syntax:

    auto b = RVector!int(a);    // templatized

>
>
> 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


October 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!

October 02, 2022
On Sunday, 2 October 2022 at 18:24:51 UTC, Ali Çehreli wrote:
> On 10/2/22 10:55, data pulverizer wrote:
> > ```
> > 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);    // templatized
>
> >
> >
> > 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... :/

It's a bug in the documentation.

https://issues.dlang.org/show_bug.cgi?id=23382
https://github.com/dlang/dlang.org/pull/3427
« First   ‹ Prev
1 2