Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Религиозные войны > Exception vs Error Code


Автор: neutrino 16.6.2008, 13:23
Привет!

Вот собственно и вопрос. Кто чем пользуется и почему.
Про себя: всегда только Exception-ами. ИМХО нет случая, когда необходимо пользовать Error Code. Если необходимо использовать Error Code, значит ошибка в дизайне.

Или я еще не сталкивался с настоящими случаями в которых нужен Error Code.

PS: Тут не имеются в виду всякие там АПИ. Только ваш код.

Автор: maxim1000 16.6.2008, 22:52
честно говоря, чего-то полностью устоявшегося у меня в голове нет, но придумалось такое правило:
для исключительных ситуаций - исключения
для ошибок (остальных) - коды ошибок

естественно, это - не строгое правило, но смысл передаёт

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

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

вариант в голосовании так и не смог выбрать - и то, и то использую

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

Автор: Lazin 16.6.2008, 23:46
в основном исключения, завтра напишу подробно  smile 

Автор: chipset 17.6.2008, 00:21
А если пишешь DLL которая будет юзаться, к примеру дельфой или джавой?

Автор: neutrino 17.6.2008, 07:41
Спасибо за ответы.

Вот как мне думается все эти коды возврата делают код менее удобочитаемым. Плюс эксепшены я могу объединять в классы (наследование) ошибок и ловить скажем только ошибки ввода-вывода. Если бы я использовал коды возврата, то пришлось бы перечислять все в ифах. Эксепшены кстати можно ловить все в одной точке. А управление кодами - это еще то занятие... Короче если есть ошибка - это ексепшен. Если нужен код возврата, то это не ошибка а состояние объекта. И нужно сделать дизайн по-другому.
Цитата(maxim1000 @  16.6.2008,  21:52 Найти цитируемый пост)
теперь рассмотрим объект читающий текстовый файл
так вот его функция чтения строки вполне может возвращать бинарный флаг, информирующий, был ли достигнут конец файла

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

Автор: chipset 17.6.2008, 08:17
Цитата(maxim1000 @  16.6.2008,  12:52 Найти цитируемый пост)

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

ИМХО, это усложнит процесс обработки ошибок. Усложнять процесс обработки ошибок не нужно потому-что баги в ошибко-репортинге где-то хрен знает где на другом полушарии это очень и очень неприятно smile

Добавлено через 40 секунд
Цитата(neutrino @  16.6.2008,  21:41 Найти цитируемый пост)

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

Так построено большинство библиотек, тот-же STL smile

Автор: Lazin 17.6.2008, 08:36
попробую разложить по полочкам:
  • исключения очень сильно упрощают код
  • исключения делают код надежнее
  • исключения документируют код
1. исключения очень сильно упрощают код, так как позволяют разделить алгоритм нормального выполнения программы и алгоритм аварийной работы. Код получается более прозрачный, и компактный. Самое главное подсистема обработки ошибок не влияет на семантику класса.
Пример:
Код

