Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.78/9: Рейтинг темы: голосов - 9, средняя оценка - 4.78
154 / 31 / 11
Регистрация: 29.10.2012
Сообщений: 397
1

Optional versus Throw Exception

06.07.2020, 17:35. Показов 1752. Ответов 43

Author24 — интернет-сервис помощи студентам
Всем привет. Задумался о семантике. Мол когда лучше возвращать из функции optional, а когда бросать исключение. На данный момент я знаю, что первый вариант нужно использовать тогда, когда ожидаемо, что значение может быть, а может и не быть. А второй вариант лучше использовать, когда происходит что-то, well, исключительное, неожиданное, что-ли.

Но я не могу понять, как правильно определить, что ожидаемо, а что нет? например, у меня есть класс Grid. В нём, скажем, есть квадратики. Допустим, я хочу достать из него квадратик, передав двумерный индекс в функцию. Всё просто, если мы передадим индекс выходящий за пределы объекта Grid, то естессно мы не можем получить квадратик. В таком случае, что лучше использовать: optional или бросить исключение? С одной стороны я ожидаю, что, если индекс будет неверный, то квадратик я не получу. С другой стороны, я не ожидаю, что передам неверный индекс в функцию изначально и исключение бы мне указало, что что-то неверно.

Или второй пример. Скажем, мы считываем данные из файла. И что, если файла нет? Я как бы ожидаю, что, если файла нет, то хрен, я что получу. С другой стороны, я как бы вызываю функцию для того, чтобы получить данные, и, если их не получаю, то, ёлки, что там творится такое исключительное, что данные я не могу получить?!

Как вы разделяете эти понятия?
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
06.07.2020, 17:35
Ответы с готовыми решениями:

Exception throw в деструкторе
Почему-то происходит выброс при вызове деструктора. Деструктор вызывается в main при завершении...

Const throw(), наследование от класса exception
В принципе, проблема решена, но хочу разобраться. Код в MSVS: #include <iostream> #include...

Самопальный optional, темплейты, попытка в реализацию fmap для optional
Вкратце, есть у меня мой собственный optional. То, что это optional, не суть важно, дело не в нем,...

Throw exception
Внутри метода myReader catch (FileNotFoundException e) понятно. Но внутри runа не могу понять, что...

43
18844 / 9843 / 2408
Регистрация: 30.01.2014
Сообщений: 17,285
06.07.2020, 17:41 2
Цитата Сообщение от Pro100Tom Посмотреть сообщение
Всё просто, если мы передадим индекс выходящий за пределы объекта Grid, то естессно мы не можем получить квадратик. В таком случае, что лучше использовать: optional или бросить исключение?
А когда такая ситуация может возникнуть?

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

Добавлено через 2 минуты
Цитата Сообщение от Pro100Tom Посмотреть сообщение
И что, если файла нет?
Если вы не можете продолжить работу в этой ветке исполнения при отсутствии файла, но можете в какой-то другой, то это может быть исключительной ситуацией.

Запросили файл, файла нет, выбросили исключение, обработали, например, предложением пользователю этот файл создать или скачать из репозитория.
0
154 / 31 / 11
Регистрация: 29.10.2012
Сообщений: 397
06.07.2020, 17:46  [ТС] 3
Цитата Сообщение от DrOffset Посмотреть сообщение
А когда такая ситуация может возникнуть?
На практике я пришёл к выводу, что не надо думать, как такая ситуация может случиться. Если есть функция, в которую можно передать индекс, это означает, что туда просто напросто можно передать цифру, что находится за пределами массива (включая отрицательные значания). То есть должна происходить некая валидация данных. И если данные неверны, я вот не знаю, что с семантической точки зрения лучше подходит. А assert, чёрт, я вообще пока не знаю, что это. Буду гуглить. Но, если вдруг это некое предупреждение мол: чувак, используй функцию аккуратно - то, не, не прокатывает. Бывают большие приложения, и мало ли что может случиться, что неверные данные случайно будут переданы... Да, баг, но о нём как-то надо оповестить. Но опять же, тот же вопрос...
0
18844 / 9843 / 2408
Регистрация: 30.01.2014
Сообщений: 17,285
06.07.2020, 17:49 4
Цитата Сообщение от Pro100Tom Посмотреть сообщение
На практике я пришёл к выводу, что не надо думать, как такая ситуация может случиться.
Конечно надо. От этого зависит выбор стратегии обработки.

