Recommended way to do RAII cleanly

torhu no at spam.invalid
Mon Jul 12 13:17:04 PDT 2010


On 12.07.2010 08:25, Jonathan M Davis wrote:
> Okay. There are cases where you want a constructor to do something when the
> class/struct is created, and you want the destructor to do something when the
> class/struct goes out of scope. A classic example would be an autolock for a
> mutex. Another would be the hourglass in MFC - it's displayed when the object is
> created and disappears when the object is destroyed (so all you have to do is
> declare the object it at the beggining of the function and it automatically is
> displayed and then disappears). This is classic RAII.
>
> Obviously, because classes are reference types with infinite lifetime while
> structs are value types with their lifetime restricted to their scope, structs
> would be the better choice for RAII. I have noticed a bit of a snag however:
> structs can't have default constructors.
>
> After reading TDPL, I completely understand that structs can't have default
> constructors due to how the init property works. However, the classic case where
> you want to simply declare an object and have it do what it does through RAII
> then falls apart. Ideally, you'd have something like this
>
> struct S
> {
>      this()
>      {
>          /* do something */
>      }
>
>      ~this()
>      {
>         /* undo what you did before or do whatever clean up is required for it */
>      }
> }
>
> void main()
> {
>      auto s = S();
>     /* whatever the rest of main() does */
> }
>
>
> Thanks to the lack of default constructor, you can't do that. Therefore, I see 2
> options:
>
> 1.  Create a nonsensical constructor that takes an argument of _some_ kind which
> is totally ignored.
>
> 2. Create a factory function to create the struct, and it does whatever would
> have been in the default constructor.
>
>
> Out of those two options, the second seems the best, but it seems to me that
> there have got to be more options than that. So, the question is what would be
> the best option (be it one of those or another that I haven't though of) to do
> RAII in the general case? What would be "best practice" for D when dealing with
> structs intended for RAII without any arguments to their constructor when you
> can't have a default constructor?
>
> - Jonathan M Davis

Appender in std.array has the same issue, and solves it with a static 
assert and a factory function:
http://www.dsource.org/projects/phobos/changeset/

Well, except that Appender doesn't have a destructor.

But other than that, wouldn't most structs that use RAII have a 
constructor that at least requires some kind of handle as an argument?

For you hourglass example, wouldn't you need to call two methods anyway? 
I googled for "MFC hourglass". Then it'd look like this:

BeginWaitCursor();
scope (exit) EndWaitCursor();

The Mutex in Phobos is a class, so you'd have to do basically the same 
thing. But if you only need a local mutex, you'd probably use a 
synchronized statement instead.

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.


More information about the Digitalmars-d-learn mailing list