HRESULT f1()
{
    HRESULT hr = f2();
    if ( FAILED(hr) )
    {
        return hr;
    }
    hr = f3();
    if ( FAILED(hr) )
    {
        return hr;
    }
    hr = f4();
    if ( FAILED(hr) )
    {
        return hr;
    }    
}
...
HRESULT hr = f1();
if ( FAILED(hr)
{
    //handle error
}

не очень ясный код, неправда-ли? smile 
Код

void f1()
{
    f2();
    f3();
    f4();
}
...
try
{
    f1();
}
catch( std::exception& e)
{
    //handle error
}

уже намного понятнее.. самое главное, в функции f1 вообще нет логики для обработки ошибок smile 
2. исключения делают код надежнее, в первую очередь из-за того, что исключение нельзя не обработать, как в случае с кодом ошибки, который можно легко потерять. В случае исключения проще найти причину ошибки.. так как исключение как правило несет в себе больше информации.
3. здесь все просто. 
Код

HRESULT func()
{
    ...
    if (...)
        return E_FAIL;
}

код возврата говорит только о том, что произошла какая-то ошибка и все
Код

void func()
{
    ...
    if (...)
        throw std::runtime_error("func: причина возникновения ошибки");
}

здесь, мало того, что исключение содержит диагностическую информацию, оно еще и документирует код... smile 

конечно, в некоторых случаях проще вернуть код ошибки, чем выбросить исключение, но эти случаи как правило можно отнести к нормальному выполнению программы, а не аварийному. Такой пример:
Код

int func(int key)
{
    ...
    std::map<int, int>::iterator i = data.find(key);
    //допустим data содержит положительные элементы
    if (i == data.end())//нет такого ключа
        return -1;
    return i->second;
}


Код

int func(int key)
{
    ...
    std::map<int, int>::iterator i = data.find(key); 
    if (i == data.end())//нет такого ключа
        throw std::runtime_error("func: нет значения в контейнере!");
    return i->second;
}

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

Автор: neutrino 17.6.2008, 09:57
Lazin, спасибо за пост.

Цитата(Lazin @  17.6.2008,  07:36 Найти цитируемый пост)
что из этого лучше трудно сказать, это зависит от контекста использования... например если отсутствие ключа в контейнере обычное явление, то наверное лучше первый вариант, а если отсутствие ключа означает сбой в работе программы, то второй... 

Вот как раз об этом и речь. 

Автор: Nastya 17.6.2008, 12:20
п.2.
Error Code, если :
1. не всегда exception -ы возможны, помнится на PowerTV OS их юзать было нельзя :( 
2. Если изначально написание идет в си- стиле (не с++)
3. Интерфейсы типа idl и т.д.

может когда еще.
а так  exception

Автор: neutrino 17.6.2008, 12:53
Интересно отметить, что ни в одном фундаментальном фреймворке вообще нет кодов возврата. Например в .NET-е или в Java.

Автор: neutrino 17.6.2008, 12:53
Интересно отметить, что ни в одном фундаментальном фреймворке вообще нет кодов возврата. Например в .NET-е или в Java.

Автор: neutrino 17.6.2008, 12:53
Интересно отметить, что ни в одном фундаментальном фреймворке вообще нет кодов возврата. Например в .NET-е или в Java.

Автор: LSD 17.6.2008, 13:15
Цитата(neutrino @  17.6.2008,  13:53 Найти цитируемый пост)
Интересно отметить, что ни в одном фундаментальном фреймворке вообще нет кодов возврата. Например в .NET-е или в Java.

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

Хороший пример использования кодов: это библиотека HttpClient. Метод execute() возвращает код возврата связанный с ошибками HTTP протокола и выкидывает ексепшн когда происходят ошибки сети или парсинга HTTP-ответа.

Автор: skyboy 17.6.2008, 13:19
Цитата(neutrino @  17.6.2008,  11:53 Найти цитируемый пост)
 ни в одном фундаментальном фреймворке вообще нет кодов возврата. Например в .NET-е или в Java.

т.е. поиск позиции подстроки в строке выбрасывает Exception? smile

Добавлено через 1 минуту и 10 секунд
имел в виду ситуацию, когда подстрока в строке отсутствует

Автор: neutrino 17.6.2008, 13:59
LSD, 
skyboy, 
Да вы правы.

Каким же правилом руководствоваться и в каких ситуациях?

Автор: neutrino 18.6.2008, 07:35
Цитата(neutrino @  17.6.2008,  11:53 Найти цитируемый пост)
Интересно отметить, что ни в одном фундаментальном фреймворке вообще нет кодов возврата. Например в .NET-е или в Java. 


Цитата(neutrino @  17.6.2008,  11:53 Найти цитируемый пост)
Интересно отметить, что ни в одном фундаментальном фреймворке вообще нет кодов возврата. Например в .NET-е или в Java. 


Цитата(neutrino @  17.6.2008,  11:53 Найти цитируемый пост)
Интересно отметить, что ни в одном фундаментальном фреймворке вообще нет кодов возврата. Например в .NET-е или в Java.

Вот меня заело smile

Автор: skyboy 18.6.2008, 09:47
Цитата(neutrino @  18.6.2008,  06:35 Найти цитируемый пост)
Вот меня заело

видать, реально в душу запало  smile 

Автор: mr.DUDA 18.6.2008, 20:41
Исключения нельзя использовать в real-time приложениях - слишком большие накладные расходы, по крайней мере в .NET

Но коды ошибок есть зло.  smile 

Автор: nickless 19.6.2008, 00:06
Для обработки ошибок - исключения (если они есть и их можно использовать), для всего остального - всё остальное smile 

Ситуации вроде этой
Цитата(skyboy @  17.6.2008,  12:19 Найти цитируемый пост)
имел в виду ситуацию, когда подстрока в строке отсутствует 

исключительными не являются, соответственно можно например вернуть -1 smile 

Автор: neutrino 19.6.2008, 09:23
Цитата(mr.DUDA @  18.6.2008,  19:41 Найти цитируемый пост)
Исключения нельзя использовать в real-time приложениях - слишком большие накладные расходы, по крайней мере в .NET

С этим согласен. Но и с этим:

Цитата(mr.DUDA @  18.6.2008,  19:41 Найти цитируемый пост)
Но коды ошибок есть зло. 

тоже  smile 

Автор: LSD 19.6.2008, 12:10
Тут есть тонкость, есть коды ошибок, а есть коды возврата. И это две большие разницы smile

Автор: neutrino 19.6.2008, 13:18
Цитата(LSD @  19.6.2008,  11:10 Найти цитируемый пост)
Тут есть тонкость, есть коды ошибок, а есть коды возврата. И это две большие разницы

Гы smile Какая разница? Вон -1, если нет подстроки - это что?

Автор: JackYF 19.6.2008, 14:59
Цитата(neutrino @  19.6.2008,  12:18 Найти цитируемый пост)
это что? 

индекс  smile 

По сабжу - больше люблю исключения. Но юзать приходится больше коды возврата - код на С ещё никто не отменял.

Автор: neutrino 19.6.2008, 15:04
Мдя. Вопрос надо было перефразировать. Это касается только .NET/Java

Автор: LSD 19.6.2008, 17:18
Цитата(neutrino @  19.6.2008,  14:18 Найти цитируемый пост)
Какая разница? Вон -1, если нет подстроки - это что?

Сам спросил, счас будет многа букф smile

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

1. Можно вообще вернуть такое значение которое бы сигнализировало об ошибке? 
Например 1 / 0 - среди целых нет такого значения которое могло бы сигнализировать о том, что произошло деление на ноль, потому тут будет ексепшн. А вот для 1.0 / 0.0 такое значение есть, и тут будет positive infinity.
Ну и конечно конструкторы из которых вообще ничего нельзя вернуть. Или прокси объекты которые осущесвляют ленивую закгрузку/инициализацию и т.п.

2. По логике работы функции: насколько эта ситуация является "черезвычайной"?
По идее при нормальной работе приложения эксепшенов должно быть минимум, только по внешним поводам котрые нельзя контролировать. Т.е. если проверили входные параметры на валидность, убедились что система в "правильном состоянии", эксепшены должны быть только если что-то не так с внешними условиями.
Тот же поиск строки в подстроке: там есть перегруженных метод который позволяет искать начиная с определенной позиции. Если строка не найдена -1 ибо проверить есть ли такая подстрока можно только самому реализовав подобный метод. А вот если индекс выходит за границы строки будет эксепшн, потому что программист может и должен следить за тем, чтобы он не выходил за границы.
Другой пример чтение из потока. Когда поток заканчивается, мы получаем -1, потому что мы совершенно точно знаем, что поток рано или поздно должен закончится. А вот когда происходят внешние проблемы (разорвалось соединение, проблемы с диском) мы получаем эксепшн ибо ситуация непредвиденная и по хорошему ее быть не должно.

Т.е. код возврата это одно из возвращаемых значение, это не ошибка в работе функции, это ее результат. А сама функция отработала корректно. Просто некоторые из значений для вызывающего кода могут обозначать ошибку (а могут и нет).

Автор: mr.DUDA 23.6.2008, 19:14
Плохой пример кодов возврата - COM с его HRESULT. Хороший пример - возврат функцией true/false для успеха или неудачи. ИМХО.

Автор: Mephisto 23.6.2008, 22:34
Я юзаю в основном только исключения. Ибо наглядно, удобно, очевидно... Если приложение многопоточное, то отдебагить методы в которые заходят несколько потоков практически невозможно, если не использовать исключения. 
Разумно их использовать если код будет внутри либы, тогда возможен только вариант с кодами ошибок.

Цитата(chipset @  17.6.2008,  01:21 Найти цитируемый пост)
А если пишешь DLL которая будет юзаться, к примеру дельфой или джавой? 

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

Автор: neutrino 19.7.2009, 14:22
Приветствую!

Собственно, понятное дело, что если подстрока не нашлась в строке надыть вернуть код ошибки. Но вот с этим: 
Цитата(mr.DUDA @  23.6.2008,  18:14 Найти цитируемый пост)
Хороший пример - возврат функцией true/false для успеха или неудачи. ИМХО. 

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

Автор: morfus 28.8.2009, 18:49
Я вообще в последнее время склоняюсь что надо брать пример с перла... (для web примерно так - нет ошибки всё хорошо, есть ошибка тупо выдавать 500 или 404 и не париться)

Т.е тупо "программа выполнила фигню и будет закрыта"

Автор: bilbobagginz 28.8.2009, 21:06
в принципе в unix есть переменная errno.
т.е. функция может вернуть какой-то код, и записать что-то в добавок в errno.
получится что-то вроде exception, но вложения перепишут errno, и фиг поймёшь что произошло точно.

Lazin, 
я рассуждаю проще:
язык C позволяет только коды ошибок. там - только коды ошибок.
C++,Java и т.д. позволяют коды ошибок и exceptions, которые явно более наглядны, и позволяют больше. поэтому в чисто c++/java, etc. пользуемся exception-ами,
за исключением работы с внешними модулями:
напр. приложение на C++ юзает либу на C, и т.п.

neutrino, http://webcourse.cs.technion.ac.il/234122/Spring2009/ho/WCFiles/m10_EXC.pdf

smile

Автор: Alexeis 28.8.2009, 22:27
Цитата(bilbobagginz @  28.8.2009,  20:06 Найти цитируемый пост)
язык C позволяет только коды ошибок. там - только коды ошибок.

  Ну возможности языка С не ограничиваются CRT, под винду есть SEH, так что можно работать и с исключениями. Наверняка в других оськах есть свои нативные механизмы.

Автор: bilbobagginz 5.9.2009, 13:33
Цитата(Alexeis @  28.8.2009,  21:27 Найти цитируемый пост)
 Ну возможности языка С не ограничиваются CRT, под винду есть SEH, так что можно работать и с исключениями. Наверняка в других оськах есть свои нативные механизмы. 

извратов - хватает.
http://www.nicemice.net/cexcept/

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)