Re: D RAII with postblit disabled
On Thursday, 29 March 2018 at 04:16:55 UTC, Adam D. Ruppe wrote: On Thursday, 29 March 2018 at 04:12:38 UTC, Norm wrote: Is there a way to do this in D, or does it require special "create" functions for every struct that has a RAII-like struct as a member? You'll have to do it all the way up (unless you can use a constructor with an argument and call that instead) OK, thanks.
Re: D RAII with postblit disabled
On Thursday, 29 March 2018 at 04:12:38 UTC, Norm wrote: Is there a way to do this in D, or does it require special "create" functions for every struct that has a RAII-like struct as a member? You'll have to do it all the way up (unless you can use a constructor with an argument and call that instead)
Re: D RAII with postblit disabled
On Tuesday, 27 March 2018 at 02:43:15 UTC, Adam D. Ruppe wrote: On Tuesday, 27 March 2018 at 02:35:23 UTC, Norm wrote: What's the best way to do this in D? I'd also add `@disable this();` and then a `static O make() { return O(theAllocator.make!int(99)); }` than you construct it with that static make function. OK, that got me over the first hurdle but I still cannot use RAII with struct member vars. E.g. --- struct Resource { this() {allocate_something();} ~this() {release_something();} } struct S { Resource resource; } --- Is there a way to do this in D, or does it require special "create" functions for every struct that has a RAII-like struct as a member? Thanks, Norm
Re: D RAII with postblit disabled
On Tuesday, 27 March 2018 at 02:43:15 UTC, Adam D. Ruppe wrote: On Tuesday, 27 March 2018 at 02:35:23 UTC, Norm wrote: What's the best way to do this in D? I'd also add `@disable this();` and then a `static O make() { return O(theAllocator.make!int(99)); }` than you construct it with that static make function. Perfect, thanks.
Re: D RAII with postblit disabled
On Tuesday, 27 March 2018 at 02:35:23 UTC, Norm wrote: What's the best way to do this in D? I'd also add `@disable this();` and then a `static O make() { return O(theAllocator.make!int(99)); }` than you construct it with that static make function.
D RAII with postblit disabled
Hi All, What's the best way to do this in D? E.g. --- struct O { int* value; @disable this(this); /+ this() { this.value = theAllocator.make!int(99); } +/ ~this() { theAllocator.dispose(this.value); } } O obj = O(); // Ideally this would be allocated but it simply run O.init --- Thanks Norm
Re: D structs weak identity and RAII
On Monday, 19 June 2017 at 06:34:49 UTC, Ali Çehreli wrote: It's unreliable because structs are value types in D, which means that they can be moved around freely. This is why self-referencing structs are illegal in D. I guess it's more like the spec states, that they can be moved vithout notice. Value type semantics themselves do not mean spontaneous uncontrolled mobility. I can't claim expertise but here is a quick and dirty proof of concept that Vittorio Romeo and I had played with a few weeks ago: ... shared byte b; // to prevent compiler optimizations struct UniquePtr { void * p; this(void * p) { this.p = p; import core.atomic; core.atomic.atomicOp!"+="(b, 1); } Very interesting hack, thank you. Note how post-blit is disabled there. In addition to a moveFrom(), that's exactly what Atila Neves had to do in this automem library as well: https://github.com/atilaneves/automem Nice, thanks.
Re: D structs weak identity and RAII
On 06/18/2017 06:22 PM, Boris-Barboris wrote: > https://dpaste.dzfl.pl/d77c72198095 > > 1). line 47 and 76 together mean, that there is basically no reliable > way to write uniqueptr method wich returns WeakPointer, registered by > the correct pointer in it's constructor. Or is there? Is usage of &this > in constructor (or in struct methods in general) fundamentally > unreliable in D? It's unreliable because structs are value types in D, which means that they can be moved around freely. This is why self-referencing structs are illegal in D. > 2). postblit was never called when returning struct from function. Rvalues are automatically moved and there is also RVO. > 3). Is there a way to reliably encapsulate all assignments? I think so. > 4). Any suggested workarounds? I never tried it in C++, but, IIRC, > struct life cycle is much more consistent there ang gives the strict > control I desire. Yes, C++ has a very well defined object lifecycle. > 5). If all this is a design choice, what is the reason behind it? Some are design choices and some are bugs. For example, a fundamental bug has just been fixed: When a constructor threw, the destructors of already-constructed members were not being called. I think the fix is in git head but not released yet. I can't claim expertise but here is a quick and dirty proof of concept that Vittorio Romeo and I had played with a few weeks ago: import core.stdc.stdio; import core.stdc.stdlib; import std.algorithm; import std.stdio; shared byte b; // to prevent compiler optimizations struct UniquePtr { void * p; this(void * p) { this.p = p; import core.atomic; core.atomic.atomicOp!"+="(b, 1); } ~this() { free(p); } @disable this(this); UniquePtr move() { void * old = p; p = null; return UniquePtr(old); } } void consumer(UniquePtr u) { } UniquePtr producer(int i) { auto u = UniquePtr(malloc(42)); return i ? UniquePtr(malloc(56)) : u.move(); } void main() { consumer(UniquePtr(malloc(2))); auto u = UniquePtr(malloc(5)); consumer(u.move()); auto u2 = producer(43); } Note how post-blit is disabled there. In addition to a moveFrom(), that's exactly what Atila Neves had to do in this automem library as well: https://github.com/atilaneves/automem Ali
D structs weak identity and RAII
Hello, I was trying to write some templated unique pointers. Idea was simple: struct UniquePointer that handles underlying pointer in RAII-style and WeakPointer struct that is spawned by UniquePointer. Weak pointer is handled differently in my collections, wich subscribe to the event of UniquePointer destruction, so that there are no dangling references left all over the heap (I use Mallocator). Collection's insertFront\Back methods register callbacks in Weak pointer itself, and all existing weak pointers are registered in Unique pointer. That, unfortunately, failed at the very beginning. Then I wrote a unit test to investigate: https://dpaste.dzfl.pl/d77c72198095 1). line 47 and 76 together mean, that there is basically no reliable way to write uniqueptr method wich returns WeakPointer, registered by the correct pointer in it's constructor. Or is there? Is usage of &this in constructor (or in struct methods in general) fundamentally unreliable in D? 2). postblit was never called when returning struct from function. I noticed it was called once on line 91, if i removed opAssign with ref argument. What are the rules? Why is it always called when passing struct to function and creating local copy of the parameter (lines 113, 127, 139), and why is opAssign not called? 3). Is there a way to reliably encapsulate all assignments? Looks like complete mess will happen should I apply some std.algorithm functions on array of structs with overloaded operators. 4). Any suggested workarounds? I never tried it in C++, but, IIRC, struct life cycle is much more consistent there ang gives the strict control I desire. 5). If all this is a design choice, what is the reason behind it?
OT: RAII pointers
On Saturday, 3 June 2017 at 21:46:45 UTC, Stanislav Blinov wrote: On Saturday, 3 June 2017 at 21:39:54 UTC, Moritz Maxeiner wrote: It's always true, because I explicitly wrote *might*, not *will*, to indicate that it depends on your use case. Your example is a common use case where you can't skip the check. Programmers and their tight-binding logic... - Honey, please buy a loaf of bread. If there are eggs, buy a dozen. ... - Hello, do you have eggs? - Yes. - Dozen loaves of bread, please. ;) That usually happens when two sides interpret an ambiguous statement ;)
Re: RAII pointers
On Saturday, 3 June 2017 at 21:39:54 UTC, Moritz Maxeiner wrote: On Saturday, 3 June 2017 at 21:16:08 UTC, Stanislav Blinov wrote: On Saturday, 3 June 2017 at 20:53:05 UTC, Moritz Maxeiner wrote: Quite, but if you backtrack to my initial statement, it was about ptr not being/becoming null (implicitly) in the first place, which *might* allow you to skip the check (if you don't set it to null via external means, such as memcpy, move, etc). It's only true as long as you have full control of the source. Once you're using libraries and generic code, it's possible that it's out of your hands: It's always true, because I explicitly wrote *might*, not *will*, to indicate that it depends on your use case. Your example is a common use case where you can't skip the check. Programmers and their tight-binding logic... - Honey, please buy a loaf of bread. If there are eggs, buy a dozen. ... - Hello, do you have eggs? - Yes. - Dozen loaves of bread, please. ;)
Re: RAII pointers
On Saturday, 3 June 2017 at 21:16:08 UTC, Stanislav Blinov wrote: On Saturday, 3 June 2017 at 20:53:05 UTC, Moritz Maxeiner wrote: Quite, but if you backtrack to my initial statement, it was about ptr not being/becoming null (implicitly) in the first place, which *might* allow you to skip the check (if you don't set it to null via external means, such as memcpy, move, etc). It's only true as long as you have full control of the source. Once you're using libraries and generic code, it's possible that it's out of your hands: It's always true, because I explicitly wrote *might*, not *will*, to indicate that it depends on your use case. Your example is a common use case where you can't skip the check.
Re: RAII pointers
On Saturday, 3 June 2017 at 20:53:05 UTC, Moritz Maxeiner wrote: On Saturday, 3 June 2017 at 20:25:22 UTC, Stanislav Blinov wrote: On Saturday, 3 June 2017 at 20:13:30 UTC, Moritz Maxeiner wrote: Calling std.algorithm.move is explicit programmer intent, I consider that about as accidental as calling memcpy with a source full of zeroes. In any case, having that check in the destructor is fairly cheap, so better safe than sorry (and include it). Yes, it's explicit. Destructor call is still implicit though, and it better not be performing null dereference or something equally nasty :) Quite, but if you backtrack to my initial statement, it was about ptr not being/becoming null (implicitly) in the first place, which *might* allow you to skip the check (if you don't set it to null via external means, such as memcpy, move, etc). It's only true as long as you have full control of the source. Once you're using libraries and generic code, it's possible that it's out of your hands: import core.stdc.stdio; import std.exception; // external library struct RingBuffer(T,size_t size) { T[size] values = T.init; // the culprit // interface snipped... } // own code struct FileWrapper { FILE* file; @disable this(); @disable this(this); this(FILE* file) { enforce(file); this.file = file; } ~this() { fclose(file); } } void main() { // whoops, segfault RingBuffer!(FileWrapper,8) container; }
Re: RAII pointers
On Saturday, 3 June 2017 at 20:25:22 UTC, Stanislav Blinov wrote: On Saturday, 3 June 2017 at 20:13:30 UTC, Moritz Maxeiner wrote: Calling std.algorithm.move is explicit programmer intent, I consider that about as accidental as calling memcpy with a source full of zeroes. In any case, having that check in the destructor is fairly cheap, so better safe than sorry (and include it). Yes, it's explicit. Destructor call is still implicit though, and it better not be performing null dereference or something equally nasty :) Quite, but if you backtrack to my initial statement, it was about ptr not being/becoming null (implicitly) in the first place, which *might* allow you to skip the check (if you don't set it to null via external means, such as memcpy, move, etc).
Re: RAII pointers
On Saturday, 3 June 2017 at 20:13:30 UTC, Moritz Maxeiner wrote: Calling std.algorithm.move is explicit programmer intent, I consider that about as accidental as calling memcpy with a source full of zeroes. In any case, having that check in the destructor is fairly cheap, so better safe than sorry (and include it). Yes, it's explicit. Destructor call is still implicit though, and it better not be performing null dereference or something equally nasty :)
Re: RAII pointers
On Saturday, 3 June 2017 at 19:55:30 UTC, ag0aep6g wrote: On 06/03/2017 09:37 PM, Moritz Maxeiner wrote: Of course, but AFAIK you'd need to explicitly assign it to an object, so `ptr` won't null by accident, but only by explicit programmer intent (same as overwriting the memory the object lives in via things like `memcpy`); and you can always screw things intentionally (you could also assign some invalid value to the pointer via `memcpy`). I'd say `.init` can easily happen accidentally. Especially when `@disable this(this);` is involved. When you can't copy, you may have to move sometimes. But std.algorithm.move overwrites the old location with `.init`, assuming that `.init` can safely be destroyed. Calling std.algorithm.move is explicit programmer intent, I consider that about as accidental as calling memcpy with a source full of zeroes. In any case, having that check in the destructor is fairly cheap, so better safe than sorry (and include it).
Re: RAII pointers
On Saturday, 3 June 2017 at 19:55:30 UTC, ag0aep6g wrote: On 06/03/2017 09:37 PM, Moritz Maxeiner wrote: Of course, but AFAIK you'd need to explicitly assign it to an object, so `ptr` won't null by accident, but only by explicit programmer intent (same as overwriting the memory the object lives in via things like `memcpy`); and you can always screw things intentionally (you could also assign some invalid value to the pointer via `memcpy`). I'd say `.init` can easily happen accidentally. Especially when `@disable this(this);` is involved. When you can't copy, you may have to move sometimes. But std.algorithm.move overwrites the old location with `.init`, assuming that `.init` can safely be destroyed. struct S { void* ptr; @disable this(this); ~this() { assert(ptr !is null); /* fails */ } } void f(S s) {} void main() { auto a = S(new int); import std.algorithm: move; f(move(a)); /* overwrites `a` with `S.init` */ } Yep, that's exactly why I added the null check in the example. If the struct has a postblit or a destructor, `move` will be destructive, and will overwrite the source with .init. Sometimes it doesn't matter (i.e. free() is allowed to take a null pointer), but in general, for things like smart pointers where you'd do arbitrary access in destructor, it's a good habit to check for .init values first, in case the object has been moved.
Re: RAII pointers
On 06/03/2017 09:37 PM, Moritz Maxeiner wrote: Of course, but AFAIK you'd need to explicitly assign it to an object, so `ptr` won't null by accident, but only by explicit programmer intent (same as overwriting the memory the object lives in via things like `memcpy`); and you can always screw things intentionally (you could also assign some invalid value to the pointer via `memcpy`). I'd say `.init` can easily happen accidentally. Especially when `@disable this(this);` is involved. When you can't copy, you may have to move sometimes. But std.algorithm.move overwrites the old location with `.init`, assuming that `.init` can safely be destroyed. struct S { void* ptr; @disable this(this); ~this() { assert(ptr !is null); /* fails */ } } void f(S s) {} void main() { auto a = S(new int); import std.algorithm: move; f(move(a)); /* overwrites `a` with `S.init` */ }
Re: RAII pointers
On Saturday, 3 June 2017 at 19:21:58 UTC, ag0aep6g wrote: On 06/03/2017 09:06 PM, Moritz Maxeiner wrote: - null check in destructor: That's just because I forgot to add it. If you add `@disable(this)` (disable the default constructor), all elaborate constructors ensure it is not null, and no members can set it to null, you might be able to skip the check, but I may have missed some corner cases, so better be safe. `.init` is the corner case. `.init` is always there, even with `@disable this();`. Of course, but AFAIK you'd need to explicitly assign it to an object, so `ptr` won't null by accident, but only by explicit programmer intent (same as overwriting the memory the object lives in via things like `memcpy`); and you can always screw things intentionally (you could also assign some invalid value to the pointer via `memcpy`). Are there any accidental corner cases?
Re: RAII pointers
On 06/03/2017 09:06 PM, Moritz Maxeiner wrote: - null check in destructor: That's just because I forgot to add it. If you add `@disable(this)` (disable the default constructor), all elaborate constructors ensure it is not null, and no members can set it to null, you might be able to skip the check, but I may have missed some corner cases, so better be safe. `.init` is the corner case. `.init` is always there, even with `@disable this();`.
Re: RAII pointers
On Saturday, 3 June 2017 at 17:40:32 UTC, Russel Winder wrote: Would one be considered more idiomatic D, or is it a question of different circumstances different approaches. The differences are mainly in construction I believe. Well, the differences I spot are: - null check in destructor: That's just because I forgot to add it. If you add `@disable(this)` (disable the default constructor), all elaborate constructors ensure it is not null, and no members can set it to null, you might be able to skip the check, but I may have missed some corner cases, so better be safe. - factory functions (Stanislav) vs. elaborate constructors (me): + If you don't need to be able to construct the object without arguments, it's a stylistic choice and I consider the elaborate constructors to be more idiomatic. + otherwise (i.e. you need to be able to construct the object without arguments), you need the factory functions, because elaborate constructors for structs cannot have zero arguments, as that would clash with the default constructor that must be computable at compile time (for the struct's `.init` value) - inout: You can use that in what I wrote, as well; that's just a shorthand way to write several functions that do the same thing: one for `const T`, one for `immutable T`, and one for `T`
Re: RAII pointers
Thanks to Moritz and Stanislav for their examples, most useful. There are similarities (which I have just taken :-) but also some differences. Would one be considered more idiomatic D, or is it a question of different circumstances different approaches. The differences are mainly in construction I believe. On Tue, 2017-05-30 at 00:31 +, Moritz Maxeiner via Digitalmars-d- learn wrote: > […] > > struct FrontendParameters_Ptr { > private: > dvb_v5_fe_parms* ptr; > public: > @disable this(this); > this(ref const FrontendId fei, const uint verbose = 0, const > uint legacy = 0) { ... } > ~this() { dvb_fe_close(ptr); } > auto c_ptr() const { return ptr; } > alias c_ptr this; > } > --- On Tue, 2017-05-30 at 00:32 +, Stanislav Blinov via Digitalmars-d- learn wrote: […] > > struct FrontendParametersPtr > { > // No constructors, initialization with parameters > // is done via the frontendParametersPtr function > @disable this(this); > > ~this() > { > // null check is often useful to detect e.g. > // if this object has been `move`d > if (_ptr) dvb_fe_close(_ptr); > } > > // with DIP1000, could also return `scope` > inout(dvb_v5_fe_parms)* ptr() inout { return _ptr; } > alias ptr this; > package: > > void construct(/*your args here*/) { /*...*/ } > > private: > dvb_v5_fe_parms* _ptr; > } > > /// Replaces constructor, i.e. can be called with no arguments for > /// replacing "default" construction of C++ > auto frontendParametersPtr(Args...)(auto ref Args args) > { > import std.functional : forward; > FrontendParametersPtr result = void; > result.construct(forward!args); > return result; // moves result, no copy is made > } > […] -- Russel. = Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.win...@ekiga.net 41 Buckmaster Roadm: +44 7770 465 077 xmpp: rus...@winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder signature.asc Description: This is a digitally signed message part
Re: RAII pointers
On Monday, 29 May 2017 at 23:39:17 UTC, Russel Winder wrote: C++ allows one to create types that are pointer types but wrap a primitive pointer to give RAII handling of resources. For example: class Dvb::FrontendParameters_Ptr { private: dvb_v5_fe_parms * ptr; public: FrontendParameters_Ptr(FrontendId const & fei, unsigned int const verbose = 0, unsigned int const legacy = 0); FrontendParameters_Ptr(FrontendParameters_Ptr const &) = delete; FrontendParameters_Ptr & operator=(FrontendParameters_Ptr const &) = delete; ~FrontendParameters_Ptr() {dvb_fe_close(ptr); } dvb_v5_fe_parms * c_ptr() const { return ptr; } dvb_v5_fe_parms * operator->() const { return ptr; } }; Has anyone any experience of doing the analogous thing idiomatically in D. I just re-realised I manually constructed a C++ abstraction layer around some of libdvbv5, so I am going to do the same for D. However whilst I (sort of) understand doing the wrapping with C++, I am not sure I have seen anyone wrapping C pointers with RAII in D. I've found this pattern works rather well: module frontendparametersptr; struct FrontendParametersPtr { // No constructors, initialization with parameters // is done via the frontendParametersPtr function @disable this(this); ~this() { // null check is often useful to detect e.g. // if this object has been `move`d if (_ptr) dvb_fe_close(_ptr); } // with DIP1000, could also return `scope` inout(dvb_v5_fe_parms)* ptr() inout { return _ptr; } alias ptr this; package: void construct(/*your args here*/) { /*...*/ } private: dvb_v5_fe_parms* _ptr; } /// Replaces constructor, i.e. can be called with no arguments for /// replacing "default" construction of C++ auto frontendParametersPtr(Args...)(auto ref Args args) { import std.functional : forward; FrontendParametersPtr result = void; result.construct(forward!args); return result; // moves result, no copy is made } ///- module app; import frontendparametersptr; void main() { auto ptr = frontendParametersPtr(/* your args here */); } The main idea is that construction is handled by the `construct` function (which could be overloaded), instead of `this(...)` constructors: this way client code would either get default-initialized (.init) pointers, or those constructed with appropriate arguments (even with no arguments, if such is needed). Disabling copying is obvious. The rest depends on taste and purpose.
Re: RAII pointers
On Monday, 29 May 2017 at 23:39:17 UTC, Russel Winder wrote: C++ allows one to create types that are pointer types but wrap a primitive pointer to give RAII handling of resources. For example: class Dvb::FrontendParameters_Ptr { private: dvb_v5_fe_parms * ptr; public: FrontendParameters_Ptr(FrontendId const & fei, unsigned int const verbose = 0, unsigned int const legacy = 0); FrontendParameters_Ptr(FrontendParameters_Ptr const &) = delete; FrontendParameters_Ptr & operator=(FrontendParameters_Ptr const &) = delete; ~FrontendParameters_Ptr() {dvb_fe_close(ptr); } dvb_v5_fe_parms * c_ptr() const { return ptr; } dvb_v5_fe_parms * operator->() const { return ptr; } }; Has anyone any experience of doing the analogous thing idiomatically in D. Yes, I generally use structs with `@disable this(this)` and std.algorithm.mutation.move for this. Something like (untested, but general approach): --- module dvb; struct FrontendParameters_Ptr { private: dvb_v5_fe_parms* ptr; public: @disable this(this); this(ref const FrontendId fei, const uint verbose = 0, const uint legacy = 0) { ... } ~this() { dvb_fe_close(ptr); } auto c_ptr() const { return ptr; } alias c_ptr this; } --- Be aware that the above deliberately prohibits normal struct copy construction, so there is always only a single struct object. "Borrow" it via ref / ref const or std.algorithm.mutation.move it to change the owner. Or wrap this struct itself in a lifetime management struct (such as std.typecons.RefCounted) if you need multiple owners.
Re: RAII pointers
On Monday, 29 May 2017 at 23:39:17 UTC, Russel Winder wrote: C++ allows one to create types that are pointer types but wrap a primitive pointer to give RAII handling of resources. For example: [...] std.stdio.File does basically the same thing with C's FILE*
RAII pointers
C++ allows one to create types that are pointer types but wrap a primitive pointer to give RAII handling of resources. For example: class Dvb::FrontendParameters_Ptr { private: dvb_v5_fe_parms * ptr; public: FrontendParameters_Ptr(FrontendId const & fei, unsigned int const verbose = 0, unsigned int const legacy = 0); FrontendParameters_Ptr(FrontendParameters_Ptr const &) = delete; FrontendParameters_Ptr & operator=(FrontendParameters_Ptr const &) = delete; ~FrontendParameters_Ptr() {dvb_fe_close(ptr); } dvb_v5_fe_parms * c_ptr() const { return ptr; } dvb_v5_fe_parms * operator->() const { return ptr; } }; Has anyone any experience of doing the analogous thing idiomatically in D. I just re-realised I manually constructed a C++ abstraction layer around some of libdvbv5, so I am going to do the same for D. However whilst I (sort of) understand doing the wrapping with C++, I am not sure I have seen anyone wrapping C pointers with RAII in D. -- Russel. = Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.win...@ekiga.net 41 Buckmaster Roadm: +44 7770 465 077 xmpp: rus...@winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder signature.asc Description: This is a digitally signed message part
Re: RAII
Arun Chandrasekaran wrote: On Thursday, 23 February 2017 at 09:57:09 UTC, ketmar wrote: Thanks for your help. NRVO looks interesting. However this may not be RAII after all. Or may be too much of C++ has spoiled me. I am not familiar enough with D to appreciate/question the language design choice. I'll accept this and move forward and see how my code evolves. Nevertheless, I'm still learning something new. :) also, why structs at all? there are another ways! ;-) void doWithLock (scope void delegate () action) { lockSomething(); scope(exit) unlockSomething(); action(); } no, wait, this is not so limiting as it may look! void foo () { int i = 42; doWithLock({ writeln("i=", i); // yay, 42! }); } this looks *almost* like `synchronized(obj) { ... }`! and if we'll ever press D devs to allow us to omit "()" when the only argument is arg-less lambda... it will be indistinguishable from built-in! ;-)
Re: RAII
On Thursday, 23 February 2017 at 21:05:48 UTC, cym13 wrote: It reminds me of https://w0rp.com/blog/post/an-raii-constructor-by-another-name-is-just-as-sweet/ which isn't what you want but may be interesting anyway. It is interesting, indeed, thanks.
Re: RAII
On Thursday, 23 February 2017 at 09:57:09 UTC, ketmar wrote: Arun Chandrasekaran wrote: I'm trying to write an RAII wrapper on Linux. I understand struct in D doesn't have default constructor (for .init reasons). I don't want to use `scope`. Is there an elegant way to achieve this in D? why not static method or free function that returns struct? due to NRVO[0] it won't even be copied. auto lock = MyWrapper(); `MyWrapper()` may return voldemort type, so user won't create your struct accidentally. [0] https://dlang.org/glossary.html#nrvo Thanks for your help. NRVO looks interesting. However this may not be RAII after all. Or may be too much of C++ has spoiled me. I am not familiar enough with D to appreciate/question the language design choice. I'll accept this and move forward and see how my code evolves. Nevertheless, I'm still learning something new. :)
Re: RAII
On Thursday, 23 February 2017 at 09:52:26 UTC, Arun Chandrasekaran wrote: I'm trying to write an RAII wrapper on Linux. I understand struct in D doesn't have default constructor (for .init reasons). I don't want to use `scope`. Is there an elegant way to achieve this in D? ``` import core.sys.posix.pthread; import core.sys.posix.sys.types; /// Makes pthread_mutexattr_t cleanup easy when using exceptions struct mutexattr_wrapper { /// Constructor this(bool _) { if (pthread_mutexattr_init(&m_attr) != 0 || pthread_mutexattr_setpshared(&m_attr, PTHREAD_PROCESS_SHARED)!= 0) // may be I will add few more methods here throw custom_exception("pthread_mutexattr_ failed"); } /// Destructor ~this() { pthread_mutexattr_destroy(&m_attr); } /// This allows using mutexattr_wrapper as pthread_mutexattr_t alias m_attr this; pthread_mutexattr_t m_attr; } ``` It reminds me of https://w0rp.com/blog/post/an-raii-constructor-by-another-name-is-just-as-sweet/ which isn't what you want but may be interesting anyway.
Re: RAII
On Thursday, 23 February 2017 at 18:46:58 UTC, kinke wrote: A constructor is just a factory function with a special name... Wrt. the special name, that's obviously extremely useful for generic templates (containers etc.).
Re: RAII
On Thursday, 23 February 2017 at 14:24:14 UTC, Adam D. Ruppe wrote: On Thursday, 23 February 2017 at 10:48:38 UTC, kinke wrote: That's not elegant. You need a factory function for each type containing one of these structs then. A constructor is just a factory function with a special name... it is almost equal amount of work. Sorry but this is just completely wrong. RAII is all about me NOT having to call special functions all over the place. struct S { this() { /* initialize */ } } S[123] myArray; // hooray, no need to invoke a factory in a loop struct Container { S s; // hooray, implicit this() automatically calls s.this() }
Re: RAII
On Thursday, 23 February 2017 at 10:48:38 UTC, kinke wrote: That's not elegant. You need a factory function for each type containing one of these structs then. A constructor is just a factory function with a special name... it is almost equal amount of work.
Re: RAII
On Thursday, 23 February 2017 at 09:52:26 UTC, Arun Chandrasekaran wrote: I'm trying to write an RAII wrapper on Linux. I understand struct in D doesn't have default constructor (for .init reasons). I don't want to use `scope`. Is there an elegant way to achieve this in D? static opCall() is the way to emulate an argumentless struct constructor. You still need to call it explicitly, though: ``` import std.stdio : writefln; struct Foo { int a; static Foo opCall() { Foo foo; foo.a = 42; return foo; } ~this() { writefln("destroyed with a=%d", a); } @disable this(this); @disable void opAssign(Foo); } void main() { auto foo1 = Foo(); // ok Foo foo2; // == Foo.init } ```
Re: RAII
On Thursday, 23 February 2017 at 09:57:09 UTC, ketmar wrote: Arun Chandrasekaran wrote: Is there an elegant way to achieve this in D? why not static method or free function that returns struct? due to NRVO[0] it won't even be copied. That's not elegant. You need a factory function for each type containing one of these structs then.
Re: RAII
Arun Chandrasekaran wrote: I'm trying to write an RAII wrapper on Linux. I understand struct in D doesn't have default constructor (for .init reasons). I don't want to use `scope`. Is there an elegant way to achieve this in D? why not static method or free function that returns struct? due to NRVO[0] it won't even be copied. auto lock = MyWrapper(); `MyWrapper()` may return voldemort type, so user won't create your struct accidentally. [0] https://dlang.org/glossary.html#nrvo
RAII
I'm trying to write an RAII wrapper on Linux. I understand struct in D doesn't have default constructor (for .init reasons). I don't want to use `scope`. Is there an elegant way to achieve this in D? ``` import core.sys.posix.pthread; import core.sys.posix.sys.types; /// Makes pthread_mutexattr_t cleanup easy when using exceptions struct mutexattr_wrapper { /// Constructor this(bool _) { if (pthread_mutexattr_init(&m_attr) != 0 || pthread_mutexattr_setpshared(&m_attr, PTHREAD_PROCESS_SHARED)!= 0) // may be I will add few more methods here throw custom_exception("pthread_mutexattr_ failed"); } /// Destructor ~this() { pthread_mutexattr_destroy(&m_attr); } /// This allows using mutexattr_wrapper as pthread_mutexattr_t alias m_attr this; pthread_mutexattr_t m_attr; } ```
Re: RAII and classes
On Wednesday, 9 March 2016 at 10:48:30 UTC, cym13 wrote: On Wednesday, 9 March 2016 at 10:28:06 UTC, John Colvin wrote: Potential for leaking references from alias this aside, is there some reason that I shouldn't do this for all my C++-like RAII needs: class A { ~this(){ import std.stdio; writeln("hello"); } } auto RAII(T)() if (is(T == class)) { struct Inner { private ubyte[__traits(classInstanceSize, T)] buff; T c; alias c this; ~this() { destroy(c); } } Inner tmp; import std.conv : emplace; tmp.c = tmp.buff.emplace!T; return tmp; } void main() { auto a = RAII!A; } That's almost literally what std.typecons.scoped does. Ok, I forgot std.typecons.scoped, nothing to see here .
Re: RAII and classes
On Wednesday, 9 March 2016 at 10:28:06 UTC, John Colvin wrote: Potential for leaking references from alias this aside, is there some reason that I shouldn't do this for all my C++-like RAII needs: class A { ~this(){ import std.stdio; writeln("hello"); } } auto RAII(T)() if (is(T == class)) { struct Inner { private ubyte[__traits(classInstanceSize, T)] buff; T c; alias c this; ~this() { destroy(c); } } Inner tmp; import std.conv : emplace; tmp.c = tmp.buff.emplace!T; return tmp; } void main() { auto a = RAII!A; } That's almost literally what std.typecons.scoped does.
RAII and classes
Potential for leaking references from alias this aside, is there some reason that I shouldn't do this for all my C++-like RAII needs: class A { ~this(){ import std.stdio; writeln("hello"); } } auto RAII(T)() if (is(T == class)) { struct Inner { private ubyte[__traits(classInstanceSize, T)] buff; T c; alias c this; ~this() { destroy(c); } } Inner tmp; import std.conv : emplace; tmp.c = tmp.buff.emplace!T; return tmp; } void main() { auto a = RAII!A; }
Re: RAII trouble
On Friday, 20 November 2015 at 23:35:50 UTC, Spacen Jasset wrote: On Friday, 20 November 2015 at 23:21:03 UTC, anonymous wrote: [...] FT_Init_FreeType must be read at compile time, because freeType is a module level variable, and initializers for module variables must be static values. The initializer is run through CTFE (Compile Time Function Evaluation). Put the initialization/assignment in a function and it should work. That function can be a static constructor or the main function: [...] Yes, I see. I made a mistake. I need to initialize it elsewhere. It was quite confusing. Thanks. I Just noticed this trick you posted, that's interesting. Of course I couldn't get it working without the void to discard the initialization; FreeType freeType = void; /* 'void' prevents default initialization */ static this() {freeType = FreeType.initialise();} /* should work */
Re: RAII trouble
On Friday, 20 November 2015 at 23:21:03 UTC, anonymous wrote: [...] FT_Init_FreeType must be read at compile time, because freeType is a module level variable, and initializers for module variables must be static values. The initializer is run through CTFE (Compile Time Function Evaluation). Put the initialization/assignment in a function and it should work. That function can be a static constructor or the main function: [...] Yes, I see. I made a mistake. I need to initialize it elsewhere. It was quite confusing. Thanks.
Re: RAII trouble
On 20.11.2015 23:56, Spacen Jasset wrote: The ideal would be to have a struct that can be placed inside a function scope, or perhaps as a module global variable. Why does Ft_Init_FreeType have to be read at compile time? text.d(143,15): Error: static variable FT_Init_FreeType cannot be read at compile time text.d(174,43):called from here: initialise() struct FreeType { @disable this(); static FreeType initialise() { return FreeType(0); } this(int) { int error = FT_Init_FreeType(&library); /// ERROR enforce(!error); } alias library this; ~this() { FT_Done_FreeType(library); } FT_Library library; } FreeType freeType = FreeType.initialise(); FT_Init_FreeType must be read at compile time, because freeType is a module level variable, and initializers for module variables must be static values. The initializer is run through CTFE (Compile Time Function Evaluation). Put the initialization/assignment in a function and it should work. That function can be a static constructor or the main function: FreeType freeType = void; /* 'void' prevents default initialization */ static this() {freeType = FreeType.initialise();} /* should work */ void main() {freeType = FreeType.initialise();} /* too */
Re: RAII trouble
On 11/20/2015 02:56 PM, Spacen Jasset wrote: > FT_Init_FreeType is a static which is set at some previous time to a > function entry point in the FreeType library. > text.d(143,15): Error: static variable FT_Init_FreeType cannot be read > at compile time The compiler seems to think that FT_Init_FreeType is a variable. Is that really a function pointer? Can you show how it's defined. My simple test works: extern (C) int init_func(double){ return 42; } static auto my_func = &init_func; struct S { static S init() { return S(0); } this(int) { my_func(1.5); } } void main() { auto s = S.init(); } Ali
RAII trouble
I have the following code in attempt to create an RAII object wrapper, but it seems that it might be impossible. The problem here is that FT_Init_FreeType is a static which is set at some previous time to a function entry point in the FreeType library. I could use a scope block, but would rather have a self contained thing to do the work. Perhaps some sort of mixin is the other solution? The ideal would be to have a struct that can be placed inside a function scope, or perhaps as a module global variable. Why does Ft_Init_FreeType have to be read at compile time? text.d(143,15): Error: static variable FT_Init_FreeType cannot be read at compile time text.d(174,43):called from here: initialise() struct FreeType { @disable this(); static FreeType initialise() { return FreeType(0); } this(int) { int error = FT_Init_FreeType(&library); /// ERROR enforce(!error); } alias library this; ~this() { FT_Done_FreeType(library); } FT_Library library; } FreeType freeType = FreeType.initialise();
Re: RAII and Deterministic Destruction
Thanks. I had not looked at some of those yet. Jim
Re: RAII and Deterministic Destruction
Thanks for all the info. It's a good comparison of structs and classes to keep handy. Actually, I'm fine with the GC. I don't mean to avoid it. I just would like some way to also have non-memory resources automatically released in a timely, predictable way. One common thing to do in C++ is to manage the lifetime of an object with std::unique_ptr but then use its .get() function to get a native pointer to use temporarily. You just have to ensure that the native pointer doesn't outlive the unique_ptr, which isn't that difficult. True, if the unique_ptr gets destructed the native pointer is invalid and that could be risky, but you limit its use. Unique in D doesn't seem to have anything like a get() function but I wonder if it could. That is, it would get another "native" reference to the Unique resource that is managed by the garbage collector. So if the Unique went out of scope and got destructed, the reference would at least refer to valid memory although not a valid object because its destructor had already been called. Not perfectly safe, but no worse than the C++ case. Just a thought. Jim
Re: RAII and Deterministic Destruction
Thanks for the thorough response. I'm aware of some of what you explained. Maybe I should have asked differently. Rather than asking what RAII facilities do exist, I guess I was looking for the answer, "Here's what you typically do in C++ RAII that you can't do in D." I could probably find out things by experimenting too (and not be too lazy). I just didn't want to rely on my assumptions only. For example, say object A is a member of object B which is in turn a member of object C. If C is deleted or goes out of scope, does the destructor of A get called? If all three are classes, obviously not. But if all three are structs? What if they are classes but are all managed by Unique? If I use Unique for all of my heap-allocated classes (as I would with std::unique_ptr in C++) am I assured of destructors being called when the owning classes get destructed? I'm wondering about these various nesting/owning combinations. Jim
Re: RAII and Deterministic Destruction
A serious bug affecting RAII is https://issues.dlang.org/show_bug.cgi?id=14903, but apparently its importance hasn't been properly acknowledged yet. Improving the performance of binaries produced by dmd's backend is obviously way more important than fixing serious bugs or commenting on related PRs.
Re: RAII and Deterministic Destruction
On Tuesday, 25 August 2015 at 22:35:57 UTC, Jim Hewes wrote: Although C++ can be ugly, one reason I keep going back to it rather then commit more time to reference-based languages like C# is because I like deterministic destruction so much. My question is whether D can REALLY handle this or not. I've not been sure about this for some time so now I'm just going to come out and finally ask. You may also find http://wiki.dlang.org/Memory_Management useful. It shows a number of different patterns one can employ for deterministic memory management. The one which you'll probably find most C++-like is http://wiki.dlang.org/Memory_Management#Explicit_Class_Instance_Allocation Mike
Re: RAII and Deterministic Destruction
On Wednesday, 26 August 2015 at 01:18:43 UTC, ZombineDev wrote: On Wednesday, 26 August 2015 at 01:09:15 UTC, ZombineDev wrote: On Tuesday, 25 August 2015 at 22:35:57 UTC, Jim Hewes wrote: Although C++ can be ugly, one reason I keep going back to it rather then commit more time to reference-based languages like C# is because I like deterministic destruction so much. My question is whether D can REALLY handle this or not. I've not been sure about this for some time so now I'm just going to come out and finally ask. I know about this RAII section in the documentation: http://dlang.org/cpptod.html#raii But I don't believe that handles all cases, such as having classes as member variables of other classes. (Do the members get destructors called too?) Then there is std.typecons.Unique and std.typecons.RefCounted. With these, can I really get deterministic destruction for all cases like I would in C++? If so, it might be a good idea to emphasize this more in the documentation because I'd think people coming from C++ would be looking for this. Jim Structs in D behave like in C++ - they are created on the stack (unless you use new or malloc), passed by value and are automatically destroyed at the end of the scope. All their members are destroyed recursively after the user defined destructor is called. Just try it out in a toy program and see if it works as you expect. Classes on the other hand are reference types and are created on the garbage-collected heap by default (though you can do your own memory management). Similarly, structs created by new are also allocated on the GC heap. During collections the GC will call destructors, but it is not guaranteed and you should not rely on it. For example (depending on your GC usage and configuration) only a single collection may occur at the end of the program. Generally a very tutorial and reference is Ali's book [1], which is listed under Books and Articles in the sidebar of dlang.org. You should check the chapters Constructor and Other Special Functions [2] and Memory Management [3] (even though it covers mostly the GC). If you wish to avoid the GC, you can annotate your functions with the @nogc attribute [4] which enforces at compile-time that the annotated function won't use the GC. It is transitive which means that if you annotate your main() function with it, you shouldn't be allowed to use anything that allocates memory from the GC in your whole program. Since this feature was added there is an ongoing effort to minimize GC usage in the standard library, but there's still stuff that require it. That said the recommended approach is to build your data processing on ranges [5] (see the linked article by Andrei Alexandrescu) because it allows you to encapsulate memory usage more strategically. This is enabled by the lazy nature of ranges - they process data only when needed and so they don't need to allocate memory. Probably the best introduction to this technique is Walter Bright's keynote at DConf 2015 [6]: https://www.youtube.com/watch?v=znjesAXEEqw Be sure to check the DConf website as there's lots of great content from 2013 [7], 2014 [8] and 2015 [9]. [1]: http://ddili.org/ders/d.en/index.html [2]: http://ddili.org/ders/d.en/special_functions.html [3]: http://ddili.org/ders/d.en/memory.html [4]: http://dlang.org/attribute.html#nogc [5]: http://dlang.org/phobos/std_range.html [6]: https://www.youtube.com/watch?v=znjesAXEEqw [7]: http://dconf.org/2013/ [8]: http://dconf.org/2014/ [9]: http://dconf.org/2015/
Re: RAII and Deterministic Destruction
On Tuesday, 25 August 2015 at 22:35:57 UTC, Jim Hewes wrote: Although C++ can be ugly, one reason I keep going back to it rather then commit more time to reference-based languages like C# is because I like deterministic destruction so much. My question is whether D can REALLY handle this or not. I've not been sure about this for some time so now I'm just going to come out and finally ask. I know about this RAII section in the documentation: http://dlang.org/cpptod.html#raii But I don't believe that handles all cases, such as having classes as member variables of other classes. (Do the members get destructors called too?) Then there is std.typecons.Unique and std.typecons.RefCounted. With these, can I really get deterministic destruction for all cases like I would in C++? If so, it might be a good idea to emphasize this more in the documentation because I'd think people coming from C++ would be looking for this. Jim To add to what the other people said, there exists scoped!T in std.typecons to allocate a class on the stack, and Unique/RefCounted as you mentioned. AFAIK refcounted is in the process of being overhauled, but the user should notice no differences.
Re: RAII and Deterministic Destruction
On Tuesday, 25 August 2015 at 22:35:57 UTC, Jim Hewes wrote: Although C++ can be ugly, one reason I keep going back to it rather then commit more time to reference-based languages like C# is because I like deterministic destruction so much. My question is whether D can REALLY handle this or not. I've not been sure about this for some time so now I'm just going to come out and finally ask. I know about this RAII section in the documentation: http://dlang.org/cpptod.html#raii But I don't believe that handles all cases, such as having classes as member variables of other classes. (Do the members get destructors called too?) Then there is std.typecons.Unique and std.typecons.RefCounted. With these, can I really get deterministic destruction for all cases like I would in C++? If so, it might be a good idea to emphasize this more in the documentation because I'd think people coming from C++ would be looking for this. Jim Hi Jim. RAII in D is a common issue for people coming from C++. I don't know everything on the subject and would be glad to be corrected if I write a mistake but here is my share on the subject. There are two very different kind of objects in D and memory management is different for each: structs and classes. I think you mostly want to hear about classes, but I think each should be studied nonetheless. Structs: = Why would you use structs? - They are stack-allocated by default (but can be heap-allocated at will) - They are destroyed at the end of the scope, hence supporting RAII properly - They provide rather nice idioms for RAII [1] - They support single-inheritance through “alias this” - They support compile-time polymorphism through templates - They support complex object composition through template mixins [2] Why wouldn't you use structs? - You want to plug yourself on an already existing class-based system - You want to use interfaces - You need runtime-polymorphism Classes: = Why would you use classes? - They are heap-based and managed by the GC: lazy destruction - They provide full support for inheritance and OOP (including interfaces) - They support compile-time polymorphism through templates - They support complex object composition through template mixins [2] Why wouldn't use classes? - They are heap-based - They don't allow deterministic implicit destruction How to solve these problems? == Classes don't have *implicit* deterministic destruction but nothing prevents you from calling a destructor explicitely (be it ~this or another dedicated function). This can be easily done using scope(...) statements. If this is cumbersome, std.typecons.Unique and std.typecons.RefCounted can be used to provide a comportment analogous to that of modern C++. We said that structs are scope-based, a solution is to wrap an object into a struct and set the structs destructor to destroy the object as well at the end of the scope. std.typecons.scoped provides a nice wrapper arround this behaviour. The destruction of an object doesn't mean the destruction of the objects that it referenced. That can also be done explicitely. A word on the GC === I see a lot of C++ programmer who come to D with a single thought in mind: “Avoid the GC at all cost, let me manage my damn memory”. This is understandable, yet biaised. True, a GC isn't for all applications, but it has its place. Instead of fearing it, learning to use it well is important. One often doesn't need deterministic destruction. Lazy destruction has its benefits. I strongly advise not to rush trying to avoid the GC, instead profile profile profile. Conclusion === Doing RAII is possible for structs and for classes, but it is *really* easier with structs. D structs are really powerful and often underestimated by newcommers who rush on the "class" keyword. D classes aren't C++ classes. My advice would be to use structs as much as possible, to use classes where needed relying on the GC, and after profiling to introduce RAII behaviour into problematic classes. [1] https://w0rp.com/blog/post/an-raii-constructor-by-another-name-is-just-as-sweet/ [2] https://blog.dicebot.lv/posts/2015/08/OOP_composition_with_mixins
Re: RAII and Deterministic Destruction
On Wednesday, 26 August 2015 at 01:09:15 UTC, ZombineDev wrote: On Tuesday, 25 August 2015 at 22:35:57 UTC, Jim Hewes wrote: Although C++ can be ugly, one reason I keep going back to it rather then commit more time to reference-based languages like C# is because I like deterministic destruction so much. My question is whether D can REALLY handle this or not. I've not been sure about this for some time so now I'm just going to come out and finally ask. I know about this RAII section in the documentation: http://dlang.org/cpptod.html#raii But I don't believe that handles all cases, such as having classes as member variables of other classes. (Do the members get destructors called too?) Then there is std.typecons.Unique and std.typecons.RefCounted. With these, can I really get deterministic destruction for all cases like I would in C++? If so, it might be a good idea to emphasize this more in the documentation because I'd think people coming from C++ would be looking for this. Jim Structs in D behave like in C++ - they are created on the stack (unless you use new or malloc), passed by value and are automatically destroyed at the end of the scope. All their members are destroyed recursively after the user defined destructor is called. Just try it out in a toy program and see if it works as you expect. Classes on the other hand are reference types and are created on the garbage-collected heap by default (though you can do your own memory management). Similarly, structs created by new are also allocated on the GC heap. During collections the GC will call destructors, but it is not guaranteed and you should not rely on it. For example (depending on your GC usage and configuration) only a single collection may occur at the end of the program.
Re: RAII and Deterministic Destruction
On Tuesday, 25 August 2015 at 22:35:57 UTC, Jim Hewes wrote: Although C++ can be ugly, one reason I keep going back to it rather then commit more time to reference-based languages like C# is because I like deterministic destruction so much. My question is whether D can REALLY handle this or not. I've not been sure about this for some time so now I'm just going to come out and finally ask. I know about this RAII section in the documentation: http://dlang.org/cpptod.html#raii But I don't believe that handles all cases, such as having classes as member variables of other classes. (Do the members get destructors called too?) Then there is std.typecons.Unique and std.typecons.RefCounted. With these, can I really get deterministic destruction for all cases like I would in C++? If so, it might be a good idea to emphasize this more in the documentation because I'd think people coming from C++ would be looking for this. Jim Structs in D behave like in C++ - they are created on the stack (unless you use new or malloc), passed by value and are automatically destroyed at the end of the scope. All their members are destroyed recursively after the user defined destructor is called. Just try it out in a toy program and see if it works as you expect.
RAII and Deterministic Destruction
Although C++ can be ugly, one reason I keep going back to it rather then commit more time to reference-based languages like C# is because I like deterministic destruction so much. My question is whether D can REALLY handle this or not. I've not been sure about this for some time so now I'm just going to come out and finally ask. I know about this RAII section in the documentation: http://dlang.org/cpptod.html#raii But I don't believe that handles all cases, such as having classes as member variables of other classes. (Do the members get destructors called too?) Then there is std.typecons.Unique and std.typecons.RefCounted. With these, can I really get deterministic destruction for all cases like I would in C++? If so, it might be a good idea to emphasize this more in the documentation because I'd think people coming from C++ would be looking for this. Jim
Re: RAII limitations in D?
On Thu, 2014-08-21 at 19:22 -0700, Timothee Cour via Digitalmars-d-learn wrote: > What would be a good answer to this article? > http://swiftcoder.wordpress.com/2009/02/18/raii-why-is-it-unique-to-c/ > > Especially the part mentioning D:{ > D’s scope keyword, Python’s with statement and C#’s using declaration all > provide limited RAII, by allowing resources to have a scoped lifetime, but > none of them readily or cleanly support the clever tricks allowed by C++’s > combination of smart pointers and RAII, such as returning handles from > functions, multiple handles in the same scope, or handles held by multiple > clients. > } The author has clearly not actually used Python. There is nothing that C ++ can do with RAII that you cannot do with Python/context managers/with statement. Nothing. -- Russel. = Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.win...@ekiga.net 41 Buckmaster Roadm: +44 7770 465 077 xmpp: rus...@winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder
Re: RAII limitations in D?
On Friday, 22 August 2014 at 02:22:16 UTC, Timothee Cour via Digitalmars-d-learn wrote: Especially the part mentioning D:{ D’s scope keyword, Python’s with statement and C#’s using declaration all provide limited RAII, by allowing resources to have a scoped lifetime, but none of them readily or cleanly support the clever tricks allowed by C++’s combination of smart pointers and RAII, such as returning handles from functions, multiple handles in the same scope, or handles held by multiple clients. } Even for C# those are not really problems. Returning from functions is not a problem: you just return it and that's it, because resource management is decoupled from types, the types have no RAII semantics and you can move them around however you want. On the other hand, in C# it's very easy to declare a resource, while in C++ you would need to learn all the black magic of RAII before you can declare a RAII type. Multiple handles in the same scope are not frequent, nested if's cause much more trouble. Handles held by multiple clients are even more rare, and it's usually easy to figure out lifetime for non-memory resources.
Re: RAII limitations in D?
On Friday, 22 August 2014 at 03:00:01 UTC, Timothee Cour via Digitalmars-d-learn wrote: On Thu, Aug 21, 2014 at 7:26 PM, Dicebot via Digitalmars-d-learn < digitalmars-d-learn@puremagic.com> wrote: http://dlang.org/phobos/std_typecons.html#.RefCounted That doesn't work with classes though; is there any way to get a ref counted class? (and btw RefCounted should definitely appear in http://dlang.org/cpptod.html#raii) It can be made to work with classes and probably should be. There's no fundamental reason why it can't. It's probably just more complicated. - Jonathan M Davis
Re: RAII limitations in D?
On Thu, Aug 21, 2014 at 7:26 PM, Dicebot via Digitalmars-d-learn < digitalmars-d-learn@puremagic.com> wrote: > http://dlang.org/phobos/std_typecons.html#.RefCounted That doesn't work with classes though; is there any way to get a ref counted class? (and btw RefCounted should definitely appear in http://dlang.org/cpptod.html#raii)
Re: RAII limitations in D?
On Friday, 22 August 2014 at 02:22:16 UTC, Timothee Cour via Digitalmars-d-learn wrote: What would be a good answer to this article? It's own publication date: Feb 2009. The D struct has changed a lot since then, getting new features (@disable, postblit, more reliable destructor) and bugs notwithstanding is pretty well on point nowadays. All the stuff mentioned in there works now.
Re: RAII limitations in D?
http://dlang.org/phobos/std_typecons.html#.RefCounted
RAII limitations in D?
What would be a good answer to this article? http://swiftcoder.wordpress.com/2009/02/18/raii-why-is-it-unique-to-c/ Especially the part mentioning D:{ D’s scope keyword, Python’s with statement and C#’s using declaration all provide limited RAII, by allowing resources to have a scoped lifetime, but none of them readily or cleanly support the clever tricks allowed by C++’s combination of smart pointers and RAII, such as returning handles from functions, multiple handles in the same scope, or handles held by multiple clients. } This morning I was pointing to some deprecated usage of scope mentioned in docs (EMAIL:scope classes mentioned in tutorials, but deprecated). The pull request (https://github.com/D-Programming-Language/dlang.org/pull/637/files) mentions using struct or classes allocated on the stack via typecons.scoped. However what about the RAII usage mentioned in the above article that allows C++ to return handles for eg (impossible via scope), that get deterministically destroyed?
Re: Sparse Aggregate Assignment/Initialization (RAII)
On Monday, 7 July 2014 at 21:49:22 UTC, Justin Whear wrote: On Mon, 07 Jul 2014 21:34:05 +, Nordlöw wrote: However using this function through UFCS auto cx = new C().set!"x"(11); fails as algorithm_ex.d(1257,17): Error: template algorithm_ex.set cannot deduce function from argument types !("x")(C, int), candidates are: algorithm_ex.d(1242,7):algorithm_ex.set(string member, T, U)(ref T a, in U value) if (isAggregateType!T && hasMember!(T, member)) Instead I have to use auto c = new C(); set!"x"(c, 11); which is not as elegant. Why doesn't UCFS work here and is there a solution using DMD git master? You need to use `auto ref` to have this work with both classes and structs. A working version of your code here: auto dx = D().set!"x"(11); assert(dx.x == 11); To elaborate: `new C()` is an r-value, and references can only be taken from l-values.
Re: Sparse Aggregate Assignment/Initialization (RAII)
On Monday, 7 July 2014 at 21:50:22 UTC, Justin Whear wrote: Copy and paste gone astray; should be this link: http://dpaste.dzfl.pl/3c33ad70040f Thx!
Re: Sparse Aggregate Assignment/Initialization (RAII)
On Mon, 07 Jul 2014 21:49:22 +, Justin Whear wrote: > On Mon, 07 Jul 2014 21:34:05 +, Nordlöw wrote: > >> However using this function through UFCS >> >> auto cx = new C().set!"x"(11); >> >> fails as >> >> algorithm_ex.d(1257,17): Error: template algorithm_ex.set cannot deduce >> function from argument types !("x")(C, int), candidates are: >> algorithm_ex.d(1242,7):algorithm_ex.set(string member, T, >> U)(ref T a, in U value) if (isAggregateType!T && hasMember!(T, >> member)) >> >> Instead I have to use >> >> auto c = new C(); set!"x"(c, 11); >> >> which is not as elegant. >> >> Why doesn't UCFS work here and is there a solution using DMD git >> master? > > You need to use `auto ref` to have this work with both classes and > structs. A working version of your code here: auto dx = > D().set!"x"(11); > assert(dx.x == 11); Copy and paste gone astray; should be this link: http://dpaste.dzfl.pl/3c33ad70040f
Re: Sparse Aggregate Assignment/Initialization (RAII)
On Mon, 07 Jul 2014 21:34:05 +, Nordlöw wrote: > However using this function through UFCS > > auto cx = new C().set!"x"(11); > > fails as > > algorithm_ex.d(1257,17): Error: template algorithm_ex.set cannot deduce > function from argument types !("x")(C, int), candidates are: > algorithm_ex.d(1242,7):algorithm_ex.set(string member, T, > U)(ref T a, in U value) if (isAggregateType!T && hasMember!(T, > member)) > > Instead I have to use > > auto c = new C(); set!"x"(c, 11); > > which is not as elegant. > > Why doesn't UCFS work here and is there a solution using DMD git master? You need to use `auto ref` to have this work with both classes and structs. A working version of your code here: auto dx = D().set!"x"(11); assert(dx.x == 11); On Mon, 07 Jul 2014 21:34:05 +, Nordlöw wrote: > Further Is there a cleverer way to do this without resorting to mixins? __traits(getMember, ...) is useful here, see this version of your code: http://dpaste.dzfl.pl/75e03fbec020
Sparse Aggregate Assignment/Initialization (RAII)
Because D currently doesn't support RAII using named parameters like in Python I tried the following. Say I have an aggregate class C { int x,y,z,w; } or similarly struct C { int x,y,z,w; } I know define a generic _free_ function ref T set(string member, T, U)(ref T a, in U value) if (isAggregateType!T && hasMember!(T, member)) { mixin(`a.` ~ member ~ ` = value;`); return a; } which I want to use for flexible initialization of several members at once. However using this function through UFCS auto cx = new C().set!"x"(11); fails as algorithm_ex.d(1257,17): Error: template algorithm_ex.set cannot deduce function from argument types !("x")(C, int), candidates are: algorithm_ex.d(1242,7):algorithm_ex.set(string member, T, U)(ref T a, in U value) if (isAggregateType!T && hasMember!(T, member)) Instead I have to use auto c = new C(); set!"x"(c, 11); which is not as elegant. Why doesn't UCFS work here and is there a solution using DMD git master? Further Is there a cleverer way to do this without resorting to mixins? This old post http://forum.dlang.org/thread/mailman.2966.1301533296.4748.digitalmars-d-le...@puremagic.com talks about opDispatch() but, to my knowledge, it requires the aggregate to have extra members doing the initialization. I want this because I've identified in some parts of my code that I have large structures were only a few of the elements are initialized to non-default values.
Re: Bug or feature? std.c.stdlib.exit() breaks RAII
On 06/01/2012 14:44, Andrej Mitrovic wrote: Just implement your own exception type, e.g. ExitException, and then use: That's more or less what people have already said. What's more, Ashish has already suggested a further improvement whereby the custom exception carries an exit code. void main() { try { realMain(); } catch (ExitException e) { return 0; } That should have been int main. That's not the only typo - you've forgotten to finish that function with a return for if ExitException is never thrown and a closing }. Stewart.
Re: Bug or feature? std.c.stdlib.exit() breaks RAII
That should have been int main.
Re: Bug or feature? std.c.stdlib.exit() breaks RAII
Just implement your own exception type, e.g. ExitException, and then use: void main() { try { realMain(); } catch (ExitException e) { return 0; } void exit() { throw ExitException(); } where realMain is the actual main function. Then just call exit() whenever you want to.
Re: Bug or feature? std.c.stdlib.exit() breaks RAII
On 29/12/2011 19:09, Jacob Carlborg wrote: Could druntime hook up on the atexit function to run destructors and similar when the program exits? I'm not sure. Maybe it could be called upon to run static destructors and destruct heap-allocated objects. But in order to call scope guards and RAII, it would need to unwind the call stack, which could get complicated if you're trying to do it from within a function. It's much simpler not to use exit() and throw a custom exception instead. Stewart.
Re: Bug or feature? std.c.stdlib.exit() breaks RAII
Thanks, Jonathan, for your detailed answer. Ashish On Fri, Dec 30, 2011 at 1:41 PM, Jonathan M Davis wrote: > On Friday, December 30, 2011 10:45:43 Ashish Myles wrote: >> Ok, now there are two issues here: >> IMPLEMENTATION: Implementation of a safe_exit() without an explicit >> Exception seems to be easy to do at the language level for a >> single-threaded program -- you simply have a hidden/system class like, >> say, __SystemException from which Exception derives that be comes the >> base class of all throwable objects. __ExitException could then >> derive from __SystemException and store the exit value. But it is not >> clear how this would work for multithreaded programs, with which I >> have little experience in the context of C++ exceptions. Presumably, >> the __ExitException would have to be thrown in all threads and could >> interrupt functions that would otherwise not throw exceptions -- I >> can't say I understand all the implications. > > It's more complicate than that. The base class of all throwable objects is > Throwable. Error and Exception are derived from Throwable. Destructors, > finally > blocks, and scope statements are all skipped when a Throwable is thrown unless > it is derived from Exception. So, there is no proper cleanup unless an > Exception is thrown. Right now, the compiler, the runtime, and programmers can > all assume that > > try > { > //code > //1 > } > catch(Exception e) > { > //2 > } > > either #1 or #2 will be hit in that code if proper cleanup is occuring. In > fact nothrow relies on this. If you wrap a function call in a try-catch block > which catches Exception, then the function it's called in can be nothrow even > if the function being called throws an exception. If we tried to have another > exception type which was for exiting, then you'd get this weird situation > where nothrow functions _can_ throw when the program is being shutdown > properly, and that could be a big problem. > > Functions in D are set up around the idea that the only way to exit a function > and have proper cleanup occur is to either return from it or have an Exception > thrown from it. You're trying to have another way added. It's not that it's > necessarily impossible, but it would likely require the redesign of several > features and would break the assumptions made by a lot of code. > >> For cleanup that needs to be done no matter what the exception, I >> would just use a finally{} block. > > Yes and no. finally gets hit whether an Exception is thrown or the try block > is > exited normally, but it isn't run when a non-Exception Throwable (generally an > Error) is thrown, so it gurantees nothing on unsafe shutdown. And if you were > using exit, what would be the proper behavior? Neither the remainder of the > try block nor the catch block would be run (since exit would skip the rest of > the try block and skip the catch block entirely), which would likely break the > assumptions made by a lot of code. It would certainly break scope. All of a > sudden, you have something other than Error which won't hit scope(success) or > scope(failure) but _will_ hit scope(exit), and that something is trying to be > exiting _cleanly_ - unlike Error. > >> UTILITY: Now, the actual utility of having a safe exit seems to be in >> question here. A common use of this is in OpenGL programs (that may be >> implicitly multithreaded) where the keyboard handler exit()s when I >> hit 'q' or ESC (which is quite common). Moreover, the underlying GUI >> framework or other APIs being used may conceivably have multiple >> threads and abstract this out for the user. Is this an unreasonable >> use case for a safe exit? Or would this be too difficult to implement >> cleanly? > > Exceptions only affect a single thread, so they're not going to help you > terminate a multi-threaded program regardless. And to terminate another > thread, you need a way to terminate it. The only ways to do that are to tell > them to terminate themselves or to kill them. There is no way that I'm aware > of built into threads to tell them that it's time to shutdown and then let > them do it cleanly (which is what you'd need for a clean shutdown). You could > use std.concurrency to inform them to shutdown or have a shared flag which > indicates that it's time for all threads to shutdown, but you couldn't use > pthreads or the Windows equivalent to tell a thread to shutdown cleanly. So, > the only means generally available to terminate a thread is to forcibly kill > it (as C's exit does), making automatic cleanup impossible. > > _
Re: Bug or feature? std.c.stdlib.exit() breaks RAII
On Friday, December 30, 2011 10:45:43 Ashish Myles wrote: > Ok, now there are two issues here: > IMPLEMENTATION: Implementation of a safe_exit() without an explicit > Exception seems to be easy to do at the language level for a > single-threaded program -- you simply have a hidden/system class like, > say, __SystemException from which Exception derives that be comes the > base class of all throwable objects. __ExitException could then > derive from __SystemException and store the exit value. But it is not > clear how this would work for multithreaded programs, with which I > have little experience in the context of C++ exceptions. Presumably, > the __ExitException would have to be thrown in all threads and could > interrupt functions that would otherwise not throw exceptions -- I > can't say I understand all the implications. It's more complicate than that. The base class of all throwable objects is Throwable. Error and Exception are derived from Throwable. Destructors, finally blocks, and scope statements are all skipped when a Throwable is thrown unless it is derived from Exception. So, there is no proper cleanup unless an Exception is thrown. Right now, the compiler, the runtime, and programmers can all assume that try { //code //1 } catch(Exception e) { //2 } either #1 or #2 will be hit in that code if proper cleanup is occuring. In fact nothrow relies on this. If you wrap a function call in a try-catch block which catches Exception, then the function it's called in can be nothrow even if the function being called throws an exception. If we tried to have another exception type which was for exiting, then you'd get this weird situation where nothrow functions _can_ throw when the program is being shutdown properly, and that could be a big problem. Functions in D are set up around the idea that the only way to exit a function and have proper cleanup occur is to either return from it or have an Exception thrown from it. You're trying to have another way added. It's not that it's necessarily impossible, but it would likely require the redesign of several features and would break the assumptions made by a lot of code. > For cleanup that needs to be done no matter what the exception, I > would just use a finally{} block. Yes and no. finally gets hit whether an Exception is thrown or the try block is exited normally, but it isn't run when a non-Exception Throwable (generally an Error) is thrown, so it gurantees nothing on unsafe shutdown. And if you were using exit, what would be the proper behavior? Neither the remainder of the try block nor the catch block would be run (since exit would skip the rest of the try block and skip the catch block entirely), which would likely break the assumptions made by a lot of code. It would certainly break scope. All of a sudden, you have something other than Error which won't hit scope(success) or scope(failure) but _will_ hit scope(exit), and that something is trying to be exiting _cleanly_ - unlike Error. > UTILITY: Now, the actual utility of having a safe exit seems to be in > question here. A common use of this is in OpenGL programs (that may be > implicitly multithreaded) where the keyboard handler exit()s when I > hit 'q' or ESC (which is quite common). Moreover, the underlying GUI > framework or other APIs being used may conceivably have multiple > threads and abstract this out for the user. Is this an unreasonable > use case for a safe exit? Or would this be too difficult to implement > cleanly? Exceptions only affect a single thread, so they're not going to help you terminate a multi-threaded program regardless. And to terminate another thread, you need a way to terminate it. The only ways to do that are to tell them to terminate themselves or to kill them. There is no way that I'm aware of built into threads to tell them that it's time to shutdown and then let them do it cleanly (which is what you'd need for a clean shutdown). You could use std.concurrency to inform them to shutdown or have a shared flag which indicates that it's time for all threads to shutdown, but you couldn't use pthreads or the Windows equivalent to tell a thread to shutdown cleanly. So, the only means generally available to terminate a thread is to forcibly kill it (as C's exit does), making automatic cleanup impossible. _Some_ cleanup can be done when exit is called using atexit and on_exit, but the stack won't be unwound properly, so RAII, scope statements, and finally blocks aren't going to be run properly. So, critical, global stuff can be potentially cleaned up, but you can't get a fully clean shutdown without actually returning or having an Exception thrown from every function in every thread. So, in general, the best way to handle taking down a multi
Re: Bug or feature? std.c.stdlib.exit() breaks RAII
On Fri, Dec 30, 2011 at 5:43 AM, Jonathan M Davis wrote: > On Thursday, December 29, 2011 23:03:23 Ashish Myles wrote: >> Since D >> could conceivably implement a very safe exit() without an explicit use >> of Exceptions to get around the "catch Exception() {}" problem you >> mentioned above, does it make sense to request a safer exit() feature >> for D? > > And how would it do that? The only way in the language to properly unwind the > stack without returning from each and every function is to throw an Exception. > If you wanted to do an exit function, it would somehow have to do the exact > same thing that happens when you throw an Exception except that it's not an > Exception and isn't caught by catch(Exception) {}. That may not be impossible, > but I expect that it would complicate things quite a bit. And scope statements > are designed around exceptions such that if you didn't throw an Exception, > they wouldn't work properly. The same goes for finally blocks. Also, what is > the correct thing to do in a situation like this > Ok, now there are two issues here: IMPLEMENTATION: Implementation of a safe_exit() without an explicit Exception seems to be easy to do at the language level for a single-threaded program -- you simply have a hidden/system class like, say, __SystemException from which Exception derives that be comes the base class of all throwable objects. __ExitException could then derive from __SystemException and store the exit value. But it is not clear how this would work for multithreaded programs, with which I have little experience in the context of C++ exceptions. Presumably, the __ExitException would have to be thrown in all threads and could interrupt functions that would otherwise not throw exceptions -- I can't say I understand all the implications. > try > { > //code > } > catch(Exception e) > { > //do stuff > } > > The code in the catch block assumes that it's always going to be run when the > code in the try block is not properly completed. If an exit call were made > from within the try block (be it directly in it or in a function that was > called inside it), how would the catch block be handled? Without an Exception, > it would be skipped, what's in that catch block wouldn't be run, and there > would be no proper cleanup. > For cleanup that needs to be done no matter what the exception, I would just use a finally{} block. UTILITY: Now, the actual utility of having a safe exit seems to be in question here. A common use of this is in OpenGL programs (that may be implicitly multithreaded) where the keyboard handler exit()s when I hit 'q' or ESC (which is quite common). Moreover, the underlying GUI framework or other APIs being used may conceivably have multiple threads and abstract this out for the user. Is this an unreasonable use case for a safe exit? Or would this be too difficult to implement cleanly?
Re: Bug or feature? std.c.stdlib.exit() breaks RAII
On Thursday, December 29, 2011 23:03:23 Ashish Myles wrote: > Since D > could conceivably implement a very safe exit() without an explicit use > of Exceptions to get around the "catch Exception() {}" problem you > mentioned above, does it make sense to request a safer exit() feature > for D? And how would it do that? The only way in the language to properly unwind the stack without returning from each and every function is to throw an Exception. If you wanted to do an exit function, it would somehow have to do the exact same thing that happens when you throw an Exception except that it's not an Exception and isn't caught by catch(Exception) {}. That may not be impossible, but I expect that it would complicate things quite a bit. And scope statements are designed around exceptions such that if you didn't throw an Exception, they wouldn't work properly. The same goes for finally blocks. Also, what is the correct thing to do in a situation like this try { //code } catch(Exception e) { //do stuff } The code in the catch block assumes that it's always going to be run when the code in the try block is not properly completed. If an exit call were made from within the try block (be it directly in it or in a function that was called inside it), how would the catch block be handled? Without an Exception, it would be skipped, what's in that catch block wouldn't be run, and there would be no proper cleanup. The very concept of exit violates how the language functions with regards to stack unwinding. Stack unwinding is built around how exceptions function. exit, on the other hand, tries to avoid the whole exception thing and just kill your program. But ultimately, you _can't_ ignore the fact that in order to ensure proper stack unwinding, you either need to return from each function on the stack, or throw an Exception from them. Anything else is going to fail to unwind the stack properly. And honestly, I would generally consider it bad practice to use an exit function. It violates the proper flow of the program - as the issues with stack unwinding illustrate. If you want to do the equivalent of an exit function and have proper cleanup occur, you really need to be throw an Exception designated for that and have your code let it pass all the way through to main so that it can exit properly after having unwound the stack. - Jonathan M Davis
Re: Bug or feature? std.c.stdlib.exit() breaks RAII
On Thu, Dec 29, 2011 at 7:16 PM, Jonathan M Davis wrote: > > A D exit function would have to do essentially the same thing as throw an > exception and catch it in main anyway. The only way that the stack is going to > be unwound properly is if you actually unwind it. The only way in the language > to do that without actually returning from each and every function on the > stack is to throw an exception. > > How many modern languages do you know of with an exit function that cleans > everything up properly? C++ won't for the same reasons that D won't. Java gets > away with it because it doesn't have destructors or scope statements or > anything else that would actually require unwinding the stack. > > All a D exit function _could_ do would be to throw an exception and then have > the runtime catch it - which still wouldn't work if the programmer was foolish > enough to do something like > > catch(Exception) {} > > in their code. So, throwing an exception and catching it _is_ the way to do > it, and it really makes more sense if you're doing it yourself, since then > you're less likely to make that mistake and catch all Exceptions somewhere in > your code and eat the one which is supposed to exit the program. > > - Jonathan M Davis Hm...embarassingly, it didn't occur to me that C++ didn't clean up either; but sure enough, the following code shows that exit() breaks C++ RAII. #include #include struct SafeExit { ~SafeExit() { std::cout << "Safely exit with destructor." << std::endl; } }; int main(int argc, char** argv) { SafeExit safeExit; std::cout << "Test if std.c.stdlib.exit() breaks RAII." << std::endl; std::cout << "Pre-exit!" << std::endl; exit(0); std::cout << "Post-exit! Should not get here!" << std::endl; return 0; } On the other hand, ruby happily *does* unwind the stack properly on exit(). def safe_exit begin yield ensure puts "Safely exit with ensure." end end safe_exit do puts "Test if std.c.stdlib.exit() breaks RAII." puts "Pre-exit!" exit(0); puts "Post-exit! Should not get here!" end Honestly, I would rather have the latter robustness. While I have always thought of abort() as being a dirty exit, I had, until now, always thought of exit() as being very safe. Violating RAII on a safely-intended exit() is a really Bad Thing, I would think. Since D could conceivably implement a very safe exit() without an explicit use of Exceptions to get around the "catch Exception() {}" problem you mentioned above, does it make sense to request a safer exit() feature for D? Ashish
Re: Bug or feature? std.c.stdlib.exit() breaks RAII
On Thursday, December 29, 2011 13:43:36 Ashish Myles wrote: > On Thu, Dec 29, 2011 at 1:26 PM, Andrej Mitrovic > > wrote: > > Probably the easiest thing to do is to throw a custom exception and > > catch it somewhere in main() to return your status code. Unlike > > exit(), throwing will take care of RAII stuff. > > Thanks, Andrej. That option had occurred to me, but I figured that > shouldn't be the way to do things given that most other languages have > a native exit function. Given that this code transformation isn't > particularly difficult (put main in a try/catch, have a custom > exception storing exit code, return the exit code in the catch block), > would it be reasonable to do a feature request for a > D-language-supported exit()? A D exit function would have to do essentially the same thing as throw an exception and catch it in main anyway. The only way that the stack is going to be unwound properly is if you actually unwind it. The only way in the language to do that without actually returning from each and every function on the stack is to throw an exception. How many modern languages do you know of with an exit function that cleans everything up properly? C++ won't for the same reasons that D won't. Java gets away with it because it doesn't have destructors or scope statements or anything else that would actually require unwinding the stack. All a D exit function _could_ do would be to throw an exception and then have the runtime catch it - which still wouldn't work if the programmer was foolish enough to do something like catch(Exception) {} in their code. So, throwing an exception and catching it _is_ the way to do it, and it really makes more sense if you're doing it yourself, since then you're less likely to make that mistake and catch all Exceptions somewhere in your code and eat the one which is supposed to exit the program. - Jonathan M Davis
Re: Bug or feature? std.c.stdlib.exit() breaks RAII
On 12/29/2011 12:43 PM, Ashish Myles wrote: On Thu, Dec 29, 2011 at 1:26 PM, Andrej Mitrovic wrote: Probably the easiest thing to do is to throw a custom exception and catch it somewhere in main() to return your status code. Unlike exit(), throwing will take care of RAII stuff. Thanks, Andrej. That option had occurred to me, but I figured that shouldn't be the way to do things given that most other languages have a native exit function. Given that this code transformation isn't particularly difficult (put main in a try/catch, have a custom exception storing exit code, return the exit code in the catch block), would it be reasonable to do a feature request for a D-language-supported exit()? Yeah, really. I'd been using the C exit() as well. Seems like a pretty fundamental feature. :O
Re: Bug or feature? std.c.stdlib.exit() breaks RAII
On 2011-12-29 18:22, Jakob Ovrum wrote: On Thursday, 29 December 2011 at 16:27:33 UTC, Ashish Myles wrote: std.c.stdlib.exit() seems to break RAII. The code below tests this both using a struct destructor and an explicit scope(exit) {}. Is this an intentional feature or a bug? import std.stdio; import std.c.stdlib; void main() { struct SafeExit { ~this() { writeln("Safely exit with destructor."); } } SafeExit safeExit; scope(exit) { writeln("Safely exit with scope(exit)."); } scope(failure) { writeln("Safely exit with scope(failure)."); } writeln("Test if std.c.stdlib.exit() breaks RAII."); writeln("Pre-exit!"); std.c.stdlib.exit(0); writeln("Post-exit! Should not get here!"); } The C runtime is beyond D's immediate control. You would have to replace C's exit function with a custom one or make the compiler recognize calls to it. Calling 'exit' doesn't properly shut down the D runtime either, it's not just constructors. It's neither a bug or a feature. The bug is arguably in your program. Could druntime hook up on the atexit function to run destructors and similar when the program exits? -- /Jacob Carlborg
Re: Bug or feature? std.c.stdlib.exit() breaks RAII
On Thu, Dec 29, 2011 at 1:26 PM, Andrej Mitrovic wrote: > Probably the easiest thing to do is to throw a custom exception and > catch it somewhere in main() to return your status code. Unlike > exit(), throwing will take care of RAII stuff. Thanks, Andrej. That option had occurred to me, but I figured that shouldn't be the way to do things given that most other languages have a native exit function. Given that this code transformation isn't particularly difficult (put main in a try/catch, have a custom exception storing exit code, return the exit code in the catch block), would it be reasonable to do a feature request for a D-language-supported exit()?
Re: Bug or feature? std.c.stdlib.exit() breaks RAII
Probably the easiest thing to do is to throw a custom exception and catch it somewhere in main() to return your status code. Unlike exit(), throwing will take care of RAII stuff.
Re: Bug or feature? std.c.stdlib.exit() breaks RAII
On Thu, Dec 29, 2011 at 12:22 PM, Jakob Ovrum wrote: > On Thursday, 29 December 2011 at 16:27:33 UTC, Ashish Myles wrote: >> >> std.c.stdlib.exit() seems to break RAII. The code below tests this >> both using a struct destructor and an explicit scope(exit) {}. Is >> this an intentional feature or a bug? >> >> import std.stdio; >> import std.c.stdlib; >> >> void main() >> { >> struct SafeExit { >> ~this() { >> writeln("Safely exit with destructor."); >> } >> } >> SafeExit safeExit; >> >> scope(exit) { writeln("Safely exit with scope(exit)."); } >> scope(failure) { writeln("Safely exit with scope(failure)."); } >> >> writeln("Test if std.c.stdlib.exit() breaks RAII."); >> writeln("Pre-exit!"); >> std.c.stdlib.exit(0); >> writeln("Post-exit! Should not get here!"); >> } > > > The C runtime is beyond D's immediate control. You would have to replace C's > exit function with a custom one or make the compiler recognize calls to it. > > Calling 'exit' doesn't properly shut down the D runtime either, it's not > just constructors. > > It's neither a bug or a feature. The bug is arguably in your program. In that case, what is the recommended way to exit a program from a deeply nested level and also possibly specify a return value? Ashish
Re: Bug or feature? std.c.stdlib.exit() breaks RAII
On Thursday, 29 December 2011 at 17:22:33 UTC, Jakob Ovrum wrote: Calling 'exit' doesn't properly shut down the D runtime either, it's not just constructors. I mean destructors*.
Re: Bug or feature? std.c.stdlib.exit() breaks RAII
On Thursday, 29 December 2011 at 16:27:33 UTC, Ashish Myles wrote: std.c.stdlib.exit() seems to break RAII. The code below tests this both using a struct destructor and an explicit scope(exit) {}. Is this an intentional feature or a bug? import std.stdio; import std.c.stdlib; void main() { struct SafeExit { ~this() { writeln("Safely exit with destructor."); } } SafeExit safeExit; scope(exit) { writeln("Safely exit with scope(exit)."); } scope(failure) { writeln("Safely exit with scope(failure)."); } writeln("Test if std.c.stdlib.exit() breaks RAII."); writeln("Pre-exit!"); std.c.stdlib.exit(0); writeln("Post-exit! Should not get here!"); } The C runtime is beyond D's immediate control. You would have to replace C's exit function with a custom one or make the compiler recognize calls to it. Calling 'exit' doesn't properly shut down the D runtime either, it's not just constructors. It's neither a bug or a feature. The bug is arguably in your program.
Bug or feature? std.c.stdlib.exit() breaks RAII
std.c.stdlib.exit() seems to break RAII. The code below tests this both using a struct destructor and an explicit scope(exit) {}. Is this an intentional feature or a bug? import std.stdio; import std.c.stdlib; void main() { struct SafeExit { ~this() { writeln("Safely exit with destructor."); } } SafeExit safeExit; scope(exit) { writeln("Safely exit with scope(exit)."); } scope(failure) { writeln("Safely exit with scope(failure)."); } writeln("Test if std.c.stdlib.exit() breaks RAII."); writeln("Pre-exit!"); std.c.stdlib.exit(0); writeln("Post-exit! Should not get here!"); }
Re: How to use structs for RAII?
Ah, I keep forgetting about opCall. Nice tips there.
Re: How to use structs for RAII?
On 2011-01-23 01:14, Andrej Mitrovic wrote: A workaround: import std.stdio; import std.exception; struct A { int x; this(void* none) { if (none !is null) { enforce(0, "Tried to pass a parameter to A's constructor"); } writeln("in constructor"); // construction from here.. x = 5; } } void main() { auto a = A(null); } I think that would be safe, and closest to a "default" constructor. A static opCall would be better. See my answer to the original post. -- /Jacob Carlborg
Re: How to use structs for RAII?
On 2011-01-23 00:03, Sean Eskapp wrote: It was recommended to me to use structs for RAII instead of scope classes, since scope is being removed (?). However, since default-constructors for structs can't exist, how does one do this? You can use a static opCall, like this: struct Foo { static Foo opCall () { Foo foo; // initialize foo return foo; } } auto foo = Foo(); -- /Jacob Carlborg
Re: How to use structs for RAII?
On Saturday 22 January 2011 15:09:29 bearophile wrote: > Sean Eskapp: > > It was recommended to me to use structs for RAII instead of scope > > classes, since scope is being removed (?). However, since > > default-constructors for structs can't exist, how does one do this? > > Inside the ~this() you may put code, to deallocate resources you have > allocated in an explicit constructor (or in some creation method). Is this > enough? The typical thing to do when you want a default constructor for a struct is to use a static opCall(). Then, for struct S, you'd do auto s = S(); If you did S s(), it still wouldn't work, so you might want to add an assertion in the destructor which verifes that the struct wasn't constructor with S.init, in which case what the destructor would be doing would likely be wrong. Personally, I _always_ declare struct variables in this form auto s = S(); and never as S s(); It avoids most of the problem with a struct's init value. But you can still get structs initialized to their init when they're in arrays and the like (which is a good part of the reason that init exists in the first place). - Jonathan M Davis
Re: How to use structs for RAII?
Actually this becomes rather annoying, since I can't use any closures on the object. Stupidly, I can still use closures with an object which is deleted at the end of the function.
Re: How to use structs for RAII?
A workaround: import std.stdio; import std.exception; struct A { int x; this(void* none) { if (none !is null) { enforce(0, "Tried to pass a parameter to A's constructor"); } writeln("in constructor"); // construction from here.. x = 5; } } void main() { auto a = A(null); } I think that would be safe, and closest to a "default" constructor.
Re: How to use structs for RAII?
== Quote from bearophile (bearophileh...@lycos.com)'s article > Sean Eskapp: > > It was recommended to me to use structs for RAII instead of scope classes, > > since scope is being removed (?). However, since default-constructors for > > structs can't exist, how does one do this? > Inside the ~this() you may put code, to deallocate resources you have > allocated in an explicit constructor (or in some creation method). Is this enough? > Bye, > bearophile By explicit constructor, I assume you mean one with parameters? I guess that's alright.
Re: How to use structs for RAII?
Sean Eskapp: > It was recommended to me to use structs for RAII instead of scope classes, > since scope is being removed (?). However, since default-constructors for > structs can't exist, how does one do this? Inside the ~this() you may put code, to deallocate resources you have allocated in an explicit constructor (or in some creation method). Is this enough? Bye, bearophile
How to use structs for RAII?
It was recommended to me to use structs for RAII instead of scope classes, since scope is being removed (?). However, since default-constructors for structs can't exist, how does one do this?
Re: Recommended way to do RAII cleanly
On Monday, July 12, 2010 18:15:04 Nick Sabalausky wrote: > "torhu" wrote in message > news:i1ft84$2h4...@digitalmars.com... > > > I think the conclusion is that RAII is less important in D than in C++. > > In D you use scope (exit), or even finally, like in Java or Python. The > > other use of scope, as a storage class, is supposed to go away, and I > > suspect I'm not the only one who's going to miss it. > > As good as scope guards are, anytime you have an object that has some sort > of cleanup function that needs to be called when you're done with it, it's > absurd to think that requiring the *user* of the object to use scope guards > *by convention* is ever a satisfactory substitute for real RAII. > > And I have to say, I'm rather disappointed to hear that scope objects are > going away. What is the reason for that? IIRC, Walter was of the opinion that you could do the same with structs without needing scope. And as long as you can default construct them or your RAII object requires arguments to its constructor, that's more or less true (if arguably limiting). The question, therefore is whether there is a means to effectively default construct structs which is not going to be deprecated. I'd still like scope to stick around (even if all it did was put the class on the heap as normal and called clear() on it when it left scope rather than putting it on the stack like I believe it does at the moment), but if structs can totally take care of the RAII issue, then it's not as bad. But the situation with structs and default constructors is a bit frustrating. - Jonathan M Davis
Re: Recommended way to do RAII cleanly
"torhu" wrote in message news:i1ft84$2h4...@digitalmars.com... > > I think the conclusion is that RAII is less important in D than in C++. In > D you use scope (exit), or even finally, like in Java or Python. The other > use of scope, as a storage class, is supposed to go away, and I suspect > I'm not the only one who's going to miss it. As good as scope guards are, anytime you have an object that has some sort of cleanup function that needs to be called when you're done with it, it's absurd to think that requiring the *user* of the object to use scope guards *by convention* is ever a satisfactory substitute for real RAII. And I have to say, I'm rather disappointed to hear that scope objects are going away. What is the reason for that?
Re: Recommended way to do RAII cleanly
torhu: > I think that's supposed to go away, but I'm not 100% sure. Would make > sense, since it was added as a sort of stopgap measure when there were > no struct literals. Well, if you have found a use case for static opCall then it needs to be shown to the people that want to deprecate the static opCall, to ask for ways to replace its functionality. Bye, bearophile
Re: Recommended way to do RAII cleanly
On Monday, July 12, 2010 15:40:24 torhu wrote: > On 13.07.2010 00:09, bearophile wrote: > > Jonathan M Davis: > >> There are lots of cases where using scope(exit) makes sense, and it's a > >> great construct. But there are also plenty of cases where using plain > >> old RAII with a single declaration is better. It works fine in D as > >> long as the struct in question doesn't need a default constructor. But > >> if it does, then it becomes a problem. > > > > Can't you use a static opCall (also suggested by Jacob Carlborg)? > > > > Bye, > > bearophile > > I think that's supposed to go away, but I'm not 100% sure. Would make > sense, since it was added as a sort of stopgap measure when there were > no struct literals. This would be why I like Berophile's suggestion on the main list of having a page which lists deprecated and intended to be deprecated constructs. opCall is a good solution and basically fulfills the requirements of a default constructor, but if it's going away, then it's in the same camp as using scope on a class. - Jonathan M Davis
Re: Recommended way to do RAII cleanly
On 13.07.2010 00:09, bearophile wrote: Jonathan M Davis: There are lots of cases where using scope(exit) makes sense, and it's a great construct. But there are also plenty of cases where using plain old RAII with a single declaration is better. It works fine in D as long as the struct in question doesn't need a default constructor. But if it does, then it becomes a problem. Can't you use a static opCall (also suggested by Jacob Carlborg)? Bye, bearophile I think that's supposed to go away, but I'm not 100% sure. Would make sense, since it was added as a sort of stopgap measure when there were no struct literals.