На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
  
> Что должен возвращать оператор [] ограниченного массива?
    Ограниченный массив отличается тем, что он защищён от выхода за границы. В данном случае как ограниченный массив должен вести себя класс. Что должен возвращать оператор []? Обычно он возвращает ссылку на элемент. Но если индекс выходит за границы массива, то элемента нет. Возвращать указатель не охота.
      Можно так.
      А по-моему лучше кидать исключение.
        Так о несуществовании элемента будет знать только пользователь, но не вызывающй код. А должно быть наоборот. Вот представьте себе: элемент сам содержит массив, пользователь дал команду на удаление элемента члена элемента и не существующий индекс элемента, но существующий индекс элемента члена нулевого элемента, оператор [] написал, что элемент не существует, но прочитал это только пользователь, а в вызывающий код оператор [] вернул ссылку на нулевой элемент, вызывающий код получил эту ссылку и спокойно передал по ней члену нулевого элемента команду на удаление элемента, этот элемент удалился, а в нём был не сохранённый результат пары часов редактирования. Резервировать нулевой элемент как заглушку несущесвующего и на этом успокоиться тоже нельзя. Иначе пользователь введёт информацию и туда. И её может быть много. Если пользователю надо знать, что он сделал что-то не то, то сообщить об этом должен не оператор, а вызывающий код. Хотя бы потому, что вызывающий код «знает», куда пользователь ввёл не существующий индекс. Индекс, кстати, строковый, а не числовой, так что сравнить в вызывающем коде значение индекса с количеством элементов нельзя.

        Добавлено
        Цитата JoeUser @
        А по-моему лучше кидать исключение.
        Куда? В ветвь оконной процедуры? И как потом продолжить нормальную работу гуя? Ладно, не велика сложность. Для одного раза. Но ловить исклюкаку придётся по всей приладе, а это уже повод для максимального упрощения. Трай им не страдает.
        Сообщение отредактировано: Ирокез -
          Цитата Ирокез @
          Куда? В ветвь оконной процедуры?

          Кидают не куда, а где. А "куда" - это где обрабатывают.
          Простой пример, понятный и удобный;
          ExpandedWrap disabled
            #include <iostream>
            using namespace std;
             
            class MyArray10 {
              public:
                MyArray10() : a{'0','1','2','3','4','5','6','7','8','9'} {};
                char &operator[](size_t idx) {
                  if (idx>9) throw std::out_of_range("MyArray10");
                  return a[idx];
                }
              private:  
                char a[10];
            };
             
            int main() {
              MyArray10 Arr;
              try {
                std::cout << std::hex << Arr[1] << std::endl;
                std::cout << std::hex << Arr[10] << std::endl;
              } catch (std::out_of_range &e) {
                std::cout << "Выход за границы массива в: " << e.what() << std::endl;
              } catch (...) {
                std::cout << "Произошла неведомая хрень" << std::endl;
              }
              return 0;
            }


          Добавлено
          Цитата Ирокез @
          И как потом продолжить нормальную работу гуя?

          Не сложнее, если ты будешь отлавливать предварительно условными операторами.
          Сообщение отредактировано: JoeUser -
            Хотелось бы без неё, пусть даже переусложнив одно место. А если уж делать через неё, то какую именно исклюкаку принято бросать? Я попробовал
            ExpandedWrap disabled
              throw *this
            и поймать исклюкаку с параметром ссылка на объект-массив, софтина упала при закрытии.

            Добавлено
            Цитата JoeUser @
            Кидают не куда
            Ну где – это в операторе.
              Цитата Ирокез @
              то какую именно исклюкаку принято бросать

              Я в примере ее привел - std::out_of_range

              Добавлено
              Если хочется еще и this знать - нужно реализовать свой класс исключения, наследовавшись от std::out_of_range.

              Добавлено
              Хотя ... можно и адрес передавать при выбросе исключения:

              ExpandedWrap disabled
                #include <iostream>
                using namespace std;
                 
                template<typename S,typename D>
                D type_cast(S s) {
                  union Tmp {
                    S src;
                    D dst;
                  } T;
                  T.src = s;
                  return T.dst;
                }
                 
                class MyArray10 {
                  public:
                    MyArray10() : a{'0','1','2','3','4','5','6','7','8','9'} {};
                    char &operator[](size_t idx) {
                      if (idx>9) throw std::out_of_range(type_cast<MyArray10*,char*>(this));
                      return a[idx];
                    }
                  private:
                    char a[10];
                };
                 
                int main() {
                  MyArray10 Arr;
                  try {
                    std::cout << std::hex << Arr[1] << std::endl;
                    std::cout << std::hex << Arr[10] << std::endl;
                  } catch (std::out_of_range &e) {
                    std::cout << "Выход за границы массива в: " << std::hex << static_cast <const void *> (e.what()) << std::endl;
                  } catch (...) {
                    std::cout << "Произошла неведомая хрень" << std::endl;
                  }
                  return 0;
                }
                Цитата JoeUser @
                Хотя ... можно и адрес передавать при выбросе исключения:
                Что я с ним делать буду? Объект хоть тип имеет, по нему можно понять, оператор-член какого класса бросал. А адрес я куда приткну?
                Сообщение отредактировано: Ирокез -
                  Цитата Ирокез @
                  Что я с ним делать буду?

                  Без понятия. Это был ответ на ваш вопрос #5.
                  Вы в исключении передавали this и у вас программа падала. Я показал как передать, чтобы не падала, и как это адрес в обработчике достать.
                  Хотите более детальной обработки, творите. Default решений тут нет.

                  Цитата Ирокез @
                  Объект хоть тип имеет, по нему можно понять, оператор-член какого класса бросал

                  Откройте для себя RTTI, хотя бы почитайте про std::type_info.
                    Цитата JoeUser @
                    Вы в исключении передавали this
                    Учитесь читать. Я передал ссылку на this.

                    Добавлено
                    Цитата JoeUser @
                    Откройте для себя RTTI,
                    Вот только она разбирает фактические типы объектов, а не адреса кода.
                      Цитата Ирокез @
                      Учитесь читать. Я передал ссылку на this.

                      И результат - падение программы. Я это читал.

                      Добавлено
                      Цитата Ирокез @
                      Объект хоть тип имеет, по нему можно понять, оператор-член какого класса бросал

                      Цитата Ирокез @
                      Вот только она разбирает фактические типы объектов, а не адреса кода.

                      Сначала же надо было знать тип?! Или, по мере обсуждения, цели меняются? :lol:
                        Цитата JoeUser @
                        И результат - падение программы. Я это читал.
                        Не читал, а выхватил.

                        Добавлено
                        Цитата JoeUser @
                        Сначала же надо было знать тип?!
                        Да. Но Вы-то предлагали адрес операции, на которой исклюкака брошена.

                        Добавлено
                        Цитата JoeUser @
                        Или, по мере обсуждения, цели меняются? :lol:
                        Не цели, а средства. Цель – узнать, чем вызвана исклюкака.
                          В последнем моем варианте в исключении прилетает адрес экземпляра класса, в операторе которого произошло исключение. Тип этого экземпляра класса узнать можно. Если нужно еще что-то, это нужно реализовывать самостоятельно. В отличии от языков с динамической типизацией, в С++ из подобного только RTII со своими фишками. Но нет преграды патриотам.

                          Добавлено
                          Цитата Ирокез @
                          Вы-то предлагали адрес операции, на которой исклюкака брошена

                          Адрес точно операции? :wacko:
                            Цитата JoeUser @
                            Адрес точно операции?
                            А чего? Места жительства Меркель?

                            Добавлено
                            Цитата JoeUser @
                            В последнем моем варианте в исключении прилетает адрес экземпляра класса, в операторе которого произошло исключение. Тип этого экземпляра класса узнать можно. Если нужно еще что-то, это нужно реализовывать самостоятельно.
                            Чтоб по адресу узнать фактический тип, его надо запихать в указатель на базовый класс. А что это за класс?

                            Добавлено
                            Цитата JoeUser @
                            В отличии от языков с динамической типизацией,
                            В c++ размещаемые в куче экзмлпяры полиморфных классов получают фактические типы вполне динамически.
                            Сообщение отредактировано: Ирокез -
                              Цитата JoeUser @
                              А по-моему лучше кидать исключение.
                              Лучше? Только исключение. Никаких вариантов.

                              Добавлено
                              Цитата Ирокез @
                              Что я с ним делать буду? Объект хоть тип имеет, по нему можно понять, оператор-член какого класса бросал. А адрес я куда приткну?
                              Нагрузить экземпляр исключения любой информацией никто не мешает. Какая там будет нужна информация, кроме автора кода, знать некому, так что "пилите Шура, пилите"©, компилятор сам по наитию этого не сделает
                                Цитата Ирокез @
                                Чтоб по адресу узнать фактический тип, его надо запихать в указатель на базовый класс. А что это за класс?

                                Ну я же давал ссылку на доки. Что и так почитать лень? :facepalm:
                                Хотите узнать название класса? В доке и пример есть:
                                ExpandedWrap disabled
                                  #include <iostream>
                                  #include <typeinfo>
                                   
                                  struct Base { virtual ~Base() = default; };
                                  struct Derived : Base {};
                                   
                                  int main() {
                                      Base b1;
                                      Derived d1;
                                   
                                      const Base *pb = &b1;
                                      std::cout << typeid(*pb).name() << '\n';
                                      pb = &d1;
                                      std::cout << typeid(*pb).name() << '\n';
                                  }


                                Добавлено
                                Цитата Qraizer @
                                Лучше? Только исключение. Никаких вариантов.

                                Да, тамошний пример от интуристов, что я приводил, в плане работы с ошибками - хрень. Согласен.
                                  Цитата JoeUser @
                                  Default решений тут нет.

                                  Очень возможно, но я попытаюсь.
                                  ---
                                  В подобной ситуации больше всего интересует как починить.
                                  Таких массивов может быть много, создаваться могут по-разному.
                                  Блокировка выхода за границы никак не поможет найти ошибку, скорее наоборот.
                                  ---
                                  Значит, для быстрого определения кто виноват, надо явным образом
                                  идентифицировать каждый массив.
                                  Можно присвоить уникальный номер каждому, можно имя в виде текстовой
                                  строки. Как нравится.
                                  Исключение бросать не обязательно - можно просто сформировать диагностику
                                  и завершить приложение. А можно вернуть ссылку на dummy - элемент. По вкусу.
                                  В диагностику запишем имя, размер массива, не правильный индекс.
                                  Весма полезно добавить адрес возврата (откуда вызван). Но это на любителя.
                                  Обязательно ограничение на количество диагностических сообщений.
                                  Поскольку ошибка работы с массивом может привести к огромному
                                  потоку диагностических сообщений, необходимо ограничить их число.
                                  (По очевидным причинам)
                                  ---
                                  Куда выводить.
                                  Давайте определим интерфейс вывода инфы об исключительных ситуациях и ошибках
                                  для всего приложения в целом. И все процедуры для вывода будут пользоваться
                                  им. Указатель на него будем заносить где-то в начале приложения.
                                  Без инициализации будем считать, что инициализация произошла с какой-то
                                  простейшей реализацией. (Например, только вывод в DbgView).
                                  Всё хозяйство занесём в библиотеку и будем строгать приложения совершенно
                                  одинаковыми (с выводом критической информации) не тратя много времени
                                  на эту деятельность.
                                  Если потребуется, для каждого приложения можно определить свою собственную реализацию
                                  интерфейса вывода, поэтому любые разрабатываемые компоненты не будут никак зависеть
                                  от выбранного ранее способа спасения информации.
                                  Сообщение отредактировано: ЫукпШ -
                                    Цитата Qraizer @
                                    Никаких вариантов

                                    Редкие варианты есть, все зависит от фантазии и от реализуемой бизнес-логики ... :victory:
                                    Синтетический пример навскидку:

                                    ExpandedWrap disabled
                                      #include <iostream>
                                       
                                      class Enigma {
                                        public:
                                          Enigma() : a{0xf0,0x0f,0xaa,0x33} {};
                                          unsigned char &operator[] (size_t idx) {
                                            return a[idx % 3];
                                          }
                                        private:
                                          unsigned char a[4];
                                      };
                                       
                                      int main() {
                                        Enigma Eni;
                                        unsigned char Str[] = "Hello World!";
                                        for (size_t i=0; i<12; i++) std::cout << (Str[i]^Eni[i]) << " ";
                                        std::cout << std::endl;
                                        return 0;
                                      }


                                    Добавлено
                                    Цитата ЫукпШ @
                                    Можно присвоить уникальный номер каждому, можно имя в виде текстовой
                                    строки. Как нравится.

                                    Самый идеальный вариант, который я видел, когда программа завершается с печатью полного стека вызовов. Как такое на С++ сделать ... да и еще и универсально - не знаю.
                                      Цитата JoeUser @
                                      Самый идеальный вариант, который я видел, когда программа завершается с печатью полного стека вызовов. Как такое на С++ сделать ... да и еще и универсально - не знаю.

                                      А почему именно это - идеально ? :huh:
                                      Копаться в двоичных кодах удовольствие ниже средного.
                                      И в любом случае необходим переход к исходным текстам,
                                      а копаясь в двоичных кодах это сделать ой как не просто.
                                      Используя идентификатор мы решим проблему стремительным
                                      и дешёвым домкратом.
                                      Так гораздо легче сразу узнать, какой именно экземпляр массива
                                      проблемный, и в чём проблема заключается.
                                      (не соответствие размера массива заданному для операции индексу)
                                      ---
                                      Содержимое стека, контекста и участок образа кодов я вывожу
                                      из функции фильтра исключений. Тоже так же, как описал выше.
                                      Однако, однажды разбираться c этим пришлось месяцы.
                                      ---
                                      Про полный стек вызовов я не знаю.
                                      Но кусок стека вывести можно приблизительно так:
                                      ExpandedWrap disabled
                                        // ---------------------------------------------------------------------------------
                                        #pragma warning (push)
                                        #pragma warning (disable:4700)
                                        #pragma optimize ("", off)
                                        // ---------------------------------------------------------------------------------
                                        #ifdef _M_IX86
                                        static const size_t GRA_RET_ADDR_STACK_OFFSET = 2;
                                        #endif
                                        #ifdef _M_AMD64
                                        static const size_t GRA_RET_ADDR_STACK_OFFSET = 3;
                                        #endif
                                        // ---------------------------------------------------------------------------------
                                        PVOID WINAPI GetRetAddr(void)
                                        {
                                        // caller содержит адрес возврата.
                                         volatile PVOID caller = (PVOID)(&caller)[GRA_RET_ADDR_STACK_OFFSET];
                                        // дальше, если надо, увеличиваем индекс и выводим значения.
                                        // ....  
                                         return caller;
                                        }
                                        // ---------------------------------------------------------------------------------
                                        #pragma optimize ("", on)
                                        #pragma warning (pop)
                                        // ---------------------------------------------------------------------------------
                                      Сообщение отредактировано: ЫукпШ -
                                        Цитата ЫукпШ @
                                        А почему именно это - идеально ? :huh:
                                        Копаться в двоичных кодах удовольствие ниже средного.

                                        Да нет, речь идет о человеко-понятном варианте. Названия процедур, переменных и методов в столбик.
                                          О, вспомнил где некоторое подобие видел:

                                          user posted image
                                            Цитата JoeUser @
                                            Редкие варианты есть, все зависит от фантазии и от реализуемой бизнес-логики ...
                                            Нету. Фантазиям тут места нет. Там, где код в принципе не способен продолжить исполнение своих контрактов, он должен остановить исполнение.
                                              Цитата ЫукпШ @
                                              Исключение бросать не обязательно - можно просто сформировать диагностику
                                              и завершить приложение.
                                              Не смешно. Вот представь: ты весь день что-то редактируешь, а вечером софтина из-за одной очепятки закрывается. Ничего, что как раз для предотвращения этого проверка на ошибки и была придумана? И что ты собрался делать с диагностикой после закрытия?

                                              Добавлено
                                              Цитата ЫукпШ @
                                              А можно вернуть ссылку на dummy - элемент.
                                              Нельзя. Пользователь введёт данные в него, а потом будет удивляться, что его нет в сохранённой версии.

                                              Добавлено
                                              Цитата ЫукпШ @
                                              Весма полезно добавить адрес возврата (откуда вызван).
                                              А ничего, что это как раз итак известно?

                                              Добавлено
                                              Цитата ЫукпШ @
                                              Поскольку ошибка работы с массивом может привести к огромному
                                              потоку диагностических сообщений, необходимо ограничить их число.
                                              А как сделаешь дробное количество сообщений? Я хочу видеть это чудо.

                                              Добавлено
                                              Цитата ЫукпШ @
                                              будем строгать приложения совершенно
                                              одинаковыми
                                              Это не среда разработки.

                                              Добавлено
                                              Цитата ЫукпШ @
                                              тратя много времени
                                              Кто такая?

                                              Добавлено
                                              Цитата JoeUser @
                                              Самый идеальный вариант, который я видел, когда программа завершается с печатью полного стека вызовов. Как такое на С++ сделать ... да и еще и универсально - не знаю.
                                              Нафига он пользователю? Вот в чём вопрос.

                                              Добавлено
                                              Цитата ЫукпШ @
                                              (не соответствие размера массива заданному для операции индексу)
                                              А одно другому вообще не соответствует. Индексы не упорядочены и даже не являются числами.

                                              Добавлено
                                              Цитата JoeUser @
                                              Да нет, речь идет о человеко-понятном варианте. Названия процедур, переменных и методов в столбик.
                                              Ну ка какому юзверю это понятно?
                                              Сообщение отредактировано: Ирокез -
                                                Цитата Ирокез @
                                                Ну ка какому юзверю это понятно?

                                                Обычно у юзверя просят отослать это разработчику. И имя пересылаемому - багрепорт.

                                                Добавлено
                                                Цитата Ирокез @
                                                Нафига он пользователю? Вот в чём вопрос.

                                                См выше.
                                                  Цитата Ирокез @
                                                  Добавлено
                                                  Цитата ЫукпШ @
                                                  Поскольку ошибка работы с массивом может привести к огромному
                                                  потоку диагностических сообщений, необходимо ограничить их число.
                                                  А как сделаешь дробное количество сообщений? Я хочу видеть это чудо.

                                                  Программист - это тот, кто создаёт алгоритмы.
                                                  А ты простой алгоритм сообразить не можешь.
                                                  Плохие для тебя прогнозы.
                                                  ExpandedWrap disabled
                                                    template <class T,size_t Nsize>
                                                    T& WINAPI SomeArray<T, Nsize>::operator [] (size_t index)
                                                    {
                                                     if(index > max_index)
                                                     {
                                                      if(typeErrCount++ < 2)
                                                      {
                                                       AppTypeMessageF (_T("NameArray:%s INDEX OUT OF RANGE :=%u/%u"), NameArray, index, max_index);
                                                      }
                                                      return dummy;
                                                     }
                                                     
                                                     return pdata [index];
                                                    }
                                                  Сообщение отредактировано: ЫукпШ -
                                                    Если работать без исключений, то std::optional можно вернуть вместо ссылки, но тогда придётся везде проверять возвращаемое значение.
                                                      Цитата OpenGL @
                                                      Если работать без исключений, то std::optional можно вернуть вместо ссылки, но тогда придётся везде проверять возвращаемое значение.

                                                      А как это сделать ?
                                                      Проблема такая:
                                                      ExpandedWrap disabled
                                                         SomeArray<int,10> xxMm;
                                                         
                                                         xxMM [50] = 0; // выход за границы

                                                      Если всё уже пошло кувырком, можно в качестве возврата использовать
                                                      хоть бы даже и 0-й элемент. Главное - получить информацию об этом факте.
                                                        Можно много чего напридумывать, и всё это будет иметь какой-то смысл. Но всё это будет неправильно. Единственно правильный вариант – либо ты исполняешь контракт, либо нет по объективным причинам, а вызывающий код должен знать, выполнен ли контракт или нет и соответственно на это отреагировать.
                                                        Фактически от операции индексирования требуется два результата: качественный итог успеха и при положительном итоге собственно результат. Сигнатура operator[] не предусматривает такой возможности. Один из вариантов – заменить оператор на функцию и возвращать два значения. Плохой вариант по нескольким причинам. Полная смена семантики кода, например. Другой вариант – вернуть любое валидное значения, что не намного лучше (а по ряду причин даже хуже), т.к. вызывающий код просто ни о чём не узнает, у него нет возможности отличить нормальную ситуацию от невыполнения контракта. Можно выделить специальное значение как признак ошибки, причём оно должно быть уникальным и не пересекаться ни с какими валидными значениями. Вариант уже лучше, но всё равно плохой. Если б operator[] возвращал указатель, это ещё было бы терпимо, есть специальное выделенное значение nullptr, но для ссылок нет специальных значений, так что его придётся выдумать. Итп. Все эти варианты всё равно оставляют лазейку в том смысле, что вызывающий код может просто проигнорировать признак невыполнения контракта, причём необязательно из-за лени разработчика, а просто по невнимательности.
                                                        Единственный надёжный способ – т.к. вернуть можно лишь одно значение, а именно ссылку, для которого нет специальных значений, нужно прекратить выполнение, не формируя никакого результата.
                                                          Цитата ЫукпШ @
                                                          А как это сделать ?

                                                          Просто сигнатура будет а-ля std::optional<T&> operator[](size_t index). Впрочем, сейчас подумалось, что в плюсах это будет не только неудобно, а ещё и неработоспособно, поскольку придётся либо проверять возвращённый optional, либо просто делать deref без проверки, что является UB если optional пустой. Т.е., фактически, это решение просто меняет одно UB на другое :D Так что исключение - единственный надёжный вариант.
                                                            Цитата Qraizer @
                                                            Можно много чего напридумывать, и всё это будет иметь какой-то смысл. Но всё это будет неправильно. Единственно правильный вариант – либо ты исполняешь контракт, либо нет по объективным причинам, а вызывающий код должен знать, выполнен ли контракт или нет и соответственно на это отреагировать.

                                                            С этим и спорить то невозможно.
                                                            Мои предложения чуть выше по теме - это выдавать диагностику
                                                            и принять решение о дальнейшей судьбе приложени посредством интерфейса.
                                                            Это абстрактная сущность, поэтому никак не связывает никакой
                                                            компонент, (даже библиотечный) никакими конкретными решениями.
                                                            При этом конкретный объект, обеспечивающий работу интерфейса,
                                                            для каждого приложения может быть свой. В связи с его изолированностью
                                                            (от приложения) он тоже может быть библиотечным.
                                                            Таких разнообразных объектов может быть много.
                                                            В результате всё хозяйство оказывается "под капотом", сделанное один раз
                                                            и используемое постоянно.
                                                            Принятое архитектурное решение становится "стандартным" и возможным
                                                            для массового распространения. Остаётся возможность замены
                                                            реализации интерфейса в процессе выполнения программы.
                                                            Также остаётся возможность для реализации любого, самого экзотического
                                                            варианта, как только он потребуется.
                                                            Сообщение отредактировано: ЫукпШ -
                                                            0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                                            0 пользователей:


                                                            Рейтинг@Mail.ru
                                                            [ Script execution time: 0,0933 ]   [ 17 queries used ]   [ Generated: 19.04.24, 12:21 GMT ]