Но конкретно с индексами можно и не думать. Это всегда ошибка программиста, а значит не должна обрабатываться как нормальная (пусть даже исключительная) работа.
0
154 / 31 / 11
Регистрация: 29.10.2012
Сообщений: 397
06.07.2020, 17:52  [ТС] 5
Цитата Сообщение от DrOffset Посмотреть сообщение
Конечно надо. От этого зависит выбор стратегии обработки.
Мне кажется, мы немного о разных вещах говорим. Конечно надо думать. Но надо думать о том, чтобы убедиться что передаваемые данные в функцию будут верны. Но это не спасает от того, что, если вдруг всё-таки где-то как-то каким-то непредвиденным образом случилась такая фигня, то внутри этой функции должно быть что-то, что работает с этой "исключительной (или нет?)" ситуацией
0
18844 / 9843 / 2408
Регистрация: 30.01.2014
Сообщений: 17,285
06.07.2020, 17:54 6
Цитата Сообщение от Pro100Tom Посмотреть сообщение
Бывают большие приложения, и мало ли что может случиться, что неверные данные случайно будут переданы... Да, баг, но о нём как-то надо оповестить.
У ассерта может быть разные стратегии исполнения, но в целом все сводится к тому, чтобы оповестить разработчика. Снять дамп памяти, записать в лог, и т.д. на уровне логики программы, а именно для этого и нужды исключения, ничего делать не нужно. Мы уже за пределами логики, из-за ошибки разработчика. А пользователю лишь надо знать, что программа аварийно завершилась, а разработчики оповещены о проблеме.

Добавлено через 1 минуту
Цитата Сообщение от Pro100Tom Посмотреть сообщение
Бывают большие приложения
Я знаю, у нас как раз такие.
0
154 / 31 / 11
Регистрация: 29.10.2012
Сообщений: 397
06.07.2020, 17:54  [ТС] 7
Цитата Сообщение от DrOffset Посмотреть сообщение
Но конкретно с индексами можно и не думать. Это всегда ошибка программиста, а значит не должна обрабатываться как нормальная (пусть даже исключительная) работа.
Если бы вы писали программу, вы бы проверяли внутри функции значение переданного индекса? Если да, и, если бы оно оказалось неверным, вы бы что сделали?
0
18844 / 9843 / 2408
Регистрация: 30.01.2014
Сообщений: 17,285
06.07.2020, 18:05 8
Цитата Сообщение от Pro100Tom Посмотреть сообщение
То есть должна происходить некая валидация данных.
Именно, валидация должна происходить в соответствии с контрактом. Если у функции написано, в документации, что она принимает числа от 0 до 100, то это контракт. Внутри этой функции конечно же может стоять проверка и выброс исключения, но чаще всего так никто не делает. Внутри функции ставят assert (т.е. по-русски утверждение), а "валидация" выполняется перед вызовом, тем самым делая утверждение истинным.

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

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

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

Добавлено через 5 минут
Классический пример - цикл и массив

C++
1
2
3
4
for(int i = 0; i < arr.size(); ++i)
{
    std::cout << arr[i] << ' ';
}
Допустим arr[i] - это вызов перегруженного оператора.
Допустим в массиве 100 элементов.
Где мы обеспечили выполнение контракта? Правильно, в условии цикла. Нужно ли нам, чтобы на каждом вызове arr[i]
еще раз проверял i на вхождение в в диапазон [0..size)? Очевидно, нет.

Что будет, если мы ошибемся и напишем так:
C++
1
2
3
4
for(int i = 0; i <= arr.size(); ++i)
{
    std::cout << arr[i] << ' ';
}
Правильно, будет логическая ошибка. Мы зачем-то решили посмотреть значение несуществующего элемента на последней итерации. Если в операторе arr[i] будет стоять ассерт, то программист будет оповещен о своей ошибке. При этом в релизе программы не будет лишних проверок и замедления работы.

Зачем в этой ситуации завязывать логику программы на заведомо некорректную ситуацию, вводя сюда исключения? Правильно, незачем.
0
154 / 31 / 11
Регистрация: 29.10.2012
Сообщений: 397
06.07.2020, 18:18  [ТС] 9
Перевариваю. И правда есть разница. Тогда вопрос. Можно пожалуйста пример в студию, где использование исключения является "правильным" решением. И ещё один пример, где "optional" является "правильным" решением. Заранее спасибо. Я тут на грани переосмысливания своей жизни
0
4064 / 3318 / 924
Регистрация: 25.03.2012
Сообщений: 12,495
Записей в блоге: 1
06.07.2020, 18:43 10
Цитата Сообщение от DrOffset Посмотреть сообщение
Классический пример - цикл и массив
классический пример скорее vector[i] и vector.at(i)
C++
1
2
3
4
5
for(int i = 0; i < arr.size(); ++i)
{
    std::cout << arr[i] << ' ';
    std::cout << arr.at(i) << ' ';
}
в первом размерность не проверяется, во втором проверяется и бросается исключение.
Программисту даётся возможность как-то оптимизировать свою программу отсутствием проверок,
если он уверен, что в своём алгоритме использует только верные индексы.
1
154 / 31 / 11
Регистрация: 29.10.2012
Сообщений: 397
06.07.2020, 19:05  [ТС] 11
Вы говорили про ассерт? Это вот этот http://www.cplusplus.com/reference/cassert/assert/ ?

Добавлено через 5 минут
Я пока что пришёл к следующим выводам:
1). если данные не проходят валидацию - это не исключительная ситуация и бросать исключение ненужно.
2). optional можно использовать, если, например, в классе есть поле, которое необязательно должно иметь значение. А если его (обычное поле, без optional)не инициализировать, то он всё равно будет дефолтно инициализирован. optional предотвращает это.
3). для валидации данных можно использовать assert, плюс перед вызовом функции убедиться, что параметры верны, а если они всё-таки не верны, то программа всё равно там выплюнет какую-нить ошибку и это будет видно.

Я пока, правда, ешё не понимаю, как поймать эту ошибку, если, скажем элемент массива был неверный выбран, это ведь не совсем exception вылезает, а что-то другое. И как такое скажем в лог записать?

Добавлено через 6 минут
К тому же я не понимаю ещё и вот чего. Если assert вырубают на продакшне, то как ловить ошибки от неверных параметров, если, скажем, эти параметры, не вызывают out of range errors, как с индексами и массивами. Ведь бывает, что данные какие-нибудь на продакшне оказались неправильные, а в процессе разработки этого не учли. И как тогда определить проблему? Assert выключен, exceptiion не выплёвывается. Что тогда делать?
0
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
06.07.2020, 19:05 12
Цитата Сообщение от Pro100Tom Посмотреть сообщение
индекс выходящий за пределы объекта Grid
Цитата Сообщение от Pro100Tom Посмотреть сообщение
optional или бросить исключение?
ни то, и ни другое.

для отлова программных ошибок используют assert
0
154 / 31 / 11
Регистрация: 29.10.2012
Сообщений: 397
06.07.2020, 19:10  [ТС] 13
Цитата Сообщение от hoggy Посмотреть сообщение
для отлова программных ошибок используют assert
Да мне уже написали об этом. Там кстати, в документации этой макро пишут следующее:
Therefore, this macro is designed to capture programming errors, not user or run-time errors, since it is generally disabled after a program exits its debugging phase.
Какие ошибки являются user errors. И какие ошибки являются run-time errors? Как я понял, ошибки данных относят к программным
0
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
06.07.2020, 19:17 14
Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
если он уверен, что в своём алгоритме использует только верные индексы.
а если не уверен, тогда что?

думаешь, at каким то волшебным образом починит баг в твоей программе?

