Pull to refresh

Comments 10

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

Только не забывайте -fno-delete-null-pointer-checks и откажитесь от использования clang'а — а так всё хорошо.

А что не так с clang и no-delete-null-pointer-checks?

Обращение по nullptr — это неопределённое поведение. То есть в правильно написанной программе его быть не должно. И компиляторы на это полагаются. То есть, например, если у вас есть что-то такое:
  int x = *p;
  if (p == nullptr) {
    std::cerr << "p is null" << std::endl;
  }

то и clang и gcc могут этим воспользоваться и упростить программу, выкинув из неё if (и всё, что там внутри!) к чертям собачьим. Вообще любой код, про который компилятор может доказать, что он может быть выполнен, только если какая-то переменная оказывается равна nullptr и после этого разименовывается или участвует в арифметике может быть выкинут.

У gcc есть опция, которая это отключает, у clang'а нет.

Не вижу проблем — до условного оператора выполнение не дойдет в любом случае.

Не вижу проблем — до условного оператора выполнение не дойдет в любом случае.
Вы это серьёзно?

Хорошо, вот вам полный пример:
#include <iostream>

int __attribute__((noinline)) foo(int* p) {
  int x = *p;
  if (p == nullptr) {
    std::cerr << "p is null" << std::endl;
  }
  return x;
}

int main() {
  foo(nullptr);
  std::cerr << "Bwahaha" << std::endl;
}


$ g++ -O3 -std=c++11 test.cc -o test-bad
$ g++ -O3 -std=c++11 -fno-delete-null-pointer-checks test.cc -o test-good
$ test-bad
Bwahaha
$ test-good
Segmentation fault (core dumped)
$ g++ --version
g++ (GCC) 5.2.0
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Всё ещё не видите проблемы и разницы между двумя вариантами?

Полагаться на то, что обращение к nullptr вызовет исключение — очень плохая идея. Сегодня — оно срабатывает, а завтра — вдруг перестанет, причём «малые шевеления» кода могут приводить к тому, что эффект будет то появляться, то исчезать…

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


Если падение программы предотвратил компилятор — ну и замечательно.


PS если переменную x не забывать, а использовать — компилятор операцию чтения не убирает. Хотя стандартом такое поведение и не гарантируется, но выглядит оно очень логичным…

Если падение программы предотвратил компилятор — ну и замечательно.
Проблема в том, что компилятор мог «предотвратить падание программы» выкинув из неё кучу кода — что может приводить (и реально приводило) к дырам в безопасности. Если вашей программе очень-очень не стоит падат — то точно ли вам будет хорошо, если она вдруг сделает что-то не то?

Грубо говоря: если ваша программа управляет ядреным реактором — то, понятно, очень не хочется чтобы она вдруг внезапно остановилась и реактор бы взорвался… но если компилятор «предотвратит падение» позволив вам выташить из реактора стержни — то кому будет хорошо от того, что ваша программ не упадёт?

PS если переменную x не забывать, а использовать — компилятор операцию чтения не убирает.
Посмотрите внимательно на код, а? Там переменная как раз обязательно используется и разименовывается. Да и функция помечена как noinline чтобы компилятор ненароком её не обработал как ему хочется. Что не спасло от того, что компилятор, исследовав код, обнаружил, что вызов этой функции можно выкинуть, так как её результат «на более высоком уровне абстракции» никому не нужен. А проверку — мы выкинули «за ненадобностью» ранее.

Смысл подхода, предложенного автором — не столько в том чтобы разрешить nullptr, сколько в том чтобы предотвратить падение программы. И это должен быть последний барьер, а не первый.
Для этого вполне достаточно следить за программой «снаружи» и перезапускать «в случае чего». Превращение обращения в память в выброс C++ исключения и обработка этого исключения превращает критическую ошибку в что-то, что является почти «штатной» проблемой и поощряет написание небезопасного кода.

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


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


А вот падение программы посреди заполнения длинной формы, да еще и в третий раз подряд, как раз может быть серьезной проблемой.

Sign up to leave a comment.

Articles