尝试从“boost::asio::ip::tcp::iostream”捕获异常时,“在抛出'std::__ios_failure'实例后终止调用”

"Terminate called after throwing an instance of 'std::__ios_failure'" when trying to catch exception from `boost::asio::ip::tcp::iostream`

提问人:yeputons 提问时间:4/21/2021 更新时间:4/21/2021 访问量:2405

问:

我写了一个简单的阻塞服务器,它等待单个客户端并淹没它。

我使用类与客户端交互,因为我想在将来使用格式化的 I/O。我还想在每次操作后使用异常进行错误处理,而不是手动检查标志,所以我以类似于 basic_ios::exceptions 的方式调用(我假设 Boost 源自后者,不是吗?boost::asio::ip::tcp::iostream.exceptions()

下面是生成的代码:

#include <boost/asio.hpp>
#include <exception>
#include <iostream>
#include <sstream>
#include <utility>

using boost::asio::ip::tcp;

int main() {
    try {
        try {
            boost::asio::io_context io_context;
            tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 10000));
            std::cout << "Listening at " << acceptor.local_endpoint() << "\n";

            tcp::iostream client(acceptor.accept());
            client.exceptions(std::ios_base::failbit | std::ios_base::badbit |
                              std::ios_base::eofbit);  // (1)
            while (client << "x\n") {
            }
            std::cout << "Completed\n";
        } catch (std::exception &e) {
            std::cout << "Exception: " << e.what() << "\n";
        }
    } catch (...) {
        std::cout << "Unknown exception\n";
    }
    return 0;
}

不幸的是,看起来一些例外情况仍然从裂缝中消失。如果我启动服务器,通过连接,看一会儿 s,然后 Ctrl+C 客户端,终止它,服务器 s 出现以下错误消息:nc localhost 10000xstd::terminate

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
terminate called after throwing an instance of 'std::__ios_failure'
  what():  basic_ios::clear: iostream error

如果我注释掉该行,则一切正常,当客户端断开连接时,服务器会以一条消息终止。(1)Completed

我已经在 Windows(和 Boost 1.75.0-3)和 Ubuntu 20.04(和 Boost 1.71.0.0ubuntu2)上尝试过。g++ (Rev6, Built by MSYS2 project) 10.2.0g++-10 (Ubuntu 10.2.0-5ubuntu1~20.04) 10.2.0

我做错了什么?

Ubuntu 上的 GDB 会话显示异常是从析构函数内部抛出的,这大概是 ,这有点奇怪:noexcept

#0  0x00007ffff7e86762 in __cxa_throw () from /lib/x86_64-linux-gnu/libstdc++.so.6
#1  0x00007ffff7e7dc63 in std::__throw_ios_failure(char const*) () from /lib/x86_64-linux-gnu/libstdc++.so.6
#2  0x00007ffff7ef0dd2 in std::basic_ios<char, std::char_traits<char> >::clear(std::_Ios_Iostate) ()
   from /lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff7f0f28e in std::ostream::sentry::~sentry() () from /lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x00007ffff7f0f975 in std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_trait
s<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) ()
   from /lib/x86_64-linux-gnu/libstdc++.so.6
#5  0x00007ffff7f0fd5c in std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(s
td::basic_ostream<char, std::char_traits<char> >&, char const*) () from /lib/x86_64-linux-gnu/libstdc++.so.6
#6  0x000055555555d3a8 in main () at test.cpp:19
C++ 异常 提升 IOSTREAM noexcept

评论

0赞 yeputons 4/21/2021
看起来 libstdc++ 出于任何原因在哨兵析构函数中调用 setstate,这会抛出,这会导致 .如果我用 clang 和 编译示例,问题就会消失:不再 ,但根本没有抛出异常。std::terminate-stdlib=libc++std::terminate
0赞 Zoso 4/21/2021
这并不表示是~sentrynoexcept
1赞 yeputons 4/21/2021
找到相关的 LWG 问题 397,该问题已通过“准备审查”LWG 835(尚未在标准中)解决。
0赞 yeputons 4/21/2021
@Zoso据我所知,析构函数从 C++11 开始就隐含了,除非基础析构函数可能会抛出。不确定标准的意图是什么。noexceptsentry
1赞 Zoso 4/21/2021
您提到的默认情况下是 true,除非析构函数可能会抛出。这种召唤可能会投掷LLVM 更明确地捕捉到了这一点。noexceptsetstate~sentry

答: 暂无答案