На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
Страницы: (2) 1 [2]  все  ( Перейти к последнему сообщению )  
> Что должен возвращать оператор [] ограниченного массива?
    Цитата 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,0544 ]   [ 16 queries used ]   [ Generated: 29.03.24, 14:05 GMT ]