Добавлено через 6 минут
Цитата Сообщение от Pro100Tom Посмотреть сообщение
Какие ошибки являются user errors. И какие ошибки являются run-time errors? Как я понял, ошибки данных относят к программным
user errors - ошибки программиста.
в результате которых алгоритмы работают не правильно.
например, выход за пределы диапазона, или обращение по битому указателю.


run-time errors - строго говоря, это вообще не ошибки.
это штатные (ожидаемые) случаи отказа оборудования,
которые происходят по причинам, которые не зависят от твоей программы.

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

может быть 100500 всяких "может быть" из-за которых та,
иная операция провалилась.


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

то бишь в этом случае ты кидаешь эксепшен.
1
18844 / 9843 / 2408
Регистрация: 30.01.2014
Сообщений: 17,285
06.07.2020, 20:26 15
Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
классический пример скорее vector[i] и vector.at(i)
Вообще-то у меня в примере вектор и есть. Посмотрите внимательнее.
Я назвал его массивом, потому что он предоставляет интерфейс массива.

Цитата Сообщение от Pro100Tom Посмотреть сообщение
Вы говорили про ассерт? Это вот этот http://www.cplusplus.com/reference/cassert/assert/ ?
Это стандартная Си-шная реализация. Ей можно пользоваться, и она хорошо справляется, в окружении, где автоматически генерируются дампы памяти, как, например, в linux. Но иногда в проекте требуется кое-что посильнее (в смысле более функционально богатое). В Qt, например, есть помимо стандартных еще и собственные, "мягкие", ассерты, которые программу не роняют, но пишут в лог. Однако суть та же.

Цитата Сообщение от Pro100Tom Посмотреть сообщение
Можно пожалуйста пример в студию, где использование исключения является "правильным" решением.
Учитывая, что войны "коды возврата vs исключения" не утихают до сих пор, то вряд ли можно привести такой пример, с которым все вокруг согласятся. Вот даже Kuzia domovenok нашел к чему пристать, хотя казалось бы...

Однако исключения очень хорошо себя показывают в двух ситуациях. Первая ситуация - это когда у нас много вызовов функций, например это какая-нибудь инициализация, и каждая из них может закончиться предусмотренной ошибкой. Например, начальная инициализация сервера. Псевдокод:
C++
1
2
3
4
5
auto sock = open();
setsockopt(sock, NONBLOCK);
setsockopt(sock, REUSEADDR);
bind(sock);
listen(sock);
Можно поместить этот код в функцию init() (псевдокод):
C++
1
2
3
4
5
6
7
8
9
auto init()
{
    auto sock = open();
    setsockopt(sock, NONBLOCK);
    setsockopt(sock, REUSEADDR);
    bind(sock);
    listen(sock);
    return sock;
}
Исключения, соответственно можно перехватить один раз, в месте вызова init или выше. Сама процедура инициализации не загромождена лишним деталями и понятна по естественному порядку операций. Надо заметить, что это хорошо работает, если sock - это RAII объект.

Классический вариант с обработкой кодов возврата в псевдокоде выглядит примерно так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
auto init()
{
      auto sock = open();
      if (sock < 0)
      {
         return -1;
      }
      int rc = setsockopt(sock, REUSEADDR);
      if (rc < 0)
      {
         return -1;
      }
      rc = setsockopt(sock, NONBLOCK);
      if (rc < 0)
      {
         return -1;
      }
      rc = bind(sock);
      if (rc < 0)
      {
         return -1;
      }
      rc = listen(sock);
      if (rc < 0)
      {
         return -1;
      }
      return sock;
}
Здесь sock - тоже RAII объект, так что проблемы освобождения ресурсов тут тоже нет.
Однако есть другая пролема, которая у меня запланирована в ситуации номер два.
Вы видите, что везде возвращается -1, и таким образом снаружи init не понятно, какой же именно этап дал сбой. Возможно нам пригодилось бы это для диагностики проблемы. Чтобы это обеспечить, нам придется вводить коды ошибок на каждый этап, и обрабатывать их все еще раз в месте вызова init. Если же уровней вложенности от момента, когда ошибка произошла, до момента, когда есть возможность и целесообразность ее обрабатывать больше двух вложенностей, то кодирование всего этого превращается в "квест". Исключения же позволили бы не делать этого.
Есть возражения на тезисы выше, в которых постулируется, что обработка ошибок должна быть как можно ближе к месту ошибки. В принципе это справедливо и при использовании исключений, если вы можете обработать ошибку сразу, то исключения вам не нужны. Однако это не всегда возможно. Если взять проект покрупнее, то это может быть нетривиальной задачей (особенно при желании сохранить структурированность и понятность кода). Например в СУБД Postgresql, которая написана, как известно, на Си (в Си нет исключений), таки пришлось эмулировать оные через функции setjmp\longjmp именно из-за невозможности обрабатывать все ошибки по месту.

