На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела:
1. Название темы - краткое описание кто/что против кого/чего
2. В первом сообщении - список параметров, по которым идет сравнение.
3. Старайтесь аргументировать свои высказывания. Фразы типа "Венда/Слюникс - ацтой" считаются флудом.
4. Давайте жить дружно и не доводить обсуждение до маразма и личных оскорблений.
Модераторы: Модераторы, Комодераторы
Страницы: (3) [1] 2 3  все  ( Перейти к последнему сообщению )  
> C++ RAII/exceptions vs Golang defer/panic/recover
    Я, конечно, помню, что исключения в деструкторах --- это плохо, но всякое ж бывает (возможно, кроме этого, я что-то неправильно написал в C++ коде):

    ExpandedWrap disabled
      #include <iostream>
      #include <stdexcept>
       
      using namespace std;
       
      class Resource
      {
      private:
          string _name;
          bool _exn;
      public:
          Resource(string name, bool exn) {
              _exn = exn;
              _name = name;
              cout << "Open " << _name << endl;
          }
          virtual ~Resource() {
              cout << "Close " << _name << endl;
              if (_exn)
                  throw runtime_error("EXCEPTION: " + _name);
          }
      };
       
      void test()
      {
          Resource a("A", false);
          Resource b("B", true);
          cout << "test" << endl;
      }
       
      int main()
      {
          try {
              test();
          } catch (runtime_error& e) {
              cout << "CATCH: " << e.what() << endl;
          }
          return 0;
      }


    stdout:
    ExpandedWrap disabled
      Open A
      Open B
      test
      Close B


    stderr:
    ExpandedWrap disabled
      terminate called after throwing an instance of 'std::runtime_error'
        what():  EXCEPTION: B


    --- http://ideone.com/zH6e0Q

    Golang:
    ExpandedWrap disabled
      package main
       
      import "fmt"
       
      type Resource struct {
          name string
          exn  bool
      }
       
      func OpenResource(name string, exn bool) (*Resource, error) {
          r := &Resource{name, exn}
          fmt.Println("Open", name)
          return r, nil
      }
       
      func (r *Resource) Close() error {
          fmt.Println("Close", r.name)
          if r.exn {
              panic("EXCEPTION: Close " + r.name)
          }
          return nil
      }
       
      func test() error {
          a, err := OpenResource("A", false)
          if err != nil {
              return err
          }
          defer a.Close()
          b, err := OpenResource("B", true)
          if err != nil {
              return err
          }
          defer b.Close()
          fmt.Println("test")
          return nil
      }
       
      func main() {
          defer rec()
          test()
      }
       
      func rec() {
          if r := recover(); r != nil {
              fmt.Println("CATCH:", r)
          }
      }


    stdout:
    ExpandedWrap disabled
      Open A
      Open B
      test
      Close B
      Close A
      CATCH: EXCEPTION: Close B


    --- http://play.golang.org/p/bG-YnPIGX0
      korvin, видимо, понимаешь не до конца. В деструкторах не должно быть исключений. Совсем.

      Добавлено
      Ну и да, код не аналогичен :) Для аналога того, что написано в Go, тебе нужно реализовать defer(в виде некоторого guard'а), в котором дергать метод close ресурса.
        Цитата D_KEY @
        korvin, видимо, понимаешь не до конца. В деструкторах не должно быть исключений. Совсем.

        Но если там довольно сложная логика освобождения ресурса, оно, тем не менее, может произойти, из-за какой-нибудь вызываемой библиотечной функции или аппаратной ошибки, 100% гарантии нет, или как?

        Цитата D_KEY @
        Ну и да, код не аналогичен :) Для аналога того, что написано в Go, тебе нужно реализовать defer(в виде некоторого guard'а), в котором дергать метод close ресурса.

        Может и не аналогичен, но, пусть это чуть разные механизмы, но они используются для одной и той же цели и с более-менее схожим смыслом.
        Раньше это не мешало сравнивать RAII и try/catch/finally например. =)

        Кстати, как раз недавно наткнулся на реализацию чего-то подобного defer (точнее, документ опубликован ещё до выхода бета-версий Go, так что правильней наоборот, да и вообще, но не суть). Правда там для Java, но всё же.
        Сообщение отредактировано: korvin -
          С этим кодом всё в порядке. В смысле, он, конечно, говнокод, но работать должен, как задумывалось. Что там с вашим g++14, не знаю, а вот Студия, Интел Компилер и g++11:
          ExpandedWrap disabled
            Open A
            Open B
            test
            Close B
            Close A
            CATCH: EXCEPTION: B
          Исключения в деструкторах в любом случае могут возникать, но не должны покидать их пределов. Если исключение покидает пределы деструктора, это не причина программе падать, это лишь означает, что деструктор не закончил работу, и следовательно объект недоразрушен со всеми вытекающими.
          Языком не допускаются только такая ситуация, в которой исключение бросается в тот момент, когда предыдущее ещё не поймано. Потому что если возникает такая ситуация, то эти два исключения невозможно однозначно отсортировать для обработки. Это может произойти только в тот момент, когда на пути от throw до catch выполниться ещё один throw, а это возможно только в деструкторе объекта, лежащего на разматываемой области стека. Т.к. заранее нельзя знать, по какой причине деструктор вызван, поэтому и нельзя допускать исключений, покидающих деструктор от слова вообще.
            Цитата korvin @
            Я, конечно, помню, что исключения в деструкторах --- это плохо, но всякое ж бывает (возможно, кроме этого, я что-то неправильно написал в C++ коде)

            Просто современный компилятор юзаешь.
            ExpandedWrap disabled
              virtual ~Resource() noexcept(false) {

            Цитата Qraizer @
            а вот Студия, Интел Компилер

            Это ожидаемо.
            Цитата Qraizer @
            g++11

            А это был баг в 4.7
              Да ну? Ну посмотри в "15.5.1 The std::terminate() function". Там нет ни одного упоминания исключения в деструкторе без дополнительного специального условия:
              Цитата
              1 In some situations exception handling must be abandoned for less subtle error handling techniques. [ Note: These situations are:
              • when the exception handling mechanism, after completing the initialization of the exception object but before activation of a handler for the exception (15.1), calls a function that exits via an exception, or
              • when the exception handling mechanism cannot find a handler for a thrown exception (15.3), or
              • when the search for a handler (15.3) encounters the outermost block of a function with a noexcept-specification that does not allow the exception (15.4), or
              • when the destruction of an object during stack unwinding (15.2) terminates by throwing an exception, or
              • when initialization of a non-local variable with static or thread storage duration (3.6.2) exits via an exception, or
              • when destruction of an object with static or thread storage duration exits via an exception (3.6.3), or
              • when execution of a function registered with std::atexit or std::at_quick_exit exits via an exception (18.5), or
              • when a throw-expression with no operand attempts to rethrow an exception and no exception is being handled (15.1), or
              • when std::unexpected throws an exception which is not allowed by the previously violated dynamicexception-specification, and std::bad_exception is not included in that dynamic-exception-specification (15.5.2), or
              • when the implementation’s default unexpected exception handler is called (D.11.1), or
              • when the function std::nested_exception::rethrow_nested is called for an object that has captured no exception (18.8.6), or
              • when execution of the initial function of a thread exits via an exception (30.3.1.2), or
              • when the destructor or the copy assignment operator is invoked on an object of type std::thread that refers to a joinable thread (30.3.1.3, 30.3.1.4).
              —end example ]
              Так что да, вполне ожидаемо отсутствие исключения в приведённом выше коде. А что там у вас в C++14 за баги, я не в курсе.
              Сообщение отредактировано: Qraizer -
                Цитата korvin @
                Цитата D_KEY @
                korvin, видимо, понимаешь не до конца. В деструкторах не должно быть исключений. Совсем.

                Но если там довольно сложная логика освобождения ресурса, оно, тем не менее, может произойти, из-за какой-нибудь вызываемой библиотечной функции или аппаратной ошибки, 100% гарантии нет, или как?

                Если есть опасения, значит отлавливай исключения в деструкторе.
                  Зато есть
                  Цитата 15.2 Constructors and destructors
                  3 The process of calling destructors for automatic objects constructed on the path from a try block to a throw-expression is called “stack unwinding.” If a destructor called during stack unwinding exits with an exception, std::terminate is called (15.5.1). [ Note: So destructors should generally catch exceptions and not let them propagate out of the destructor. —end note ]


                  Добавлено
                  Ага. А ещё есть
                  Цитата 15.4 Exception specifications
                  15 A deallocation function (3.7.4.2) with no explicit exception-specification is treated as if it were specified with noexcept(true).
                  так что формально деструктор без явной спецификации исключений считается nothrow. :yes: Как интересно.
                  Сообщение отредактировано: Qraizer -
                    Цитата Qraizer @
                    Да ну?

                    Ну да.
                    Цитата Qraizer @
                    Ну посмотри в "15.5.1 The std::terminate() function"

                    А нахрена мне туда смотреть. Ещё рано. Начать надо с
                    Цитата ISO/IEC 14882:2011 12.4/3
                    A declaration of a destructor that does not have an exception-specification is implicitly considered to have
                    the same exception-specification as an implicit declaration (15.4).

                    Цитата ISO/IEC 14882:2011 15.4/14
                    An implicitly declared special member function (Clause 12) shall have an exception-specification. If f is
                    an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment
                    operator, or move assignment operator, its implicit exception-specification specifies the type-id T if and only
                    if T is allowed by the exception-specification of a function directly invoked by f’s implicit definition; f shall
                    allow all exceptions if any function it directly invokes allows all exceptions, and f shall allow no exceptions
                    if every function it directly invokes allows no exceptions.

                    А вот теперь можете смотреть и думать.
                    Цитата Qraizer @
                    Так что да, вполне ожидаемо отсутствие исключения в приведённом выше коде для тех, кто не знает C++

                    fixed
                    Цитата Qraizer @
                    А что там у вас в C++14 за баги, я не в курсе.

                    У нас всё в порядке. А вот вышеозначенные гранды опять сделали поделки, не выдерживающие элементарной критики.

                    Добавлено
                    Цитата Qraizer @
                    так что формально деструктор без явной спецификации исключений считается nothrow

                    :facepalm: Деточка, deallocation function - это не деструктор.
                      Мальчик, deallocation function – это общее понятие, частным случаем которого являются деструкторы.
                        Цитата Qraizer @
                        Мальчик, deallocation function – это общее понятие, частным случаем которого являются деструкторы.

                        Нам нужен смайлик рука_кирпич...
                        Цитату, сестра, цитату!
                          P.S. std::operator<<<>(std::basic_ostream<>& os, const char* s) не имеет ограничения nothrow.

                          Добавлено
                          Цитата MyNameIsIgor @
                          Нам нужен смайлик рука_кирпич...
                          Сфоткай свой экзерсис. Я с удовольствием посмотрю. Буду сильно надеяться, что последний раз.
                          Сообщение отредактировано: Qraizer -
                            Цитата Qraizer @
                            Сфоткай свой экзерсис. Я с удовольствием посмотрю. Буду сильно надеяться, что последний раз.

                            Цитата Qraizer @
                            std::operator<<<>(std::basic_ostream<>& os, const char* s) не имеет ограничения nothrow.

                            :facepalm: Совсем не читатель?
                            Цитата MyNameIsIgor @
                            a function directly invoked by f’s implicit definition;
                              korvin, можешь кратенько сформулировать суть холивара?

                              Добавлено
                              Цитата
                              C++ RAII/exceptions vs Golang defer/panic/recover

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

                              Что касается исключений, то это отдельный холивар.
                                Цитата D_KEY @
                                korvin, можешь кратенько сформулировать суть холивара?

                                Гм... RAII vs defer (как способы освобождения ресурсов) [vs другие способы освобождения ресурсов (using, with, etc), если есть кому что сказать про них]

                                Цитата D_KEY @
                                У RAII перед defer преимущество, по сути, одно. Освобождение ресурса срабатывает всегда при разрушении объекта, причём это происходит и для полей и для базы(в случае наследования).

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

                                Цитата D_KEY @
                                Далее, defer выразим через RAII посредством guard'ов.

                                Возможно, но будет ли это выражение таким же надёжным или опять его можно будет обойти и ввести систему в некорректное состояние?

                                Цитата D_KEY @
                                RAII через defer не сделать.

                                Да как-то и желания нет.

                                Цитата D_KEY @
                                Что касается исключений, то это отдельный холивар.

                                А исключения тут как пример возможности испортить поведение механизма.
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:
                                Страницы: (3) [1] 2 3  все


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0471 ]   [ 16 queries used ]   [ Generated: 28.03.24, 10:01 GMT ]