C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

raii vs lazy acquisition
Goto page 1, 2  Next
 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
John Dill
Guest





PostPosted: Tue Oct 07, 2003 1:42 am    Post subject: raii vs lazy acquisition Reply with quote



I have object wrapper around a file stream object and was wondering
what the differences are between raii and lazy acquisition, mostly in
the area of exception handling. The classic raii approach to file
management is

class FileResource
{
public:
FileResource( const std::string& name, std::ios::open_mode )
{
// open file here
...
}

~FileResource()
{
// if file open, close it
...
}
}

I have a slightly different scheme where I call this a lazy
acquisition approach, where I have two members Acquire and Release
which open and close the file respectively.

class FileResource
{
public:
FileResource( const std::string& name, std::ios::open_mode mode )
{
// just init variables
}

~FileResource()
{
// if file open, close it, through test and call to Release
...
}

bool Acquire()
{
// opens the file
...
}

bool Release()
{
// closes the file
...
}
}

My question is what are the exception safety differences between raii
and lazy acquisition. Both have automatic cleanup in their
destructors, so if an exception occurs (assuming construction is
good), they can clean themselves up. The benefit of lazy acquisition
is storing the file information, but not having the file open if it is
not used. Are there any problems with the lazy acquisition method wrt
exception safety in theory?

Many thanks.
John

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Aaron Bentley
Guest





PostPosted: Tue Oct 07, 2003 3:29 pm    Post subject: Re: raii vs lazy acquisition Reply with quote



Whenever I have used lazy aquisition, I have regretted it. One problem
with lazy acquisiition is that it can lead you down the garden path,
thinking that a resource has been correctly allocated, when in fact, it
cannot be.

But the main problem I've found is with const-correctness. Either you
wind up with all your class members as mutable, or you do evil things
with const casts.

So far, the safest approach I've found to lazy aquisition is
smart-pointer based. That is, the "pointer" takes care of constructing
an instance when * or -> is called.

So the RAII lazy initialization would go

class FileResourcePtr
{
private:
FileResource *fr;
string name;
std::ios::open_mode open_mode;
void init()
{
if (fr==NULL) fr=new FileResource(name, open_mode);
}
public:
FileResourcePtr(const std::string a_name, std::ios::open_mode
a_open_mode)
:
fr(NULL), name(a_name), open_mode(a_open_mode)
{}
~FileResourcePtr()
{
delete fr;
}
FileResource &operator*()
{
init();
return *fr;
}
FileResource *operator ->()
{
init();
return fr;
}
};

John Dill wrote:
Quote:
I have object wrapper around a file stream object and was wondering
what the differences are between raii and lazy acquisition, mostly in
the area of exception handling. The classic raii approach to file
management is

class FileResource
{
public:
FileResource( const std::string& name, std::ios::open_mode )
{
// open file here
...
}

~FileResource()
{
// if file open, close it
...
}
}

I have a slightly different scheme where I call this a lazy
acquisition approach, where I have two members Acquire and Release
which open and close the file respectively.

class FileResource
{
public:
FileResource( const std::string& name, std::ios::open_mode mode )
{
// just init variables
}

~FileResource()
{
// if file open, close it, through test and call to Release
...
}

bool Acquire()
{
// opens the file
...
}

bool Release()
{
// closes the file
...
}
}

My question is what are the exception safety differences between raii
and lazy acquisition. Both have automatic cleanup in their
destructors, so if an exception occurs (assuming construction is
good), they can clean themselves up. The benefit of lazy acquisition
is storing the file information, but not having the file open if it is
not used. Are there any problems with the lazy acquisition method wrt
exception safety in theory?

--
Aaron Bentley
www.aaronbentley.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Christoph Schulz
Guest





PostPosted: Tue Oct 07, 2003 6:15 pm    Post subject: Re: raii vs lazy acquisition Reply with quote



John Dill <john-dill (AT) uiowa (DOT) edu> schrieb:

Quote:
I have object wrapper around a file stream object and was wondering
what the differences are between raii and lazy acquisition, mostly in
the area of exception handling. The classic raii approach to file
management is

[snip RAII example]

I have a slightly different scheme where I call this a lazy
acquisition approach, where I have two members Acquire and Release
which open and close the file respectively.

class FileResource
{
public:
FileResource( const std::string& name, std::ios::open_mode mode )
{
// just init variables
}

~FileResource()
{
// if file open, close it, through test and call to Release
...
}

bool Acquire()
{
// opens the file
...
}

bool Release()
{
// closes the file
...
}
}

