Автор Тема: Обработка "ошибок".  (Прочитано 27447 раз)

valexey

  • Administrator
  • Hero Member
  • *****
  • Сообщений: 1990
    • Просмотр профиля
Обработка "ошибок".
« : Апрель 17, 2012, 11:16:24 pm »
Предположим у нас нет никаких исключений. Также у нас нет монад и вообще мы не можем использовать выражения (expressions) чтобы связать вычисения, только чистые statement'ы. И у нас есть код, последовательность вызова функций. Каждая функция возвращает код ошибки. Если код nil, значит все хорошо и нужно продолжать вычисления дальше. Если же нет, то нужно напечатать что-то в лог и прекратить вычисление.

Если делать тупо в лоб, то получается как-то так:
err = f1()
if (nil == err) {
    err = f2()
    if (nil == err) {
        err = f3()
        if (nil == err) {
        ...
        }
    } else {
        logError();
    }
} else {
    logError();
}
То есть куча вложенных if'ов на ровном месте и радостное перемешивание кода логики с кодом обработки ошибок.

Есть такой распространненный паттерн решения этой задачки:
err = f1()
if (err!=nil) {
    logError()
    return
}
err = f2()
if (err!=nil) {
    logError()
    return
}
err = f3()
if (err!=nil) {
    logError()
    return
}
Откровенно говоря, он мне тоже не нравится (хотя читается несколько лучше предыдущего) - код и логика опять смешаны. И опять туча дублирующегося кода.

Кто как справлялся с таким вот когда (и если) доводилось сталкиваться?
« Последнее редактирование: Апрель 18, 2012, 08:04:20 am от valexey »
"но сейчас, чтобы компенсировать растущую мощность компьютеров, программисты используют фреймворки"

Peter Almazov

  • Sr. Member
  • ****
  • Сообщений: 482
    • Просмотр профиля
Re: Обреботка "ошибок".
« Ответ #1 : Апрель 18, 2012, 03:25:36 am »
Кто как справлялся с таким вот когда (и если) доводилось сталкиваться?
Сталкивался многократно. Как справлялся - никак.  :(

vlad

  • Hero Member
  • *****
  • Сообщений: 1391
    • Просмотр профиля
Re: Обреботка "ошибок".
« Ответ #2 : Апрель 18, 2012, 05:09:03 am »
Кто как справлялся с таким вот когда (и если) доводилось сталкиваться?

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

vlad

  • Hero Member
  • *****
  • Сообщений: 1391
    • Просмотр профиля
Re: Обреботка "ошибок".
« Ответ #3 : Апрель 18, 2012, 05:12:10 am »
(опять форум сглючил)

Кто как справлялся с таким вот когда (и если) доводилось сталкиваться?

Если выбирать из зол - то я бы выбрал досрочные ретурны. Либо вариант с проверками на кажом шаге (если надо дойти до конца функции):
err = f1();
if (!err)
   err = f2();
if (!err)
    err = f3();

adva

  • Sr. Member
  • ****
  • Сообщений: 385
    • Просмотр профиля
Re: Обреботка "ошибок".
« Ответ #4 : Апрель 18, 2012, 06:20:37 am »
Во втором случае, чтобы понять что после первой (n - ной) ошибки дальше не отрабатывает, приходится просматривать весь код до конца, в первом случае сразу понятно. Хотя я использую оба варианта.

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

kemiisto

  • Jr. Member
  • **
  • Сообщений: 64
    • Просмотр профиля
    • kemiisto.ru
Re: Обреботка "ошибок".
« Ответ #5 : Апрель 18, 2012, 07:25:05 am »
Наверное как вариант, можно предложить в каждую функцию передавать параметр ошибки, и если он есть, то внутри функции уже не отрабатывать.
Передавать не всегда обязательно. Можно просто иметь некую "глобальную" переменную err, например, поле объекта, а вызываться будут его методы.

valexey

  • Administrator
  • Hero Member
  • *****
  • Сообщений: 1990
    • Просмотр профиля
Re: Обработка "ошибок".
« Ответ #6 : Апрель 18, 2012, 08:08:55 am »
Во втором случае, чтобы понять что после первой (n - ной) ошибки дальше не отрабатывает, приходится просматривать весь код до конца, в первом случае сразу понятно. Хотя я использую оба варианта.
Во втором случае тоже не надо, а вот в варианте влада - надо. То есть в третьем случае.

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

Valery Solovey

  • Hero Member
  • *****
  • Сообщений: 509
    • Просмотр профиля
Re: Обработка "ошибок".
« Ответ #7 : Апрель 18, 2012, 08:16:31 am »
Можно ещё вот так извратиться.
if (f1() == nil &&
      f2() == nil &&
      f3() == nil) {
        ...
} else {
    logError();
}

valexey

  • Administrator
  • Hero Member
  • *****
  • Сообщений: 1990
    • Просмотр профиля
Re: Обработка "ошибок".
« Ответ #8 : Апрель 18, 2012, 08:26:14 am »
Можно ещё вот так извратиться.
if (f1() == nil &&
      f2() == nil &&
      f3() == nil) {
        ...
} else {
    logError();
}
А это уже противоречит изначальной постановке задачи, ибо связывает вычисления через выражение. :-)

