Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there cases where a "finally" construct would be useful in C++?

Tags:

c++

raii

finally

Bjarne Stroustrup writes in his C++ Style and Technique FAQ, emphasis mine:

Because C++ supports an alternative that is almost always better: The "resource acquisition is initialization" technique (TC++PL3 section 14.4). The basic idea is to represent a resource by a local object, so that the local object's destructor will release the resource. That way, the programmer cannot forget to release the resource. For example:

class File_handle {
    FILE* p;
public:
    File_handle(const char* n, const char* a)
        { p = fopen(n,a); if (p==0) throw Open_error(errno); }
    File_handle(FILE* pp)
        { p = pp; if (p==0) throw Open_error(errno); }

    ~File_handle() { fclose(p); }

    operator FILE*() { return p; }

    // ...
};

void f(const char* fn)
{
    File_handle f(fn,"rw"); // open fn for reading and writing
    // use file through f
}

In a system, we need a "resource handle" class for each resource. However, we don't have to have an "finally" clause for each acquisition of a resource. In realistic systems, there are far more resource acquisitions than kinds of resources, so the "resource acquisition is initialization" technique leads to less code than use of a "finally" construct.

Note that Bjarne writes "almost always better" and not "always better". Now for my question: What situation would a finally construct be better than using the alternative construct (RAII) in C++?

like image 637
dalle Avatar asked Dec 21 '08 22:12

dalle


1 Answers

The difference between them is that destructors emphasise reuse of the cleanup solution by associating it with the type being used, whereas try/finally emphasises one-off cleanup routines. So try/finally is more immediately convenient when you have a unique one-off cleanup requirement associated with the point of use, rather than a reusable cleanup solution that can be associated with a type you're using.

I haven't tried this (haven't downloaded a recent gcc for months), but it should be true: with the addition of lambdas to the language, C++ can now have the effective equivalent of finally, just by writing a function called try_finally. Obvious usage:

try_finally([]
{
    // attempt to do things in here, perhaps throwing...
},
[]
{
    // this always runs, even if the above block throws...
}

Of course, you have to write try_finally, but only once and then you're good to go. Lambdas enable new control structures.

Something like:

template <class TTry, class TFinally>
void try_finally(const TTry &tr, const TFinally &fi)
{
    try
    {
        tr();
    }
    catch (...)
    {
        fi();
        throw;
    }

    fi();
}

And there is no link at all between the presence of a GC and a preference for try/finally instead of destructors. C++/CLI has destructors and GC. They're orthogonal choices. Try/finally and destructors are slightly different solutions to the same problem, both deterministic, needed for non-fungible resources.

C++ function objects emphasise reusability but make one-off anonymous functions painful. By adding lambdas, anonymous code blocks are now easy to make, and this avoids C++'s traditional emphasis on "forced reusability" expressed through named types.

like image 59
Daniel Earwicker Avatar answered Sep 22 '22 03:09

Daniel Earwicker