2017-09-05 40 views
7

Yakın zamanda bir kişinin iostreams istisnalarına girebileceğini öğrendim. Bir dosya Denedim ve bu davranışa koştu açıksa amacıyla elle kontrol etmek zorunda değil:iostreams ile ilgili istisnalar

istisnalar kapalı ayarlanır
#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); 
} 

bu normalde çalışır. Ama istisnaları kullandığımda, basic_ios::clear'dan birini atıyorum ve nedenini düşünemiyorum.

basic_ios::clear, failbit değerini cppreference'a göre ayarlayamayan işlev altında listelenmez.

Şimdiden teşekkürler.

Düzeltme: Aşağıdaki yanıtlar, bunun nedenini zaten yanıtladı. Şimdi ek sorum, bu istisnanın nasıl önleneceğidir? Benim ikinci girişimi bu döngü tarafından std::copy değiştirmekti:

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

aynı durum üretti. Yoksa bu davranış bile amaçlanmış mı?

Not: clang bu davranışı göstermiyor.

+0

'basic_ios :: clear' iostate'i kuran en düşük düzey girdidir. Diğer fonksiyonlar 'basic_ios :: clear' diyerek failbit/badbit/eofbit olarak ayarlandı. – cpplearner

+0

@cpplearner: Bunu netleştirdiğiniz için teşekkür ederiz. Yukarıdaki snippet'in neden bir istisna oluşturduğunu hala bilmiyorum. – sv90

cevap

3

Jonesinator size istisnanın sebebini vermiştir, sadece hatanın istisnasız olarak bağımsız olup olmadığını vurgulamak isterim. Aslında, işleviniz eşdeğer değildir, istisna dışındaki branşta işlemden sonra akışı denetlemezsiniz. Aslında, bir hata sessizce gerçekleşir. Eğer eş bir şekilde iki işlevi yazarsanız size eşdeğer bir sonuç elde edecektir:

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

çıkışı:

yakalandı hatası!

yakalandı hatası: basic_ios ::

IMHO berrak, bu istisnalar senaryonun büyük çoğunluğunda sonuç kodu üzerinde üstündür neden büyük bir örnektir ve ön tanımlı olarak kullanılmalıdır.

+0

Cevabınız için teşekkürler! Hala anlamadığım tek şey istisnanın neden ilk etapta atıldığıdır. Yinelemeyi algoritmalarla kullanmanın dosyanın okunması için deyimsel bir yol olduğunu düşünmüştüm. Ham döngü kullanmak dışında bu istisnaları veya hata kodlarını almamak için kodu değiştirmenin bir yolunu biliyor musunuz? – sv90

+0

@ user4990485 Giriş sırasında EOF'ye ulaşılırsa, hata noktası eofbit ile ayarlanır. Bu yüzden istisna maskesinin badbit'ini ayarlayın, ancak failbit veya eofbit ayarlamayın. Bu kod takip edilecek bir uygulama göstermemektedir. – Oliv

+0

@Oliv: Açıklamanız için teşekkürler. Ne yazık ki 'badbit' bir dosya açılamadığında ayarlanmadı, bu yüzden elimden gelmek istediğimi elle kontrol etmek zorunda kaldım ... – sv90

4

GDB'yi kullanarak, std::istream_iterator artırıldığında hata olduğunu görebilirsiniz.

#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 

Döngüler, bunun giriş akışı boş olduğunda, yani std::istream_iterator::operator++ çağrı soruna neden olur artışına son arama olduğunu bulabilirsiniz.

Yığın izine yakından bakıldığında, son artış, akış boş olduğunda std :: istream :: operator >> çalışıyor. cppreference uyarınca, işlem, akıştan bir tamsayı ayıklayamadığından başarısızlık değerinin ayarlanmasıyla sonuçlanır.