digitalmars.D.learn - raii
- Ellery Newcomer (67/68) Feb 28 2010 The problem:
-
sybrandy
(10/10)
Feb 28 2010
- Ellery Newcomer (5/15) Feb 28 2010 They might be good enough, I don't know. I thought they wouldn't be
- Jonathan M Davis (16/42) Feb 28 2010 Without RAII, it's likely something that they have to remember too. Unle...
- Ellery Newcomer (5/8) Feb 28 2010 One thing I'm curious about in particular is how to ensure that the
- =?ISO-8859-1?Q?Pelle_M=E5nsson?= (29/99) Feb 28 2010 Maybe something along the lines of this
Hello The impetus:I agree, except I more and more think that scope classes were a mistake. Structs with destructors are a much better solution, and wrapping a class inside a struct would give it RAII semantics.The problem: In designing a library (or rather touching up someone else's library), I have a class (interface?) Foo, and it has a method close (actually several methods). Almost invariably (although conceivably not always), usage involves manipulating Foo, and then remembering to call close. That last part is obnoxious. One obtains a Foo from function foo. What I'd like is for the result of foo to have RAII semantics by default, with the possibility of nixing it if the user actually cares. I want to take the burden of remembering that stupid close off the user. For example: void bar(){ auto a = foo(); a.doStuff(); } has an implicit call to close after doStuff. The impetus suggests I can do this by wrapping Foo inside a struct, but I'm not so sure how well this would work out. Also note that close can fail in a number of ways relating to file io. So I wrote up a simple prototype. Thoughts? import std.stdio; class Foo{ int k; this(int i){ writefln("bin %d",k); k = i; } void doStuff(){ writefln("do stuff %d",k); } void close(){ writefln("close %d",k); } } struct RAII(T){ T b; bool extracted = false; alias b this; this (T i){ assert(i !is null); writeln("in"); b = i; } ~this(){ writeln("~this"); if(!extracted) b.close(); } T extract(){ assert(!extracted); T t = b; b = null; extracted = true; return t; } } RAII!(Foo) foo(int i){ return RAII!(Foo)(new Foo(i)); } void main(){ auto a = foo(1); auto b = foo(2).extract(); a.doStuff(); b.doStuff(); }
Feb 28 2010
<snip> I have two questions for you: 1) Are class destructors not good enough? If so, why? 2) Have you looked into static destructors? I created a logging library and that worked perfect for me to ensure that if I killed the program via Ctrl-C, it would flush the output buffer and close the file handle. Personally, I really like the use of "scope" vs. RAII. Keeping the code to clean up a file handle or the like right by where it's declared seems very sensible to me. Casey
Feb 28 2010
On 02/28/2010 02:34 PM, sybrandy wrote:<snip> I have two questions for you: 1) Are class destructors not good enough? If so, why?They might be good enough, I don't know. I thought they wouldn't be guaranteed to be called on scope exit, though.2) Have you looked into static destructors? I created a logging library and that worked perfect for me to ensure that if I killed the program via Ctrl-C, it would flush the output buffer and close the file handle.I'm not really looking at unexpected terminationsPersonally, I really like the use of "scope" vs. RAII. Keeping the code to clean up a file handle or the like right by where it's declared seems very sensible to me.I like scope too. But it's still something the user has to remember.Casey
Feb 28 2010
Ellery Newcomer wrote:On 02/28/2010 02:34 PM, sybrandy wrote:Without RAII, it's likely something that they have to remember too. Unless they specifically call close or whatever it is that's supposed to be done when the object leaves scope, it won't happen. scope is just a better way to explicitly make it happen without worrying about things like exceptions. If you want to guarantee that something will be destroyed when it leaves scope, you're going to have to go with a struct, since that's guaranteed by the language - the user of your struct can't forget it like they can with scope and classes. I like scope, but it's something that the person using the class has to remember just like they'd have to remember to call the close function or its equivalent if there were no scope modifier. It's just the nature of classes. And really, it's not all that different from C++. It's just D's way of declaring classes on the stack but with the benefit that you're not passing objects around on the stack and incurring those copying costs. - Jonathan M Davis<snip> I have two questions for you: 1) Are class destructors not good enough? If so, why?They might be good enough, I don't know. I thought they wouldn't be guaranteed to be called on scope exit, though.2) Have you looked into static destructors? I created a logging library and that worked perfect for me to ensure that if I killed the program via Ctrl-C, it would flush the output buffer and close the file handle.I'm not really looking at unexpected terminationsPersonally, I really like the use of "scope" vs. RAII. Keeping the code to clean up a file handle or the like right by where it's declared seems very sensible to me.I like scope too. But it's still something the user has to remember.Casey
Feb 28 2010
On 02/28/2010 02:16 PM, Ellery Newcomer wrote:RAII!(Foo) foo(int i){ return RAII!(Foo)(new Foo(i)); }One thing I'm curious about in particular is how to ensure that the destructor of Foo doesn't get called inside foo. With this particular code, it evidently doesn't, but I can't tell if it's a guarantee of the language, or a side effect of some optimization.
Feb 28 2010
On 02/28/2010 09:16 PM, Ellery Newcomer wrote:Hello The impetus:Maybe something along the lines of this struct RAII(T : Object){ T b; alias b this; void delegate(T) destroyer; this (T i, void delegate(T) called_at_exit){ b = i; destroyer = called_at_exit; } ~this(){ if (b) destroyer(b); } T extract(){ T t = b; b = null; return t; } } class Foo { this() { } void close() { writeln("closed."); } } RAII!Foo foo() { return RAII!Foo(new Foo, (Foo f) {f.close;}); } for a slightly more generalized RAII struct, which works on classes only.I agree, except I more and more think that scope classes were a mistake. Structs with destructors are a much better solution, and wrapping a class inside a struct would give it RAII semantics.The problem: In designing a library (or rather touching up someone else's library), I have a class (interface?) Foo, and it has a method close (actually several methods). Almost invariably (although conceivably not always), usage involves manipulating Foo, and then remembering to call close. That last part is obnoxious. One obtains a Foo from function foo. What I'd like is for the result of foo to have RAII semantics by default, with the possibility of nixing it if the user actually cares. I want to take the burden of remembering that stupid close off the user. For example: void bar(){ auto a = foo(); a.doStuff(); } has an implicit call to close after doStuff. The impetus suggests I can do this by wrapping Foo inside a struct, but I'm not so sure how well this would work out. Also note that close can fail in a number of ways relating to file io. So I wrote up a simple prototype. Thoughts? import std.stdio; class Foo{ int k; this(int i){ writefln("bin %d",k); k = i; } void doStuff(){ writefln("do stuff %d",k); } void close(){ writefln("close %d",k); } } struct RAII(T){ T b; bool extracted = false; alias b this; this (T i){ assert(i !is null); writeln("in"); b = i; } ~this(){ writeln("~this"); if(!extracted) b.close(); } T extract(){ assert(!extracted); T t = b; b = null; extracted = true; return t; } } RAII!(Foo) foo(int i){ return RAII!(Foo)(new Foo(i)); } void main(){ auto a = foo(1); auto b = foo(2).extract(); a.doStuff(); b.doStuff(); }
Feb 28 2010