digitalmars.D.learn - Is it possible to avoid call to destructor for structs?
- Haridas (19/19) Sep 24 2017 In the following code, Bar is an element of struct Foo. Is there
- Adam D. Ruppe (9/12) Sep 24 2017 No, but you could just set a flag in Bar that the destructor
- Haridas (33/33) Sep 24 2017 Thanks Adam
- bitwise (28/31) Sep 24 2017 Don't construct it to begin with.
- Adrian Matoga (7/33) Sep 25 2017 You shouldn't store the pointer to barBuffer inside Foo. The
- bitwise (6/14) Sep 25 2017 Good point - but it's a mistake ;) 'Foo' is a class in the OP's
- Elronnd (20/20) Sep 27 2017 Here's a simple solution. Just make Bar a pointer and free it
- Elronnd (20/20) Sep 27 2017 Here's a simple solution. Just make Bar a pointer and free it
In the following code, Bar is an element of struct Foo. Is there a way to avoid a call to ~Bar when ~Foo is getting executed? // >>>>>>>>>> import std.stdio; struct Foo { Bar bar; ~this() { writeln("~Foo"); // some code that disables call to ~Bar } } struct Bar { ~this() { writeln("~Bar"); } } void main() { Foo foo; }
Sep 24 2017
On Sunday, 24 September 2017 at 17:11:26 UTC, Haridas wrote:In the following code, Bar is an element of struct Foo. Is there a way to avoid a call to ~Bar when ~Foo is getting executed?No, but you could just set a flag in Bar that the destructor checks and skips running if it is set. In fact, the destructor should do nothing if bar is in its initial state, so that's how you could check it. For example ~this() { if(this.member is null) return; // finalize this.member }
Sep 24 2017
Thanks Adam Actually Bar's API and implementation is not in my control. Consider the scenario where it is implemented in a library. Or a scenario where I have millions of instances of Bar (not necessarily as a component of Foo) and I do not want to add to runtime memory footprint. Ok, consider the following code. Now I am (seemingly) avoiding a call to Bar's destructor by way of using pointers. But I have doubts if delete on void pointer would reclaim the memory that has been allocated while still not calling the destructor? // >>>> import std.stdio; struct Foo { Bar* bar; this(size_t l) { bar = cast(Bar*) new Bar[l]; } ~this() { writeln("~Foo"); void *tmp = cast(void*) bar; // would this reclaim memory // allocated to Bar* delete(tmp); } } struct Bar { ~this() { writeln("~Bar"); } } void main() { Foo foo = 4; }
Sep 24 2017
Also consider the following code. Please let me know if I am doing the right thing for dynamic arrays. My hack seems to have the desired effect on shutting down the destructor. Is this hack legal use of D? Can you please guide me if/how it can be achieved for std.container.Array? // >>>> import std.stdio; struct Bar { ~this() { writeln("~Bar"); } } void main() { { // dynamic array Bar[] bars; bars.length = 4; void* tmp = bars.ptr; delete(tmp); bars.length = 0; } { // std.container.Array import std.container: Array; Array!Bar bars; bars.length = 6; // does not seem to work void* tmp = &(bars[0]); delete(tmp); bars.length = 0; } }
Sep 24 2017
On Sunday, 24 September 2017 at 18:46:15 UTC, Haridas wrote:Also consider the following code. Please let me know if I am doing the right thing for dynamic arrays. My hack seems to have the desired effect on shutting down the destructor. Is this hack legal use of D? Can you please guide me if/how it can be achieved for std.container.Array? // >>>> import std.stdio; struct Bar { ~this() { writeln("~Bar"); } } void main() { { // dynamic array Bar[] bars; bars.length = 4; void* tmp = bars.ptr; delete(tmp); bars.length = 0; } { // std.container.Array import std.container: Array; Array!Bar bars; bars.length = 6; // does not seem to work void* tmp = &(bars[0]); delete(tmp); bars.length = 0; } }Since you're deleting the memory the dynamic array is pointing to, what you're doing is potentially unsafe - if anyone touches that memory after it's been deleted, nasal demons may follow. What you want is something like this: import std.stdio; struct Bar { this(int n) {} ~this() { writeln("~Bar"); } } struct SuppressGC(T) { // Disguise T as a humble array. private ubyte[T.sizeof] _payload; // Create from instance of T. this(T arg) { _payload = *cast(ubyte[T.sizeof]*)&arg; } // Or forward constructor arguments to T's constructor. static if (__traits(hasMember, T, "__ctor")) { this(Args...)(Args args) if (__traits(compiles, (Args e){__traits(getMember, T.init, "__ctor")(e);})) { __traits(getMember, get, "__ctor")(args); } } // Pretend to be a T. property ref T get() { return *cast(T*)_payload.ptr; } alias get this; } void useBar(ref Bar b) {} unittest { // Construct from instance. //This creates a temporary on the stack, and its destructor will be called. SuppressGC!Bar a = Bar(3); // Or by forwarding constructor arguments. // This constructs in-place inside SuppressGC, and no destructor will be called. auto b = SuppressGC!Bar(3); SuppressGC!Bar[] arr; arr.length = 3; // Another stack temporary. Destructor will be called. arr[0] = Bar(5); // No temp arr[1] = SuppressGC!Bar(5); // It even pretends to be the wrapped struct: useBar(b); } In general, of course, this is a bad idea - there's probably a reason that destructor does the thing it's doing. If you're sure skipping it is what you want, go ahead. -- Biotronic
Sep 24 2017
In general, of course, this is a bad idea - there's probably a reason that destructor does the thing it's doing. If you're sure skipping it is what you want, go ahead.Biotronic, the code you have provided may be exactly what I am looking for. Let me explain my situation. I have a library that has been ported from a C/C++ code. A struct Bar that is part of the library implements its own refcounted GC. The refcounted GC of course works via constructor and destructor. But this refcounted GC also uses some thread local variables since there is a need to implement parallelism. It all works well so far. But as soon as I create an instance of Bar inside a Dlang class (say Foo) or as part of a Dlang dynamic array, hell follows. At some point, Dlang's GC kicks in and Bar's destructor gets called from within Dlang's GC. Now since Dlang executes GC on a different thread, the destructor gets confused and segfaults. Fortunately for me, it would be fine if the call to destructor is completely avoided within the realm of Dlang's GC since I do not need the Bar instances thereafter. All I need to do is to dismantle Bar completely from the memory to avoid leaks. So long as I can avoid calls to Bar's destructor from within Dlang's GC, I am fine. I also have some limitations. Since we need to run all this on an embedded system with limited RAM (and given that I have millions of instances of Bar which is only 32-bit wide), I do not want to add a flag or other stuff to Bar. And that is the reason we are using thread local storage in the refcounted GC. So there. I will try your way. Let me see if I can make it to work in my situation. Thanks for sharing.
Sep 24 2017
On Monday, 25 September 2017 at 01:46:15 UTC, Haridas wrote:[...] It all works well so far. But as soon as I create an instance of Bar inside a Dlang class (say Foo) or as part of a Dlang dynamic array, hell follows. At some point, Dlang's GC kicks in and Bar's destructor gets called from within Dlang's GC. Now since Dlang executes GC on a different thread, the destructor gets confused and segfaults.have D classes that own non thread-safe resources. So in the destructor of the D class, I add a call that queues the destruction to the main thread's dispatcher. In your case, the postblit of Bar is still going to run and add a ref to it's count when you place it in Foo, right? That means that if you don't destroy it, it will leak memory or resources. Unfortunately, my dispatcher is not production-ready yet, but you can get around this with a simpler approach. Just keep a shared container of your ref counted object type somewhere. When a destructor of a GC class runs, move the ref counted object into the trash container. Then, next time you want to create an instance of the ref counted object, you can empty the trash container at the same time. You should protect the container with a Mutex of some kind. Also, be sure that the container doesn't allocate using the GC since it will be called from class destructors. IIRC std.container.Array uses malloc, not GC, so you may be able to use that.
Sep 24 2017
In your case, the postblit of Bar is still going to run and add a ref to it's count when you place it in Foo, right? That means that if you don't destroy it, it will leak memory or resources.Actually no. Since when Foo (class that instantiates Bar) gets GCed, that is the point that I need to destroy the whole infrastructure around Bar, including its recounted GC. So I am Ok if destructor of Bar does not get called this one time since I am just going to delete the whole block of memory that I have allocated for placing the refcounted instances of Bar.
Sep 24 2017
On Sunday, 24 September 2017 at 17:11:26 UTC, Haridas wrote:In the following code, Bar is an element of struct Foo. Is there a way to avoid a call to ~Bar when ~Foo is getting executed?Don't construct it to begin with. struct Bar { import std.stdio : writeln; int a = 123; void boink() { writeln(a); } ~this(){ writeln("bar dtor"); } } struct Foo { ubyte[Bar.sizeof] barBuffer; Bar* _bar = null; ref Bar bar() { import std.conv : emplace; if(!_bar) { _bar = cast(Bar*)barBuffer.ptr; emplace(_bar); } return *_bar; } } int main(string[] argv) { Foo foo; foo.bar.boink(); return 0; }
Sep 24 2017
On Sunday, 24 September 2017 at 19:52:52 UTC, bitwise wrote:On Sunday, 24 September 2017 at 17:11:26 UTC, Haridas wrote:You shouldn't store the pointer to barBuffer inside Foo. The language allows moving the structure around with a simple memcpy, so _bar is likely to point into garbage soon after it's assigned. Why don't you just return *cast(Bar*)barBuffer.ptr in bar()? You could still emplace a Bar inside barBuffer in Foo's constructor, if needed.In the following code, Bar is an element of struct Foo. Is there a way to avoid a call to ~Bar when ~Foo is getting executed?Don't construct it to begin with. struct Bar { import std.stdio : writeln; int a = 123; void boink() { writeln(a); } ~this(){ writeln("bar dtor"); } } struct Foo { ubyte[Bar.sizeof] barBuffer; Bar* _bar = null; ref Bar bar() { import std.conv : emplace; if(!_bar) { _bar = cast(Bar*)barBuffer.ptr; emplace(_bar); } return *_bar; } }
Sep 25 2017
On Monday, 25 September 2017 at 08:39:26 UTC, Adrian Matoga wrote:[...] You shouldn't store the pointer to barBuffer inside Foo. The language allows moving the structure around with a simple memcpy, so _bar is likely to point into garbage soon after it's assigned.Good point - but it's a mistake ;) 'Foo' is a class in the OP's code, so no problem.Why don't you just return *cast(Bar*)barBuffer.ptr in bar()?Lazy constructionYou could still emplace a Bar inside barBuffer in Foo's constructor, if needed.So you KNOW it's a class then...since structs can't have default ctors.. :P
Sep 25 2017
Here's a simple solution. Just make Bar a pointer and free it before it can be destructed! import std.stdio; struct Bar { ~this() { writeln("~bar"); } } struct Foo { Bar *bar; this(int why_the_fuck_dont_structs_have_default_constructors) { bar = new Bar; } ~this() { writeln("~foo"); import core.memory; GC.free(bar); } }
Sep 27 2017
Here's a simple solution. Just make Bar a pointer and free it before it can be destructed! import std.stdio; struct Bar { ~this() { writeln("~bar"); } } struct Foo { Bar *bar; this(int why_the_fuck_dont_structs_have_default_constructors) { bar = new Bar; } ~this() { writeln("~foo"); import core.memory; GC.free(bar); } }
Sep 27 2017