My question is what are the exception safety differences between raii
and lazy acquisition. Both have automatic cleanup in their
destructors, so if an exception occurs (assuming construction is
good), they can clean themselves up. The benefit of lazy acquisition
is storing the file information, but not having the file open if it is
not used. Are there any problems with the lazy acquisition method wrt
exception safety in theory?


I don't see any problems related to exception safety. I do, however, see
the possibility that the user of the "lazy acquisition" idiom *may*
forget
to invoke Acquire() before accessing (probably existing) other methods,
resulting in using an object which has not initialized itself properly.

The advantage of the RAII idiom in this case is that you cannot forget
the initialization because it's done (completely) in the constructor,
which
is always called. If the initialization succeeds, you are guaranteed to
have
a fully initialized object which can be dealt with. If the
initialization
fails, an exception will be thrown, so you won't even have an object
when
catching the exception. So you'll never end up with accessing a partly
initialized object when using RAII.

To sum up, the "lazy acquisition" idiom simply requires the programmer
to be more careful about object initialization.

Best regards,
Christoph



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
David B. Held
Guest





PostPosted: Tue Oct 07, 2003 6:22 pm    Post subject: Re: raii vs lazy acquisition Reply with quote

"John Dill" <john-dill (AT) uiowa (DOT) edu> wrote

Quote:
[...]
Are there any problems with the lazy acquisition method wrt
exception safety in theory?

As long as your interface consistently provides the basic
guarantee or better, I'd say no.

Dave



---
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system ([url]http://www.grisoft.com)[/url].
Version: 6.0.521 / Virus Database: 319 - Release Date: 9/23/2003



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Hovik Melikyan
Guest





PostPosted: Tue Oct 07, 2003 6:27 pm    Post subject: Re: raii vs lazy acquisition Reply with quote


"John Dill" <john-dill (AT) uiowa (DOT) edu> wrote

Quote:

[...]
My question is what are the exception safety differences between raii
and lazy acquisition. Both have automatic cleanup in their
destructors, so if an exception occurs (assuming construction is
good), they can clean themselves up. The benefit of lazy acquisition
is storing the file information, but not having the file open if it is
not used. Are there any problems with the lazy acquisition method wrt
exception safety in theory?


To my understanding, "lazy acquisition" is more flexibile wrt exception
handling. The first approach (RAII) requires you to enclose your
FileResource object entirely within a try{} block, but then you are unable
to obtain the state of your file resource in catch{} in case you want to
give a good, descriptive error message or you want to do some kind of error
recovery on that resource.

With lazy acq. you declare the object outside the try{}catch{} block thus
ensuring the object is available in catch{}.

{
FileResource f("filename");
try
{
Acquire();
// ...
Release();
}
catch(excpetion)
{
f.showerror();
}
}

There's only one drawback with this approach: like you said, you have to
make sure the destructor never throws exceptions (e.g. by simply catching
and discarding them in the dtor, or by calling a `silent' private method
SysRelease()). Note that the only reason your destructor may be forced to
call [Sys]Release() is if the program did not reach the explicit call to
Release() in the try{} block, which means an error occurred along the way
and that it's been handled in catch{} already.

In short, it is better to have explicit methods for manipulating external
resources and to have a destructor that can silently clean things up if
necessary. I find it safer and more practical than RAII.

Any thoughts?

--
Hovik Melikyan



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
spipyeah
Guest





PostPosted: Tue Oct 07, 2003 7:09 pm    Post subject: Re: raii vs lazy acquisition Reply with quote

Quote:
My question is what are the exception safety differences between raii
and lazy acquisition. Both have automatic cleanup in their
destructors, so if an exception occurs (assuming construction is
good), they can clean themselves up. The benefit of lazy acquisition
is storing the file information, but not having the file open if it is
not used. Are there any problems with the lazy acquisition method wrt
exception safety in theory?

I would say with RAII, your object cannot exist in an invalid state.

With lazy acquisition (as you portrayed it), you can come across an
object without knowing if its valid or not. You would need an
isValid() method to know if Open() has previously been called. But
then every single read/write method of your object would have to check
if the file has been previously opened. You could say that this is
error prone should someone else have to maintain the object later on.

Again, with RAII, if a read/write method is actually called, it means
the object was previously successfully constructed. I like it because
it's clear. The object exists, so it has been initialized.

These are just a few thoughts really... I think others will come up
with more convincing arguments.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Roland Pibinger
Guest





PostPosted: Tue Oct 07, 2003 10:36 pm    Post subject: Re: raii vs lazy acquisition Reply with quote

On 7 Oct 2003 11:29:38 -0400, Aaron Bentley
<aaron.bentley (AT) utoronto (DOT) ca> wrote:

Quote:
Whenever I have used lazy aquisition, I have regretted it. One problem
with lazy acquisiition is that it can lead you down the garden path,
thinking that a resource has been correctly allocated, when in fact, it
cannot be.

But the main problem I've found is with const-correctness. Either you
wind up with all your class members as mutable, or you do evil things
with const casts.

So far, the safest approach I've found to lazy aquisition is
smart-pointer based. That is, the "pointer" takes care of constructing
an instance when * or -> is called.

So the RAII lazy initialization would go

class FileResourcePtr
{
private:
FileResource *fr;
string name;
std::ios::open_mode open_mode;
void init()
{
if (fr==NULL) fr=new FileResource(name, open_mode);
}
public:
FileResourcePtr(const std::string a_name, std::ios::open_mode
a_open_mode)
:
fr(NULL), name(a_name), open_mode(a_open_mode)
{}
~FileResourcePtr()
{
delete fr;
}
FileResource &operator*()
{
init();
return *fr;
}
FileResource *operator ->()
{
init();
return fr;
}
};

I see two problems with FileResourcePtr:
1. close() of a file might fail (which is ignored)
2. copy constructor and operator= should be private

Best wishes,
Roland Pibinger


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
John Dill
Guest





PostPosted: Wed Oct 08, 2003 6:12 pm    Post subject: Re: raii vs lazy acquisition Reply with quote

Quote:

With lazy acq. you declare the object outside the try{}catch{} block thus
ensuring the object is available in catch{}.

{
FileResource f("filename");
try
{
Acquire();
// ...
Release();
}
catch(excpetion)
{
f.showerror();
}
}

Interesting. I've not used this type of exception catching idiom
before.

Quote:
In short, it is better to have explicit methods for manipulating external
resources and to have a destructor that can silently clean things up if
necessary. I find it safer and more practical than RAII.

Any thoughts?

I have tended to lean on the side of RAII in the past. I use it
heavily with thread synchronization. I'm exploring this lazy approach
since I have a system which initializes these resources, but the
program may not use them. There are times (not too many though) when
it seems to be beneficial to be explicit, and times when it's better
to be implicit (from my experience).

Could one use a lazy acquisition as a base to implementing RAII? I
then could let the developer choose how to use the file resource.

class FileResource
{
Acquire, Release, etc...
};

class RAII_FileResource
: private FileResource
{
RAII_FileResource( filename, openmode )
: FileResource( filename, openmode )
{ Acquire(); }

~RAII_FileResource()
{ Release(); }
};

There might be some merit to this approach.

Best,
John

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Michiel Salters
Guest





PostPosted: Wed Oct 08, 2003 10:10 pm    Post subject: Re: raii vs lazy acquisition Reply with quote

"Hovik Melikyan" <hovik (AT) melikyan (DOT) com> wrote in
news:bltpu1$gfsn4$1 (AT) ID-207516 (DOT) news.uni-berlin.de:

Quote:
To my understanding, "lazy acquisition" is more flexibile wrt
exception handling. The first approach (RAII) requires you to enclose
your FileResource object entirely within a try{} block, but then you
are unable to obtain the state of your file resource in catch{} in
case you want to give a good, descriptive error message or you want to
do some kind of error recovery on that resource.

If your object can do recovery on failed initialization, you already have
an unusual situation. Why couldn't it do that recovery in the constructor?
Probably because the object needs extra information from the caller. Why
can't the caller try to create a new object instead of trying to recover
the old object? Probably because there already was an expensive
construction.

I think this situation is very, very rare in well-designed code. First,
most objects can't be partially constructed anyway. Secondly, for those
that can, finishing them usually takes more than a few extra bits from the
caller. Finally, even when this situation exists the constructor can
usually check whether it has all information needed before doing expensive
work.

Quote:
With lazy acq. you declare the object outside the try{}catch{} block
thus ensuring the object is available in catch{}.

{
FileResource f("filename");
try
{
Acquire();
// ...
Release();
}
catch(excpetion)
{
f.showerror();
}
}

What's the gain over
catch (FileResource::exception const& fre)
{
fre.show_error()?
}
All useful information in f can be contained in the exception object, in
the most extreme case the exception could hold a copy of the object!

Quote:
There's only one drawback with this approach: like you said, you have
to make sure the destructor never throws exceptions (e.g. by simply
catching and discarding them in the dtor, or by calling a `silent'
private method SysRelease()). Note that the only reason your
destructor may be forced to call [Sys]Release() is if the program did
not reach the explicit call to Release() in the try{} block, which
means an error occurred along the way and that it's been handled in
catch{} already.

Destructors shouldn't throw when using RAII either. Some people argue that
throwing destrutors should be outlawed altogether, for exactly that reason.
I disagree with that; classes exclusively constructed by factories don't
work as RAII classed anyway and aren't subject to this rule.

In my opinion, there are two additional disadvantages: The code is longer,
and the try-catch block is more inportant. I tend to collect all my
exceptions at the subsytem interface, so I have only a few catches. In
these places the throwing objects wouldn't be available anyway. I would
have to add the extra catch just to access object.show_error, where in my
RAII design that information is sent to the single outer catch-block inside
the exeption objet.

Regards,
--
Michiel Salters

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
David Abrahams
Guest





PostPosted: Thu Oct 09, 2003 5:19 pm    Post subject: Re: raii vs lazy acquisition Reply with quote

Michiel Salters <Michiel.Salters (AT) cmg (DOT) nl> writes:

Quote:
Destructors shouldn't throw when using RAII either. Some people argue that
throwing destrutors should be outlawed altogether, for exactly that reason.
I disagree with that; classes exclusively constructed by factories don't
work as RAII classed anyway and aren't subject to this rule.

What about:

std::auto_ptr<Foo> raii(FooFactory());

??

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Aaron Bentley
Guest





PostPosted: Thu Oct 09, 2003 5:31 pm    Post subject: Re: raii vs lazy acquisition Reply with quote

Roland Pibinger wrote:

Quote:
On 7 Oct 2003 11:29:38 -0400, Aaron Bentley

I see two problems with FileResourcePtr:

It was only meant as an example-- in fact, I wrote it while drunk.

Quote:
1. close() of a file might fail (which is ignored)

Closing the file is left to FileResource-- this is no worse than
standard RAII.

Quote:
2. copy constructor and operator= should be private

True. I almost never want those.

Aaron
--
Aaron Bentley
www.aaronbentley.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Hovik Melikyan
Guest





PostPosted: Thu Oct 09, 2003 8:15 pm    Post subject: Re: raii vs lazy acquisition Reply with quote


"Michiel Salters" <Michiel.Salters (AT) cmg (DOT) nl> wrote

Quote:

In my opinion, there are two additional disadvantages: The code is longer,
and the try-catch block is more inportant. I tend to collect all my
exceptions at the subsytem interface, so I have only a few catches. In
these places the throwing objects wouldn't be available anyway. I would
have to add the extra catch just to access object.show_error, where in my
RAII design that information is sent to the single outer catch-block
inside the exeption objet.


Error recovery on external objects (files, sockets, DB connections, GUI and
other system objects) may or may not be so trivial that you can pass all
necessary information through an exception object. You may want, for
instance, to restore integrity of a file after discovering an error; to
recover a socket by sending special protocol-level commands, by shutting
down the socket 'gracefully' or whatever; you may want to roll back the last
transaction on a DB, etc.

OTOH, objects that are entirely defined in your class and there are no
external associations (i.e. when the functionality *is* the class and vice
versa) can be as C++ish (RAII) as you like.

John Dill in the other posting suggests that you create a RAII-wrapper
around a `lazy' class so that you have a choice of using either one.

class AutoFileResource
{
FileResource fr;
public:
AutoFileResource(name) { fr.Acquire(name); }
~AutoFileResource() { fr.Release(); }
};

Templates may also be appropriate for this.

--
Hovik Melikyan



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
John Dill
Guest





PostPosted: Thu Oct 09, 2003 8:45 pm    Post subject: Re: raii vs lazy acquisition Reply with quote

Quote:
I think this situation is very, very rare in well-designed code. First,
most objects can't be partially constructed anyway. Secondly, for those
that can, finishing them usually takes more than a few extra bits from the
caller. Finally, even when this situation exists the constructor can
usually check whether it has all information needed before doing expensive
work.

The context that brought this up with in looking at a filtering
streambuf which could be used to read multiple files. (See "wrapping
a stream interface" post and Kanze's filtering streambuf papers). I
was considering the difference between

1. Having a filtering streambuf which opens filenames in sequence from
a vector of filenames internally in the filtering streambuf through an
internally allocated filebuf.

or

2. A list of FileResources which manage their own streambuf, and my
filtering streambuf acquires the resources, points itself to the
resource's streambuf, then closes it on end.

When I looked at the first design, there is a lifetime handling
responsibility of deallocating the internal filebuf when done with a
filebuf (present in the deleteWhenFinished flag). If I use the
second, I believe I can get rid of the lifetime management in the
interface of the filtering streambuf because each file resource
manages the file streambuf's lifetime internally. The problem with
RAII in this instance is that it just seems that keeping all these
files open is not good use of resources in this type of problem. If I
can adapt this MultipleFileInputStream to say reading in a sequence of
image slices to load a volume, the number of image files can easily be
over 100 and upwards to make up a volume. Since only one needs to be
open at a time, you waste a lot of file resources.

I am also trying to think of a filtering streambuf for output which
also maintains a set of destination resources (cout, cerr, a file,
etc...), and then through some fashion switch the destination of the
filtering streambuf to the right destination depending on some enum or
key. I think the second paradigm would be easily to manage than the
first design in this context, if I wanted to have one filtering
streambuf for a set of keys. Before I was thinking along the lines of
using a template parameter to create a separate filtering streambuf
class for each enum or key value, but that didn't appeal as much as
this if I can get it to work.

Thanks for any comments.
John

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Dave Harris
Guest





PostPosted: Thu Oct 09, 2003 8:45 pm    Post subject: Re: raii vs lazy acquisition Reply with quote

[email]hovik (AT) melikyan (DOT) com[/email] (Hovik Melikyan) wrote (abridged):
Quote:
With lazy acq. you declare the object outside the try{}catch{}
block thus ensuring the object is available in catch{}.

{
FileResource f("filename");
try
{
Acquire();
// ...
Release();
}
catch(excpetion)
{
f.showerror();
}
}

How can you be sure that the exception came from f and not some other
code in the try block?

It seems like f has 3 states. The first state is when it is newly
constructed. Presumably the only operation supported is Acquire().
The second state is when it has just thrown an exception. Presumably
the only operation supported is showerror(). The third state is at
all other times, when all other operations are supported. This is
a little bit more complicated than I would like.

-- Dave Harris, Nottingham, UK

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Aaron Bentley
Guest





PostPosted: Fri Oct 10, 2003 4:17 pm    Post subject: Re: raii vs lazy acquisition Reply with quote

Hovik Melikyan wrote:

Quote:
John Dill in the other posting suggests that you create a RAII-wrapper
around a `lazy' class so that you have a choice of using either one.

To me, this seems backwards. The RAII object seems like a simpler idea,
so I feel that the lazy class should wrap around an RAII. Lazy
allocation requires mutable members. When there's only one member, it
doesn't matter, but otherwise, a lazy wrapper saves you from making all
your members mutable.

As a bonus, you can treat a lazy wrapper like a smart pointer to the
RAII object. You can't do that with an RAII wrapper, because the lazy
version supports operations (acquire/release) that don't make sense for
the RAII version.

Quote:
class AutoFileResource
{
FileResource fr;
public:
AutoFileResource(name) { fr.Acquire(name); }
~AutoFileResource() { fr.Release(); }
};

Templates may also be appropriate for this.

The lazy version can go like this:
template <R, P>
class LazyPointer
{
typedef P ParamType;
ParamType param;

typedef R RAII;
mutable RAII *raii;
/* copy construction & assignment are possible if ParamType supports
them. But make sure raii is not copied. */

public:
LazyPointer(ParamType const &p) : param(p), raii(0) {}
~LazyPointer {delete raii;}
...

RAII *operator->()
{ return raii?raii:raii=new RAII(param);}

RAII const *operator->() const
{ return raii?raii:raii=new RAII(param);}

};

Of course, you need to arrange for the RAII type to have a single-param
constructor (where the sole param may be a struct or tuple).

Aaron

--
Aaron Bentley
www.aaronbentley.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated) All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.