Preventing creation of temporary RAII objects

T

tomthemighty

A simple RAII wrapper for acquiring a Thingy that has two alternative
acquire methods, acquire() and acquireAndFurtle():

class ThingyAcquirer : private boost::noncopyable
{
public:
explicit ThingyAcquirer (Thingy& thingy, bool furtle = false) :
mThingy(thingy)
{
if(furtle)
mThingy.acquireAndFurtle();
else
mThingy.acquire();
}

~ThingyAcquirer ()
{
mThingy.release();
}

private:
mThingy;
};

When creating objects like this, I (scarily frequently) find myself
typing the wrong thing. Instead of
ThingyAcquirer acquirer(someThingy);
I find my fingers typing:
ThingyAcquirer(someThingy);

This is OK because it causes a compiler error - the compiler parses it
as declaring an object called someThingy, which already exists with a
different type.

However, with the extra parameter this becomes legal C++:
ThingyAcquirer(someThingy, true);
, which compiles happily and releases the thingy immediately!

I've been caught out by this a few times, though so far only ever with
one parameter. If it ever happens with a 2-parameter constructor (such
as those on a couple of boost::thread's RAII locker classes), I'll
probably have a nasty bug on my hands.

Maybe it's just me, but I find this a really easy mistake to make. One
reason is that RAII wrappers often represent something that is
intuitively an action (acquiring a resource), which something stupid in
my subconscious interprets as a function call. Another reason is that I
am not going to be refering to this thing again (not much point - its
only public members are the constructor and destructor), so it's easy
to forget to give it a name.

It seems to me that creating a temporary RAII object is something you
would never, ever want to do, but I can't think of any way to modify
ThingyAcquirer that would make the above code illegal. Can anyone else?
 
T

tomthemighty

Heh,
s/mThingy/Thingy& mThingy/
Guess I ought to check things actually compile before posting them,
sorry...
 
R

Roland Pibinger

A simple RAII wrapper for acquiring a Thingy ....
When creating objects like this, I (scarily frequently) find myself
typing the wrong thing. Instead of
ThingyAcquirer acquirer(someThingy);
I find my fingers typing:
ThingyAcquirer(someThingy);

This is OK because it causes a compiler error - the compiler parses it
as declaring an object called someThingy, which already exists with a
different type.

Hmm, really?
However, with the extra parameter this becomes legal C++:
ThingyAcquirer(someThingy, true);
, which compiles happily and releases the thingy immediately!

I've been caught out by this a few times, though so far only ever with
one parameter. If it ever happens with a 2-parameter constructor (such
as those on a couple of boost::thread's RAII locker classes), I'll
probably have a nasty bug on my hands.

Maybe it's just me, but I find this a really easy mistake to make. One
reason is that RAII wrappers often represent something that is
intuitively an action (acquiring a resource), which something stupid in
my subconscious interprets as a function call. Another reason is that I
am not going to be refering to this thing again (not much point - its
only public members are the constructor and destructor), so it's easy
to forget to give it a name.

It seems to me that creating a temporary RAII object is something you
would never, ever want to do, but I can't think of any way to modify
ThingyAcquirer that would make the above code illegal. Can anyone else?

Declare a free function:

void ThingyAcquirer (Thingy& thingy, bool furtle = false);

and never define it. Should give a liker error.

Best wishes,
Roland Pibinger
 
?

=?ISO-8859-15?Q?Juli=E1n?= Albo

A simple RAII wrapper for acquiring a Thingy that has two alternative
acquire methods, acquire() and acquireAndFurtle():

class ThingyAcquirer : private boost::noncopyable
{
public:
explicit ThingyAcquirer (Thingy& thingy, bool furtle = false) :
mThingy(thingy)
{
if(furtle)
mThingy.acquireAndFurtle();
else
mThingy.acquire();
}

~ThingyAcquirer ()
{
mThingy.release();
}

private:
mThingy;
};

I suggest to not do that, better something like this:

class ThingyAcquirerBase
{
protected:
ThingyAcquirerBase (Thingy & thingy) :
mThingy (tingy)
{ }
~ThingyAcquirer ()
{
mThingy.release();
}
void acquire ()
{
mThingy.acquire ();
}
void acquireAndFurtle ()
{
mThingy.acquireAndFurtle ();
}
private:
mThingy;
};

class ThingyAcquirer
{
public:
explicit ThingyAcquirer (Thingy & thingy) :
ThingyAcquirerBase (tingy)
{
acquire ();
}
};

class ThingyAcquirerFurtler
{
public:
explicit ThingyAcquirerFurtler (Thingy & thingy) :
ThingyAcquirerBase (tingy)
{
acquireAndFurtle ();
}
};
 
T

tomthemighty

....
Hmm, really?

Indeed. It came as a surprise to me. The relevant part of the standard
is 6.8 - Ambiguity Resolution.

Declare a free function:

void ThingyAcquirer (Thingy& thingy, bool furtle = false);

and never define it. Should give a liker error.

Nice idea. The downside is that in order to use it the ThingyAcquirer
you'll have to use an elaborated type specifier (9.1 para 2):

class ThingyAcquirer acquirer(someThingy, true);

I think this is a small price to pay for avoiding this pitfall
(pratfall?). Where there are 3rd party RAII classes, like the boost
ones, I could have a #ifdef that declares these functions for a
make-sure-I-haven't-made-that-stupid-mistake-with-the-RAII-objects
build. Cool.

Thanks for your help.

Tom
 
T

tomthemighty

That would be much better here. Unfortunately it isn't possible if the
RAII class is third-party, or if the underlying acquire() method takes
two or more parameters.

Thanks for your help.

Tom
 
R

Roland Pibinger

Nice idea. The downside is that in order to use it the ThingyAcquirer
you'll have to use an elaborated type specifier (9.1 para 2):

class ThingyAcquirer acquirer(someThingy, true);

Actually, that's not really elegant. It's unfortunate that the type
specifier is needed even though the use is not ambigous. Maybe you
should write a macro that creates the object for you??

Best wishes,
Roland Pibinger
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top