Does C++ support 'finally' blocks? (And what's this 'RAII' I keep hearing about?)

268,000

Solution 1

No, C++ does not support 'finally' blocks. The reason is that C++ instead supports RAII: "Resource Acquisition Is Initialization" -- a poor name for a really useful concept.

The idea is that an object's destructor is responsible for freeing resources. When the object has automatic storage duration, the object's destructor will be called when the block in which it was created exits -- even when that block is exited in the presence of an exception. Here is Bjarne Stroustrup's explanation of the topic.

A common use for RAII is locking a mutex:

// A class with implements RAII
class lock
{
    mutex &m_;

public:
    lock(mutex &m)
      : m_(m)
    {
        m.acquire();
    }
    ~lock()
    {
        m_.release();
    }
};

// A class which uses 'mutex' and 'lock' objects
class foo
{
    mutex mutex_; // mutex for locking 'foo' object
public:
    void bar()
    {
        lock scopeLock(mutex_); // lock object.

        foobar(); // an operation which may throw an exception

        // scopeLock will be destructed even if an exception
        // occurs, which will release the mutex and allow
        // other functions to lock the object and run.
    }
};

RAII also simplifies using objects as members of other classes. When the owning class' is destructed, the resource managed by the RAII class gets released because the destructor for the RAII-managed class gets called as a result. This means that when you use RAII for all members in a class that manage resources, you can get away with using a very simple, maybe even the default, destructor for the owner class since it doesn't need to manually manage its member resource lifetimes. (Thanks to Mike B for pointing this out.)

For those familliar with C# or VB.NET, you may recognize that RAII is similar to .NET deterministic destruction using IDisposable and 'using' statements. Indeed, the two methods are very similar. The main difference is that RAII will deterministically release any type of resource -- including memory. When implementing IDisposable in .NET (even the .NET language C++/CLI), resources will be deterministically released except for memory. In .NET, memory is not deterministically released; memory is only released during garbage collection cycles.

 

† Some people believe that "Destruction is Resource Relinquishment" is a more accurate name for the RAII idiom.

Solution 2

In C++ the finally is NOT required because of RAII.

RAII moves the responsibility of exception safety from the user of the object to the designer (and implementer) of the object. I would argue this is the correct place as you then only need to get exception safety correct once (in the design/implementation). By using finally you need to get exception safety correct every time you use an object.

Also IMO the code looks neater (see below).

Example:

A database object. To make sure the DB connection is used it must be opened and closed. By using RAII this can be done in the constructor/destructor.

C++ Like RAII

void someFunc()
{
    DB    db("DBDesciptionString");
    // Use the db object.

} // db goes out of scope and destructor closes the connection.
  // This happens even in the presence of exceptions.

The use of RAII makes using a DB object correctly very easy. The DB object will correctly close itself by the use of a destructor no matter how we try and abuse it.

Java Like Finally

void someFunc()
{
    DB      db = new DB("DBDesciptionString");
    try
    {
        // Use the db object.
    }
    finally
    {
        // Can not rely on finaliser.
        // So we must explicitly close the connection.
        try
        {
            db.close();
        }
        catch(Throwable e)
        {
           /* Ignore */
           // Make sure not to throw exception if one is already propagating.
        }
    }
}

When using finally the correct use of the object is delegated to the user of the object. i.e. It is the responsibility of the object user to correctly to explicitly close the DB connection. Now you could argue that this can be done in the finaliser, but resources may have limited availability or other constraints and thus you generally do want to control the release of the object and not rely on the non deterministic behavior of the garbage collector.

Also this is a simple example.
When you have multiple resources that need to be released the code can get complicated.

A more detailed analysis can be found here: http://accu.org/index.php/journals/236

Solution 3

RAII is usually better, but you can have easily the finally semantics in C++. Using a tiny amount of code.

Besides, the C++ Core Guidelines give finally.

Here is a link to the GSL Microsoft implementation and a link to the Martin Moene implementation

Bjarne Stroustrup multiple times said that everything that is in the GSL it meant to go in the standard eventually. So it should be a future-proof way to use finally.

You can easily implement yourself if you want though, continue reading.

In C++11 RAII and lambdas allows to make a general finally:

namespace detail { //adapt to your "private" namespace
template <typename F>
struct FinalAction {
    FinalAction(F f) : clean_{f} {}
   ~FinalAction() { if(enabled_) clean_(); }
    void disable() { enabled_ = false; };
  private:
    F clean_;
    bool enabled_{true}; }; }

template <typename F>
detail::FinalAction<F> finally(F f) {
    return detail::FinalAction<F>(f); }

example of use:

#include <iostream>
int main() {
    int* a = new int;
    auto delete_a = finally([a] { delete a; std::cout << "leaving the block, deleting a!\n"; });
    std::cout << "doing something ...\n"; }

the output will be:

doing something...
leaving the block, deleting a!

Personally I used this few times to ensure to close POSIX file descriptor in a C++ program.

Having a real class that manage resources and so avoids any kind of leaks is usually better, but this finally is useful in the cases where making a class sounds like an overkill.

Besides, I like it better than other languages finally because if used naturally you write the closing code nearby the opening code (in my example the new and delete) and destruction follows construction in LIFO order as usual in C++. The only downside is that you get an auto variable you don't really use and the lambda syntax make it a little noisy (in my example in the fourth line only the word finally and the {}-block on the right are meaningful, the rest is essentially noise).

Another example:

 [...]
 auto precision = std::cout.precision();
 auto set_precision_back = finally( [precision, &std::cout]() { std::cout << std::setprecision(precision); } );
 std::cout << std::setprecision(3);

The disable member is useful if the finally has to be called only in case of failure. For example, you have to copy an object in three different containers, you can setup the finally to undo each copy and disable after all copies are successful. Doing so, if the destruction cannot throw, you ensure the strong guarantee.

disable example:

//strong guarantee
void copy_to_all(BIGobj const& a) {
    first_.push_back(a);
    auto undo_first_push = finally([first_&] { first_.pop_back(); });

    second_.push_back(a);
    auto undo_second_push = finally([second_&] { second_.pop_back(); });

    third_.push_back(a);
    //no necessary, put just to make easier to add containers in the future
    auto undo_third_push = finally([third_&] { third_.pop_back(); });

    undo_first_push.disable();
    undo_second_push.disable();
    undo_third_push.disable(); }

If you cannot use C++11 you can still have finally, but the code becomes a bit more long winded. Just define a struct with only a constructor and destructor, the constructor take references to anything needed and the destructor does the actions you need. This is basically what the lambda does, done manually.

#include <iostream>
int main() {
    int* a = new int;

    struct Delete_a_t {
        Delete_a_t(int* p) : p_(p) {}
       ~Delete_a_t() { delete p_; std::cout << "leaving the block, deleting a!\n"; }
        int* p_;
    } delete_a(a);

    std::cout << "doing something ...\n"; }

Hopefully you can use C++11, this code is more to show how the "C++ does not support finally" has been nonsense since the very first weeks of C++, it was possible to write this kind of code even before C++ got its name.

Solution 4

Beyond making clean up easy with stack-based objects, RAII is also useful because the same 'automatic' clean up occurs when the object is a member of another class. When the owning class is destructed, the resource managed by the RAII class gets cleaned up because the dtor for that class gets called as a result.

This means that when you reach RAII nirvana and all members in a class use RAII (like smart pointers), you can get away with a very simple (maybe even default) dtor for the owner class since it doesn't need to manually manage its member resource lifetimes.

Solution 5

why is it that even managed languages provide a finally-block despite resources being deallocated automatically by the garbage collector anyway?

Actually, languages based on Garbage collectors need "finally" more. A garbage collector does not destroy your objects in a timely manner, so it can not be relied upon to clean up non-memory related issues correctly.

In terms of dynamically-allocated data, many would argue that you should be using smart-pointers.

However...

RAII moves the responsibility of exception safety from the user of the object to the designer

Sadly this is its own downfall. Old C programming habits die hard. When you're using a library written in C or a very C style, RAII won't have been used. Short of re-writing the entire API front-end, that's just what you have to work with. Then the lack of "finally" really bites.

Share:
268,000

Related videos on Youtube

Kevin
Author by

Kevin

Updated on November 07, 2020

Comments

  • Kevin
    Kevin over 3 years

    Does C++ support 'finally' blocks?

    What is the RAII idiom?

    What is the difference between C++'s RAII idiom and C#'s 'using' statement?

  • Kevin
    Kevin over 15 years
    RAII is stuck -- there's really no changing it. Trying to do so would be foolish. However, you have to admit though that "Resource Acquisition Is Initialization" is still a pretty poor name.
  • gbjbaanb
    gbjbaanb over 15 years
    bear in mind that's not really C++ exceptions, but SEH ones. You can use both in MS C++ code. SEH is an OS exception handler that is the way VB, .NET implement exceptions.
  • gbjbaanb
    gbjbaanb over 15 years
    and you can use SetUnhandledExceptionHandler to create a 'global' un-catched exception handler - for SEH exceptions.
  • Edward Kmett
    Edward Kmett about 14 years
    Cute idiom, but its not quite the same. returning in the try block or catch won't pass through your 'finally:' code.
  • Myto
    Myto almost 14 years
    Managed languages need finally-blocks precisely because only one kind of resource is automatically managed: memory. RAII means that all resources can be handled in the same way, so no need for finally. If you actually used RAII in your example (by using smart pointers in your list instead of naked ones), the code would be simpler than your "finally"-example. And even simpler if you don't check the return value of new - checking it is pretty much pointless.
  • Hasturkun
    Hasturkun almost 14 years
    new doesn't return NULL, it throws an exception instead
  • Ankit Roy
    Ankit Roy almost 14 years
    You raise an important question, but it does have 2 possible answers. One is that given by Myto -- use smart pointers for all dynamic allocations. The other is to use standard containers, which always destroy their contents upon destruction. Either way, every allocated object is ultimately owned by a statically allocated object which automatically frees it upon destruction. It's a real shame that these better solutions are hard for programmers to discover because of the high visibility of plain pointers and arrays.
  • James Johnston
    James Johnston over 12 years
    Exactly... RAII seems nice from an ideal perspective. But I have to work with conventional C APIs all the time (like C-style functions in Win32 API...). It's very common to acquire a resource that returns some kind of a HANDLE, which then requires some function like CloseHandle(HANDLE) to clean up. Using try ... finally is a nice way of dealing with possible exceptions. (Thankfully, it looks like shared_ptr with custom deleters and C++11 lambdas should provide some RAII-based relief that doesn't require writing entire classes to wrap some API I only use in one place.).
  • u0b34a0f6ae
    u0b34a0f6ae over 12 years
    C++11 improves this and includes std::shared_ptr and std::unique_ptr directly in the stdlib.
  • Mark Ransom
    Mark Ransom over 12 years
    @JamesJohnston, it's very easy to write a wrapper class that holds any kind of handle and provides RAII mechanics. ATL provides a bunch of them for example. It seems that you consider this to be too much trouble but I disagree, they're very small and easy to write.
  • Philip Couling
    Philip Couling over 12 years
    Simple yes, small no. Size is dependent on the complexity of the library you're working with.
  • Sebastian Mach
    Sebastian Mach almost 12 years
    @Kevin: During usage, RAII is really not similar to using: You can't span a using-statement over your class' initializer/finalizer. You have to be explicit and not forget the using-statement. Nesting gets horrible. The usecases for using vs. RAII are basically nought: scoped locks, vectors, strings, shared pointers, transactions, and all those without the need to manually write using(). In the way you present it, using is a poor defense before RAII, and I realise many .net-devs try hard to convince themselves that RAII is similar, but in their heart they (should) know it is not.
  • ebasconp
    ebasconp over 11 years
    void DoStuff(const vector<string>& input) { list<shared_ptr<Foo>> myList; for (int i = 0; i < input.size(); ++i) { auto tmp = make_shared<Foo>(input[i]); myList.push_back(tmp); } DoSomething(myList); }
  • supercat
    supercat over 11 years
    @MarkRansom: Is there any mechanism via which RAII can do something intelligent if an exception occurs during cleanup while another exception is pending? In systems with try/finally, it's possible--though awkward--to arrange things so that the pending exception and the exception that occurred during cleanup both get stored in a new CleanupFailedException. Is there any plausible way to achieve such a result using RAII?
  • Mark Ransom
    Mark Ransom over 11 years
    @supercat, it's even worse in C++, an exception generated during exception processing can shut things down entirely. See c2.com/cgi/wiki?BewareOfExceptionsInTheDestructor
  • supercat
    supercat over 11 years
    @MarkRansom: I'm somewhat ambivalent as to whether the Java/.net approach of junking the existing instruction or the C++ approach of forcing a system meltdown is worse. Frankly, I consider both approaches pretty horrid. An advantage of finally, though, is that if the rest of the try block is properly written, the code within finally can know whether an exception is pending. Of course, if a language could simply provide a finally Exception ex construct with any pending exception (null if none) stored in ex, even that scenario would be much cleaner.
  • supercat
    supercat over 11 years
    @couling: Dispose has nothing to do with the garbage collector, but is rather the deterministic way of notifying objects their services are no longer required. Having an explicit "cleanup on success" call, and having the default cleanup method assume it's cleaning up for an error if the "success" part didn't run first is somewhat workable, but it's icky since accidental omission of the "success" part cannot safely trigger an exception (which would otherwise be the normal method of notifying someone that a problem exists).
  • supercat
    supercat over 11 years
    @couling: For resources which can implement consistent "rollback if not committed" semantics, it would probably be reasonable to have the default cleanup method perform a rollback of any non-committed transactions. If user code accidentally forgets a commit, the effects will be obvious. It's far less clear, though, what one should do for things like general-purpose files, though, where the normal semantics of Dispose would be expected to behave essentially the same as Close. If Dispose can't throw exceptions when things go wrong, it should not be usable as a normal "success" cleanup...
  • supercat
    supercat over 11 years
    @couling: ...but it's not exactly clear what else it should do. For special-purpose files, one could have the first write operation which modifies a file set a "dirty" flag in the file which is only cleared by a "proper" Close, but such an approach requires that the class implementing it know about how the dirty flag, etc. will work (which could be complicated if multiple applications can access different parts of the file at once).
  • Philip Couling
    Philip Couling over 11 years
    @supercat oops, meant finalize which has everything to do with the GC. Was thinking of the wrong language ... see rewrite:
  • Philip Couling
    Philip Couling over 11 years
    @supercat Its interesting how the different idioms make it difficult to compare. the c++ example is (in my opinion) comparable to throwing a RuntimeException on finalize() ; effectively trying to crash the garbage collector. Good choice here is to melt down. In java/.net, I find that it is best to have a separate "cleanup on error" to "cleanup on success" where I rollback vs commit and ignore(log only) vs throw exceptions. As a result I now use finally less and less. It's my belief that on no occasion should a developer allow that "nested" exception to affect program flow.
  • Philip Couling
    Philip Couling over 11 years
    @supercat I understand that writing the same code twice in this way seems "icky" but my experience is that in practise, the "on success" and "on error" code blocks subtly differ so frequently that I rarely get to use finally without a number of if statements. Of these differences, exception handling is the most frequent.
  • supercat
    supercat over 11 years
    @couling: There are many cases where a program will call a SomeObject.DoSomething() method and want to know whether it (1) succeeded, (2) failed with no side-effects, (3) failed with side-effects the caller is prepared to cope with, or (4) failed with side-effects the caller cannot cope with. Only the caller is going to know what situations it can and cannot cope with; what the caller needs is a way of knowing what the situation is. It's too bad there's no standard mechanism for supplying the most important information about an exception.
  • Mark Lakata
    Mark Lakata over 11 years
    It's worth keeping this wrong answer (with 0 rating), since Edward Kmett brings up a very important distinction.
  • Warren  P
    Warren P over 11 years
    This leaves you stuck when you have something to clean up that doesn't match any C++ object's lifetime. I guess you end up with Lifetime Equals C++ Class Liftime Or Else It Gets Ugly (LECCLEOEIGU?).
  • Ben Voigt
    Ben Voigt about 11 years
    The reason your example is so horrid looking isn't because RAII is flawed, rather it is because you failed to use it. Raw pointers are not RAII.
  • Ben Voigt
    Ben Voigt about 11 years
    Even bigger flaw (IMO): This code eats all exceptions, which finally does not do.
  • Cemafor
    Cemafor almost 11 years
    // Make sure not to throw exception if one is already propagating. It is important for C++ destructors not to throw exceptions as well for this very reason.
  • Martin York
    Martin York almost 11 years
    @Cemafor: The reason for C++ not to throw exceptions out of the destructor is different than Java. In Java it will work (you just loose the original exception). In C++ its really bad. But the point in C++ is that you only have to do it once (by the designer of the class) when he writes the destructor. In Java you have to do it at the point of usage. So it is the responsibility of the user of the class to write the same boiler plate very time.
  • paulm
    paulm almost 11 years
    SEH is horrible and also prevents C++ destructors from being called
  • ToolmakerSteve
    ToolmakerSteve over 10 years
    I'm thinking something similar to Warren: wait, what happens when you have code that should Definitely Execute, but not at the end of an object lifetime? Isn't there still a use case for "finally", and the absence of that in C++ is therefore a hole in design space? In addition, relying on Destructor means that C++ is forced to always be implemented using DETERMINISTIC memory lifetimes. No future version of C++ could use Garbage Collection instead, because everyone is relying on timely execution of destructors.
  • Jasper
    Jasper about 10 years
    @WarrenP Then you should wrap that "something" into a class. That's the entire core of RAII, and in fact it will often force you into a better design.
  • Jasper
    Jasper about 10 years
    @ToolMakerSteve The same goes for you, wrap it. It's also not a hole in design, since if you wanted to do gc in C++, you'd probably do it on raw pointers (or maybe on a specific pointer class, but then there's even less relevance), and delete always comes before a gc can kick in. In other words, it'll only influence broken(/leaking) current code where you already couldn't rely on constructors.
  • Jasper
    Jasper about 10 years
    @couling: It doesn't have to be long. One never said you had to wrap the entire library. Just have a MyResource class that initializes the handle in its constructor, releases it in its destructor and has a getHandle() class for you to use the API. It's perhaps not the nicest ever, but it's RAII for your C library and it will take ~20 lines at most (it may cause more changes in the calling code and if you think that's too wordy, overloading dereferencing may be appropriate)
  • Warren  P
    Warren P about 10 years
    @Jasper: And that's why there are so many almost entirely useless classes in every codebase. Because C++ gives you no other choice. That's called a language wart.
  • Jasper
    Jasper about 10 years
    @WarrenP No, that's called the single responsibility principle.
  • Warren  P
    Warren P about 10 years
    I always like to thing of the single responsibility of a class as having more bandwidth inside it than "paper over this language's limitations".
  • Jasper
    Jasper about 10 years
    Well, personally, I prefer it when a language decides not to do magic and instead gives you the powerful tools to do things yourself, and telling the only way to do it is in a maintainable and scalable way that solves the problem internally to the code isn't a problem to me, honestly. Look at the alternatives, Java puts it in the hands of the calling code with finally, C# gives the same method and some magic through using, scripting languages often give you similar solutions but also say that limited execution time should prevent big issues. I'd take RAII any day.
  • ToolmakerSteve
    ToolmakerSteve about 10 years
    @Jasper: Wrapping into a class is obviously sensible when (the state to be managed) is part of what a service does. However, It seems cumbersome when it is a one-off need (of the client or GUI code) as part of responding to a specific user action. For example, at one point in my client logic, I need to remember the current value of several (fields of a rendering engine - I can't change its source code), set them to needed values, and then restore them when done. To create a class for that single usage in a single routine is overkill. "Finally" would be a concise and safe solution.
  • ToolmakerSteve
    ToolmakerSteve about 10 years
    @Jasper: I deleted one of my comments -- decided adding non-deterministic GC to C++ would make an already too-complex language even more head-spinning. The real issue is that the years I programmed in C++, before Java or .Net existed, I found it unpleasant - and still do, when I need to use it. I'd love to see a re-invention of C++, starting from scratch, that was as clean as python or C#, with a class library based on RAII, smart pointers, and/or weak references: deterministic GC solutions. Yes, all that exists in C++, but a cleaner language to think in. Admittedly off-topic...
  • Jasper
    Jasper about 10 years
    @ToolmakerSteve Deleted one of my response to it before that :P, when I realized the mistake I was making. Anyway, I agree that a clean non-managed language would be great (or, perhaps there might even be a good way to mix managed and unmanaged). Even Bjarne Soustroup basically said as much (he would now rather not have the baggage of C-compatibility, but he can't change that now). I've found needing to think about ownership a good thing. Of course, you should almost always use C++ things over C-things, which doesn't always happen and wasn't always possible back when Java and didn't exist yet.
  • Devolus
    Devolus almost 10 years
    Personally I don't feel that this RAll is really good alternative for a finally block. It just means that, for proper cleanup, you have to wrap resources into artificial objects just to get the automatic cleanuo, as Warren P pointed ou tin his comment above (Language wart).
  • Jon
    Jon almost 10 years
    Noone is going to rewrite established code to make full textbook use of RAII, even if they fully understood all of it's nuances. The finally block is a useful construct that allows programmers to write shorter code that performs the required cleanup of resources, without going into "layers-of-abstraction-hell". It's a lot nicer - and more readable - to catch an exception, do something with it, perhaps rethrow it, and know that the finally will always run for you, rather than setting some flags in catch{}, freeing the resources, then checking the flags and acting on them. I hate flag soup.
  • Trinidad
    Trinidad over 9 years
    If it's a matter of being "needed", you don't need RAII either. Let's get rid of it! :-) Jokes aside, RAII is fine for a lot cases. What RAII does makes more cumbersome is cases when you want to execute some code (not resource related) even if the code above returned early. For that either you use gotos or separate it into two methods.
  • Martin York
    Martin York over 9 years
    @Trinidad: You are going to have to be a lot more specific than that. You should start a question about situations where RAII is more cumbersome and get some real feedback on the ways that you should be doing it.
  • Trinidad
    Trinidad over 9 years
    @LokiAstari Oh it's pretty simple, imagine for example I'm building a datastructure from an XML file and I want to print it out in the end of the function. The thing is my code has a bunch of returns in the loading part, so I don't have to do a lot of ifs down the road. RAII would be very cumbersome, as it would require me to create a class just to do the pretty-print, ridiculous. Either you use gotos instead of the returns, or separate the loading from the printing as different functions. I agree this perhaps it's not the best example, but it's ludicrous to think RAII=finally
  • Martin York
    Martin York over 9 years
    @Trinidad: Its not a simple as you think (as all your suggestions seem to pick the worst options possible). Which is why a question may be a better place to explore this than the comments.
  • sethobrien
    sethobrien about 9 years
    I'm glad that you mentioned the finally block might throw; it's the thing that most of the "use RAII" answers seem to ignore. To avoid having to write the finally block twice, you could do something like std::exception_ptr e; try { /*try block*/ } catch (...) { e = std::current_exception(); } /*finally block*/ if (e) std::rethrow_exception(e);
  • Jan Smrčina
    Jan Smrčina about 9 years
    @Devolus You can create a very simple template object to do such thing for whatever two methods you need to call on initialization and deinitialization. To be honest, I wouldn't be surprised if such template class was already in the STL. (But it is most likely not there (yet?))
  • Tony BenBrahim
    Tony BenBrahim almost 9 years
    Yes, because this is so much less code than <snark> mutex_.lock(); try{ foobar(); }finally{ mutex_,unlock(); } </snark> wrapper objects, templates, boy this c++11 sure is much simpler!
  • Tony BenBrahim
    Tony BenBrahim almost 9 years
    java 7+ finally: try ( DB db = new DB("DBDesciptionString")){ //use db}
  • Martin York
    Martin York almost 9 years
    @TonyBenBrahim: At least Java is moving towards C++ in small steps. But that is the adoption of C# use directive. A step but not as powerfull as RAII ;-)
  • Cubic
    Cubic almost 9 years
    @TonyBenBrahim Multiplied throughout your entire code base, applying to all resources for which you need deterministic cleanup? Yes. Yes it is.
  • Tony BenBrahim
    Tony BenBrahim almost 9 years
    @Cubic, yes I realize that now. Takes some getting used to though, coming from Java.
  • VinGarcia
    VinGarcia almost 8 years
    That's all I wanted to know! Why none of the other answers explained that a catch(...) + empty throw; works almost like a finally block? Sometimes you just need it.
  • Fabio A.
    Fabio A. almost 8 years
    Hi, I believe my answer above (stackoverflow.com/a/38701485/566849) fully satisfies your requirements.
  • Fabio A.
    Fabio A. almost 8 years
    The solution I provided in my answer (stackoverflow.com/a/38701485/566849) should allow for throwing exceptions from inside the finally block.
  • Fabio A.
    Fabio A. almost 8 years
    @MarkLakata, I updated the post with a better implementation which supports throwing exceptions and returns.
  • Mark Lakata
    Mark Lakata almost 8 years
    Looks good. You could get rid of Caveat 2 by just putting in an impossible catch(xxx) {} block at the start of the finally macro, where xxx is a bogus type just for the purposes of having at least one catch block.
  • Fabio A.
    Fabio A. almost 8 years
    @MarkLakata, I thought of that too, but that would make it impossible to use catch(...), wouldn't it?
  • Mark Lakata
    Mark Lakata almost 8 years
    I don't think so. Just make up an obscure type xxx in a private namespace that will never be used.
  • Fabio A.
    Fabio A. almost 8 years
    Gcc spits out finally.cpp:60:5: error: ‘...’ handler must be the last handler for its try block. Clang does the same. I believe the standard just forbids to have a catch(...) block in the middle.
  • Mark Lakata
    Mark Lakata almost 8 years
    I understand your point now (I missed it the first time). Maybe the c++ gods have declared this is just not meant to be :)
  • peterh
    peterh over 7 years
    Although I think your answer is very good, I would argue against the "because of RAII". I think it would be better to say: "no, but you can avoid it miss by using RAII".
  • user1633272
    user1633272 over 7 years
    There might be a possible problem: in function 'finally(F f)' it returns a object of FinalAction, so the deconstructor might be called before returning of function finally. Maybe we should use std::function instead of template F.
  • burlyearly
    burlyearly over 7 years
    This doesn't work, because the whole point of a finally block is to perform cleanup even when the code should allow an exception to leave the code block. Consider: ` try { // stuff possibly throwing "B" } catch (A & a) { } finally { // if C++ had it... // stuff that must happen, even if "B" is thrown. } // won't execute if "B" is thrown. ` IMHO, the point of exceptions is to reduce error-handling code, so catch blocks, wherever a throw might occur, is counterproductive. This is why RAII helps: if applied liberally, exceptions matter most at the top and bottom layers.
  • Justin Time - Reinstate Monica
    Justin Time - Reinstate Monica over 7 years
    @Jasper I can think of a situation (probably uncommon, but I don't know) where it would probably be cleaner to at least have try...finally available, provided it wasn't needed in multiple locations: Working directly with hardware. Suppose that a specific memory location should normally have an "all clear" flag, but the flag should be changed during certain operations. If these operations are handled by a single function, it would be cleaner to have try { uintX_t* flag = some_address; *flag = WHATEVER; /* ... */ } finally { *flag = ALL_CLEAR; },
  • Justin Time - Reinstate Monica
    Justin Time - Reinstate Monica over 7 years
    than it would to have to write a class to wrap a non-owning pointer that's only used by that one function: In this case, the class would just add a level of indirection, and make it less clear to the programmer what's going on. Now, if the flag needed to be checked in multiple locations, it would be a different story, but this particular design would be a bit cleaner without battering RAII into an awkward kludge.
  • Justin Time - Reinstate Monica
    Justin Time - Reinstate Monica over 7 years
    Generally speaking, though, RAII is definitely the cleaner alternative, IMO. I just find it strange that the language doesn't have both, even though sometimes a simple one-off may be cleaner than a class that's only used in one particular location for one particular task.
  • Justin Time - Reinstate Monica
    Justin Time - Reinstate Monica over 7 years
    Then again, I'm the type of person that prefers to have exactly the right tool for a given job instead of improvising with another one, if possible, so that likely influences my belief that both options should be available instead of just one or the other.
  • jave.web
    jave.web over 7 years
    @burlyearly altough your opinion is not holy, I get the point, but in C++ is no such thing so you have to consider this as a top layer that emulates this behavior.
  • dan04
    dan04 over 7 years
    So, if C++ destructors can't throw exceptions and don't have return values, how the hell are you supposed to report errors from them? Like if a database connection destructor needs to commit or rollback an unfinished transaction, or if a file stream destructor needs to flush a buffer, and this operation fails for whatever reason. You could solve this issue by introducing an explicit cleanup function (close/dispose/disconnect/etc.) that returns an error code, but basically this makes C++'s implicit destructor calls useless.
  • Martin York
    Martin York over 7 years
    @dan04: C++ destructors CAN throw exceptions. Its just not a goo idea usually. They just can't throw an exception when an exception is already propagating. See: stackoverflow.com/questions/130117/… and codereview.stackexchange.com/questions/540/… (read the last comment on this one).
  • NotVeryMoe
    NotVeryMoe about 7 years
    @dan04 There's another idiom for these cases. It's called a scope guard. The scope guard takes a lambda/function pointer/whatever-you-want on construction and executes it upon destruction. This allows you to define a section of code that effectively runs like a finally block, except the cleanup code can be easily disabled, changed to different scopes or injected into other places. I don't recommend using exceptions in any implicitly called code (IE destructors) at all, as it may cause exceptions to occur at unintended times.
  • anderas
    anderas about 7 years
    Note that FinalAction is basically the same as the popular ScopeGuard idiom, only with a different name.
  • kyb
    kyb almost 7 years
    Super. Very nice style. I also think that we could use not only lambda, but inheritance with virtual destructor.
  • Paolo.Bolzoni
    Paolo.Bolzoni almost 7 years
    You would lose the fact that the opening and closing code are nearby, besides if you have to write your class then just use RAII.
  • Nulano
    Nulano almost 7 years
    Is this optimization safe?
  • Paolo.Bolzoni
    Paolo.Bolzoni almost 7 years
    Nowadays it should be supported by all major compilers. What are you are worried about?
  • Nulano
    Nulano over 6 years
    @Paolo.Bolzoni Sorry for not replying sooner, I didn't get a notification for your comment. I was worried the finally block (in which I call a DLL function) would be called before the end of the scope (because the variable is unused), but have since found a question on SO that cleared my worries. I would link to it, but unfortunately, I can't find it anymore.
  • Max Barraclough
    Max Barraclough over 6 years
    @Jan It's not in the C++ standard library, but there's BOOST_SCOPE_EXIT if you can forgive the syntactic boilerplate, and that the body of your code mustn't throw. boost.org/doc/libs/1_65_1/libs/scope_exit/doc/html/scope_exi‌​t/…
  • ceztko
    ceztko about 5 years
    Criticizing the "is NOT required because of RAII": there are plenty of cases where adding ad-hoc RAII would be too much boilerplate code to add, and try-finally would just be extremely appropriate.
  • Martin York
    Martin York about 5 years
    @ceztko I would argue that try/finally uses much more bolierplate. The trouble is that try/finally needs to be done each time it is used (see example above). While RAII only has to be done once by the original author. The other problem with try/finally is you are shifting the burden of correct usage from the author to the user. Getting the user to do it correctly every time is much harder than getting the author to do it correctly once.
  • ceztko
    ceztko about 5 years
    @MartinYork I'm not saying try-finally should be always used instead of RAII. You are right that APIs should provide appropriate destructors for their structs, or provide RAII guards where appropriate (example for handling transactions). What I'm saying is that there are some circumstances in final user code where adding RAII is not the most sound instrument he may want to use, and try-finally would do the job without creating boring ad-hoc structs for RAII. In short: both would be good to have and the wise programmer would use the most appropriate in each situation.
  • user2445507
    user2445507 about 5 years
    The disable() function is kind of a wart on your otherwise clean design. If you want the finally to be only called in case of failure, then why not just use catch statement? Isn't that what it's for?
  • Paolo.Bolzoni
    Paolo.Bolzoni about 5 years
    Take my example, how would you write the code inside the catch? Inside the catch block you do not know what copy failed, first_, second_, or _ third? You can probably check somewhat, but I find it clearer like this. It might well be a matter of taste and habit.
  • smichak
    smichak over 4 years
    Would it make sense to remove the disable() method and call the function from the destructor only when called while handling an exception? ~FinalAction() { if (enabled_ && std::uncaught_exceptions() > 0) clean_(); }
  • PieterNuyts
    PieterNuyts about 4 years
    @MartinYork Sometimes the "user vs author" distinction just doesn't apply. Say at the end of my main() I want to write out a file that says ERROR or OK depending on what happened. I would do something like bool ok = false; try { /* do stuff */; ok = true; } catch (...) { ... } finally { write_my_file(ok); }. Now I have to write a class for that and I become the "author" of a class I wouldn't have needed if I were able to fix it being a "user".
  • Martin York
    Martin York about 4 years
    @PieterNuyts True. But in your way, then every-time you want to use this pattern you have to manually add all the above boiler-plate (and remember to do it correctly and test it works correctly). In my way you do it once in a class. Now you can test it once (it will forever after be correct once tested) and you can re-use the pattern simply be declaring an object in the correct scope. So if you only ever do it once then sure the finally may work out simpler. But how often do you do something once in your lifetime? There are also libraries that simply execute a lambda on destruction.
  • Martin York
    Martin York about 4 years
    @PieterNuyts bool ok = false; gsl::final_act finalAction([&ok](){write_my_file(ok);}); /* do stuff */; ok = true; Seems a lot less bolier-platey to me.
  • PieterNuyts
    PieterNuyts about 4 years
    @MartinYork, At first sight, your piece of code is about as long as mine (and you left out the try/catch block which would still be needed), so I'm not sure why you find it less boiler-platey. But anyone who wants to understand yours, would need to know or look up what gsl::final_act is and how exactly it works.
  • Martin York
    Martin York about 4 years
    @PieterNuyts The whole point is you don't need the try/catch block (or finally) as all that would be done be the appropriate destructor. The finally example I append there is a simple alternative technique to provide finally functionality if you really want to. Normally you would not but its simple to add to C++. As for knowing what final_act does: Coming to a standard near you (C++ gsl => modernescpp.com/index.php/… )
  • Martin York
    Martin York about 4 years
    @PieterNuyts Here is how I see it. More than happy to have a discussion around the subject if you want to.
  • PieterNuyts
    PieterNuyts about 4 years
    No destructor can replace the catch block, since destructors can't catch exceptions thrown from outside them. So you can drop the finally block, but you still need try/catch (assuming you want to catch and handle the exception of course).
  • PieterNuyts
    PieterNuyts about 4 years
    You're right that I neglected to catch any exceptions inside the finally block. So then your code is perhaps slightly shorter. Still, just because some function is or becomes part of a standard, that doesn't mean people automatically know its exact name and signature by heart. Granted, you also need to know what finally means but it seems much easier to remember, at least to me. Maybe I'm just not familiar enough with modern C++ constructs but the [&ok]() part also requires a lot of thinking from my part...
  • Prasanth Kumar
    Prasanth Kumar about 2 years
    @WarrenP I included your suggested acronym, along with others, in this answer: stackoverflow.com/a/1846812/96780