Поясню почему такое ограничение наложено - у нас ведь скорее всего будут не просто вызовы функций, но и другие statement'ы между ними. То есть без обработки ошибок это нечто вроде:
f1()
a:=c-d
f2()
g; v; z;
f3()
"но сейчас, чтобы компенсировать растущую мощность компьютеров, программисты используют фреймворки"

Valery Solovey

  • Hero Member
  • *****
  • Сообщений: 509
    • Просмотр профиля
Re: Обработка "ошибок".
« Ответ #9 : Апрель 18, 2012, 08:29:55 am »
Можно ещё вот так извратиться.
if (f1() == nil &&
      f2() == nil &&
      f3() == nil) {
        ...
} else {
    logError();
}
А это уже противоречит изначальной постановке задачи, ибо связывает вычисления через выражение. :-)
Как раз-таки изначальной постановке задачи оно не противоречит : ).

Valery Solovey

  • Hero Member
  • *****
  • Сообщений: 509
    • Просмотр профиля
Re: Обработка "ошибок".
« Ответ #10 : Апрель 18, 2012, 08:34:34 am »
А можно ещё так:
err = f1()
if (nil == err) {
    wrap_f2()
} else {
    logError();
}
, где wrap_f2() - это обёртка над f2(), прилегающими командами и обработкой ошибки. (Там внутри ещё будет вызван wrap_f3()).

valexey

  • Administrator
  • Hero Member
  • *****
  • Сообщений: 1990
    • Просмотр профиля
Re: Обработка "ошибок".
« Ответ #11 : Апрель 18, 2012, 08:38:55 am »
Цитата: Valery Solovey link=topic=226.msg5114#msg5114
раз-таки изначальной постановке задачи оно не противоречит : ).

Противоречит.
Цитировать
Предположим у нас нет никаких исключений. Также у нас нет монад и вообще мы не можем использовать выражения (expressions) чтобы связать вычисения, только чистые statement'ы.
"но сейчас, чтобы компенсировать растущую мощность компьютеров, программисты используют фреймворки"

Romiras

  • Sr. Member
  • ****
  • Сообщений: 264
    • Просмотр профиля
    • Romiras Dev Lab
Re: Обработка "ошибок".
« Ответ #12 : Апрель 18, 2012, 09:02:38 am »
Предположим у нас нет никаких исключений. Также у нас нет монад и вообще мы не можем использовать выражения (expressions) чтобы связать вычисения, только чистые statement'ы. И у нас есть код, последовательность вызова функций. Каждая функция возвращает код ошибки. Если код nil, значит все хорошо и нужно продолжать вычисления дальше. Если же нет, то нужно напечатать что-то в лог и прекратить вычисление.

