简体   繁体   English

Google C++ 风格指南的无例外规则; STL?

[英]Google C++ style guide's No-exceptions rule; STL?

Google's C++ style guide says "We do not use exceptions". Google 的C++ 风格指南说“我们不使用异常”。 The style does not mention STL with respect to usage of exception.该样式没有提及关于异常使用的 STL。 Since STL allocators can fail, how do they handle exceptions thrown by containers?既然 STL 分配器可能会失败,那么它们如何处理容器抛出的异常呢?

  1. If they use STL, how is the caller informed of allocation failures?如果他们使用 STL,调用者如何获知分配失败? STL methods like push_back() or map operator[] do not return any status codes.push_back()或 map operator[]这样的 STL 方法不返回任何状态代码。
  2. If they do not use STL, what container implementation do they use?如果他们不使用 STL,他们使用什么容器实现?

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.由于 Google 现有的大多数 C++ 代码都没有准备好处理异常,因此采用生成异常的新代码相对困难。

The usual legacy problem.通常的遗留问题。 :-( :-(

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.自 2008 年以来,我一直是 Google Search 的一名工程师,使用 C++。我们确实经常使用 STL 容器。 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."我个人无法回忆起一个单一的重大失败或错误,这些错误可以追溯到像 vector::push_back() 或 map::operator[] 失败,我们说“哦,伙计,我们必须重写这段代码,因为分配可能失败”或“该死,如果我们使用异常,这本来可以避免的。” Does a process ever run out of memory?进程是否会耗尽内存? Yes, but this is usually a simple mistake (eg, 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.是的,但这通常是一个简单的错误(例如,有人向程序添加了一个大的新数据文件而忘记增加 RAM 分配)或灾难性的失败,没有好的方法可以恢复和继续。 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.所以据我所知,这里没有问题。

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).如果您查看他们的cpplint 脚本,它会检查以确保您包含正确的 STL 容器标头(如向量、列表等)。

I have found that Google mentions this explicitly about STL and exceptions (emphasis is mine):我发现谷歌明确提到了 STL 和异常(重点是我的):

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++.尽管您不应该在自己的代码中使用异常,但它们在 ATL 和一些 STL(包括 Visual C++ 附带的 STL)中被广泛使用。 When using the ATL, you should define _ATL_NO_EXCEPTIONS to disable exceptions.使用 ATL 时,您应该定义 _ATL_NO_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.您应该调查是否还可以在 STL 中禁用异常,但如果不能,则可以在编译器中打开异常。 (Note that this is only to get the STL to compile. You should still not write exception handling code yourself. ) (注意,这只是为了让 STL 编译,你还是不要自己写异常处理代码。

I don't like such decisions (lucky that I am not working for Google), but they are quite clear about their behaviour and intentions.我不喜欢这样的决定(幸运的是我不是为 Google 工作),但他们非常清楚自己的行为和意图。

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 .例如,如果您在 Linux 上调用malloc()并要求获得非常大的内存块,即使实际上没有支持它所需的内存,它也会成功。 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.尽管如此,捕获未处理的异常并记录e.what()输出,然后重新throw是一种很好的做法,因为这可能比回溯提供更多信息,并且典型的 C++ 库实现不会自动为您执行此操作。

  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 . C(++) 标准可能无法保证,但在现代系统上,如果内存不足,您唯一可以依赖的就是崩溃 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.特别是,您不能依赖于从分配器获取NULL或任何其他指示,甚至包括 C++ 异常。

  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.如果您发现自己在一个可以访问第 0 页的嵌入式系统上,我强烈建议您通过在该位置映射一个无法访问的页面来解决这个问题。 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 .不能依赖人类在任何地方检查NULL指针,但是您可以通过一次映射页面来解决这个问题,而不是尝试纠正每个可能(过去、现在将来)有人可能错过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;但总的来说,在21世纪,恐怕你不太可能得到这个机会; the first you'll know that your system is out of memory is when things start crashing.当系统开始崩溃时,您首先会知道系统内存不足。

Stl itself is directly only throwing in case of memory allocation failure. Stl 本身只是在内存分配失败的情况下直接抛出。 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.在 32 位系统上,内存分配失败不应被忽略,因为它可能会发生。 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.并且 C++ 异常处理早于 64 位体系结构很长一段时间。 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.我记得 1997 年左右来自 IBM 的一些论文,其中说明 IBM 的某些人对 C++ 异常处理的含义的理解和欣赏程度如何。 Ok professionalism is not necessary an indicator of success.好的,专业精神不是成功的必要指标。 So giving up exception handling is not only a problem if one uses STL.因此,如果使用 STL,放弃异常处理不仅是个问题。 It is a problem if one uses C++ as such.如果像这样使用 C++,这是一个问题。 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.难怪人们在 C++ 异常处理出现之前使用两步构造。
  • 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避免了诸如将指向静态内存对象的指针返回到 FlexLm 的作者所做的消息分配失败之类的黑客攻击
  • 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)在这种情况下,当访问有问题的内存时会发生分配失败。(好吧,目前这仅适用于 Windows,但苹果迫使 gnu 团队在 G++ 编译器中提供必要的功能。Linux g++ 开发人员需要施加更多压力来提供这个功能也适用于他们)(哎呀——这甚至适用于 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能够将这种 C 风格的编码留在我们身后(忽略返回值),并且必须使用带有调试可执行文件的调试器来找出在具有子进程和第三方提供的共享库或进行远程执行的非平凡环境中失败的原因
  • being able to return rich error information to the caller without just dumping everything to stderr能够向调用者返回丰富的错误信息,而无需将所有内容转储到 stderr

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.特别是,这需要 cusror 分配器。

Index-out-of-bound exceptions are less interesting in this context, because application can ensure they won't happen using pre-checks.在这种情况下,索引越界异常不太有趣,因为应用程序可以使用预检查确保它们不会发生。

Late to the party here although I didn't see any comparisons between C++ exception handling at Google and exception handling in Go.虽然我没有看到 Google 的 C++ 异常处理和 Go 的异常处理之间有任何比较,但还是迟到了。 Specifically, Go only has error handling via a built-in error type.具体来说, Go 仅通过内置error类型进行错误处理 The linked Golang blog post explicitly concludes链接的 Golang 博客文章明确得出结论

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.通过使用本文中描述的技术,您应该能够编写更可靠、更简洁的 Go 代码。

The creation of Golang certainly took considerations of best practices from working with C++ into account. Golang 的创建当然考虑了使用 C++ 的最佳实践。 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.我没有在 Google 工作过,但确实发现他们对 C++ 的使用和 Golang 的创建可能暗示了公司的潜在最佳实践。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM