2017-09-05 85 views
7

我最近了解到,可以選擇加入iostreams的例外。爲了不必手動檢查文件是否打開我試過了,跑進此行爲:iostreams的例外

#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); 
} 

當異常掀起這個工作正常。但是當我使用例外時,我從basic_ios::clear得到一個,我想不出爲什麼。

basic_ios::clear未列在根據cppreference可以設置failbit的功能下。

在此先感謝。

編輯:下面的答案已經回答爲什麼發生這種情況。我現在另外的問題是如何避免這種異常?我的第二次嘗試通過這個循環替換std::copy

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

產生相同的異常。或者這種行爲是甚至打算的?

說明: clang不顯示此行爲。

+0

'basic_ios :: clear'是設置iostate的最低級別條目。其他函數通過調用'basic_ios :: clear'來設置failbit/badbit/eofbit。 – cpplearner

+0

@carderarner:謝謝你的闡述。我仍然不知道爲什麼上面的代碼片段拋出任何異常。 – sv90

回答

3

Jonesinator給了你異常的原因,我想強調的是,這個錯誤是獨立於異常之外的。實際上你的函數不是等價的,你不需要在no-exception分支中的操作之後檢查流。事實上,一個錯誤默默地發生。如果你寫了兩個函數等效的方式,您將得到同樣的結果:

#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); 
} 

輸出:

陷入錯誤!

陷入錯誤:basic_ios ::明確

恕我直言,這是一個很好的例子,爲什麼例外是在結果碼優於絕大多數情況下,他們應作爲默認。

+0

感謝您的回答!我仍然不明白的唯一原因是首先拋出異常。我認爲使用算法迭代器應該是閱讀文件的慣用方式。 您是否知道修改代碼的方法,以便除了使用原始循環外不會獲得這些異常或錯誤代碼? – sv90

+0

@ user4990485如果在輸入過程中達到EOF,則將使用eofbit設置失敗位。所以只需設置例外掩碼的壞點,但不要設置failbit或eofbit。此代碼沒有顯示遵循的做法。 – Oliv

+0

@奧利夫:謝謝你的解釋。當文件無法打開時,可悲的是'badbit'沒有設置,所以我將不得不手動檢查那個是我想要避免的... – sv90

4

使用GDB可以看到當std::istream_iterator增加時發生錯誤。

#0 __GI_raise ([email protected]=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 

解開循環,你可以發現它是增量的最後一個電話引起該問題,即調用std::istream_iterator::operator++當輸入流爲空。

仔細觀察堆棧跟蹤,當流爲空時,最後一個增量正在嘗試std :: istream :: operator >>。根據cppreference,由於操作無法從流中提取整數,因此會導致設置失敗位。