Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making a heap copy of a struct in D

How can I create a garbage-collected copy of a struct that's on the stack?

Coming from a C++ background, my first guess would be a copy constructor like the one below, but it doesn't seem very idiomatic for D, and I haven't seen one in any of the D projects I've taken a look at.

struct Foo {
    immutable int bar;

    this(int b) { bar = b; }

    // A C++-style copy constructor works but doesn't seem idiomatic.
    this(ref const Foo f) { bar = f.bar; }
}

void main()
{
    // We initialize a Foo on the stack
    auto f = Foo(42);

    // Now I want to get a heap copy of its member. How?

    // A C++-style copy constructor works but doesn't seem idiomatic.
    Foo* f1 = new Foo(f);
}
like image 701
Matt Kline Avatar asked Dec 14 '22 22:12

Matt Kline


1 Answers

Your example is overly complicated and doesn't even compile, but essentially, it sounds like what you want to be able to do is something like

struct Foo
{
    int i;
}

void main()
{
    auto f = Foo(5);
    auto g = new Foo(f);
}

What you can do without any special constructors is

void main()
{
    auto f = Foo(5);
    auto g = new Foo;
    *g = f;
}

but obviously that's more steps than you probably want. The normal "copy constructor" for D is a postblit constructor, which is declared as this(this) {...}, but that's not required for Foo as it's declared here (since all of its members are value types), and it doesn't help with allocating a struct on the heap anyway - just with copying a struct. Arguably

auto f = Foo(5);
auto g = new Foo(f);

should just work, but at the moment, it doesn't, and there is unfortunately, no support in the language for defining it for you. So, unfortunately, I think that you're currently forced to do something akin to what you're trying to avoid. e.g.

struct Foo
{
    int i;

    this(int j)
    {
        i = j;
    }

    this(Foo rhs)
    {
        this = rhs;
    }
}

void main()
{
    auto f = Foo(5);
    auto g = new Foo(f);
}

However, I just opened an enhancement request for making it so that new Foo(foo) will just work, and with the next release of dmd (2.066) we're going to get universal construction for primitive types (e.g. new int(5) will now work), so I think that there's a very good argument for this just working as well.

For now though, you're going to have to provide an extra constructor to get this to work.

like image 127
Jonathan M Davis Avatar answered Jan 02 '23 04:01

Jonathan M Davis