Google C++ 风格指南的无例外规则;STL?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5184115/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Google C++ style guide's No-exceptions rule; STL?
提问by Andrei
Google's C++ style guidesays "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?
Google 的C++ 风格指南说“我们不使用异常”。该样式未提及有关异常使用的 STL。既然 STL 分配器可能会失败,那么它们如何处理容器抛出的异常呢?
- If they use STL, how is the caller informed of allocation failures? STL methods like
push_back()
or mapoperator[]
do not return any status codes. - If they do not use STL, what container implementation do they use?
- 如果他们使用 STL,调用者如何获知分配失败?像
push_back()
或 mapoperator[]
这样的STL 方法不返回任何状态代码。 - 如果他们不使用 STL,他们使用什么容器实现?
回答by Bo Persson
They say that theydon'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. :-(
通常的遗留问题。:-(
回答by hoffmanj
We simply don'thandle 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.
自 2008 年以来,我一直是 Google Search 的一名工程师,使用 C++。我们确实经常使用 STL 容器。我个人无法回忆起一个单一的重大失败或错误,这些错误可以追溯到像 vector::push_back() 或 map::operator[] 失败,我们说“哦,伙计,我们必须重写这段代码,因为分配可能失败”或“该死,如果我们使用异常,这本来可以避免的。” 进程是否会耗尽内存?是的,但这通常是一个简单的错误(例如,有人向程序添加了一个大的新数据文件而忘记增加 RAM 分配)或灾难性的失败,没有好的方法可以恢复和继续。我们的系统已经自动管理和重新启动作业,以对有故障磁盘、宇宙射线等的机器保持稳健,这真的没有什么不同。
So as far as I can tell, there is no problem here.
所以据我所知,这里没有问题。
回答by Mark Loeser
I'm pretty sure that they mean they do not use exceptions in theircode. 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 容器标头(如向量、列表等)。
回答by Yongwei Wu
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++. 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.)
尽管您不应该在自己的代码中使用异常,但它们在 ATL 和一些 STL(包括 Visual C++ 附带的 STL)中被广泛使用。使用 ATL 时,您应该定义 _ATL_NO_EXCEPTIONS 以禁用异常。您应该调查是否还可以在 STL 中禁用异常,但如果不能,则可以在编译器中打开异常。(注意,这只是为了让 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 工作),但他们非常清楚自己的行为和意图。
回答by alastair
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.
在现代操作系统上无论如何您都无法处理分配失败;作为性能优化,它们通常会过度使用内存。例如,如果您malloc()
在 Linux 上调用并要求获得非常大的内存块,即使实际上不存在支持它所需的内存,它也会成功。只有当您访问它时,内核才真正尝试分配页面来支持它,此时再告诉您分配失败了,为时已晚。
So:
所以:
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.
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.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 onlything 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.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 oncerather than trying to correct every possible (past, present andfuture) location at which someone might have missed aNULL
.
除特殊情况外,不要担心分配失败。如果机器内存不足,那就是灾难性的故障,您无法可靠地从中恢复。
尽管如此,捕获未处理的异常并记录
e.what()
输出,然后重新记录是一种很好的做法throw
,因为这可能比回溯提供更多信息,并且典型的 C++ 库实现不会自动为您执行此操作。上面关于在内存不足时如何不依赖于崩溃的整个大线程是完整的,完全是垃圾。C(++) 标准可能无法保证,但在现代系统上,如果内存不足,您唯一可以依靠的就是崩溃。特别是,您不能依赖于
NULL
从您的分配器获取或实际上任何其他指示,直到并包含 C++ 异常。如果您发现自己在一个可以访问第 0 页的嵌入式系统上,我强烈建议您通过在该位置映射一个无法访问的页面来解决这个问题。不能依靠人类
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 youcan 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.
我将通过说您可能正在使用某种自定义分配器来限定上述内容,或者您在一个不会过度提交的系统上(没有交换的嵌入式系统是一个例子,但不是唯一的)例子)。在这种情况下,也许您可以在系统上优雅地处理内存不足的情况。但总的来说,在21世纪,恐怕你不太可能得到这个机会;当系统开始崩溃时,您首先会知道系统内存不足。
回答by alastair
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
Stl 本身只是在内存分配失败的情况下直接抛出。但通常现实世界的应用程序可能会由于多种原因而失败,内存分配失败只是其中之一。在 32 位系统上,内存分配失败不应被忽略,因为它可能发生。所以上面关于内存分配失败不会发生的整个讨论是毫无意义的。即使假设是这样,也必须使用两步初始化来编写代码。并且 C++ 异常处理早于 64 位体系结构很长一段时间。我不确定我应该在多大程度上尊重谷歌在此处显示的负面专业精神,并且只回答提出的问题。我记得 1997 年左右来自 IBM 的一些论文,其中说明 IBM 的某些人对 C++ 异常处理的含义的理解和欣赏程度如何。好的,专业精神不是成功的必要指标。因此,如果使用 STL,放弃异常处理不仅是个问题。如果像这样使用 C++,这是一个问题。意味着放弃
- 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
- 构造函数失败
- 能够使用成员对象和基类对象作为以下任何基类/成员类构造函数的参数(无需任何测试)。难怪人们在 C++ 异常处理出现之前使用两步构造。
- 在允许客户或第三方提供代码并抛出错误的环境中放弃分层和丰富的错误消息,这是调用代码的原始作者在编写调用代码时无法预见的,并且可以提供空间因为在他的返回错误代码范围内。
- 避免了诸如将指向静态内存对象的指针返回到 FlexLm 的作者所做的消息分配失败之类的黑客攻击
- 能够使用内存分配器将地址返回到内存映射稀疏文件中。在这种情况下,当访问有问题的内存时会发生分配失败。(好吧,目前这仅适用于 Windows,但苹果迫使 gnu 团队在 G++ 编译器中提供必要的功能。Linux g++ 开发人员需要施加更多压力来提供这个功能也适用于他们)(哎呀——这甚至适用于 STL)
- 能够将这种 C 风格的编码留在我们身后(忽略返回值),并且必须使用带有调试可执行文件的调试器来找出在具有子进程和第三方提供的共享库或进行远程执行的非平凡环境中失败的原因
- 能够向调用者返回丰富的错误信息,而无需将所有内容转储到 stderr
回答by Andrei
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.
在这种情况下,索引越界异常不太有趣,因为应用程序可以使用预检查确保它们不会发生。
回答by Tyler
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 handlingvia a built-in error
type. The linked Golang blog post explicitly concludes
虽然我没有看到 Google 的 C++ 异常处理和 Go 的异常处理之间有任何比较,但还是迟到了。具体来说,Go 仅通过内置error
类型进行错误处理。链接的 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. 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.
Golang 的创建当然考虑了使用 C++ 的最佳实践。所述底层的直觉是少可以成多。我没有在 Google 工作过,但确实发现他们对 C++ 的使用和 Golang 的创建可能暗示了公司的潜在最佳实践。