Boost logo

Boost :

From: Philip Nash (philip.nash_at_[hidden])
Date: 2001-07-27 14:57:09


> From: Peter Dimov [mailto:pdimov_at_[hidden]]

> > just that getting into the IiRA idiom's is probably
> > beyond the scope of shared_ptr without bloating it (which you seem to
> agree
> > with from the above).
>
> Actually neither shared_resource nor shared_ptr implements the RAII idiom.
> (auto_ptr doesn't either.)

You are right, of course.

> The RAII idiom, when followed, _enforces_ the basic exception safety
> guarantee; anything that acquires resources outside a constructor
> does not:

One possible way of making it true IIRA/RAII (choose your prefered ordering)
then, would be to move the initialiser function by passing that in as a
functor too. The downside to this is that the initialiser will almost always
have one or more parameters that you will need to supply as "constructor"
arguments. These could be captured using binders but it would make the
syntax unwieldy and detract from the usability.
Having said that I can concieve of a wrapper for boost::function that would
accept the bound arguments in their contructors, create functors that take
no arguments and use binders to delegate to the original function (objects).
In the case of the initializer the internal functor would return the
"handle" object. A finalizer would return void, although it would probably
not usually be necessary.. The parameters passed as bound arguments would be
matched at compile time so the type safety would not be compromised (I've
not tried this yet, so I could be missing something... just ruminating at
present):

This would allow a syntax like this (using the FILE* example again):

        boost::scoped_resource<FILE*>f( boost::initializer( &fopen,
"my_file.txt", "rt" ), &fclose );

What about defered initialisation, though? This would entail being able to
set up the initializer and finalizer functions in the constructor, but then
provide an init member that takes the initializer arguments (this is a good
case for disallowing finalizer arguments, as this would complicate an init
member syntax). Again the init member could be written very generally using
parameter number overloading and templated types, but the types and number
would still have to match internally.
This would allow the following syntax:

        boost::scoped_resource<FILE*>f( &fopen, &fclose );

        f.init( "my_file.txt", "rt" );

... which seems quite natural.

The way I see it this still constitutes RAII, because the resource
"aquisition" is deferred to the init member (initialisation).
If the init member is implemented by constructing a temporary object and
using the swap idiom to exchange state then the exception guarantees of RAII
are retained. The destructor would not call the finalizer if the object had
not been initialised.

Hmmm, this is starting to sound feasable, but I would obviously be
interested in any comments, positive or negative (if justified).
In the meantime I will start writing a prototype.

Although I have used scoped_resource in the interface examples here,
shared_resource would probably be the more powerful implementation for
sharing resources. However it would also open up a whole new can of worms in
a multithreaded environment.
One possibility here might be to provide a typedef for locked_resource,
which would provide a proxy for the shared resource, but would lock the
resource internally. E.g.

        typedef boost::shared_resource<FILE*> SHARED_FILE;
        SHARED_FILE f( initialiser( &fopen, "my_file.txt", "rt" ), &fclose );

        // ..

        {
                SHARED_FILE::locked_resource lf( f );

                // use lf
        }

I hear that boost::threads is nearly ready, so I would almost certainly use
that.

> I'm not saying that shared_resource is not useful; it would probably work
> well as a building block. The danger is that when it's there
> people will use it in unsafe ways.

Well, I would like to try and minimise "unsafe ways" of using it. Perhaps
this type of class is more open to misuse (abuse?) than most, because it
relies on you to pass in functions with specific semantics, but as Herb
Sutter says, we need to be careful to distinguish between protecting against
Murphy as opposed to Machiavelli (although perhaps the distinction is not
quite so clear cut for generic library code).

> > How are you implementing the finalizer, btw? Does it allow for member
> > functions (useful for obj->Release() style deletions)?
>
> std::mem_fun or
>
> http://www.mmltd.net/pdimov/mem_fun.html
>
> will take care of this.

But wouldn't it have to be used internally?

[)o
IhIL..


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk