Exceptions on iostreams

I recently found out that you can select exceptions for iostreams. In order not to manually check if the file is open, I tried this and came across this behavior:

#include <algorithm> #include <iostream> #include <iterator> #include <sstream> #include <vector> void test(std::istream& is, bool exceptions) { try { if (exceptions) is.exceptions(std::istream::failbit); std::vector<int> input; std::copy(std::istream_iterator<int>{is}, {}, std::back_inserter(input)); for (auto x : input) { std::cout << x << '\n'; } } catch (const std::ios_base::failure& f) { std::cerr << "Caught error: " << f.what() << '\n'; } } int main() { // Emulates file std::stringstream ss("1 2 3\n4 5 6\n7 8 9\n"); test(ss, true); } 

When throwing exceptions, this works fine. But when I use exceptions, I get one thrown from basic_ios::clear , and I cannot think of a reason why.

basic_ios::clear not specified in a function that can set failbit according to cppreference .

Thanks in advance.

Edit:. The answers below explain why this is happening. Now my additional question is how to avoid this exception? My second attempt was to replace std::copy with this loop:

 for (int n; is >> n;) { input.push_back(n); } 

the same exception is thrown. Or is this behavior even intended?

Note: clang does not show this behavior.

+7
c ++ iostream exception
source share
2 answers

Jonesinator gave you the reason for the exception, I would just like to emphasize that the error exists regardless of the exception or not. In fact, your function is not equivalent, you do not check the thread after the operation in the branch without exception. In fact, the mistake is silent. If you write two functions in an equivalent way, you get the equivalent result:

 #include <iostream> #include <iterator> #include <sstream> #include <vector> void test_exception(std::istream& is) { try { is.exceptions(std::istream::failbit); std::vector<int> input; std::copy(std::istream_iterator<int>{is}, {}, std::back_inserter(input)); for (auto x : input) { std::cout << x << '\n'; } } catch (const std::ios_base::failure& f) { std::cout << "Caught error: " << f.what() << '\n'; } } void test_error_code(std::istream& is) { std::vector<int> input; std::copy(std::istream_iterator<int>{is}, {}, std::back_inserter(input)); if (!is.good()) { std::cout << "Caught error!" << std::endl; return; } for (auto x : input) { std::cout << x << '\n'; } } int main() { // Emulates file std::stringstream ss_error_code("1 2 3\n4 5 6\n7 8 9\n"); test_error_code(ss_error_code); std::stringstream ss_exception("1 2 3\n4 5 6\n7 8 9\n"); test_exception(ss_exception); } 

exit:

Got a mistake!

Caught error: basic_ios :: clear

IMHO, this is a great example of why exceptions prevail over result code in the vast majority of scenarios, and they should be used by default.

+3
source share

With GDB, you can see that an error occurs when std::istream_iterator incremented.

 #0 __GI_raise ( sig=sig@entry =6) at ../sysdeps/unix/sysv/linux/raise.c:51 #1 0x00007ffff71d13fa in __GI_abort () at abort.c:89 #2 0x00007ffff7ae80ad in __gnu_cxx::__verbose_terminate_handler() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #3 0x00007ffff7ae6066 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #4 0x00007ffff7ae60b1 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #5 0x00007ffff7ae62c9 in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #6 0x00007ffff7b0eea3 in std::__throw_ios_failure(char const*) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #7 0x00007ffff7b4a82d in std::basic_ios<char, std::char_traits<char> >::clear(std::_Ios_Iostate) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #8 0x00007ffff7b4d52f in std::istream::operator>>(int&) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #9 0x00005555555556c2 in std::istream_iterator<int, char, std::char_traits<char>, long>::_M_read (this=0x7fffffffe230) at /usr/include/c++/6/bits/stream_iterator.h:121 #10 0x0000555555555ac2 in std::istream_iterator<int, char, std::char_traits<char>, long>::operator++ (this=0x7fffffffe230) at /usr/include/c++/6/bits/stream_iterator.h:95 #11 0x0000555555555a36 in std::__copy_move<false, false, std::input_iterator_tag>::__copy_m<std::istream_iterator<int, char, std::char_traits<char>, long>, std::back_insert_iterator<std::vector<int, std::allocator<int> > > > (__first=..., __last=..., __result=...) at /usr/include/c++/6/bits/stl_algobase.h:293 #12 0x0000555555555965 in std::__copy_move_a<false, std::istream_iterator<int, char, std::char_traits<char>, long>, std::back_insert_iterator<std::vector<int, std::allocator<int> > > > (__first=..., __last=..., __result=...) at /usr/include/c++/6/bits/stl_algobase.h:386 #13 0x00005555555557e2 in std::__copy_move_a2<false, std::istream_iterator<int, char, std::char_traits<char>, long>, std::back_insert_iterator<std::vector<int, std::allocator<int> > > > (__first=..., __last=..., __result=...) at /usr/include/c++/6/bits/stl_algobase.h:424 #14 0x00005555555554c9 in std::copy<std::istream_iterator<int, char, std::char_traits<char>, long>, std::back_insert_iterator<std::vector<int, std::allocator<int> > > > (__first=..., __last=..., __result=...) at /usr/include/c++/6/bits/stl_algobase.h:456 #15 0x00005555555550ed in test (is=..., exceptions=true) at sample.cpp:12 #16 0x000055555555521c in main () at sample.cpp:25 

By expanding the loop, you may find that this is the last increment call that causes the problem, i.e. calls std::istream_iterator::operator++ when the input stream is empty.

When approaching the stack trace, the last step is trying to start std :: istream :: operator → when the stream is empty. According to cppreference , this will cause the bitbit to be set since the operation failed to extract an integer from the stream.

+4
source share

All Articles