WTB: Finally Keyword
March 28th, 2007
A little while ago I wrote about my hatred of some of C++’s features. This time I’m going to add to it with a bit of praise for C++, and then some more hate. Alright, lot more hate.
Basically, it all comes down to the RAII thing much of C++’s robustness is built around. In most cases it’s nice. Really nice. To use, that is. It’s very handy to just know that when an object goes out of scope (however that may happen) it will be properly destroyed - along with anything it manages. Isn’t that neat?
Well of course! And then again no.
The irritating bit is this: every C++ compiler must be able to generate code that
mimics the try-finally construct used in other languages - yet, despite having try, it has no finally keyword.
The typical response to this is that “C++ doesn’t need finally since it already
has object destructors - finally would be redundant”, which strikes me as completely
stupid.
Suppose I’ve got a C API that’s used in one little function, somewhere in the bowels
of my application. This API has some simple rules: if begin_widget() succeeds, I need to
call end_widget(), and if alloc_widget_bits() succeeds I need to call free_widget_bits()
on its return value. In a language that includes a finally keyword that looks something
like this:
void UseCrazyCApi( void )
{
if( !begin_widget() )
throw error();
try
{
//call stuff that might throw
widget_bits_t *bits = alloc_widget_bits();
if( !bits )
throw error();
try
{
//do stuff with bits, some of which might throw
}
finally
{
free_widget_bits( bits );
}
}
finally
{
end_widget();
}
}
No, it’s not very pretty but it’s the only place this particular API is used. The only way
to write this in C++, however, is to either munge things up with try-catch (which is even
worse), or to do it “properly” with a resource-managing object, er, objects:
class WidgetBlock
{
public:
WidgetBlock( void )
{
if( !begin_widget() )
throw error();
}
~WidgetBlock( void )
{
end_widget();
}
};
class WidgetBits
{
public:
WidgetBits( void )
{
bits = alloc_widget_bits();
if( !bits )
throw error();
}
~WidgetBits( void )
{
free_widget_bits( bits );
}
widget_bits_t* operator -> ( void )
{
return bits;
}
//this or should I allow implicit conversion? hrm…
widget_bits_t* raw_ptr( void )
{
return bits;
}
private:
widget_bits_t *bits;
};
void UseCrazyCApi( void )
{
WidgetBlock the_block;
//call stuff that might throw
WidgetBits bits;
//do stuff with bits, some of which might throw
}
There’s a massive list of things that irritate me about this code:
I had to define and create objects when what I really wanted to say is “always call this code”.
The name
WidgetBlock, and the name of every instance thereof, both of which are completely useless (since aWidgetBlockhas no purpose except to exist over a given code block), yet neither of which can be omitted.WidgetBlockandWidgetBitsaren’t really complete resource management classes. They lack proper copy constructors, assignment operators, and possibly a privateoperator new(if it doesn’t make sense to allow them on the heap - though makingoperator newprivate is more of a hint than a guarantee in this case, as someone can always embed the object in something that can go on the heap).Of course, it doesn’t make sense to create full-blown resource wrappers for use in a single function - but now that they exist the temptation to use them somewhere else in an unsafe manner also exists. So I either have to trust myself (and my colleagues) to know better (and to remember to know better), or I have to write even more code to hide these away in some god-forsaken class/namespace where (I hope) no one will ever look. And then there’d be the big “Here be Dragons” comment too.
And then again it just feels wrong to have half-written resource wrappers.
It’s long and it obscures what’s going on.
If I were working with something like
shared_ptr< T >, which is used everywhere and is as idiomatic as plain oldT*, then the resource wrapper would actually improve readability. But this is the only place this code is used. So every time I go through this code I’m likely to ask myself the same questions (like “OK,WidgetBlockdoes thebegin_widgetthing…did I make it throw ifbegin_widgetfails?”), and each time I’m going to have to scroll through half a page of class definitions to find the answer.Debugging operations on the
bitsvariable is going to be annoying. Now that it’s in a wrapper class and the only way to get at it is via function (eitheroperator ->orraw_ptr) I end up stepping into those functions any time I hitF11on a line that tries to dereference or otherwise get at the pointer.
Now, if I were using this API all over the place, yes, it would make sense to create, debug, and use a set of resource management classes like those above. Templates might even take care of some of the work (depending on how similar the various parts of this API are) and it would be far less bug-prone to work with. But the point is that this isn’t the case here (or in many other bits of code).
Granted, this isn’t representative of most C++ code, but it does happen. And this is one case where the language imposes a rather severe limitation on how code can be structured (something it strives to avoid almost everywhere else).
</rant>
Sorry, comments are closed for this article.