На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
Страницы: (6) 1 [2] 3 4 ... Последняя » все  ( Перейти к последнему сообщению )  
> как скопировать модификатор noexcept в свою функцию?
    Цитата Олег М @
    Ну, у меня основная идея в том, что надо каждый вызов заворчивать в подобную конструкцию. Если у тебя не отработала одна функция в деструкторе, почему не должны отработать другие?

    Идея понятная. Подход правомочный.

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

    Ну, например, в сложном деструкторе выполняются сложные действия.
    1. сбросить кэш в файл.
    2. закрыть файл.
    3. удалить резервную копию файла.
    Если сбой произошёл на первом шаге, то остальные шаги теряют смысл и даже вредят.

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

    Напомню, что это моё, сугубо личное мнение. Вы можете делать так, как хотите.

    Добавлено
    Цитата Олег М @
    Я такую штуку довольно активно использую

    Полезная примочка.

    Добавлено
    Цитата Олег М @
    Что–то, похоже, одновременно пишем

    Я тут набегами. Отвлекусь от кода или бытовухи, проверю почту, а там уведомление...

    Вот сейчас, видимо, пойду за стол, дамы хотят праздник.
      Цитата Eric-S @
      Ну, например, в сложном деструкторе выполняются сложные действия.
      1. сбросить кэш в файл.
      2. закрыть файл.
      3. удалить резервную копию файла.
      Если сбой произошёл на первом шаге, то остальные шаги теряют смысл и даже вредят.


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

      Добавлено
      Цитата Eric-S @
      А если уж совсем правильно, то надо примерно так:

      void close()
      {


      Совсем правильно, по-моему будет void close() noexcept {......
      Зачем тебе там exception?

      Добавлено
      Цитата Eric-S @
      on_exception( std::current_exception() );

      Кстати, а как ты обрабатываешь результат std::current_exception()? С ним вроде особо нельзя ничего сделать, кроме rethrow
      Сообщение отредактировано: Олег М -
        Цитата Олег М @
        Кстати, а как ты обрабатываешь результат std::current_exception()? С ним вроде особо нельзя ничего сделать, кроме rethrow

        Основное преимущество типа std::exception_ptr в том, что он может указывать на любое исключение. Даже если это исключение неожиданного типа.

        В таком случае, прототип функции моего обработчика выглядит так:
        ExpandedWrap disabled
          void general_exception_handler( std::exception_ptr p_except );


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

        Но за любые плюшки приходится платить, в том числе и за эту универсальность. Приходится востанавливать исключение и обрабатывать его штатно.
        ExpandedWrap disabled
          /**
          @brief ссылка на поток стандартного вывода ошибок.
          */
          #if defined(_UNICODE) // _UNICODE
          basic_ostream< wchar_t >& tcerr = wcerr;
          #else // _UNICODE
          basic_ostream< char >& tcerr = cerr;
          #endif // _UNICODE
           
           
          /**
          @brief выводит описание исключения.
          @param[in] except ссылка на объект исключения.
          */
          void output_exception( const exception& except )
            {
            tcerr << _T( "catched " ) << typeid( except ).name() << endl << except.what() << endl;
            } // end function
           
           
          /**
          @brief выводит описание исключения.
          */
          void output_exception()
            {
            tcerr << _T( "catched unexpected exception" ) << endl;
            } // end function
           
           
          /**
          @brief главный обработчик исключений.
          @param[in] p_exception указатель на исключение.
          */
          void general_exception_handler( const exception_ptr p_exception )
            {
            try
              {
           
              /* повторить исключение. */
              if( nullptr != p_exception )
                {
                rethrow_exception( p_exception );
                } // end if
           
              } // end try
            catch( const exception& except )
              {
              output_exception( except );
              } // end catch
            catch( ... )
              {
              output_exception();
              } // end catch
            } // end function


        Почти так у меня сделано в консольных прогах. Срезал фигню об system_error, structured_exception и stack_trace.

        Цитата Олег М @
        Совсем правильно, по-моему будет void close() noexcept {......
        Зачем тебе там exception?


        Понятия не имею. Но в справочнике для функции CloseHandle() прописано, что она может вернуть код ошибки.
        И я, просто обязан, эту ошибку обработать!

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

        Правда вот тут, наткнулся на новую идею. Пока ещё не занялся, но серьёзно раздумываю сделать свой std::expected. С ним будет проще и дешевле обрабатывать ошибки, чем с исключениями.

        А сейчас я колупаю noexcept...

        Читал тырнет и понял, что само проверяемое выражение надо оборачивать в дополнительный noexcept.
        ExpandedWrap disabled
          template< typename ValueType >
          class someclass
          {
          public:
           
          someclass() noexcept( noexcept( _construct() ) ),
          _is_good( false )
          {
          _construct();
          }
           
          bool is_good() const noexcept
          {
          return _is_good;
          }
           
          private:
           
          void _construct() noexcept( is_nothrow_default_constructible< ValueType >::value )
          {
          new( &_value ) ValueType();
          _is_good = true;
          }
           
           
          char _value[ sizeof( ValueType ) ];
           
          /* флаг что объект построен. */
          bool _is_good;
           
          };


        Ну... Э-э-э-, как-то так.

        А теперь хардкорище
        ExpandedWrap disabled
          someclass() noexcept( noexcept( bla_bla_bla ) )

        И оно же компилируется!

        Вообщем, такая заморочка, для одной функции написал проверочку. Но оно тоже компилируется. А я же засомневался, коректна ли та проверка или компиль увидел там bla_bla_bla?

        ExpandedWrap disabled
          void set_value( const_reference value ) noexcept( noexcept( _construct( value ) ) && noexcept( _assign( value ) ) )
          {
          if( is_good() )
          {
          _assign( value );
          }
          else
          {
          _construct( value );
          }
          }


        На примерно такой код компилятор не ругался.
        Сообщение отредактировано: Eric-S -
          Цитата Eric-S @
          Понятия не имею. Но в справочнике для функции CloseHandle() прописано, что она может вернуть код ошибки.
          И я, просто обязан, эту ошибку обработать!

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


          Может быть. Я в таких случаях - CloseHandle, close и т.п. - особенно в деструкторах, просто забиваю на эти коды и всё. Смысл напрягаться, если по-любому не можешь ничего сделать и никак среагировать? Обрабатываю, только ели мне это действительно нужно. Но таких случаев не припомню.


          Цитата Eric-S @
          Правда вот тут, наткнулся на новую идею. Пока ещё не занялся, но серьёзно раздумываю сделать свой std::exception. С ним будет проще и дешевле обрабатывать ошибки, чем с исключениями.


          Что за идея?

          Добавлено
          Цитата Eric-S @
          А теперь хардкорище

          someclass() noexcept( noexcept( bla_bla_bla ) )

          И оно же компилируется!


          Почему бы нет? noexcept это и оператор и модификатор

          Добавлено
          Цитата Eric-S @
          if( is_good() )


          Что такое is_good?
            Цитата Олег М @
            Что за идея?

            Ой... Опять поспешил. Конечно же std::expected.

            Это шаблоный клас, который содержит в себе union. Он может хранить своё состояние, а так же либо код ошибки, либо возвращаемое значение.

            То есть функция возвращает объект типа std::expected.
            Если функция завершилась нормально, то в объекте лежит нормальное значение.
            Если функция завершилась с ошибкой, то в объекте лежит код ошибки.

            Тот кто вызывает функцию, должен проверить код ошибки, если всё нормально, то он может получить значение.
            Если же пытается получить значение, а в объекте код ошибки, то будет брошено исключение.
            Ну... Стратегия ленивого, отложенного исключения.
            Оно будет брошено только тогда, когда без него уже не обойтись.

            Далее, есть мысль, бросать ещё одно исключение из деструктора expected.
            Это в том случае, если вызвавший функцию, по какой-то причине вообще проигнорировал возвращёное значение.
            То есть кто-то функцию вызвал, значение запросил, но проигнорировал его.

            Добавлено
            Цитата Олег М @
            Почему бы нет? noexcept это и оператор и модификатор

            Понятно, что оператор. Но там же написано заведомо недопустимое выражение.
            Соответственно, я теперь сомневаюсь, а допустимо ли моё выражение? Или же компиль молче сжевал недопустимое?

            Добавлено
            Цитата Олег М @
            Что такое is_good?

            А... Ну, в данном случае is_good() это constexpr метод noexcept(true) который возвращает true если уже был вызван метод _construct иначе false.
            Смысл в том, что он подтверждает, что в поле _value находится действительно объект, а не сырые неинициализированные данные.
              Цитата Eric-S @
              Тот кто вызывает функцию, должен проверить код ошибки, если всё нормально, то он может получить значение.
              Если же пытается получить значение, а в объекте код ошибки, то будет брошено исключение.
              Ну... Стратегия ленивого, отложенного исключения.
              Оно будет брошено только тогда, когда без него уже не обойтись.


              Типа, возвращение к старому доброму Си? Не, по мне так лучше исключения отлавливать.


              Цитата Eric-S @
              Далее, есть мысль, бросать ещё одно исключение из деструктора expected.

              Опять же. По-моему, очень плохая мысль бросать исключение из деструктора. Недоудалённые объекты и всё такое.

              Добавлено
              Цитата Олег М @
              А... Ну, в данном случае is_good() это constexpr метод noexcept(true) который возвращает true если уже был вызван метод _construct иначе false.
              Смысл в том, что он подтверждает, что в поле _value находится действительно объект, а не сырые неинициализированные данные.


              Только там у тебя будут всегда компилироваться обе ветки. Это надо, наверное, через std::enable_if делать. Либо через requires
                Цитата Олег М @
                Только там у тебя будут всегда компилироваться обе ветки. Это надо, наверное, через std::enable_if делать. Либо через requires

                Естественно будут компилироватся обе ветки. Потому что в объекте в разное время могут быть инициализированные или неинициализированные данные. В данном случае метод is_good() будет отрабатывать в run-time.
                Но мысль, перенести часть в compile-time кажется интересной. Надо над этим подумать.
                  Цитата Eric-S @
                  Естественно будут компилироватся обе ветки. Потому что в объекте в разное время могут быть инициализированные или неинициализированные данные. В данном случае метод is_good() будет отрабатывать в run-time.
                  Но мысль, перенести часть в compile-time кажется интересной. Надо над этим подумать.

                  Думаю, там придётся переносить в compile-time. Одна специализация для construct, одна для assign и третья для обоих всесте
                    Цитата Олег М @
                    Типа, возвращение к старому доброму Си? Не, по мне так лучше исключения отлавливать.

                    Эх... Ну вообще-то да, лучше бросать и ловить исключения.
                    Но вон, как в случае выше, на счёт CloseHandle.
                    Исключение в деструкторе это плохо.
                    Но и без исключений тоже плохо.

                    Предложение std::expected в новый стандарт, было отклонено со словами, что в C++ уже есть механизм обработки ошибок.

                    Бросать кучу исключений, тоже плохо.
                    В стандартных контейнирах, вроде std::vector, есть два варианта доступа к элементам: operator[], который не бросает исключений; и метод at() который исключения бросает.
                    В сомнительных случаях, лучше юзать метод at(). А в циклах, для повышения скорости, предпочтителен operator[].
                    То есть пользователю нужно дать вариант выбора, либо метод с исключениями, либо метод без исключений, но с проверкой ошибок.
                    И std::expected предлагает вариант решения.

                    Ну.. Уже написал, что буду ещё думать. Идея интересная, но сомнительная.

                    Добавлено
                    Цитата Олег М @
                    Думаю, там придётся переносить в compile-time. Одна специализация для construct, одна для assign и третья для обоих всесте

                    Понятия не имею как такой трюк устроить. Наверное придётся магичить с шаблонами.
                      Цитата Eric-S @
                      Понятия не имею как такой трюк устроить. Наверное придётся магичить с шаблонами.

                      А ты в чём компилируешь? Там концепты поддерживаются - concept, requires? С ними совсем просто
                      Сообщение отредактировано: Олег М -
                        Цитата Олег М @
                        А ты в чём компилируешь? Там концепты поддерживаются - concept, requires? С ними совсем просто

                        visual studio 2015. Уже скачал но не установил 2017.

                        Что такое концепты примерно слышал, но реальных руками не щупал.

                        Что такое requires я не знаю, не сталкивался.
                          Цитата Eric-S @
                          Что такое концепты примерно слышал, но реальных руками не щупал.

                          Это условия для шаблонов. Крутая штука, правда я тоже пока с трудом понимаю, но использую активно, в gcc
                          ExpandedWrap disabled
                            template <typename T, typename TFunc, typename... TT> requires !std::is_same<std::result_of_t<TFunc(TT...)>, void>::value
                            auto CallNoVoid(T &&_void, TFunc &&func, TT&&... args)
                            {
                                return std::invoke(std::forward<TFunc>(func), std::forward<TT>(args)...);
                            }
                             
                            template <typename T, typename TFunc, typename... TT> requires std::is_same<std::result_of_t<TFunc(TT...)>, void>::value
                            auto CallNoVoid(T &&_void, TFunc &&func, TT&&... args)
                            {
                                std::invoke(std::forward<TFunc>(func), std::forward<TT>(args)...);
                                return std::forward<T>(_void);
                            }


                          Добавлено
                          ExpandedWrap disabled
                            template <typename T> concept bool IsSharedMutex = requires(T a) {{a.lock_shared()}; {a.unlock_shared()};};
                             
                            template <typename T> requires !IsSharedMutex<T> && !std::is_same<T, std::atomic_flag>::value
                            using lock_guard = std::lock_guard<T>;
                             
                            template <IsSharedMutex T>
                            using lock_guard2 = std::lock_guard<T>;


                          Добавлено
                          Во втором случае IsSharedMutex == true, если у объекта типа T есть методы lock_shared и unlock_shared
                          lock_guard существует для всех, кроме IsSharedMutex
                          lock_guard2 - только для IsSharedMutex

                          Добавлено
                          ExpandedWrap disabled
                            template <typename T> concept bool IsFormatable = requires(const T a, std::ostream &out) {a.FormatVal(out);};
                             
                            template <typename T>
                            void Format(std::ostream &out, T &&val)
                            {
                                out << std::forward<T>(val);
                            }
                             
                            template <IsFormatable T>
                            void Format(std::ostream &out, T &&val)
                            {
                                val.FormatVal(out);
                            }
                            Цитата Олег М @
                            Это условия для шаблонов. Крутая штука, правда я тоже пока с трудом понимаю, но использую активно

                            Эм-эм-эм... Тогда мы говорим о разных концептах.
                            Где-то, в какой-то статье подсмотрел принцип идеи. Вроде бы о языке D.
                            Концепт, в моём понимании, это специальный тип, который создаёт интерфейс для объектов со статическим полиморфизмом.

                            То есть например, некая статическая компилируемая функция, просит в качестве параметра некий концепт коллекции.
                            И этой функции, можно передать любую коллекцию, которая соответствует концепту коллекции std::vector, std::list или ещё чего.

                            Вроде бы, шёл разговор, о принятии концептов в C++ 17. Но предложение было слишком большим и сложным. Его отправили на доработку. Хотели принять облегчённый вариант, но кажись с ним тоже облом.
                              Цитата Eric-S @
                              Вроде бы, шёл разговор, о принятии концептов в C++ 17. Но предложение было слишком большим и сложным. Его отправили на доработку. Хотели принять облегчённый вариант, но кажись с ним тоже облом.

                              Я ж прислал примеры, рабочие

                              Добавлено
                              Цитата Eric-S @
                              Концепт, в моём понимании, это специальный тип, который создаёт интерфейс для объектов со статическим полиморфизмом.

                              А это не template?

                              Добавлено
                              Компилирую под gcc 6.2.0

                              Добавлено
                              Цитата Eric-S @
                              Концепт, в моём понимании, это специальный тип, который создаёт интерфейс для объектов со статическим полиморфизмом.

                              Кстати да, тут ты прав. Но, разве IsSharedMutex не определяет именно такой интерфейс, или IsFormatable?
                              Сообщение отредактировано: Олег М -
                                Цитата Олег М @
                                Я ж прислал примеры, рабочие

                                Если в каких-то компиляторах сделано, то это не значит, что принято в стандарте и наоборот.
                                1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0625 ]   [ 15 queries used ]   [ Generated: 17.05.24, 19:58 GMT ]