Что касается optional - это вариация на тему кода возврата, у нее могут возникнуть те же проблемы, что описаны выше.

Кстати, существует новый для С++ подход, где optional и исключения совмещены в одно решение. Этот подход старается взять лучшее от обеих практик. Называется std::expected (пока еще не принято в стандарт, но существует в качестве предложения). Также существует реализация для С++11\14\17 здесь: https://github.com/TartanLlama/expected

Добавлено через 5 минут
Андрей Александреску делал доклад по этой технике в 2018 году на CppCon.
https://www.youtube.com/watch?v=PH4WBuE1BHI
Чуть позднее был также доклад на CppRussia.
https://www.youtube.com/watch?v=CGwk3i1bGQI
3
154 / 31 / 11
Регистрация: 29.10.2012
Сообщений: 397
07.07.2020, 11:17  [ТС] 16
Выспался с этим вопросом, и теперь кое-какое мнение сформировалось. Может быть идея с expected и норм (вижу, что там еще и функционал монадов туда припихнуть решили), но поскольку она не стандартна, то не хочу её трогать. Её по ходу даже в С++20 не планируют включать. Видео (то, что подлиннее) я глянул и хз, чувак не совсем убедителен и на уровне интуиции не хочется ему "доверять".

Теперь по поводу optional. Хоть и структура позволяет его использовать как этот вот expected, он по названию семантически не совсем подходит для такого решения.

Теперь по поводу exceptions. Вот я много рисерча вчера сделал. И в некоторых языках даже assertions являются оболочкой exceptions. В Питоне, например, даже for цикл завершается исключением, а потом ловится keyword "for". Я всё еще не уловил чем исключения плохи для такого решения. Люди пишут, что исключения должны использоваться в исключительных ситуациях, мол код вот хороший, но там где-то что-то по независимым от программиста причинам случилось, скажем, сервер недоступен. Окей, я могу это понять. Но я не понимаю, почему не стоит их использовать в неисключительных ситуациях? Я где-то читал, что если функция не может сделать того, что она должна сделать - кидай исключение, что согласитесь, звучит вполне разумно.

И вот вчера мы говорили о user input. Я решил поразмышлять, как бы решал эту проблему на других языках. Скажем, С#. Там есть стандартный метод Int.Parse(). И он, например кидает исключение, когда получает не цифры в качестве типа string в качестве параметра. То есть в языке куча типов исключений, а ля IndexOutOfRangeException, StackOverflowException и тонна других. Вроде ситуация неисключительная, но чувствуется, что решение с исключениями отлично подходит.

Но в PHP, например, возьмём, если framework Magento 2, будь он неладен, там есть классы - репозитории, которые могут тебе вернуть record, если передать айди в качестве параметра. Но ёлки, там тоже исключение выбрасывается, если record не был найден. В этой ситуации для меня определенно странно такое решение. Я бы просто вернул null.

По поводу null. В С# я бы тоже оперировал null, в тех случаях, когда не чувствуется правильным кидать исключения (я пока не понимаю еще толком, когда это происходит), но, если гуглить аналог null в C++, то дружно рекомендуют optional.

