73

Google's C++ style guide says "We do not use exceptions". The style does not mention STL with respect to usage of exception. Since STL allocators can fail, how do they handle exceptions thrown by containers?

  1. If they use STL, how is the caller informed of allocation failures? STL methods like push_back() or map operator[] do not return any status codes.
  2. If they do not use STL, what container implementation do they use?
cylus
  • 357
  • 1
  • 4
  • 14
Andrei
  • 8,606
  • 10
  • 35
  • 43
  • 8
    Before I came in and yelled until I got my way, the shop I work at had the same silly convention. We just ignored reality. My *bet* is that this is also what google does. – Edward Strange Mar 03 '11 at 17:42
  • 1
    Few methods in the STL actually throw exceptions... – Etienne de Martel Mar 03 '11 at 17:44
  • 15
    Google has pretty archaic standards. Guess they hire a lot of students and only can afford a small number of C++ wizards. – Maxim Egorushkin Mar 03 '11 at 17:56
  • 1
    At my last job, I got dinged in a code review for throwing an exception in a constructor when one of the parameters was NULL. Apparently, using `assert` was an acceptable fix. – Daniel Gallagher Mar 03 '11 at 18:03
  • 7
    No need to assert NULL pointers. Just crash and burn. [Rule of Repair: Repair what you can — but when you must fail, fail noisily and as soon as possible.](http://www.faqs.org/docs/artu/ch01s06.html#id2878538) – Maxim Egorushkin Mar 03 '11 at 18:15
  • 21
    @Maxim: that's why you need to assert null pointers, to follow that advice. If you don't assert, there's a risk that your code might *fail* to crash and burn, because "undefined behavior" does *not* mean "segfault immediately". Admittedly a small risk, but consider for example http://www.theregister.co.uk/2009/07/17/linux_kernel_exploit/ – Steve Jessop Mar 03 '11 at 18:30
  • 1
    Mapping a page at 0 address is asking for trouble. Anyway, SIGSEGV is good enough for me, can't be bothered with asserts for NULL pointers. – Maxim Egorushkin Mar 03 '11 at 19:17
  • 12
    @Maxim: "Mapping a page at 0 address is asking for trouble." - quite, and attackers generally are asking for trouble when they maliciously pull some such stunt. That's just an example, though. If you act as though you're guaranteed to get a segfault from accessing a null pointer, eventually the compiler will surprise you (or someone else like you) by not guaranteeing that. Undefined behavior can travel back in time to make demons fly out of your nose as soon as the program starts. Either fail as soon as possible, or else don't, but don't expect to fail and then not fail. – Steve Jessop Mar 03 '11 at 22:20
  • 3
    By the time they attack, it is going to be a release build with no asserts... – Maxim Egorushkin Mar 04 '11 at 08:48
  • that who doesn't assert null pointers doesn't check for null pointers – Andrei Apr 08 '11 at 21:19
  • @Etienne de Martel: Among them are methods without which you cannot even start to use STL. – Andrei Apr 08 '11 at 21:20
  • The only time I’ve ever seen a platform not crash due to a NULL pointer dereference, it was a PowerPC build of VxWorks. The fix was to map an inaccessible page at address 0, which (surprise, surprise), caught a pile of bugs in the process. Relying on human beings to add assert() statements or similar is a foolish way to tackle this issue, and @MaximYegorushkin is quite right that allocator exceptions are not really worth worrying about (you could catch and report, then terminate, for sure, but it isn‘t much better than segfaulting). – al45tair Sep 16 '13 at 06:52
  • Further, relying on a security straw-man argument to support the notion that asserting is useful is bogus. If an attacker can map arbitrary pages in your address space, you have already lost. – al45tair Sep 16 '13 at 06:53
  • 1
    I'm amazed that google is not a bit ashamed of themselves -- going even as far as making this (the C++ guidelines forbidding the use of C++) freely accessible in the internet. Don't they know that one can google this? This very much sounds like a manager idea -- somebody who has not written a complex piece of software in his entire life or does not care about informing the caller of his code of the reason for failure. –  Dec 25 '13 at 03:11
  • STL is designed to be used without exceptions. What happens instead is usually a crash, which is basically the same as any safe exception handling would do. – Allan Jensen Jan 30 '20 at 23:07
  • @MaximEgorushkin [Repaired link](http://www.catb.org/~esr/writings/taoup/html/ch01s06.html#id2878538) :) – legends2k Mar 19 '20 at 04:43

8 Answers8

60

They say that they don't use exceptions, not that nobody should use them. If you look at the rationale they also write:

Because most existing C++ code at Google is not prepared to deal with exceptions, it is comparatively difficult to adopt new code that generates exceptions.

The usual legacy problem. :-(

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • 4
    And the question is how *they* deal with the consequences of that decision for the STL container interfaces, not how anybody else does ;-) – Steve Jessop Mar 03 '11 at 17:36
  • 4
    Not just a legacy problem. Writing exception-safe code is not trivial and no tool exists (that I know of) that helps the developers here too. So, even new codes are prone to exception dangers. – kirakun Mar 03 '11 at 17:37
  • 2
    The question is not "is their decision reasonable, or shoudl everybody follow their decision". No, no, and no. The question is, is it possible to use stl containers and abide by this rule. – Andrei Mar 03 '11 at 18:54
  • 3
    Let's be fair. Of their 6 reasons against exceptions, only one is related to legacy. Five others "cons" do not have anything to do with legacy. – Andrei Mar 05 '11 at 05:43
  • 12
    There are cons with their method as well, like having to all an Init() function for the objects. What if you forget one call? How do you do it for temporary objects? Don't use that? Do you have an empty Init() function for all classes, or do you add one just when you discover you need one? Then how do you find all places where you need to add an Init() call? They say that exceptions forces you to use RAII, and that is supposed to be hard. If you don't use exceptions, you must check return codes or object validity all over the place. That is even harder to get right. – Bo Persson Mar 05 '11 at 08:21
  • No, it is because C++ exceptions are an exceptionally bad idea. Writing exception safe code is extraordinarily difficult, and is impossible to do by anyone who thinks C++ exceptions are a good idea, because the knowledge necessary to write such code, would inform you that C++ exceptions are a terrible idea. – Allan Jensen Jan 30 '20 at 23:04
  • @Allan_Jensen Curious -- I had this problem solved in 1996. It is today called RAII! I think you should look for a different job! – Frank Puck Jun 30 '22 at 17:20
52

We simply don't handle exceptions thrown by containers, at least in application-level code.

I've been an engineer at Google Search working in C++ since 2008. We do use STL containers often. I cannot personally recall a single major failure or bug that was ever traced back to something like vector::push_back() or map::operator[] failing, where we said "oh man, we have to rewrite this code because the allocation could fail" or "dang, if only we used exceptions, this could have been avoided." Does a process ever run out of memory? Yes, but this is usually a simple mistake (e.g., someone added a large new data file to the program and forgot to increase the RAM allocation) or a catastrophic failure where there's no good way to recover and proceed. Our system already manages and restarts jobs automatically to be robust to machines with faulty disks, cosmic rays, etc., and this is really no different.

So as far as I can tell, there is no problem here.

hoffmanj
  • 637
  • 5
  • 5
  • 2
    @Hinata Hyuga: Thanks for the suggestions, but I agree with Loki Astari; my answer relies entirely on my personal experience at Google, which, I believe, is a valid source when answering a Stack Overflow question. I have taken some of your suggestions for grammar though. Thanks, both! – hoffmanj Mar 22 '13 at 18:01
  • As I said in my answer below, it’s quite unlikely that you’ll see allocator exceptions anyway on modern systems when using the standard allocator. You might conceivably with some kind of custom allocator, but otherwise it really isn’t worth worrying about. – al45tair Sep 16 '13 at 07:07
  • 2
    @hoffmanj I've seen some code using streams where the underlying stream entered a failed state and the code was silently failing. Checking the state of the stream after every insertion (ala WinAPI) would be obnoxious. Granted, the style guide forbids the use of streams for non-logging purposes (as an aside, does that include stringstream?). I suppose my question is, if you're interacting with an STL construct that doesn't have a great interface for error conditions other than exceptions, do you just ban them outright? – Brian Vandenberg Aug 06 '15 at 18:27
11

I'm pretty sure that they mean they do not use exceptions in their code. If you check out their cpplint script, it does check to ensure you are including the correct headers for STL containers (like vector, list, etc).

Mark Loeser
  • 17,657
  • 2
  • 26
  • 34
  • 1
    Homm so do they wrap every single call to stl method into catch(...) ? – Andrei Mar 03 '11 at 17:36
  • 3
    Mark, if you mean "they do not throw exceptions in ther code, but they allow the code they call to throwexceptions", this interpretation is wrong. CLick on small triangle to the left of the rule. From explanations, it becomes clear that they do not want exceptions thrown from underlying code, too. – Andrei Mar 03 '11 at 18:56
  • 4
    As stated in the "Exceptions to the Rules" section "You may diverge from the rules when dealing with code that does not conform to this style guide." certainly STL was not designed with Google conventions in mind. – Ismael Mar 04 '11 at 15:07
8

You can’t handle allocation failures anyway on modern operating systems; as a performance optimization, they typically over-commit memory. For instance, if you call malloc() and ask for a really huge chunk of memory on Linux, it will succeed even if the memory required to back it actually isn’t there. It’s only when you access it that the kernel actually tries to allocate pages to back it, and at that point it’s too late to tell you that the allocation failed anyway.

So:

  1. Except in special cases, don’t worry about allocation failures. If the machine runs out of memory, that’s a catastrophic failure from which you can’t reliably recover.

  2. Nevertheless, it’s good practice to catch unhandled exceptions and log the e.what() output, then re-throw, since that may be more informative than a backtrace, and typical C++ library implementations don’t do that automatically for you.

  3. The whole huge thread above about how you can’t rely on crashing when you run out of memory is complete and utter rubbish. The C(++) standard may not guarantee it, but on modern systems crashing is the only thing you can rely on if you run out of memory. In particular, you can’t rely on getting a NULL or indeed any other indication from your allocator, up to and include a C++ exception.

  4. If you find yourself on an embedded system where page zero is accessible, I strongly suggest you fix that by mapping an inaccessible page at that location. Human beings cannot be relied upon to check for NULL pointers everywhere, but you can fix that by mapping a page once rather than trying to correct every possible (past, present and future) location at which someone might have missed a NULL.

I will qualify the above by saying that it’s possible you’re using some kind of custom allocator, or that you’re on a system that doesn’t over-commit (embedded systems without swap are one example of that, but not the only example). In that case, maybe you can handle out-of-memory conditions gracefully, on your system. But in general in the 21st century I’m afraid you are unlikely to get the chance; the first you’ll know that your system is out of memory is when things start crashing.

al45tair
  • 4,405
  • 23
  • 30
  • 1
    (1) The machine running OOM is different from the process running OOM, at least for the (majority) of processes that are still 32bit: due to address space fragmentation, you will start to hit allocation failures for large blocks sooner rather than later. (As we experience repeatedly on Windows.) – Martin Ba Sep 16 '13 at 07:37
  • 1
    (2) If you want to crash (i.e., core dump) the process for unhandled exceptions and later analyze the crash dump, it may be better to *not* catch the exception (and rethrow) as catching them will unwind the stack, either leading to more errors or at least obfuscating the rash dump. – Martin Ba Sep 16 '13 at 07:43
  • 1
    And, correct me, last time I checked, Windows didn't overcommit. So that leaves quite a large chunk of the installation base to consider, doesn't it? – Martin Ba Sep 16 '13 at 07:45
  • @MartinBa Surprised to hear that you have address space fragmentation issues. That’s quite an unusual problem for most 32-bit programs (those that are most susceptible would have gone 64-bit already where possible). – al45tair Sep 17 '13 at 10:40
  • @MartinBa I’m not so sure Windows doesn’t ever overcommit. I recall, on Win2K?, accidentally allocating more memory than could possibly have been available and having it succeed then crash later. – al45tair Sep 17 '13 at 10:41
  • @MartinBa As for (2), agreed, depending on how exceptions are being used and on whether your compiler’s implementation saves the original backtrace. – al45tair Sep 17 '13 at 10:42
  • @MartinBa Also, on (1), allocation failures in your application imply that there may also have been allocation failures in library code that you didn’t see (some of which may have happened on threads you don’t control, and may leave things in an undefined or unusable state). The state of your process is, well, problematic, if you have seen an allocation failure. – al45tair Sep 17 '13 at 10:44
  • I agree that the state of the process is very likely "problematic" for the vast majority of allocation failures. Still, it is not a priori f'ed up, so graceful shutdown would IMHO often be possible. – Martin Ba Sep 18 '13 at 07:47
  • @user678269 Did I say it could only fail because of running out of memory? No, I didn’t. My point is specifically that worrying about allocation failures is pointless on modern systems, as you’re more likely to crash than get an exception anyway. – al45tair Dec 05 '13 at 14:34
7

I have found that Google mentions this explicitly about STL and exceptions (emphasis is mine):

Although you should not use exceptions in your own code, they are used extensively in the ATL and some STLs, including the one that comes with Visual C++. When using the ATL, you should define _ATL_NO_EXCEPTIONS to disable exceptions. You should investigate whether you can also disable exceptions in your STL, but if not, it is OK to turn on exceptions in the compiler. (Note that this is only to get the STL to compile. You should still not write exception handling code yourself.)

I don't like such decisions (lucky that I am not working for Google), but they are quite clear about their behaviour and intentions.

Yongwei Wu
  • 5,292
  • 37
  • 49
5

Stl itself is directly only throwing in case of memory allocation failure. But usually a real world application can fail for a variety of reasons, memory allocation failure just one of them. On 32 bit systems memory allocation failure is not something which should be ignored, as it can occur. So the entire discussion above that memory allocation failure is not going to happen is kind of pointless. Even assuming this, one would have to write ones code using two step initialization. And C++ exception handling predates 64 bit architectures by a long time. I'm not certain how far I should go in dignifying the negative professionalism shown here by google and only answer the question asked. I remember some paper from IBM in around 1997 stating how well some people at IBM understood & appreciated the implications of C++ Exception Handling. Ok professionalism is not necessary an indicator of success. So giving up exception handling is not only a problem if one uses STL. It is a problem if one uses C++ as such. It means giving up on

  • constructor failure
  • being able to use member objects and base class objects as arguments for any of the following base/member class constructors ( without any testing). It is no wonder that people used two step construction before C++ exception handling existed.
  • giving up on hierarchical & rich error messages in an environment which allows for code to be provided by customers or third parties and throw errors, which the original writer of the calling code could not possible have foreseen when writing the calling code and could have provided space for in his return error code range.
  • avoids such hacks as returning a pointer to a static memory object to message allocation failure which the authors of FlexLm did
  • being able to use a memory allocator returning addresses into a memory mapped sparse file. In this case allocation failure happens when one accesses the memory in question.(ok currently this works only on windows but apple forced the gnu team to provide the necessary functionality in the G++ compiler. Some more pressure from Linux g++ developer will be necessary to provide the this functionality also for them) (oops -- this even applies to STL)
  • being able to leave this C style coding behind us (ignoring return values) and having to use a debugger with debug executable to find out what is failing in a non trivial environment with child processes and shared libraries provided by third parties or doing remote execution
  • being able to return rich error information to the caller without just dumping everything to stderr
3

There is only one possibility to handle allocation failure under assumptions outlined in the question:

  • that allocator force application exit on allocation failure. In particular, this requires the cusror allocator.

Index-out-of-bound exceptions are less interesting in this context, because application can ensure they won't happen using pre-checks.

Andrei
  • 8,606
  • 10
  • 35
  • 43
3

Late to the party here although I didn't see any comparisons between C++ exception handling at Google and exception handling in Go. Specifically, Go only has error handling via a built-in error type. The linked Golang blog post explicitly concludes

Proper error handling is an essential requirement of good software. By employing the techniques described in this post you should be able to write more reliable and succinct Go code.

The creation of Golang certainly took considerations of best practices from working with C++ into account. The underlying intuition is that less can be more. I haven't worked at Google but do find their use of C++ and creation of Golang to be potentially suggestive of underlying best practices at the company.

Tyler
  • 1,050
  • 2
  • 14
  • 24