2016-08-23 6 views
4

Standard (N3337) говорит (27.5.3.1.1 Class ios_base::failure):C++ станд :: ios_base :: исключение недостаточности

Провал класса определяет базовый класс для типов всех объектов выброшенных в виде исключения, функции в iostreams библиотеки, сообщить об ошибках , обнаруженных во время операций буфера потока.

У меня есть простой тест программа, которая эмулирует ограниченную среду ресурсов при использовании в станд :: 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) я получил Caught: std::bad_alloc на stderr. One of online compilers показывает тот же результат.

Вопрос: почему тип исключения std::bad_alloc, а не std::ios_base::failure?

+4

Поскольку исключение не брошено библиотекой iostreams, но распределитель памяти? –

+0

Неужели он не будет восстановлен и упакован в исключение std :: ios_base :: failure? Я работаю с iostream не с распределителем памяти. – user1641854

+1

Вы можете так подумать, но нет, стандарт не требует этого. Существует также проблема, что после того, как «bad_alloc» спровоцирован, может не хватить места для создания другого исключения. –

ответ

2

os << 'F'; является operator<<(ostream&, char), который представляет собой форматированный вывод функция, и, со ссылкой на 27.7.3.6.1 [ostream.formatted.reqmts],

функциональных усилий, чтобы генерировать требуемый выход. Если генерация терпит неудачу, то форматированная функция вывода делает setstate(ios_base::failbit), что может вызвать исключение. Если во время вывода выбрано исключение, то ios::badbit включается, не вызывая выброса ios::failure. в состоянии *this. Если (exceptions()&badbit) != 0 того исключение вызвано повторно

В рамках вывода, эта функция вызывает stringbuf::overflow, которая указана в 27.8.2.4[stringbuf.virtuals]p8, чтобы выполнить перераспределение. Разница между libstdC++ и LIBC++ здесь является интерпретация последствий его отказа распределения:

в libstdC++, оно проливает std::bad_alloc из stringbuf::overflow, который разворачивает стеку вплоть до operator<< (технически, __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, который вы поймали.

Возможно LibC++ технически правильно stringbuf::overflow: стандарт говорит

'' Возвращает: '' traits::eof(), чтобы указать отказ.

, и трудно представить себе, как он может потерпеть неудачу, кроме как отказ от распределения, но я думаю, что интерпретация libstdC++ ближе к намерению. (В libstdC++, stringbuf::overflow все еще могут вернуться eof, если емкость буфера достигает string::max_size без первого удара bad_alloc)

0

Ошибка, которую вы создаете, не является ошибкой, вызванной операциями буфера потока как таковой. В какой-то момент у вас просто закончится нехватка памяти, а распределитель потока выкинет bad_alloc. Это исключение, которое вы видите.

Следует ли отказывать bad_alloc в качестве ошибки ios_base :: debassable, так как в результате операция потока не выполняется. Однако я не удивлен, увидев в этом случае ситуацию bad_alloc.

+0

Поведение отличается, по крайней мере, между libC++ и libstdC++. libC++ захватывает std :: bad_alloc и rethrows std :: ios_base :: отказ от потери исходных данных об исключении. Таким образом, нет никакого переносимого способа точно определить условие ENOMEM и обрабатывать его каким-то особым способом (например, ведение журнала некоторой константы из сегмента DSS). – user1641854