Так что я всё ещё нахожусь в начальной точке перед выбором optional или throw exception. С другой стороны, если не могу решить, то можно взять любое и быть consistent с этим решением и как бы будет ок, думаю, но перед тем как я сдамся, я хотел бы ещё что-нибудь от вас услышать.
0
18844 / 9843 / 2408
Регистрация: 30.01.2014
Сообщений: 17,285
07.07.2020, 11:50 17
Исходя из ваших постов я пришел к выводу, что вы ищете серебряную пулю. Однозначное и со всех сторон обоснованное решение, которые вы просто можете применять не думая. Однако так не бывает.

Цитата Сообщение от Pro100Tom Посмотреть сообщение
И в некоторых языках даже assertions являются оболочкой exceptions. В Питоне, например, даже for цикл завершается исключением, а потом ловится keyword "for".
Вообще это не слишком корректно так вот перебрасывать концепции из одного языка в другой. Особенно если эти языки отличают больше чем синтаксисом. Если мы говорим про С++, то в общем случае мы не сможем просто взять приемы Python и использовать их. Так что апелляция к другому языку - это не доказательство. Python и С++ слишком разные, чтобы это сработало. Тоже самое касается упомянутых ниже C# и PHP.

Если хотите сравнить обработку ошибок, исключительных ситуаций в С++, то сравнивайте с D, С, Rust, Go, Ada и т.п. Это по крайней мере будет рассуждение примерно на том же уровне абстракции.

Цитата Сообщение от Pro100Tom Посмотреть сообщение
Там есть стандартный метод Int.Parse().
Также там есть и альтернативный вариант (TryParse), который исключений не бросает. У программиста есть выбор исходя из его потребностей.

Цитата Сообщение от Pro100Tom Посмотреть сообщение
Но я не понимаю, почему не стоит их использовать в неисключительных ситуациях
Потому что в С++ исключения дороги, а в каком-то условном PHP - нет. Или это может быть центральная парадигма платформы - так реагировать на все, что угодно.

Цитата Сообщение от Pro100Tom Посмотреть сообщение
Так что я всё ещё нахожусь в начальной точке перед выбором optional или throw exception.
Честно говоря меня сразу же насторожило ваше противопоставление optional и исключений.
В общем случае их нельзя противопоставлять.
optional может быть частью "неисключительной" логики программы, и никак с ошибками не связанным. Например, необязательное поле в структуре данных в каком-то условном google.protobuf. Когда вы пишете optional, вы задаете, что вам важно значение, и "пустое" значение в том числе вас тоже интересует. А исключения задают другое совсем поведение: значение вас не интересует. Вас интересует, что некая операция не была выполнена и была задействована предусмотренная реакция на это. Это же совсем разные стратегии, как вы их сравниваете?

Я бы поставил под сомнение всю формулировку вашего вопроса, если честно. Да и вообще любые подобные формулировки. optional value, return codes, exceptions это все части одной картины, набор выразительных средств, которые вполне могут использоваться вместе исходя из целесообразности, логичности и просто эстетических предпочтений.
0
154 / 31 / 11
Регистрация: 29.10.2012
Сообщений: 397
07.07.2020, 12:16  [ТС] 18
Ну и опять я с вами не согласен, хоть и мнение ваше читать действительно полезно. Смотрите, вы пишете следующее:
Цитата Сообщение от DrOffset Посмотреть сообщение
Честно говоря меня сразу же насторожило ваше противопоставление optional и исключений.
В общем случае их нельзя противопоставлять.
optional может быть частью "неисключительной" логики программы, и никак с ошибками не связанным.
Я ведь об этом написал в своем начальном посте. И я чётко знаю, для чего он создан. Просто есть определенная ситуация, когда что-то идёт не так в функции и мне нужен "верный" инструмент. Да, наверное с серебряной пулей вы правы. Я пытаюсь найти замену исключениям. Expected не в стандарте, не подходит.

Второе:
Цитата Сообщение от DrOffset Посмотреть сообщение
Также там есть и альтернативный вариант (TryParse)
Я знаю, про TryParse, но я спрашивал именно про Parse. Я вижу, почему они создали TryParse кстати. Они наверняка сначала хотели создать что-то, что возвращает "IsNumber", может оно и есть даже, но просто в этой же функции скорее всего имплементация была бы именно той же, что и при Parse, только завернутое в try/catch. Поэтому они решили, а нахрена экстра шаг делать, можно же сразу сделать TryParse. И блин, это классное решение, оно мне нравится. Там идёт именно уклон на то, работает ли вещь, а результат - дело двадцатое. Но я спросил именно про Parse. И хоть и языки разные, но концепции исключений в этих языках совпадают. Ну правда ведь совпадают, вряд ли тот же код в плюсах где есть исключения, не имел бы исключений в аналоге на C#. Как вы считаете, ситуация с Parse исключительная? Или стоило бы использовать другое решение? Если стоило бы использовать другое решение, то какое? Как бы вы реализовали метод Parse без исключений? Использовали бы null? А в C++?
0
18844 / 9843 / 2408
Регистрация: 30.01.2014
Сообщений: 17,285
07.07.2020, 13:02 19
Цитата Сообщение от Pro100Tom Посмотреть сообщение
Ну и опять я с вами не согласен
Это заметно. У вас изначально была такая позиция, что вот, мол, я заранее ни с чем не согласен, а вы меня убеждайте Однако правда в том, что убеждать тут не в чем. Ибо любые убеждения, которые не привязаны к конкретному месту в конкретном проекте, найдут возражения. Потому что абстрактные ситуации можно крутить как угодно, придумывать любые ограничения или поблажки прямо на лету, после чего все доводы будут рассыпаться.

Вот вернемся к моему примеру с вектором\массивом.
Там я вам показал, что контракт проверяется в условии цикла, поэтому нет нужды проверять его чем-то большим, чем ассертом внутри оператора доступа. Так вот, давайте теперь налету изменим условия. Пусть вектор у нас меняется параллельно другим потоком (не важно для чего). Пусть сам вектор может быть совсем не std-шным, а каким-то другим, но с примерно такой же семантикой. Пусть операция доступа атомарна, но к моменту ее выполнения элемент из вектора может пропасть. Так случается не часто и обычно свидетельствует о какой-то проблеме. Нужно ли здесь исключение? Похоже, что да? Может ситуация высосана из пальца, а может и нет. Ну вот и думайте, поможет ли тут шаблонное мышление - навредит скорее. Ну и толку от этих мук выбора, когда на практике вам все равно нужно будет выбирать решение для ваших конкретных условий, отдельно оценивать достоинства и недостатки для вашей конкретной задачи?

Цитата Сообщение от Pro100Tom Посмотреть сообщение
Как вы считаете, ситуация с Parse исключительная?
Это зависит контекста применения.
И именно поэтому я упомянул TryParse, он как раз для тех контекстов, где эта ситуация НЕ исключительная.

Цитата Сообщение от Pro100Tom Посмотреть сообщение
Или стоило бы использовать другое решение?
Вопрос без конкретного места в конкретном проекте не имеет смысла.

Цитата Сообщение от Pro100Tom Посмотреть сообщение
А в C++?
Тоже самое.

Цитата Сообщение от Pro100Tom Посмотреть сообщение
Expected не в стандарте, не подходит.
Можно было бы об этом говорить, если бы мы сейчас с вами проводили экспертизу конкретного проекта, с ТЗ на руках и парой макетов. А так, опять же вопрос "не подходит для чего??". Не подходит под вашу картину мира? - Ну, возможно, только это совсем не инженерный подход. Нельзя вот себе заранее определять инструменты, не имея на руках задачи.

Добавлено через 10 минут
Цитата Сообщение от Pro100Tom Посмотреть сообщение
И хоть и языки разные, но концепции исключений в этих языках совпадают.
Это на самом деле чудовищная ошибка так думать.

Концепция использования исключений в данном примере задается не в языке, а в .NET фреймворке. (Parse - это часть Namespace System)
Ближайший аналог на C++ - это фреймворк Qt, в котором исключений вообще нет.
0
154 / 31 / 11
Регистрация: 29.10.2012
Сообщений: 397
07.07.2020, 13:10  [ТС] 20
Цитата Сообщение от DrOffset Посмотреть сообщение
И именно поэтому я упомянул TryParse, он как раз для тех контекстов, где эта ситуация НЕ исключительная.
То есть, по вашему мнению, разумно создавать две версии одного и того же метода, чтобы можно была выбрать вариант в зависимости от исключительности?

То есть вопрос исключительности лежит не в самой имплементации метода, а в контексте, где он используется?

Добавлено через 7 минут
Цитата Сообщение от DrOffset Посмотреть сообщение
Нельзя вот себе заранее определять инструменты, не имея на руках задачи.
Мне всего лишь нужно два примера, один, где исключение имеет смысл (в, скажем, использовании Parse), а другой, где нет. Мне как раз нужно увидеть эти контексты, чтобы потом понимать с каким контекстом я имею дело. Хорошо, вот вам пример:

У меня есть лабиринт. И там есть блоки. Я хочу выбрать блок OnTheLeft, скажем от текущего. Вот код:
C++
1
2
3
4
5
6
7
8
    const Block& Labyrinth::GetNeighbourBlock(
        const BlockSlot& blockSlot,
        BlockNeighbourSelector blockNeighbourSelector
    ) const {
        auto neighbourBlockSlot = GetNeighbourBlockSlot(blockSlot, blockNeighbourSelector);
 
        return blockGrid[neighbourBlockSlot.columnIndex][neighbourBlockSlot.rowIndex];
    }
BlockSlot - это индекс. Я точно не хочу так это оставить как тут. Я хочу взглянув на код видеть, что ситуация с out of range была принята во внимание. Я хочу, чтобы, если вдруг индекс оказался вне размерности лабиринта, то мы либо покажем ошибку либо вернем null, то есть optional. То есть железно мне не хочется оставить как есть и ждать run-time errors, если вдруг они возникнут. Да, я обязательно перед вызовом функции использую метод IsInBounds(). Но скажем, я забуду это сделать в какой-то момент, я хочу знать где крякнуло!

Причина по которой мне нужен "соседний блок" заключается в том, что мне нужно генерировать путь. То есть я на этом блоке иду налево, значит мне нужно сфокусироваться на левом блоке и генерировать путь дальше уже там. Бывает, что мы уже в самом левом блоке, и если мы пойдём налево, то там ничего не будет. И это не исключительная ситуация. Но тут определенно программа сломается, если проверку не сделать.
0
07.07.2020, 13:10
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
07.07.2020, 13:10
Помогаю со студенческими работами здесь

в Google Chrome завелись PUP.Optional.22ChromeEXT, PUP.Optional.Banggood, PUP.Optional.Legacy, PUP.Optional.MySearch
Случилась такая беда: В Хроме автоматически грузятся левые страницы вместо уже открытых (одна из...

в Google Chrome завелись PUP.Optional.22ChromeEXT, PUP.Optional.Legacy,PUP.Optional.Mail.Ru
Рабочий комп достался с такими проблемами : PUP.Optional.22ChromeEXT,...

PUP.Optional.Legacy, Adware.RuKometa, PUP.Optional.FakeYandex, PUP.Optional.RussAd
Доброго времени суток! Прошу помощи в удалении вирусов из заголовка. adwcleaner_7.0.8.0...

PUP.Optional.Legacy, Adware.RuKometa, PUP.Optional.FakeYandex, PUP.Optional.RussAd
Доброго времени суток! Прошу помощи в удалении вирусов из заголовка. adwcleaner_7.0.8.0...

PUM.Optiomal.DisableMRT, PUP.Optional.MailRu, PUP.Optional Softomate и adp alexa 51 в стмовских играх
Здравствуйте, у меня появилась проблема с запуском стимовской Zombie army trilogy, заглянув в...

PUP.Optional.MailRU и PUP.Optional.Legacy adwcleaner не справляется
решил скачал пару программ и полезли амиго, маилру и остальные не особо приятные вещи. От всего...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru