67

Exception handling in C++ is limited to try/throw/catch. Unlike Object Pascal, Java, C# and Python, even in C++ 11, the finally construct has not been implemented.

I have seen an awful lot of C++ literature discussing "exception safe code". Lippman writes that exception safe code is an important but advanced, difficult topic, beyond the scope of his Primer - which seems to imply that safe code is not fundamental to C++. Herb Sutter devotes 10 chapters to the topic in his Exceptional C++ !

Yet it seems to me that many of the problems encountered when attempting to write "exception safe code" could be quite well solved if the finally construct was implemented, allowing the programmer to ensure that even in the event of an exception, the program can be restored to a safe, stable, leak-free state, close to the point of allocation of resources and potentially problematic code. As a very experienced Delphi and C# programmer I use try.. finally blocks quite extensively in my code, as do most programmers in these languages.

Considering all the 'bells and whistles' implemented in C++ 11, I was astonished to find that 'finally' was still not there.

So, why has the finally construct never been implemented in C++? It's really not a very difficult or advanced concept to grasp and goes a long ways towards helping the programmer to write 'exception safe code'.

Vector
  • 3,180
  • 3
  • 22
  • 25
  • 27
    Why no finally? Because you release things in the destructor which fires automatically when the object (or smart pointer) leaves scope. Destructors are superior to finally{} since it separates workflow from cleanup logic. Just as you wouldn't want calls to free() cluttering up your workflow in a garbage collected language. – mike30 May 09 '13 at 20:11
  • 2
    See also [Did the developers of Java conciously abandon RAII?](http://programmers.stackexchange.com/questions/118295/118357#118357) – BlueRaja - Danny Pflughoeft May 09 '13 at 21:23
  • 8
    Asking the question, "Why is there no `finally` in C++, and what techniques for exception handling are used in its place?" is valid and on topic for this site. The existing answers cover this well, I think. Turning it into a discussion on "Are the C++ designers' reasons for not including `finally` worthwhile?" and "Should `finally` be added to C++?" and carrying on the discussion across comments on the question and every answer doesn't fit the model of this Q&A site. – Josh Kelley May 09 '13 at 21:31
  • 1
    Another alternative to "Why `finally` when you have RAII?" could be - Even if the class/type you are (forced into) using does not have a destructor (e.g., FILE *), and even in scenario of calling other code that can throw exceptions, one can, by following a strict SESE regimen, ensure proper clean-up of acquired resources. – Happy Green Kid Naps May 09 '13 at 21:46
  • @Kaz. Separation of concerns is not syntax. Line delimiters are not a valid analogy as they are syntax. Mixing clean up in the workflow makes the program less readable. Look at the average handle heavy Java program and you'll see what I mean. – mike30 May 10 '13 at 13:10
  • This is one of my favorite interview questions. It goes along with comparing and contrasting Java and C++. No `finally` needed in C++. – Bill Door May 10 '13 at 15:26
  • 1
    Yes, it is just syntax, when I have one single cleanup to do that is not repeated anywhere else in the program, and I'm required to write a bunch of syntax for a class, and then move the syntax for the cleanup out of the function and into that class. Keeping stuff together is important. That's why lambdas are important. Lambdas let you use a lexical scope as a first class object. And the thing about lexical scopes is that in a program there can be many of them, each quite unique. Classes are useful for cookie-cutter instantiation of many identical objects. – Kaz May 12 '13 at 07:38
  • 2
    If you have finally, you already have separation of concerns: the main code block is here, and the cleanup concern is taken care of here. – Kaz May 12 '13 at 07:40
  • 2
    @Kaz. The difference is implicit vs explicit clean up. A destructor gives you automatic clean up similar to how a plain old primitive is cleaned up as it pops off the stack. You don't need to make any explicit clean up calls and can focus on your core logic. Imagine how convoluted it would be if you had to clean up stack allocated primitives in a try/finally. Implicit clean up is superior. The comparison of class syntax to anonymous functions is not relevant. Although by passing first class functions to a function which releases a handle could centralize manual cleanup. – mike30 May 15 '13 at 13:19
  • 1
    @mike30 - the problem with this is that I see endless lines of C++ code making use of pointers and new(). Not to do so forces some very difficult constaints upon the programmer - which is why it so prevelant. I think this means that a **finally** construct would be most useful, in spite of the possible 'superiority' of implicit clean-up, which I don't necessarily deny. I am chiefly a Delphi programmer - I do not enjoy reading and writing all those **try..finally** blocks, and so over the years I have developed ways to minimize them. – Vector May 15 '13 at 16:14
  • @mike30 A destructor is not implicit; you have to write it and then explicitly instantiate the object! The behavior of calling it is then implicit. So is the invocation of a finally block. Man, people like confuse things ... – Kaz May 15 '13 at 16:26
  • 1
    @Kaz. Yes the library creator has to explicitly write the destructor. But the library consumer gets it for free. A finally block is the reverse in that the library writer doesn't handle the clean up, but the consumer must. Consumer code greatly outwighs the library code in volume. Getting it for free at the point of usage greatly outweighs. How often do you manually close a connection in a non-deterministic language? What if you forget? – mike30 May 15 '13 at 16:55
  • @Mikey. When using RAII you don't use raw pointers or the new keyword in C++. You use smart pointer templates. With minimal knowledge you can approach the freedom of garbage collection for all resources, not just memory. (from the perspective of the library consumer, not creator) – mike30 May 15 '13 at 16:59
  • 2
    @mike30 - ' You use smart pointer templates.' Smart pointer templates are certainly nice, and I make good use of them. However, Stroustrup's RAII is not talking about smart pointers! That's something that they added later on, to SOLVE THE PROBLEMS encountered when following the strict RAII paradigm. For years smart pointers were not part of the standard at all - they were only in Boost. Only 11 brought them into the standard. What does that tell you? (It tells me that RAII is unworkable as outlined-which is why no important language since C++ was ever designed to rely on RAII). – Vector May 15 '13 at 17:04
  • @Mikey. All RAII is based on stack allocation. Smart pointers are a mechanism to hook into the stack for heap allocated objects. If you use raw pointers and the new keyword then you are not using RAII. Stroustrup does not have any RAII examples using raw pointers unless he is giving an example of what RAII is not. – mike30 May 15 '13 at 17:11
  • 1
    @mike30: "Smart pointers are a mechanism to hook into the stack for heap allocated objects." Understood - it is a form of what is commonly known as "garbage collection", and is not entirely deterministic - thus we also have 'weak pointers'... Such models are also supported by MS COM, Java, C# and Python, just to name a few. So you're simply making my point: You're saying the C++ has now become more like Java and C#, thanks to smart pointers! Certainly, if a language features automated garbage collection, **finally** is less important, although it still has its place. – Vector May 15 '13 at 17:29
  • 1
    @Mikey. Yes using RAII feels more like using garbage collection from the perspective of the library consumer. But the point is about the differences of RAII and -finally-. RAII is implicit (in consumer code). Finally is explicit by the consumer. Is there a benefit of finally over RAII to justify including it? I only see disadvantages. – mike30 May 15 '13 at 17:45
  • 2
    @mike30 - 'I only see disadvantages.' I can't really argue with that, since I do my best to avoid using try..finally since I developed my bag of tricks in Delphi. All I'm really saying is that if you're going to allow explicit, unconstrained heap allocation in your language, as C++ does, you **need** a **finally** construct. – Vector May 15 '13 at 18:45
  • 2
    @Mikey. You're right, it would make sense to have a finally to handle raw pointers to the heap. For code written in the old style it would be useful. – mike30 May 15 '13 at 21:18
  • @mike30 - see Mason Wheeler's comment. – Gabriel Dec 03 '19 at 13:06

7 Answers7

60

It's really just a matter of understanding the philosophy and idioms of C++. Take your example of an operation that opens a database connection on a persistent class and has to make sure that it closes that connection if an exception is thrown. This is a matter of exception safety and applies to any language with exceptions (C++, C#, Delphi...).

In a language that uses try / finally, the code might look something like this:

database.Open();
try {
    database.DoRiskyOperation();
} finally {
    database.Close();
}

Simple and straightforward. There are, however, a few disadvantages:

  • If the language doesn't have deterministic destructors, I always have to write the finally block, otherwise I leak resources.
  • If DoRiskyOperation is more than a single method call - if I have some processing to do in the try block - then the Close operation can end up being a decent bit away from the Open operation. I can't write my cleanup right next to my acquisition.
  • If I have several resources that need to be acquired then freed in an exception-safe manner, I can end up with several layers deep of try / finally blocks.

The C++ approach would look like this:

ScopedDatabaseConnection scoped_connection(database);
database.DoRiskyOperation();

This completely solves all of the disadvantages of the finally approach. It has a couple of disadvantages of its own, but they're relatively minor:

  • There's a good chance you need to write the ScopedDatabaseConnection class yourself. However, it's a very simple implementation - only 4 or 5 lines of code.
  • It involves creating an extra local variable - which you're apparently not a fan of, based on your comment about "constantly creating and destroying classes to rely on their destructors to clean up your mess is very poor" - but a good compiler will optimize out any of the extra work that an extra local variable involves. Good C++ design relies a lot on these sorts of optimizations.

Personally, considering these advantages and disadvantages, I find RAII (Resource Acquisition Is Initialization) a much preferable technique to finally. Your mileage may vary.

Finally, because RAII is such a well-established idiom in C++, and to relieve developers of some of the burden of writing numerous Scoped... classes, there are libraries like ScopeGuard and Boost.ScopeExit that facilitate this sort of deterministic cleanup.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
Josh Kelley
  • 10,991
  • 7
  • 38
  • 50
  • 8
    C# has the `using` statement, which automatically cleans up any object implementing the `IDisposable` interface. So while it's possible to get it wrong, it's pretty easy to get it right. – Robert Harvey May 09 '13 at 19:16
  • @RobertHarvey - True. I'll update my answer; thanks. – Josh Kelley May 09 '13 at 19:24
  • 24
    Having to write an entirely new class to take care of temporary state change reversal, using a design idiom that is implemented by the compiler with a `try/finally` construct because the compiler does not expose a `try/finally` construct and the only way to access it is through the class-based design idiom, is not an "advantage;" it's the very definition of an abstraction inversion. – Mason Wheeler May 09 '13 at 20:56
  • 20
    @MasonWheeler - Umm, I didn't say that having to write a new class is an advantage. I said it's a disadvantage. On the balance, though, I prefer RAII to _having_ to use `finally`. Like I said, your mileage may vary. – Josh Kelley May 09 '13 at 21:01
  • 3
    So, when features are added to C++, is this based on some opinion poll of the user base of C++? Did `finally` actually lose out to, say, lambdas in this objective way? – Kaz May 09 '13 at 21:16
  • 2
    @Kaz I don't know how the committee decides features but generally they do not seem to add feature that are easily implemented in code. `struct Finally{ Finally(std::function fn) : fn(fn){} ~Finally() { fn();} private: std::function fn;}` Usage would be `Finally myFinally([&](){ DoWhatever();})` – NtscCobalt May 09 '13 at 21:38
  • 9
    @JoshKelley: 'Good C++ design relies a lot on these sorts of optimizations.' Writing gobs of extraneous code and then relying on compiler optimization is **Good Design**?! IMO it's the antithesis of good design. Among the fundamentals of good design is concise, easily readable code. Less to debug, less to maintain, etc etc etc. You should **NOT** be writing gobs of code and then relying on the compiler to make it all go away - IMO that makes no sense at all! – Vector May 09 '13 at 21:56
  • 19
    @Mikey: So duplicating cleanup code (or the fact that cleanup must happen) all over the code-base is "concise" and "easily readable"? With RAII, you write such code once, and it is automatically applied everywhere. – Mankarse May 10 '13 at 01:45
  • 1
    @Mankarse : 1) **INDENTATION** 2) Never write a function that requires more than one try..finally block. If you find that you need to do so, 99% of the time it means **refactor.** – Vector May 10 '13 at 09:01
  • 4
    @Josh Kelly: You prefer _having_ to use RAII to _having_ to use `finally`? – Giorgio May 10 '13 at 12:46
  • 1
    Like the C# `using` statement, Java 7 has its own version of RAII: `try (InputStream is = new FileInputStream("file.txt") ) { ... }` – Derek Ledbetter May 10 '13 at 23:22
  • 7
    @MasonWheeler The 'extra' work amounts to being refactoring work. Any bugfix in the class benefits *all* use sites. Compare that to reviewing the correctness of each and every `try/finally` constructs -- what happens if you find a bug in one? How do you know where to look in case you made a similar error, except that the other sites may be subtly different due to how those things compose and nest? RAII is not used by C++ programmers because or despite a lack of `try/finally`, but for its own merits (or: I've never, ever forgotten to close anything in C++). – Luc Danton May 11 '13 at 10:48
  • 7
    another advantage of RAII is that it works for member variables as well as local variables (neither finally nor using works for members) – jk. Sep 22 '14 at 06:36
  • 1
    Always bugs me when people answer the question by changing the design instead of answering the question. Sometimes RAII is *not* the right way to go. – Erik Aronesty Aug 09 '18 at 17:19
  • "you need to write the ScopedDatabaseConnection yourself.it's only 4 lines of code" - Yah. And this is how you end up with 500 small classes. This is why when you see a big C++ project for the first time, you feel like the knight in the front of some dark gates ready to start The Quest. You don't know what kind of furry monsters will fall over you when you open the gates, but you know there will be plenty of them. Once you defeat the gates and enter the realm of 1000 cpp files, you navigate the entangled map with a question ringing in your brain: where TF is this class defined & what is doing? – Gabriel Dec 03 '19 at 12:52
  • "only 4 lines of code" - I think in the end the amount of code is the same. In C++ you just hide it in some obscure class/file. In C#/Delphi is show on the spot what you intend to do. DON'T FORGET that you can do the same in C#/Delphi if you have many try/finally nested levels. You can take that code, make a function out of it and hide it in some obscure file/class. – Gabriel Dec 03 '19 at 13:00
  • 1. it can as well make `database.Open()` return a scoped variable which close the connection. nothing different from return a IDisposable (i.e. c# is **not** cleaner and you do need to write additional class in c#, too) – apple apple Jan 13 '21 at 17:32
  • 2. Well I think database itself should have proper destructor which close the connection (just like `fstream`) anyway, which eliminate the need of `ScopedDatabaseConnection`(which is a big concern for some people) altogether. @JoshKelley – apple apple Jan 13 '21 at 17:32
  • note: `which is a big concern for some people`: as I see in this thread. – apple apple Jan 13 '21 at 18:20
59

From Why doesn't C++ provide a "finally" construct? in Bjarne Stroustrup's C++ Style and Technique FAQ:

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.

svick
  • 9,999
  • 1
  • 37
  • 51
Nemanja Trifunovic
  • 6,815
  • 1
  • 26
  • 34
  • 1
    @Not sufficient IMO. There are MANY scenarios that this will not cover, or it is simply impossible to implement, particularly when dealing with legacy code. Simple example: opening a database connection in a member function of a PERSISTENT class, and executing code that throws an exception, leaving the connection open and perhaps locked, until the class's destructor runs - which may be **much** later. Constantly creating and destroying classes to rely on their destructors to clean up your mess is very poor practice IMO. The mess should be cleaned up when and where it was created. – Vector May 09 '13 at 17:26
  • 8
    But there's nothing about that technique that's specific to C++, is there? You can do RAII in any language with objects, constructors and destructors. It's a great technique, but RAII merely *existing* doesn't imply that a `finally` construct is always useless forever and ever, **despite** what Strousup is saying. The mere fact that writing "exception safe code" is a big deal in C++ is proof of that. Heck, C# has both destructors and `finally`, and they **both** get used. – Tacroy May 09 '13 at 17:26
  • 28
    @Tacroy: C++ is one of very few mainstream languages that has *deterministic* destructors. C# "destructors" are useless for this purpose, and you need to manually write "using" blocks to have RAII. – Nemanja Trifunovic May 09 '13 at 17:33
  • 2
    @NemanjaTrifunovic - Object Pascal/Delphi uses deterministic destructors and also supports 'finally', for the reasons I explained in my first comment. – Vector May 09 '13 at 17:37
  • 16
    @Mikey you have the answer of "Why doesn't C++ provide a "finally" construct?" directly from Stroustrup himself there. What more could you ask for? That **is** why. –  May 09 '13 at 17:45
  • 1
    @Mikey: I know very little about Delphi, but AFAIK it only supports heap-based objects - is this correct? If so, you still need to manually call Free to call the destructor. With C++, you have stack-based objects that are destroyed automatically and trigger the cleanup. – Nemanja Trifunovic May 09 '13 at 17:47
  • 1
    @NemanjaTrifunovic - correct - good point: Delphi supports only heap based objects - stack cleanup doesn't help. So in C++ we're not talking only about class destructors, we're also talking about stack based objects and stack cleanup. So is Stroustrup saying that in C++, stack based objects are the preferred designed paradigm? In truth, that's not a bad thing - particularly in multi-threaded systems - stack based objects will go a LONG WAY in helping to you write thread safe code. But they do tend to me more expensive, resource-wise. – Vector May 09 '13 at 17:59
  • 1
    @Mikey: Yes, RAII is entirely based around stack-allocated objects. – Bart van Ingen Schenau May 09 '13 at 18:04
  • @Mikey: The problem is that too many people write C++ in Java-like style. See this text for more info: http://www.codeproject.com/Articles/38449/C-Exceptions-Pros-and-Cons#3_2 – Nemanja Trifunovic May 09 '13 at 18:11
  • @BartvanIngenSchenau - OK. Then this is really a question about a fundamental language and design feature. So as someone who has worked extensively in Delphi and C# for nearly 20 years (languages which support only heaped based objects and use only object references) and only messed with C++ occasionally for my own gratification, this is something new that I need to 'wrap my mind around'. – Vector May 09 '13 at 18:16
  • 4
    @Mikey Both RAII and `finally` (as well as some others) are *tools* for writing code that behaves well in the face of exceptions. Neither the support for `finally` nor the support for RAII automatically solves this problem. One has to use the respective tool, and use it correctly, to achieve exception safety. Note that the *concept* of exception safety is language-independent; that it is less widely discussed in other communities may be due to other languages reducing the problem by managing some resources automatically or the programmers being less paranoid. –  May 09 '13 at 18:16
  • @delnan - my principle language is Delphi - no automated garbage collection, no objects on the stack, and I am very paranoid. :-) This is why my code is littered with try..finally blocks. – Vector May 09 '13 at 18:19
  • 5
    @Mikey If you worry about your code behaving well, in particular not leaking resources, when exceptions are thrown at it, you *are* worrying about exception safety/trying to write exception safe code. You're just not calling it that, and due to different tools being available you implement it differently. But it's exactly what C++ people talk about when they discuss exception safety. –  May 09 '13 at 18:22
  • @delnan - granted. But it's not a black art and no lengthy articles by gurus are necessary on the subject. Try..finally is quite simple to understand and implement. – Vector May 09 '13 at 18:30
  • @Mikey Apparently not, judging from a lot of existing code. And from my experience, people newly introduces to C++ aren't being taught this either. –  May 09 '13 at 18:35
  • 2
    @delnan - I can only speak for myself and the programmers I have worked with, who are all seasoned veterans and understand these things quite well. Resource allocation, exception/error handling and cleanup are fundamental and no programmer worthy of the name can be ignorant of these subjects, regardless of the language they use (I don't mean scripters) As for C++, if they are taught to use stack allocated objects, the problem is greatly reduced - but the subject must be introduced and dwelt upon. Any environment that's using C++ needs safe, clean, robust code. – Vector May 09 '13 at 18:42
  • @MichaelT : Strousup's answer does **NOT** address an important component of my question: why all the talk about 'exception safe code'? Lippman deems it an important but very difficult subject, beyond the scope of his 'Primer', and Sutter devotes 10 chapters to it in his Exceptional C++... – Vector May 09 '13 at 19:37
  • 1
    @Mikey that part of the question didn't come out until the comments. The answer answers the question in the title and the bold bits. –  May 09 '13 at 19:41
  • @Mikey perhaps one should emphasize that instead of "Why has the 'finally' construct NEVER been implemented in C++?! What's wrong with it? Why all the fuss about 'exception safe code'?" which is answered in Stroustrup's Style and Technique FAQ which further links to "Appendix E: Standard-Library Exception Safety" –  May 09 '13 at 19:47
  • 2
    This rationale is dumb. The programmer could just as easily forget to do a cleanup in a destructor as in a finally block. Destructors make the code harder to read when it's more logical for the cleanup to be in the same place as the function. Functions can have state, yet if you want cleanup, you have to put all the state into some surrogate object just to capture the unwind flow of control via the destructor. – Kaz May 09 '13 at 20:46
  • 1
    @Kaz - As a long time Delphi guy, I tend to agree with you. This rationale seems rather restrictive and 'pig headed' if I may. But it seems that the C++ model according to Stroustrup prefers STACK BASED OBJECTS and barring an infinite loop, your function **WILL** terminate and cleanup **WILL** happen - nothing to forget. But none of the more modern languages support stack based objects... – Vector May 09 '13 at 20:58
  • @Mikey If the same pigheadeness were applied even-handedly (-headedly? LOL), most of the C++11 cruft wouldn't be there. – Kaz May 09 '13 at 21:04
  • 20
    @Kaz: I only need to remember to do the cleanup in the destructor once, and from then on I just use the object. I need to remember to do the cleanup in the finally block every time I use the operation that allocates. – deworde May 10 '13 at 09:13
  • 1
    @deworde What if there is exactly one use of the object in the program? What if every cleanup is different from the others, so you have to make classes called `Cleanup_001`, `Cleanup_002`, ... Then what you're doing is behaving like a compiler for a language which has `finally`, using C++ as the target language. :) – Kaz May 12 '13 at 08:32
  • 3
    I mean, a destructor cleans up the object; it doesn't clean up the situation in your function, unless you express that in objects. If the situation is unique, you have to waste time expressing a unique situation in objects instead of just writing the code **right there, with full access to the lexical scope**. – Kaz May 12 '13 at 08:34
  • 1
    Remember one thing: `unwind-protect` in Lisp (basically the origin of `finally`) is a lexical closure (a.k.a. lambda) in disguise. During unwinding, the cleanup code is entered as a lexical closure in the environment that defined it, and therefore has access to that environment. C++'s destructors are the avoidance of lambda. "You don't get first class scopes; you must use objects". But that is changing, clearly. There are now lambdas (sort of) ... but no finally. – Kaz May 12 '13 at 08:36
  • 2
    @Tacroy [C# destructors are basically short-hand for calling a finalizer](http://msdn.microsoft.com/en-us/library/66x5fx1b(v=vs.71).aspx). They're not deterministic so you're not guaranteed that it will be called when the object goes out of scope - they're destructors in name only. This is perfectly natural given that the .Net GC calls the shots. Manually managing resources by taking advantage of automatic/local/stack-based lifetimes that guarantee that a given object will be destructor makes perfect sense in lieu of a monster runtime managing all of you allocations/deallocations. – nerraga May 29 '13 at 03:47
20

The reason that C++ does not have finally is because it is not needed in C++. finally is used to execute some code regardless of whether an exception has occurred or not, which almost always is some kind of cleanup code. In C++, this cleanup code should be in the destructor of the relevant class and the destructor will always be called, just like a finally block. The idiom of using the destructor for your cleanup is called RAII.

Within the C++ community there might be more talk about 'exception safe' code, but it is almost equally important in other languages that have exceptions. The whole point of 'exception safe' code is that you think about in what state your code gets left if an exception occurs in any of the functions/methods that you call.
In C++, 'exception safe' code is slightly more important, because C++ does not have automatic garbage collection that takes care of objects that are left orphaned due to an exception.

The reason the exception safety is discussed more in the C++ community probably also stems from the fact that in C++ you have to be more aware of what can go wrong, because there are fewer default safety nets in the language.

Bart van Ingen Schenau
  • 71,712
  • 20
  • 110
  • 179
  • Not needed in C++? Is there something special about C++ that makes it not needed, as opposed to those other languages, which were all developed by highly sophisticated computer scientists and implemented 'finally'? Also - see comment on the answer from Nemanja Trifunovic - RAII is hardly sufficient and can lead to poor and inefficient design. – Vector May 09 '13 at 17:31
  • 3
    Note: Please do not contend that C++ has deterministic destructors. Object Pascal/Delphi also has deterministic destructors yet also supports 'finally', for the very good reasons I explained in my first comments below. – Vector May 09 '13 at 17:39
  • 15
    @Mikey: Given that there has never been a proposal to add `finally` to the C++ standard, I think it is safe to conclude that the C++ community does not deem `the absence of finally` a problem. Most languages that do have `finally` lack the consistent deterministic destruction that C++ has. I see that Delphi has them both, but I don't know its history well enough to know which was there first. – Bart van Ingen Schenau May 09 '13 at 18:01
  • 4
    Dephi does not support stack based objects - only heap based, and object references on the stack. Therefore 'finally' is necessary to explicitly invoke destructors etc when appropriate. – Vector May 09 '13 at 18:24
  • 2
    There is a whole lot of cruft in C++ that is arguably not needed, so this can't be the right answer. – Kaz May 09 '13 at 20:46
  • 1
    @BartvanIngenSchenau : Contradiction I see here: 'because it is not needed in C++'; 'The reason the exception safety is discussed more in the C++ community probably also stems from the fact that in C++ you have to be more aware of what can go wrong,': If there is so much discussion because you have to worry so much about what can go wrong, why is **finally** not needed?! The extensive discussion proves that it **IS NEEDED.** – Vector May 09 '13 at 21:34
  • 15
    In the more than two decades I've used the language, and worked with other people that used the language, I've never encountered a working C++ programmer who said "I really wish the language had a `finally`". I can't ever recall any task that it would have made easier, had I had access to it. – Gort the Robot May 10 '13 at 02:15
  • 1
    @Mikey: The extensive discussion proves *nothing* about the language, and a great deal about people being willing to "discuss" matters of which they're clearly ignorant. – Jerry Coffin May 10 '13 at 05:21
  • 1
    @JerryCoffin: ? Most of the discussion has revolved around Stroustrup's advocacy of RAII, something which experienced C++ programmers seem quite comfortable and familiar with. Not so myself, so perhaps I don't grasp the ignorance? – Vector May 10 '13 at 05:32
  • 2
    @Mikey: I'm talking about the comments on all the answers, not just this one -- things like Kaz's apparently unthinking rejection of everything added in C++11 as "cruft" and Mason Wheeler's usual rejection of everything different from Delphi as obviously wrong. These have inflated the "discussion" tremendously, but provide little proof of anything (except, possibly, proof about the attitudes of the people expressing the opinions). – Jerry Coffin May 10 '13 at 13:17
  • @StevenBurnap: Hopefully that's not a "true Scotsman" issue? i.e. if a C++ programmer did tell you that, hopefully you wouldn't think less of them as a C++ programmer, right? Because I expect many would, hence why you might never hear that. – user541686 Dec 02 '15 at 11:14
  • 2
    No. I mean, I've never heard someone working on actual C++ code say they needed it, wanted it, or even discussed it in any fashion. That includes lots of coders ranting about C++ deficiencies. – Gort the Robot Dec 02 '15 at 16:47
  • `which almost always is some kind of cleanup code.` How about the situations besides `always`? – Saddle Point Dec 25 '17 at 08:25
  • "in C++, 'exception safe' code is slightly more important" - ha ha, I like how you tried to sneak in the word "slightly". Hell, C++ is all about that. – Gabriel Dec 03 '19 at 12:42
  • "you think about in what state your code gets left if an exception occurs" - Man! Do you really think the human brain can deal with ALL possible configurations/ways in which a single line of code can fail? What about 300 lines of code?? – Gabriel Dec 03 '19 at 12:44
  • 2
    @Vector The *major* difference between Delphi and C++ is that in C++ you **can't fail** to call the deterministic destructors of your local variables, no matter how your function is exited. – Caleth Jan 16 '20 at 11:43
  • @stackunderflow then you can use methods already provide [in](https://softwareengineering.stackexchange.com/a/197566/383041) [other](https://softwareengineering.stackexchange.com/a/197579/383041) [answers](https://softwareengineering.stackexchange.com/a/280138/383041), (yes I know your comment is 3-years old, just in case someone see this thread) – apple apple Jan 13 '21 at 18:06
14

Others have discussed RAII as the solution. It's a perfectly good solution. But that doesn't really address why they didn't add finally as well since it's a widely desired thing. The answer to that is more fundamental to the design and development of C++: throughout the development of C++ those involved have strongly resisted the introduction of design features that can be achieved using other features without a huge amount of fuss and especially where this requires the introduction of new keywords that could render older code incompatible. Since RAII provides a highly functional alternative to finally and you can actually roll your own finally in C++11 anyway, there was little call for it.

All you need to do is create a class Finally that calls the function passed to it's constructor in it's destructor. Then you can do this:

try
{
    Finally atEnd([&] () { database.close(); });

    database.doRisky();
}

Most native C++ programmers will, in general, prefer cleanly designed RAII objects however.

Jack Aidley
  • 2,954
  • 1
  • 15
  • 18
  • 3
    You are missing the reference capture in your lambda. Should be `Finally atEnd([&] () { database.close(); });` Also, I imagine the following is better: `{ Finally atEnd(...); try {...} catch(e) {...} }` (I lifted the finalizer out of the try-block so it executes after the catch blocks.) – Thomas Eding Sep 22 '14 at 20:56
3

You can use a "trap" pattern - even if you don't want to use try/catch block.

Put a simple object in the required scope. In this object's destructor put your "finaly" logic. No matter what, when the stack is unwound, object's destructor will be called and you'll get your candy.

  • 2
    This does not answer the question, and simply proves that **finally** isn't such a bad idea after all... – Vector May 09 '13 at 21:47
2

Well, you could sort of roll-your-own finally, using Lambdas, which would get the following to compile fine (using an example without RAII of course, not the nicest piece of code):

{
    FILE *file = fopen("test","w");

    finally close_the_file([&]{
        cout << "We're closing the file in a pseudo-finally clause." << endl;
        fclose(file);
    });
}

See this article.

einpoklum
  • 2,478
  • 1
  • 13
  • 30
0

I'm not sure I agree with assertions here that RAII is a superset of finally. The achilles heel of RAII is simple: exceptions. RAII is implemented with destructors, and it's always wrong in C++ to throw out of a destructor. That means that you cannot use RAII when you need your cleanup code to throw. If finally were implemented, on the other hand, there's no reason to believe that it would not be legal to throw from a finally block.

Consider a code path like this:

void foo() {
    try {
        ... stuff ...
        complex_cleanup();
    } catch (A& a) {
        handle_a(a);
        complex_cleanup();
        throw;
    } catch (B& b) {
        handle_b(b);
        complex_cleanup();
        throw;
    } catch (...) {
        handle_generic();
        complex_cleanup();
        throw;
    }
}

If we had finally we could write:

void foo() {
    try {
        ... stuff ...
    } catch (A& a) {
        handle_a(a);
        throw;
    } catch (B& b) {
        handle_b(b);
        throw;
    } catch (...) {
        handle_generic();
        throw;
    } finally {
        complex_cleanup();
    }
}

But there is no way, that I can find, to get the equivalent behavior using RAII.

If someone knows how to do this in C++, I'm very interested in the answer. I'd even be happy with something that relied on, for example, enforcing that all exceptions inherited from a single class with some special capabilities or something.

MadScientist
  • 125
  • 1
  • 2
    In your second example, if `complex_cleanup` can throw, then you could have a case where two uncaught exceptions are in flight at once, just as you would with RAII / destructors, and C++ refuses to allow this. If you want the original exception to be seen, then `complex_cleanup` should prevent any exceptions, just as it would with RAII / destructors. If you want `complex_cleanup`'s exception to be seen, then I think you can use nested try/catch blocks - although this is a tangent and hard to fit into a comment, so it's worth a separate question. – Josh Kelley Aug 31 '15 at 01:26
  • I want to use RAII to get identical behavior as the first example, more safely. A throw in a putative `finally` block would clearly work the same as a throw in a `catch` block WRT in-flight exceptions--not call `std::terminate`. The question is "why no `finally` in C++?" and the answers all say "you don't need it... RAII FTW!" My point is that yes, RAII is fine for simple cases like memory management, but until the issue of exceptions is solved it requires too much thought/overhead/concern/re-design to be a general-purpose solution. – MadScientist Aug 31 '15 at 04:49
  • But your first example is willing to lose exceptions. If `complex_cleanup` throws, then the original `A`/`B`/`...` exception is lost. From what I understand, languages like Java and C# work the same way with `finally` - if `finally` throws, then the original exception is lost. For better or worse, C++ has decided that it refuses to lose exceptions - it would rather call `terminate` - and, even if it added `finally`, `finally` would probably work the same way, so it wouldn't solve your problem. Like I said, if you're willing to lose exceptions, a nested try/catch block should work. – Josh Kelley Aug 31 '15 at 12:41
  • 4
    I understand your point - there are some legitimate issues with destructors that might throw - but those are rare. Saying that RAII + exceptions has unresolved issues or that RAII isn't a general-purpose solution simply doesn't match the experience of most C++ developers. – Josh Kelley Aug 31 '15 at 12:42
  • Yes, it is rare that someone writes a destructor like `A::~A()` that throws. But the entire point of RAII is that you take generic cleanup code, that is not normally part of a destructor, and put it into a destructor in order to ensure it's invoked (see for example SCOPE_EXIT in ScopeGuard). That generic cleanup code, if it's at all complex or interesting, _will_ want to throw: it's not rare at all. It's quite possible you're only using RAII for simple things like ensuring heap cleanup, locking, etc. and it works perfectly for that. But it is not general-purpose. – MadScientist Aug 31 '15 at 15:28
  • 1
    Regarding `finally`: yes I know exceptions don't nest in C++, but throwing from inside `catch` is well-defined and does not cause your program to terminate. Certainly a throw from `finally` would work the same way, and _not_ behave like a throw from a destructor. In cleanup() I want to throw, I want to see the exception, but I don't want to terminate my program. Thus I cannot call it from a destructor, thus I cannot use it with RAII, and thus RAII, while cool, cannot replace `finally`. Nested try/catch can't help. Destructors are a round peg for an oblong hole. – MadScientist Aug 31 '15 at 15:52
  • Let us [continue this discussion in chat](http://chat.stackexchange.com/rooms/27604/discussion-between-josh-kelley-and-madscientist). – Josh Kelley Aug 31 '15 at 15:59
  • Using RAII properly, you should not really be calling `complex_cleanup()` functions. The point of RAII is that the stack is your best cleanup tool - your code does not explicitly handle resource cleanup. BTW, the answer is not that RAII is a _superset_ of `finally`- the answer is that `finally` is an unnecessary, clumsy construct that leads to poorly structured code and improper allocation of resources as compared to RAII. I asked this question two years ago, read Stroustrup on RAII after getting the answer, and I have since written a lot of fast,efficient code that has no need for `finally`. – Vector Aug 31 '15 at 23:04
  • `finally` is necessary in a language like Delphi, which has no automated garbage collection (it really does, but that's not the point here) yet does not allow instantiation of classes on the stack, leaving you to manage their cleanup. C++ supports instantiation of classes on the stack, precluding the need for `finally` and `complexCleanup()` . – Vector Aug 31 '15 at 23:09
  • (cont..) Many developers seem to shy away from instantiation of classes on the stack because more thought and careful design is required to do it correctly than simply using pointers with `new` and then relying on constructs like `complexCleanup()` and explicitly called destructors. That doesn't mean it's the cleanest, most efficient approach to development. – Vector Aug 31 '15 at 23:17
  • I agree with those nice features of RAII and I'd use it more, except for one problem: exceptions. As I said above, the failure to manage exceptions means RAII cannot be used in many situations. I don't know about your code but regardless of how complex or not complex your cleanup code is, sometimes it will fail and when it fails, you need to throw an exception. And in that case you cannot use RAII. It's not the fault of RAII as a concept. It's the fault of C++'s inability to handle this common requirement in its RAII implementation. – MadScientist Sep 01 '15 at 04:18
  • 1
    If you find yourself with the need to raise exceptions in destructors, you're doing something wrong - probably using pointers in other places when they're not necessary. – Vector Sep 01 '15 at 23:19
  • I'm sorry but memory management is the _least_ interesting use of RAII: you can't "fail" to free memory, so if pointers is all you think about you're missing most of it. Consider a file. You want to use RAII to be _sure_ the file is closed when a function exits via either normal return or exception. You create an object representing the file whose destructor closes the file. RAII FTW! But what if the close of the file fails, and you _must_ learn about that? Destructors can't do this in C++. RAII fail. – MadScientist Sep 02 '15 at 18:21
  • Destructors can do that. Destructor is called when the function exits. If your file object is on the stack, an exception clears the stack and destructors are called. see [Are destructors called after a throw in C++?](http://stackoverflow.com/questions/8311457/are-destructors-called-after-a-throw-in-c). BTW, incorrect use of pointers leads to more than just "memory leaks", but general problems with resource allocation, like open file handles.Key to RAII is initializing objects on the stack not on the free store with pointers.Read carefully answers here and there.(also **direct** your comments) – Vector Sep 03 '15 at 00:58
  • 1
    You seem to be assuming I don't know what RAII is and as a result are not reading my comments carefully. I'm very, very familiar with RAII: you don't have to teach me how to use it. I fully understand it's based on stack objects. I will try again. A destructor has no return type. A destructor cannot safely throw an exception. Hence a destructor must always "succeed" and cannot indicate an error condition. Hence you cannot use a destructor to perform any operation which might fail in a way that must be published (the destructor must handle all error conditions internally). – MadScientist Sep 03 '15 at 13:24
  • 1
    RAII in C++ is based on destructors. Hence, it's not possible to use RAII to implement any operation in C++ which might fail in a way that cannot be completely handled within the destructor. If you take my original example and replace `complex_cleanup()` with `close()` where the close operation can fail, and where detecting that the close operation failed is important and can't be handled within the destructor, then RAII cannot be used. In short, there is no way to translate my original example safely into a model that uses RAII. That's my whole point. – MadScientist Sep 03 '15 at 13:30
  • 1
    This is too involved for comments. Post a question about it: _How would you handle this scenario in C++ using the RAII model... it doesn't seem to work..._ Again, you should **direct your comments** : type _@_ and name of the member you're talking to at the beginning of your comment. When comments are on your own post, you get notified of everything, but others don't unless you direct a comment to them. – Vector Sep 03 '15 at 20:06