Recommended way to do RAII cleanly

2010-07-12 Thread Jonathan M Davis
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


Re: Recommended way to do RAII cleanly

2010-07-12 Thread Lars T. Kyllingstad
On Sun, 11 Jul 2010 23:25:32 -0700, 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?


I'd say option 2 is your best bet.  I don't know any other way to fake 
default construction.

That said, the recommended best practice for D is, if possible, to use 
scope guards:

  void doStuffWith(string resourceName)
  {
  auto resource = acquire(resourceName);
  scope(exit) release(resource);

  ... // Do stuff with resource here
  }

-Lars


Re: Recommended way to do RAII cleanly

2010-07-12 Thread Rory McGuire
On Mon, 12 Jul 2010 08:25:32 +0200, Jonathan M Davis  
jmdavisp...@gmail.com 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


Do you know about the scope storage class, or about scope classes?

{
scope tmp = new A();

// use tmp;

tmp destructor is called.
}

scope classes are similar:
http://www.digitalmars.com/d/2.0/class.html


Re: Recommended way to do RAII cleanly

2010-07-12 Thread Jonathan M Davis
On Monday 12 July 2010 00:27:11 Rory McGuire wrote:
 Do you know about the scope storage class, or about scope classes?
 
 {
   scope tmp = new A();
 
   // use tmp;
 
   tmp destructor is called.
 }
 
 scope classes are similar:
 http://www.digitalmars.com/d/2.0/class.html

Except that as I understand it, scope as a storage class is being deprecated. 
So, while it will work now, it won't later. Otherwise, given the restrictions 
on 
default constructors with structs, I'd use scope that way to solve the problem. 
If nothing else though, I would think that this situation would be a good case 
against deprecating scope as a storage class.

- Jonathan M Davis


Re: Recommended way to do RAII cleanly

2010-07-12 Thread Jonathan M Davis
On Sunday 11 July 2010 23:41:21 Lars T. Kyllingstad wrote:
 
 That said, the recommended best practice for D is, if possible, to use
 scope guards:
 
   void doStuffWith(string resourceName)
   {
   auto resource = acquire(resourceName);
   scope(exit) release(resource);
 
   ... // Do stuff with resource here
   }
 
 -Lars

Except that that's two statements and it's no longer RAII. The beauty of doing 
it entirely in the constructor and destructor is that you only need one 
statement and the whole thing takes care of itself. With scope, you have to 
worry about remembering to use scope, and even if you do, that's two statements 
instead of one. Obviously, it works, but it's not as clean or elegant.

- Jonathan M Davis


Re: Recommended way to do RAII cleanly

2010-07-12 Thread Jacob Carlborg

On 2010-07-12 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


You can use a static opCall method (if that hasn't been deprecated as 
well), like this:


struct S
{
static S opCall ()
{
S s;
// do something with s you would otherwise do in the constructor
return s;
}
}

auto s = S();

--
Jacob Carlborg


Re: Recommended way to do RAII cleanly

2010-07-12 Thread Simen kjaeraas

Jonathan M Davis jmdavisp...@gmail.com wrote:

Except that that's two statements and it's no longer RAII. The beauty of  
doing

it entirely in the constructor and destructor is that you only need one
statement and the whole thing takes care of itself. With scope, you have  
to
worry about remembering to use scope, and even if you do, that's two  
statements

instead of one. Obviously, it works, but it's not as clean or elegant.


So use an enum and string mixins:

import std.stdio;

enum bar = q{
writeln( The bar has opened. );
scope( exit ) {
writeln( The bar has closed. );
}
};

void main( ) {
writeln( Entering... );
mixin( bar );
writeln( Exiting... );
}

Output:
Entering...
The bar has opened.
Exiting...
The bar has closed.

--
Simen


Re: Recommended way to do RAII cleanly

2010-07-12 Thread torhu

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.


Re: Recommended way to do RAII cleanly

2010-07-12 Thread Jonathan M Davis
On Monday, July 12, 2010 13:17:04 torhu wrote:
 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();

I haven't used MFC recently, I'm 99% certain that there's a class in MFC that 
wraps this for you so that all you have to do is declare a local variable of 
that type, and the hourglass is visible until that variable leaves scope and is 
destroyed. If it's not MFC, then the shop that I was working at had their own 
class that did that.

 
 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.

It's not the mutex but an autolock for a mutex which would use RAII. It would 
lock the mutex when it was declared and unlock when it left scope and was 
destroyed. However, since you'd have to give it the mutex as a parameter (as 
opposed to using a default constructor), the lack of default constructor 
wouldn't be an issue. For the most part synchronized deals with the issue 
though. My one concern with synchronized is how you'd unsynchronize in the 
middle of the synchronized block if you had to (but that's a separate issue and 
a likely cause for having to use mutexes instead of synchronized).

 
 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.

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.

- Jonathan M Davis


Re: Recommended way to do RAII cleanly

2010-07-12 Thread bearophile
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


Re: Recommended way to do RAII cleanly

2010-07-12 Thread torhu

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.


Re: Recommended way to do RAII cleanly

2010-07-12 Thread Jonathan M Davis
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

2010-07-12 Thread bearophile
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

2010-07-12 Thread Jonathan M Davis
On Monday, July 12, 2010 18:15:04 Nick Sabalausky wrote:
 torhu n...@spam.invalid 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