Если делать тупо в лоб, то получается как-то так:
err = f1()
if (nil == err) {
    err = f2()
    if (nil == err) {
        err = f3()
        if (nil == err) {
        ...
        }
    } else {
        logError();
    }
} else {
    logError();
}
То есть куча вложенных if'ов на ровном месте и радостное перемешивание кода логики с кодом обработки ошибок.

Есть такой распространненный паттерн решения этой задачки:
err = f1()
if (err!=nil) {
    logError()
    return
}
err = f2()
if (err!=nil) {
    logError()
    return
}
err = f3()
if (err!=nil) {
    logError()
    return
}
Откровенно говоря, он мне тоже не нравится (хотя читается несколько лучше предыдущего) - код и логика опять смешаны. И опять туча дублирующегося кода.

Кто как справлялся с таким вот когда (и если) доводилось сталкиваться?

Вот мой вариант обработки ошибок:
MODULE PrivErrTypes;

CONST
OK* = 0;
ERR_NO_MEM* = 101;
ERR_INVALID_MODE* = 201;

TYPE
ErrorStatus* = POINTER TO RECORD
code*: INTEGER;
location*: INTEGER;
END;

END PrivErrTypes.

MODULE PrivErrChecks;

IMPORT ETypes := PrivErrTypes, StdLog;

VAR
res: ETypes.ErrorStatus;
p: POINTER TO ARRAY 100 OF REAL;
a: INTEGER;

PROCEDURE Message(IN s: ARRAY OF CHAR);
BEGIN
StdLog.String(s); StdLog.Ln
END Message;

PROCEDURE LogReport;
VAR s: ARRAY 256 OF CHAR;
BEGIN
IF res.code # ETypes.OK THEN
CASE res.code OF
ETypes.ERR_NO_MEM: s := "No enough memory for completing of operation."
| ETypes.ERR_INVALID_MODE: s := "Invalid mode."
END;
Message("ERROR: " + s$)
END
END LogReport;


PROCEDURE Check1Passed (): BOOLEAN;
BEGIN
NEW(p);
IF p = NIL THEN
res.code := ETypes.ERR_NO_MEM; res.location := 0C1H
END;
RETURN res.code = ETypes.OK
END Check1Passed;

PROCEDURE Check2Passed (): BOOLEAN;
BEGIN
IF a < 0 THEN
res.code := ETypes.ERR_INVALID_MODE; res.location := 0C2H
END;
RETURN res.code = ETypes.OK
END Check2Passed;


PROCEDURE Run*;
VAR passed: BOOLEAN;
BEGIN
res.code := ETypes.OK; res.location := 0;

passed := Check1Passed();
passed := passed & Check2Passed();

IF passed THEN
Message("All tests passed successfully!")
ELSE
LogReport
END
END Run;

BEGIN
IF res = NIL THEN NEW(res) END;

a := -1; (* causing test to fail *)
END PrivErrChecks.

! PrivErrChecks.Run

Romiras

  • Sr. Member
  • ****
  • Сообщений: 264
    • Просмотр профиля
    • Romiras Dev Lab
Re: Обработка "ошибок".
« Ответ #13 : Апрель 18, 2012, 09:10:01 am »
Ага. Я слишком вскользь прочёл. Тут речь о каких-то монадах.
Пускай valexey сам решает подходит или нет.  ;)

valexey

  • Administrator
  • Hero Member
  • *****
  • Сообщений: 1990
    • Просмотр профиля
Re: Обработка "ошибок".
« Ответ #14 : Апрель 18, 2012, 09:17:51 am »
Вот мой вариант обработки ошибок:
...
passed := Check1Passed();
passed := passed & Check2Passed();
...
Опять связывание вычислений через выражение. Не годится по выше изложенным соображениям. Также не годится в случае если функция возвращает больше одного значения (в некоторых языках так можно):
err, res = f1()
В этом случае f1() засунуть в качестве операнда выражения уже не получится.
"но сейчас, чтобы компенсировать растущую мощность компьютеров, программисты используют фреймворки"