Thread overview
Reuse object memory?
Apr 19, 2015
Namespace
Apr 19, 2015
Ali Çehreli
Apr 19, 2015
Namespace
Apr 19, 2015
Namespace
Apr 20, 2015
Namespace
Apr 20, 2015
Ali Çehreli
Apr 20, 2015
Namespace
Apr 20, 2015
Ali Çehreli
Apr 20, 2015
Namespace
Apr 21, 2015
Marc Schütz
April 19, 2015
Is it somehow possible to reuse the memory of an object?

My current idea is:
----
@nogc
T emplace(T, Args...)(ref T obj, auto ref Args args) nothrow if (is(T == class)) {
    if (obj is null)
        return null;

    enum size_t SIZE = __traits(classInstanceSize, T);

    void[] buf = (cast(void*) obj)[0 .. SIZE];
    buf = typeid(T).init[];
    //obj = cast(T) buf.ptr;

    static if (args.length)
        obj.__ctor(args);

    return obj;
}

Foo f = new Foo(42);
Foo f2 = emplace(f, 23);
----

But is there a more elegant way to do that? Maybe without calling the internal __ctor?

In C++ you can do that:

----
#include <iostream>

class Foo {
public:
	int id;
	
	explicit Foo(int _id) : id(_id) { }
};

int main() {
	Foo* f = new Foo(42);
	std::cout << f << ':' << f->id << std::endl;
	new (f) Foo(23);
	std::cout << f << ':' << f->id << std::endl;
	delete f;
}
----
April 19, 2015
On 04/19/2015 09:04 AM, Namespace wrote:

> Is it somehow possible to reuse the memory of an object?

Yes, when you cast a class variable to void*, you get the address of the object.

> @nogc
> T emplace(T, Args...)(ref T obj, auto ref Args args) nothrow if (is(T ==
> class)) {

There is already std.conv.emplace:

import std.conv;
import core.memory;

class Foo
{
    int i;

    this(int i)
    {
        this.i = i;
    }
}

void main()
{
    void* buffer = GC.calloc(1234);

    enum FooSize = __traits(classInstanceSize, Foo);

    /* These two object are constructed on a void[] slice: */
    auto f = emplace!Foo(buffer[0..FooSize], 42);
    auto f2 = emplace!Foo(buffer[0..FooSize], 43);

    /* f3 is constructed on top of an existing object: */
    auto f2_addr = cast(void*)f2;
    auto f3 = emplace!Foo(f2_addr[0..FooSize], 44);

    /* At this point, all three reference the same object: */
    assert(&f.i == &f2.i);
    assert(&f.i == &f3.i);
}

Ali

April 19, 2015
And if I have an already instantiated object?
----
Foo f = new Foo();
// reuse f's memory
----
Is there an nicer way to override the memory instead of:
----
void[] buf = (cast(void*) f)[0 .. __traits(classInstanceSize, Foo)];
buf = typeid(Foo).init[]; // or: buf = f.classinfo.init[];
----
?

The void* cast looks very ugly.
April 19, 2015
It seems that D has currently no direct support to reuse object memory.
D should add a new-placement syntax:
----
Foo f = new Foo(42);
new (f) Foo(23);
----
and/or should add an emplace overload which takes an object:
----
T emplace(T, Args...)(ref T obj, auto ref Args args) if (is(T == class)) {
    if (obj is null)
        return null;

    enum size_t ClassSize = __traits(classInstanceSize, T);
    void[] buf = (cast(void*) obj)[0 .. ClassSize];

    import std.conv : emplace;
    return emplace!(T)(buf, args);
}
----
April 20, 2015
On Sunday, 19 April 2015 at 21:17:18 UTC, Ali Çehreli wrote:
> On 04/19/2015 09:04 AM, Namespace wrote:
>
> > Is it somehow possible to reuse the memory of an object?
>
> Yes, when you cast a class variable to void*, you get the address of the object.
>
> > @nogc
> > T emplace(T, Args...)(ref T obj, auto ref Args args) nothrow
> if (is(T ==
> > class)) {
>
> There is already std.conv.emplace:
>
> import std.conv;
> import core.memory;
>
> class Foo
> {
>     int i;
>
>     this(int i)
>     {
>         this.i = i;
>     }
> }
>
> void main()
> {
>     void* buffer = GC.calloc(1234);
>
>     enum FooSize = __traits(classInstanceSize, Foo);
>
>     /* These two object are constructed on a void[] slice: */
>     auto f = emplace!Foo(buffer[0..FooSize], 42);
>     auto f2 = emplace!Foo(buffer[0..FooSize], 43);
>
>     /* f3 is constructed on top of an existing object: */
>     auto f2_addr = cast(void*)f2;
>     auto f3 = emplace!Foo(f2_addr[0..FooSize], 44);
>
>     /* At this point, all three reference the same object: */
>     assert(&f.i == &f2.i);
>     assert(&f.i == &f3.i);
> }
>
> Ali

I'm sorry if I annoy you, but I would really like to know how you would reuse already instantiated storage of an existing object.

Example code:
----
final class Foo {
    uint id;

    @nogc
    this(uint id) {
        this.id = id;
    }
}

Foo f = new Foo(42);
----
April 20, 2015
On 04/20/2015 12:05 PM, Namespace wrote:

> I'm sorry if I annoy you

Not at all! :) Sorry for not responding earlier.

>, but I would really like to know how you would
> reuse already instantiated storage of an existing object.
>
> Example code:
> ----
> final class Foo {
>      uint id;
>
>      @nogc
>      this(uint id) {
>          this.id = id;
>      }
> }
>
> Foo f = new Foo(42);
> ----

Something like the following works. I chose to set the old object to null but it is not necessary:

final class Foo {
    uint id;

    @nogc
    this(uint id) {
        this.id = id;
    }
}

C reuse(C, T...)(ref C old, T ctorParams)
{
    import std.conv;
    import std.typetuple;

    enum objectSize = __traits(classInstanceSize, C);

    void* oldPlace = cast(void*)old;
    C newObject = emplace!C(oldPlace[0..objectSize], ctorParams);

    old = null;

    return newObject;
}

void main()
{
    Foo f = new Foo(42);

    auto f2 = f.reuse(43);

    assert(f is null);
    assert(f2.id == 43);
}

Ali

April 20, 2015
Thank you. Do you mean this is worth a PR, to add this functionality to Phobos?
My current code looks like this: http://dpaste.dzfl.pl/19b78a600b6c
April 20, 2015
On 04/20/2015 02:44 PM, Namespace wrote:

> Thank you. Do you mean this is worth a PR, to add this
> functionality to Phobos?

I am not familiar with such a need so I don't have a strong opinion.

However, if an object needs to be emplaced on top of an existing one, I can imagine that the original object was emplaced on some piece of memory anyway. In that case, the problem becomes "emplacing an object on a piece of memory", which is already supported by std.conv.emplace.

Your idea seems to be for the case where the original object is created by some third party code, and that they want us to replace it with another object. If they are aware of the wholesale change in the object, fine. :)

Ali

April 20, 2015
On Monday, 20 April 2015 at 21:58:59 UTC, Ali Çehreli wrote:
> On 04/20/2015 02:44 PM, Namespace wrote:
>
> > Thank you. Do you mean this is worth a PR, to add this
> > functionality to Phobos?
>
> I am not familiar with such a need so I don't have a strong opinion.
>
> However, if an object needs to be emplaced on top of an existing one, I can imagine that the original object was emplaced on some piece of memory anyway. In that case, the problem becomes "emplacing an object on a piece of memory", which is already supported by std.conv.emplace.
>
> Your idea seems to be for the case where the original object is created by some third party code, and that they want us to replace it with another object. If they are aware of the wholesale change in the object, fine. :)
>
> Ali

I have currently an array of objects which may be reloaded (it's a tilemap). If the array is reused, I can do that with:
----
arr.length = 0;
arr.assumeSafeAppend();
----
But then I thought: why not reuse the memory of the objects?
In C++ you can do that very elegant, but in D I have to produce garbage since the old object stays alive until the GC collects it and I have to allocate new GC memory.
April 21, 2015
On Monday, 20 April 2015 at 21:36:35 UTC, Ali Çehreli wrote:
> final class Foo {
>     uint id;
>
>     @nogc
>     this(uint id) {
>         this.id = id;
>     }
> }
>
> C reuse(C, T...)(ref C old, T ctorParams)
> {
>     import std.conv;
>     import std.typetuple;
>
>     enum objectSize = __traits(classInstanceSize, C);
>
>     void* oldPlace = cast(void*)old;

It's probably better to call the destructor here before calling emplace, to complete the lifecycle of the old object.

>     C newObject = emplace!C(oldPlace[0..objectSize], ctorParams);
>
>     old = null;
>
>     return newObject;
> }
>
> void main()
> {
>     Foo f = new Foo(42);
>
>     auto f2 = f.reuse(43);
>
>     assert(f is null);
>     assert(f2.id == 43);
> }
>
> Ali