【问题标题】:c++ std::ios_base::failure exceptionc++ std::ios_base::failure 异常
【发布时间】:2016-08-23 12:44:39
【问题描述】:

标准(N3337)说(27.5.3.1.1 Class ios_base::failure):

类失败定义了所有对象类型的基类 由 iostreams 库中的函数作为异常抛出,以报告 在流缓冲区操作期间检测到错误。

我有一个简单的测试程序,它在使用 std::ostringstream 时模拟受限资源环境:

#include <sys/time.h>
#include <sys/resource.h>

#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include <iostream>
#include <sstream>

int main(int argc, const char* argv[])
{
    rlimit limit;
    limit.rlim_cur = limit.rlim_max = 268435456;

    if(setrlimit(RLIMIT_AS, &limit)) {
        std::cerr << "Cannot set resource limit: " << strerror(errno) << std::endl;
        exit(EXIT_FAILURE);
    }

    std::ostringstream os;
    os.exceptions(std::ostringstream::badbit);

    try {
        auto iterations = 1024 * 1024 * 1024;

        while(iterations && --iterations) os << 'F';

    } catch(const std::ios_base::failure& ex) {
        std::cerr << "Caught: std::ios_base::failure" << std::endl;
    } catch(const std::bad_alloc& ex) {
        std::cerr << "Caught: std::bad_alloc" << std::endl;
    } catch(...) {
        std::cerr << "Caught: ellipsis" << std::endl;
    }

    return 0;
}

在我的环境(Linux,gcc 5.3.0)中,我在stderr 上获得了Caught: std::bad_allocOne of online compilers 显示相同的输出。

问题是:为什么异常类型是std::bad_alloc 而不是std::ios_base::failure

【问题讨论】:

  • 因为异常不是由 iostreams 库引发的,而是由内存分配器引发的?
  • 不应该重新捕获并打包在 std::ios_base::failure 异常中吗?我正在使用 iostream 而不是内存分配器。
  • 您可能会这么认为,但不,标准并不要求这样做。还有一个问题是,bad_alloc 被激发后,可能没有空间再创建一个异常了。
  • libc++ 将 std::bad_alloc 包装到 std::ios_base::failure 异常中,但丢失了 error_code 类别和值。
  • 很抱歉惹恼了您,但这正是我将其发布为评论而非答案的原因。实际上,它确实对您有所帮助;以这种方式使用标准头文件很久以前在 C++ 中已被弃用,我只是想为那些寻求解决方案并偶然发现这个问题的未来新手指出这一点。

标签: c++ linux c++11 exception iostream


【解决方案1】:

os &lt;&lt; 'F';operator&lt;&lt;(ostream&amp;, char),这是一个格式化输出函数,并且引用 27.7.3.6.1[ostream.formatted.reqmts],

该函数努力生成请求的输出。如果生成失败,则格式化输出函数会执行setstate(ios_base::failbit),这可能会引发异常。如果在输出期间抛出异常,则打开ios::badbit 而不会导致抛出ios::failure。处于*this 的错误状态。如果(exceptions()&amp;badbit) != 0 则重新抛出异常

作为输出的一部分,此函数调用stringbuf::overflow(在27.8.2.4[stringbuf.virtuals]p8 中指定)来执行重新分配。这里libstdc++和libc++的区别在于对其分配失败后果的解释:

在 libstdc++ 中,它从 stringbuf::overflow 中抛出 std::bad_alloc,这会将堆栈一直展开到 operator&lt;&lt;(技术上为 __ostream_insert),设置 badbit 并重新抛出,未修改,如上所述。

在 libc++ 中,std::bad_alloc 被捕获在 stringbuf::overflow 中,它使 overflow 返回 traits::eof,这反过来又使调用者(在本例中为 steambuf::xsputn)返回零,在反过来,使调用者__pad_and_output 完全清除流的rdbuf,进而使其调用者__put_character_sequence 同时设置badbit 和failbit。设置那个 badbit 会抛出你捕获的 ios::failure

也许 stringbuf::overflow 中的 libc++ 在技术上是正确的:标准说

''返回:'' traits::eof() 表示失败。

除了分配失败之外,很难想象它失败的方式,但我认为 libstdc++ 的解释更接近意图。 (在 libstdc++ 中,stringbuf::overflow 仍然可以返回eof,如果缓冲区容量达到string::max_size 而没有先命中bad_alloc

【讨论】:

    【解决方案2】:

    您正在创建的错误情况不是由流缓冲区操作本身引起的错误。在某些时候,你只是用完了内存,流的分配器会抛出一个bad_alloc。这是您看到的异常。

    bad_alloc 是否应该作为 ios_base::failure 重新抛出是有争议的,因为最终流操作确实失败了。不过,在这种情况下看到bad_alloc 的情况我并不感到惊讶。

    【讨论】:

    • 行为至少在 libc++ 和 libstdc++ 之间有所不同。 libc++ 捕获 std::bad_alloc 并重新抛出 std::ios_base::failure 失去原始异常细节。因此,没有可移植的方法来准确确定 ENOMEM 条件并以某种特殊方式处理它(例如从 DSS 段记录一些常量字符串)。
    猜你喜欢
    • 2012-11-19
    • 1970-01-01
    • 1970-01-01
    • 2012-08-28
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多