Что нового в работе с исключениями в C++11

    В интернете довольно много говорят о новых возможностях C++11: auto, lambda, variadic templates. Но как-то обошли стороной новые возможности работы с исключениями, которые предоставляет язык и стандартная библиотека.

    От предыдущей версии стандарта остался механизм генерации исключений (throw), проверка того, что мы находимся в процессе обработки исключения (std::uncaught_exception), механизм остановки, если исключение не было обработано. Также есть иерархия стандартных исключений на базе класса std::exception.

    Новый стандарт добавляет к этим вещам еще несколько сущностей, которые, на мой взгляд, могут существенно упростить работу с исключениями в C++.



    exception_ptr

    Итак, самое первое, с чем мы можем столкнуться — это std::exception_ptr. Этот тип позволяет в себе хранить исключение абсолютно любого типа. Стандарт не оговаривает каким образом получен этот тип. Это может быть typedef, это может быть реализация класса. Его поведение сходно поведению std::shared_ptr, то есть его можно копировать, передавать в качестве параметра, при этом само исключение не копируется. Основное предназначение exception_ptr — это передача исключений в качестве параметров функции, возможна передача исключений между потоками. Таким образом, объекты данного типа позволяют сделать обработку ошибок более гибкой:
    struct some_exception {
    	explicit some_exception(int x): v(x) {
    		std::cout << " int ctor" << std::endl;
    	}
    
    	some_exception(const some_exception & e): v(e.v) {
    		std::cout << " copy ctor" << std::endl;
    	}
    
    	int v;
    };
    
    std::exception_ptr throwExceptionAndCaptureExceptionPtr() {
    	std::exception_ptr currentException;
    	try {
    		const int throwValue = 10;
    		std::cout << "throwing           " << throwValue << "..." << std::endl;
    		throw some_exception(throwValue);
    
    	} catch (...) {
    		 currentException = std::current_exception();
    	}
    
    	return currentException;
    }
    
    void rethrowException(std::exception_ptr ePtr) {
    	try {
    		if (ePtr) {
    			std::rethrow_exception(ePtr);
    		}
    
    	} catch (const some_exception & e) {
    		std::cout << "catched int value: " << e.v << std::endl;
    	}
    
    	std::exception_ptr anotherExceptionPtr = ePtr;
    	try {
    		if (anotherExceptionPtr) {
    			std::rethrow_exception(anotherExceptionPtr);
    		}
    
    	} catch (const some_exception & e) {
    		std::cout << "catched int value: " << e.v << std::endl;
    	}
    }
    
    void checkMakeExceptionPtr() {
    	std::exception_ptr currentException = std::make_exception_ptr(some_exception(20));
    	std::cout << "exception_ptr constructed" << std::endl;
    
    	rethrowException(currentException);
    }
    
    void exceptionPtrSample() {
    	rethrowException(throwExceptionAndCaptureExceptionPtr());
    	checkMakeExceptionPtr();
    }

    Если мы запустим функцию exceptionPtrSample на выполнение, то увидим примерно следующий результат:
    throwing 10…
    int ctor
    catched int value: 10
    catched int value: 10
    int ctor
    copy ctor
    copy ctor
    exception_ptr constructed
    catched int value: 20
    catched int value: 20

    Для того чтобы можно было удобно работать с exception_ptr, есть несколько вспомогательных функций:
    • current_exception — данная функция возвращает exception_ptr. Если мы находимся внутри блока catch, то возвращает exception_ptr, который содержит обрабатываемое в данный момент текущим потоком исключение, если вызывать ее вне блока catch, то она вернет пустой объект exception_ptr
    • rethrow_exception — данная функция бросает исключение, которое содержится в exception_ptr. Если входной параметр не содержит исключения (пустой объект), то результат не определен. При этом msvc кидает std::bad_exception, а программа собранная с помощью gcc-4.7.2 неожиданно завершается.
    • make_exception_ptr — данная функция, может сконструировать exception_ptr без бросания исключения. Ее предназначение аналогично функции std::make_shared — конструирование объекта. Ее выполнение аналогично функции throwExceptionAndCaptureExceptionPtr. В реализации от gcc-4.7.2 make_exception_ptr делает два копирования объекта some_exception.


    Передача исключений между потоками

    Как мне кажется, тип exception_ptr создавался именно для решения проблемы передачи исключений между потоками, поэтому давайте разберемся, каким образом мы можем передать исключение из одного потока в другой:
    void worker(std::promise<void> & p) {
    	try {
    		throw std::runtime_error("exception from thread");
    	} catch (...) {
    		p.set_exception(std::current_exception());
    	}
    }
    
    void checkThreadAndException() {
    	std::promise<void> p;
    	auto result = p.get_future();
    
    	std::thread t(worker, ref(p));
    	t.detach();
    
    	try {
    		result.get();
    	} catch (const std::runtime_error & e) {
    		std::cout << "runtime error catched from async worker" << std::endl;
    	}
    }

    Вообще, многопоточность в C++11 — это обширная тема, там есть свои тонкости, нюансы и о них следует писать отдельно. Сейчас мы рассмотрим этот пример только ради передачи исключения. Запускаем функцию worker в отдельном потоке, и эта функция кидает исключение. Объект класса promise позволяет организовать связь между разными потоками, и атомарно передать значения из одного потока в другой (или исключение). В данном примере мы как раз пользуемся методом set_exception, который принимает exception_ptr в качестве параметра. Для того чтобы получить значение, мы создаем объект класса future — это наш result и вызываем метод get. Также необходимо у потока вызвать метод detach или join, так как при разрушении объекта t в деструкторе проверяется, чтобы joinable() == false, иначе вызывается std::terminate. Скорее всего, это связано с тем, чтобы программист не «отпускал потоки на волю», а всегда следил за ними (либо отпускал явно с помощью метода detach)

    Отдельно стоит сказать об использовании многопоточности в gcc-4.7. Изначально этот пример у меня не работал (выбрасывал исключение), погуглив, я выяснил, что для использования std::thread необходимо передать линкеру флаг -pthread. Так как я использую CMake в качестве системы сборки, то эта задача упрощается (тут сложность может возникнуть при использовании gcc на разных платформах, например на sparc solaris используется флаг -thread вместо -pthread) — есть специальный CMake модуль Threads, в котором эта проблема решена:
    find_package(Threads REQUIRED)
    #…
    target_link_libraries(cxx_exceptions ${CMAKE_THREAD_LIBS_INIT})


    Nested exceptions

    Как видно из названия, данный механизм позволяет «прицепить» к кидаемому исключению другие исключения (которые могли быть брошены раньше). Например, если у нас есть своя иерархия исключений, то мы можем ловить все «сторонние» исключения, прицеплять их к своим исключениям, а при обработке своих исключений мы можем выводить доп. информацию, которая к ним «прицеплена» (например, при отладке, мы можем печатать информацию о сторонних исключениях). Хороший пример использования nested exception приведен на cppreference.com, мой пример, отчасти пересекается с ним:
    struct third_party_exception {
    	explicit third_party_exception(int e) : code(e) {
    	}
    
    	int code;
    };
    
    
    void third_party_worker() {
    	throw third_party_exception(100);
    }
    
    class our_exception : public std::runtime_error {
    public:
    	our_exception(const std::string & e) : std::runtime_error("our error: " + e) {
    	}
    };
    
    void ourWorker() {
    	try {
    		third_party_worker();
    	} catch (...) {
    		throw_with_nested(our_exception("worker failed"));
    	}
    }
    
    void exceptionHandler(const our_exception & e, bool catchNested) {
    	std::cerr << "our exception catched: " << e.what();
    
    	if (catchNested) {
    		try {
    			rethrow_if_nested(e);
    		} catch (const third_party_exception & e) {
    			std::cerr << ", low level reason is: " << e.code;
    		}
    	}
    
    	std::cerr << std::endl;
    }
    
    void exceptionProcessing() {
    	try {
    		ourWorker();
    	} catch (const our_exception & e) {
    		exceptionHandler(e, false);
    	}
    
    	try {
    		ourWorker();
    	} catch (const our_exception & e) {
    		exceptionHandler(e, true);
    	}
    }


    Итак, у нас есть сторонняя функция, которая кидает исключение, мы можем написать адаптер, который ловит «сторонние» исключения, и из них делает «наше» исключение: в качестве «сторонней» функции и «нашей» функции выступают соответственно third_party_worker и ourWorker. Мы ловим все исключения, и бросаем далее уже наше (our_exception) исключение, при этом, к нему было прицеплено какое-то (мы в принципе можем и не знать какое) «стороннее» исключение. После этого работаем уже с нашими исключениями. При этом, если нам понадобится более детальная информация о том, что происходило на «нижнем» уровне, то мы всегда можем вызвать функцию rethrow_if_nested. Данная функция анализирует, если ли прицепленное (вложенное) исключение, и если есть, то бросает это вложенное исключение. Функция exceptionHandler принимает «наше» исключение и дополнительный флаг, который разрешает или запрещает вывод информации о стороннем исключении. Мы можем управлять выводом сторонних исключений, управляя параметром catchNested, например, из файла конфигурации (или в зависимости от сборки — Release, Debug).

    Для работы с nested exception есть один класс и две функции:
    • nested_exception — данный класс «подмешивается» к бросаемому объекту при вызове функции std::throw_with_nested — этот класс также позволяет вернуть вложенное исключение (exception_ptr) с помощью метода nested_ptr. Также этот класс имеет метод rethrow_nested, который бросает вложенное исключение
    • throw_with_nested — данная шаблонная функция принимает объект некоторого типа (назовем его InputType), и бросает объект который является наследником std::nested_exception и нашего InputType (в реализации от gcc — это шаблонный тип, который наследуется от nested_exception и InputType). Таким образом, мы можем ловить как объект нашего типа, так и объект типа nested_exception и уже потом получать наш тип через метод nested_ptr
    • rethrow_if_nested — данная функция определяет, есть ли у объекта вложенное исключение, и если есть, то бросает его. Реализация может использовать dynamic_cast для определения наследования

    В принципе, механизм nested exception довольно интересный, хотя реализация может быть довольно тривиальна, ее можно сделать самому, с помощью ранее описанных функций current_exception и rethrow_exception. В той же реализации gcc, класс nested_exception содержит одно поле типа exception_ptr, которое инициализируется в конструкторе с помощью функции current_exception, а реализация метода rethrow_nested просто вызывает функцию rethrow_exception.

    Спецификация noexcept

    Данный механизм можно рассматривать как расширенный (и ныне устаревший) механизм throw(). Основное его предназначение как и раньше — гарантия того что, функция не бросит исключение (если гарантия нарушается, то вызывается std::terminate).

    Используется этот механизм в двух видах, аналогично старому throw()
    void func() noexcept {
    //...
    }


    И в новом виде:
    void func() noexcept(boolean_expression_known_at_compile_time) {
    //...
    }

    При этом, если значение выражения вычислено как истина, то функция помечается как noexcept, иначе, такой гарантии нет.
    Также есть соответствующий оператор noexcept(expression), который тоже выполняется в compile time, этот оператор возвращает истину, если выражение не бросает исключение:
    void noexceptSample() {
    	cout << "noexcept int():         " << noexcept(int()) << endl;
    	cout << "noexcept vector<int>(): " << noexcept(vector<int>()) << endl;
    }
    

    Данный код для gcc-4.7.2 выводит:
    noexcept int(): 1
    noexcept vector<int>(): 0


    Здесь мы видим, что конструктор встроенного типа int не бросает исключение, а конструктор вектора может бросить (не помечен как noexcept).
    Это удобно применять в шаблонном метапрограмировании, используя данный оператор мы можем написать шаблонную функцию, которая в зависимости от параметра шаблона может быть помечена как noexcept или нет:
    template <typename InputTypeT>
    void func() noexcept(noexcept(InputTypeT())) {
    	InputTypeT var;
    	/// do smth with var
    	std::cout << "func called, object size: " << sizeof(var) << std::endl;
    }
    
    void noexceptSample() {
    	std::cout << "noexcept int():         " << noexcept(int()) << std::endl;
    	std::cout << "noexcept vector<int>(): " << noexcept(std::vector<int>()) << std::endl << std::endl;
    
    	/// @note function is not actually called
    	std::cout << "noexcept func<int>:         " << noexcept(func<int>()) << std::endl;
    	std::cout << "noexcept func<vector<int>>: " << noexcept(func<std::vector<int>>()) << std::endl;
    }

    Данный пример выводит:
    noexcept int(): 1
    noexcept vector<int>(): 0

    noexcept func<int>: 1
    noexcept func<vector<int>>: 0


    Резюме

    Стандарт C++11 привнес много нового в обработку ошибок, конечно, ключевой особенностью здесь остается exception_ptr и возможность передачи произвольных исключений как обычные объекты (в функции, передавать исключения между потоками). Раньше, в каждом потоке приходилось писать развесистый try… catch для всех исключений, а этот функционал существенно минимизирует количество try… catch кода.

    Также появилась возможность создавать вложенные исключения, в принципе в библиотеке boost есть механизм boost::exception, который позволяет прикреплять к объекту произвольные данные (например коды ошибок, свои сообщения и пр), но он решает другие задачи — передачу произвольных данных внутри исключения.

    Ну и наконец, для тех, кому нужны гарантии, что код не бросит исключения, есть noexcept, в частности, многие части стандартной библиотеки используют этот механизм.

    Как обычно, все примеры выложены на github

    Update 1. Убрал из примеров using namespace std, теперь видно какие сущности относятся к стандартной библиотеке, а какие нет.
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 42

      –2
      После работы с исключениями в управляемом коде (на примере C#) исключения в плюсах кажутся крайне топорными, даже в последнем стандарте.
        +2
        Мне было бы приятно иметь в шарпе noexcept
          0
          Для чего, если не секрет? Если я правильно понимаю, noexcept используется для того, чтобы быть уверенным, что в результате выброса исключения не потеряется указатель на ресурс, который необходимо освободить — тогда при нарушении гарантии выпадет вся программа и это будет заметно. В .NET наличие GC делает эту проблему крайне редкой, а если вам всё же принципиально, можно попробовать воспользоваться такой экзотической вещью, как CriticalFinalizerObject.
          +3
          Приведите пример существенных отличий, пожалуйста.
            +1
            Существенные отличия, на мой взгляд, есть:

            1. System.Exception является reference-type объектом, который всегда передается по ссылке без копирования, благодаря чему отпадает необходимость в отдельном типе exception_ptr и всех функциях, работающих с ним.
            2. Вложенные исключения доступны в виде поля Exception.InnerException и для них не нужны особые механизмы обработки.
              +4
              Ой ну ладно, раньше было в c#, теперь догадались и сделали и в с++.

              Я признаю, что С# более чем навороченный язык, но конкретно в исключениях, особенно с данными нововвидениями, разницы-то уже особо и нет — теперь-то можно и то и то.

              Просто реализовали это на 10 лет позже, это да, но такова уж плата за #стабильность.
              0
              UPD: кое-что забыл.
              3. Удобное поле Exception.StackTrace, не зависит от платформы и не требует лишних телодвижений.
                +2
                В языке, официально поддерживаемым только на одной платформе?
                Ха, в виндах я тоже легко стектрейс получу.
                А так, моно — тот же код под другую платформу, как это было бы на c++, только написанный другими людьми.
                  –1
                  Что вам далась эта «официальная поддерживаемость»? Проект Mono уже давно не кустарная поделка, им вполне можно пользоваться.
                  Поле StackTrace входит в стандартную библиотеку, а значит есть на любой платформе, которая ее реализует, и его не нужно изобретать самому.
                    +1
                    Я не знаю. Я не работаю с моно. Но от коллег-разработчиков постоянно слышу, что поддержка, конечно, есть, но шаг влево-шаг вправо (WPF, кажется, например) — и уже нет поддержки. Конкретные технологии не назову.

                    >> Поле StackTrace входит в стандартную библиотеку, а значит есть на любой платформе, которая ее реализует, и его не нужно изобретать самому.
                    Функция PrintBackTrace есть в любой приличной компании, ее тоже никто не изобретает. Просто черзе ifdef для gcc и msvc реализовать поддержку. 1 раз.
              +1
              Дальше следует очередное нытье по поводу отсутствия блока finally…
                +3
                в C++ этот блок не нужен ибо есть деструкторы и RAII
              +1
              А с помощью этого как-то можно сделать так, чтобы при выпадаении необработанного исключения печатался stack trace подобной java без существенных модификаций программы?
                0
                В любой приличной компании, есть метод PrintBackTrace, реализация которого, специфична для ОС.
                Обладая этим методом, добавление его в terminate колбек приводит к нужному результату.
                  0
                  Только в DEBUG-сборке стек хорошо раскручивается.
                  Если компилировать проект с максимальными оптимизациями, StackTrace в большинстве случаев падений недоступен.
                  +1
                  на маке при падении окошко само вылезает, на линуксе можно подцепиться, есть например Убунтовский apport, а в винде регаем что-нибудь как post mortem debugger и смотрим себе спокойно стек.
                    +2
                    Вообще ведь все эти эксепшены ориентированы не на ситуацию, когда после них программа тупо падает, даже выводя чего-нибудь информативное.

                    Их суть в удобной обработке редко возникающих ошибок. Именно обработке, после которой программа продолжает нормальную работу, без утечек памяти и прочих неприятностей.

                    Так что, на мой взгляд, отсутствие в стандарте функции стэк трейса неудивительно. Для этого, как сказали выше, можно использовать всякие сторонние средства.
                      0
                      Можно, если программа собрана с отладочной инфой. Но, почему-то никто так не делает.
                        0
                        Может потому что она или тормозит или много весит в зависимости от настроек, а стрипают в дебаг символы в отдельные пакеты только в Линуксах?
                      –1
                      Очень познавательно. Большое спасибо!
                      Скажите, пожалуйста, это вы со стандарта все или где-то в компактном виде нашли?
                        +1
                        Часть информации брал с cppreference.com, но там не полное описание, пришлось в стандарт заглядывать
                        0
                        А как можно с std::exception_ptr вынуть исключение не используя (даже косвенно) std::rethrow_exception?
                          0
                          Я думаю, никак. Ведь std::exception_ptr хранит просто указатель и не хранит тип. Поэтому std::exception_ptr нельзя разыменовать. Ведь неизвестно, какого именно типа исключение.
                            0
                            Думал, может что-то типа dynamic_cast (но без std::rethrow_exception).
                              0
                              Ааа, ну если тип исключения известен, то способ, навеное есть. Только он может быть некрасивым, типа попытки прочитать private поля std::exception_ptr
                          +4
                          Спорная это штука — noexcept… мы в свое время от использования пустого throw() в объявлениях функций отказались, так как это получалась ужасно деструктивная конструкция — в С++ очень много вещей, способных бросить исключение, и черемерное увлечение throw() приводило к падению программ в ситуациях, когда исключение было-бы обработано без проблем. Никакой пользы от throw() не было — один вред.

                          Теперь с этой новой конструкцией noexcept, нет никакого улучшения, кроме обещания, что компиляторы будут проверять условие во время компиляции (сомнительно, что смогут в реальной жизни из-за виртуальных функций, и еще более сомнительно, что все разработчики компиляторов сделают это единообразно).

                          То есть фича получилась только для разработчиков стандартной библиотеки, и даже там не везде будет применимо, ИМХО.

                          А народ будет ее пытаться в реальном коде использовать, и наступать на те-же грабли, что и с throw().
                            0
                            Не понял аргумент про throw. Почему
                            черемерное увлечение throw() приводило к падению программ в ситуациях, когда исключение было-бы обработано без проблем.
                            ?
                              +1
                              попытка выбросить исключение, не соотвествующее спецификации throw(), из функции, приводит к вызову std::unexpected() и аварийному завершению программы.

                              Теперь народ навешивает throw() на всякие тривиальные однострочные функции, свято веря, что они ни при каких обстоятельствах не бросают исключений. А про копирование объектов забывают, и в результате рано или поздно мы попадаем в std::unexpected(). А без спецификаций исключение было-бы корректно обработано и записано в лог.
                                +1
                                Но, в общем то, польза в формальной спецификации exception-safety есть. Как минимум, когда я пишу exception-safety код, я обращаю внимание на спецификацию вызываемых функций. Это даёт возможность не использовать лишний раз RAII, при должном кодировании.

                                Также nothrow спецификация move конструкторов требуется в контейнерах, чтобы гарантировать безопасное перемещение элементов.

                                Безусловно, лучше не писать nothrow, если нет уверенности. В C++ полно вещей, которыми можно отстреливать ноги :)

                                А сам nothrow лишь возможность вынести спецификации на уровень языка (и дать возможность анализировать её компилятору) и избавиться от, как минимум, возможных утечек памяти, и, как максимум, от Undefined behavior.
                                  +4
                                  Как минимум, когда я пишу exception-safety код, я обращаю внимание на спецификацию вызываемых функций.


                                  Вы проверяете, и я проверяю (и то я себе в данном случае не доверяю), а 10 соседей не проверяют — как можно потом на результат положиться? Мы поэтому и отказались от throw(), так как компилятор ничего не проверяет, а отследить вручную корректность этой спецификации невозможно.

                                  Также nothrow спецификация move конструкторов требуется в контейнерах, чтобы гарантировать безопасное перемещение элементов.


                                  Ок, как я написал, для разработчиков стандартной библиотеки какая-то польза может быть.

                                  Безусловно, лучше не писать nothrow, если нет уверенности. В C++ полно вещей, которыми можно отстреливать ноги :)


                                  И поэтому мы остаемся при промышленной разработке со старым добрым С с объектами, и не дай бог какую новую фичу применить :-)

                                  А сам nothrow лишь возможность вынести спецификации на уровень языка (и дать возможность анализировать её компилятору)


                                  Беда в том, что компиляторы не проверяют эти спецификации во время компиляции… или не все компиляторы, или не всегда. И хорошая идея превращается в пшик. Особенно критично при кросс-платформенной разработке с поддержкой всяких допотопных компиляторов типа IBM xlC и Sun (sorry, Oracle) CC — каждый интерпретирует стандарт по своему.
                                    0
                                    Согласен с предыдущим оратором.

                                    Как пример неудачного поведения, кажется в MSVC 2005 (как там в более новых студиях — не знаю, не проверял) если внутри функции с throw() таки вылетает исключение то деструкторы стековых объектов созданых внутри функции не вызываются, и если где-то в деструкторе было удаление временных файлов или другие действия над долгоживущими объектами это может приводить к утечкам долгоживущих ресурсов навроде дискового пространства, что уже совсем не ок.
                            +1
                            Статья замечательная, но примеры кода трудно читать потому что название стандартных функций и типов перемешались с названиями автора. По крайней мере это так для тех кто видит описанные в статье новшества впервые. Укажите явно пространство имен std для стандартных функций.
                              0
                              промахнулся ответом (удалено) :(
                              +1
                              Мне кажется С++ эволюционирует в новый язык, я бы даже сказал мутирует. Потому что он чрезмерно усложняется плюшками которые с виду очень прикольные но в большинстве своем кроме как «смотрите как я могу» мало зачем нужны и скорее на против сделаю логику программ еще более запутанной а код менее читаемым.

                              Это конечно не касается thread, conditional_variable, lambda, всего того что и так уже давным давно в обиходе благодаря boost. Это именно про вот эти сумасшедшие навороты на exceptions и прочее, что пока еще мало освещено но уже в стандарте.
                                +2
                                В простых программах никто не требует эти нововведения использовать.

                                А если программа достаточно сложна, чтобы в них нуждаться, то это лишь упрощает код (уменьшает объём, повышает читаемость или т.п), ну или даёт бонус в производительности, для некоторых нововведений.

                                Вообще, все эти нововведения весьма подробно аргументируются, по крайней мере те, что я читал. Ничего бесполезного, на мой взгляд, в C++ не попадает.
                                  0
                                  Но разрабам компиляторов и стандартных библиотек приходится несладко.
                                    +1
                                    Так они всегда крайние.

                                    Но, кстати, в плане стандартной библиотеки, многое (в gnu stl например) просто перетащено из буста, где уже и без этих нововведений в некотором виде существовало.
                                  +1
                                  C++ это, по сути, три языка в одном:
                                  1. Язык препроцессора
                                  2. Функциональный язык, описывающий шаблоны
                                  3. Собственно, C++
                                  0
                                  А почему авторы стандарта ввели std::exception_ptr? Почему нельзя было вместо него использовать void*? Или std::exception_ptr хранит инфу о типе исключения?
                                    0
                                    void * — это плохой вариант, сразу добавляет кучу проблем, что будет, если я скажу delete для него?
                                    К тому же exception_ptr инкапсулирует внутри себя всю работу с исключениями, и предоставляет универсальный интерфейс по передаче исключений — вполне себе ООП-style (в отличие от того же void *)
                                    0
                                    Не могу, к сожалению, похвастаться, что было время детально разобраться со статьей, так что, может быть, банальность спрошу. Я не понял, можно при помощи новых новоротов превратить стандартные классы исключений (вон сколько только errc-«объектов» (как нынче принято называть элементы enum class?) для std::system_error понаделали) в эдакие продвинутые коды ошибок? Я имею в виду новый взгляд на старый-добрый C-шный подход, без непосредственного «бросания» и «отлавливания» исключений, спецификаций throw() (в смысле noexcept) и связанных со всей этой кухней проблем? (см. хотя бы взгляд гугла) Если можно, то было бы здорово увидеть примеры, как. А проблемы, в том числе описанные гуглом, по моему мнению связаны с тем, что компиляторы C++ не поступают с исключениями так, как это делают компиляторы Java, например — не контролируют их «отлова». Вообще никак не поддерживают, фактически, возлагая весь непростой труд по созданию exception-safe кода на программиста.

                                  Only users with full accounts can post comments. Log in, please.