[C++] [:==:] работа с исключениями
От: uzhas Ниоткуда  
Дата: 09.07.11 19:17
Оценка:
поведайте плиз как же грамотнее всего работать с исключениями, чтобы собирать наиболее полную информацию об ошибке
желательно, примеры приводить в C++, но в принципе, проблема актуальна для всех языков с исключениями (возможность вставлять в ошибку стек-трейс не рассматриваю, т.к. считаю, что для юзера это крайне вредная информация. никакой инфы о коде в ошибке не должно быть, только полезные читаемые данные, чтобы юзер не обращался в саппорт)

представим ситуацию:
remote console -> {network} -> RemoteFacade::GetUser(id) -> UserStorage->TakeUser(id) -> OpenDatabase(filename) -> OpenFile(filename) {Exception: File not found)
итак покажите, как составить для юзера (remote console) подробное описание ошибки
варианты решения
  1
IDatabase OpenDatabase(filename)
{
  throw FormatError("Cannot open database due to win32 error %d", GetLastError());
}
//нигде нет catch кроме RemoteFacade
RemoteFacade::GetUser(id)
{
  try
  {
    user = UserStorage->TakeUser(id);
    Connection->SendReply(MakeReplyForUser(user)); //тоже может швырнуть, но это не очень важно в моем вопросе
  }
  catch (const std::exception& e)
  {
    Connection->SendReplyError(e);
  }
}

//в итоге remote console получит скудную ошибку: "Cannot open database due to win32 error 17, file not found"


  2
RemoteFacade::GetUser(id)
{
  try
  {
    user = UserStorage->TakeUser(id);
    Connection->SendReply(MakeReplyForUser(user));
  }
  catch (const std::exception& e)
  {
    Connection->SendReplyError(FormatError("Problem has occured during remote request processing. Details: %s", e.what());
  }
}

IUserStorage::TakeUser(id)
{
  try
  {
    db = OpenDatabase(filename);
  }
  catch (const std::exception& e)
  {
    throw FormatError("Failed to take user. Details: %s", e.what());
  }
}

IDatabase OpenDatabase(filename)
{
  throw FormatError("Cannot open database due to win32 error %d", GetLastError());
}

//В этом случае клиент получит уже более подробную ошибку:
Problem has occured during remote request processing. Details: Failed to take user. Details: Cannot open database due to win32 error 17, file not found


  3
class MyError : public std::exception
{
  MyError& SetChild(MyError child)
  {
    //deep copy of child
    .....
    return *this;
  }
....
};

MyError MakeError(.....);

RemoteFacade::GetUser(id)
{
  try
  {
    user = UserStorage->TakeUser(id);
    Connection->SendReply(MakeReplyForUser(user));
  }
  catch (const MyError& e)
  {
    Connection->SendReplyError(MakeError("Problem has occured during remote request processing.").SetChild(e));
  }
}

IUserStorage::TakeUser(id)
{
  try
  {
    db = OpenDatabase(filename);
  }
  catch (const MyError& e)
  {
    throw MakeError("Failed to take user.").SetChild(e);
  }
}

IDatabase OpenDatabase(filename)
{
  throw MakeError("Cannot open database due to win32 error %d.", GetLastError());
}

В этом случае клиенут можно промаршaлить всю MyError (цепочка ошибок) и он с разной степенью подробности может показать это (доверить ему форматирование).


буду рад советам и ссылкам. уверен, что подобные вопросы уже обсуждались.
прошу не придираться к коду, основную идею вроде отразил
спасибо
Re: [C++] [:==:] работа с исключениями
От: Abyx Россия  
Дата: 09.07.11 20:25
Оценка:
вместо того чтобы создавать новое исключение — эффективнее модифицировать имеющееся

class traced_exception : std::exception
{
.....
   void add_trace(const std::string& s) { trace.push_back(s); }
   std::vector<std::string> trace; // чтоугодно
};

... layer_n(...)
{
  ...
  try
  {
     ...
  }
  catch(traced_exception& e)
  {
     e.add_trace("doing something");
     throw;
  }
  ...
}

впрочем если мы добавляем трассу к любому исключению, то в catch() надо писать еще и std::exception (вдруг откуда-то прилетит) что и оно не осталось без трассы

или более хитрый вариант

class traced_exception;

struct with_tracing
{
   const char* lastAction; // const char* чтобы не создавать оверхед,
                           // при этом можно присваивать только строковые литералы, что в общем-то не всегда хорошо

   template<typename F>
   void wrap(const char* ctx, F f)
   {
      try
      {
         lastAction = ctx;
         f();
         lastAction = nullptr;
      }
      catch(traced_exception& e)
      {
         if(lastAction)
            e.add_trace(lastAction);
         throw;         
      }
      catch(std::exception& e)
      {
         throw traced_exception(e, lastAction );
      }
   }
};

class Foo : ..., with_tracing
{
public:
   void foo()
   {
      wrap("doing foo", [this]{
         ...
         lastAction = "do another";
         ...
      });
   }
};
In Zen We Trust
Re: [C++] [:==:] работа с исключениями
От: DGurin http://ua.linkedin.com/in/dgurin
Дата: 10.07.11 00:06
Оценка:
Здравствуйте, uzhas, Вы писали:

Сдается мне, что вопрос все-таки не об исключениях, а об обработке ошибок как таковой.
Исключения — просто удобное средство.

В момент обработки исключения у тебя есть вся необходимая информация (во всяком случае — теоретически). Полезна ли она юзеру... и полезна ли она вообще — потом разберемся.

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

Итка, мы имеем:

    1. Юзер может исправить ошибку сам. — Какая информация ему для этого нужна? Уж что-что, а код ошибки ему даром не нужен . Да и вобще, по условиям игры, юзер и не подозревает о наличии абсолютного большинства компонентов твоей программы.
    Возможно, достаточно просто объяснить, что проблема где-то там — далеко-далеко.

    2. Юзер может обратиься к сисадмину/хелпдеск/саппорт разработчика (нужное подчеркнуть). Опять таки, какая информация нужна им? Детальное описание ошибки должно подойти. Здесь — логи наше все.

    3. Юзер уже обращался и к админу, и в саппорт, и даже к другу — гуру программирования на html. Похоже, что последняяя инстанция — ты. Что тебе нужно что бы решить проблему? Э-э-э... и кстати, а где ты это хранил???

Надеюсь, что навел на нужные мысли
Удачи!
Re[2]: [C++] [:==:] работа с исключениями
От: Abyx Россия  
Дата: 10.07.11 00:21
Оценка:
Здравствуйте, DGurin, Вы писали:

DG>1. Юзер может исправить ошибку сам. — Какая информация ему для этого нужна? Уж что-что, а код ошибки ему даром не нужен .


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

т.е. ошибки "файл не найден", "файл занят", "нет прав для доступа к файлу" — юзер может и сам поправить, если будет знать код (описание) ошибки
In Zen We Trust
Re[3]: [C++] [:==:] работа с исключениями
От: DGurin http://ua.linkedin.com/in/dgurin
Дата: 10.07.11 00:44
Оценка:
Здравствуйте, Abyx, Вы писали:

A>т.е. ошибки "файл не найден", "файл занят", "нет прав для доступа к файлу" — юзер может и сам поправить, если будет знать код (описание) ошибки


Вот с этого места поподробнее пожалуйста
Как юзер будет исправлять эти ошибки? И если мне не изменяет память — на удаленной машине.

Разбираться почему "нет прав" — прийдется все-таки тому, кто эти самые права раздает.
А если "файл занят" — то вопрос к программисту, а чем это критичный для работы юзера файл так занят и уже 3-ий час подряд...

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

А уж как это реализовать...
Re[2]: [C++] [:==:] работа с исключениями
От: uzhas Ниоткуда  
Дата: 10.07.11 10:54
Оценка:
Здравствуйте, DGurin, Вы писали:

DG>В момент обработки исключения у тебя есть вся необходимая информация (во всяком случае — теоретически). Полезна ли она юзеру... и полезна ли она вообще — потом разберемся.


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

я предложил два решения с разной подробностю ошибок
1) "Cannot find file"
2) "Failed to open database. Details: cannot find file"

в плане имплементации первый вариант делается легко : пишем только throw, а catch пишем лишь на самом верху
во втором случае catch надо писать уже много где, чтобы цеплять информацию о контексте выполнении. неясно как дешево это сделать (чтобы и времени много не уходило и код не перемешивался с ценной логикой)
Re[3]: [C++] [:==:] работа с исключениями
От: DGurin http://ua.linkedin.com/in/dgurin
Дата: 10.07.11 17:44
Оценка:
Здравствуйте, uzhas, Вы писали:

U>я предложил два решения с разной подробностю ошибок

U>1) "Cannot find file"
U>2) "Failed to open database. Details: cannot find file"

Мое ИМХО — в данном случае, юзеру "что в лоб, что по лбу" .
Он не может сделать то, что хотел. Он хотел залогиниться на удаленный хост?
Он не может этого сделать по вине того самого хоста.

Никакие файлы базы ему и в страшном сне не присняться.

То, что программа не смогла открыть файл базы — это будет интересно админу.
Ну, может продвинутым юзерам это тоже будет интересно, но легче от этого знания ни им, ни вобщем-то и админу не станет. Почему? Да вобщем-то потому, что программа не может найти свой собственный файл. Только ты в момент написания (ну или по факту баг-репорта ) можешь решить как это обрабатывать.

U>в плане имплементации первый вариант делается легко : пишем только throw, а catch пишем лишь на самом верху

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

Лично я склоняюсь ко второму варианту, где все исключения внутри компонента обрабатываются на его границе (пока мы знаем максимум о контексте). Что нельзя обработать — добываем контекст и пускаем дальше новое исключение — дабы не плодить развесистые catch-блоки.
Re: [C++] [:==:] работа с исключениями
От: max-maxtor Россия www.rsdn.ru
Дата: 10.07.11 18:11
Оценка:
Здравствуйте, uzhas, Вы писали:





U>  try
U>  {
U>    user = UserStorage->TakeUser(id);
U>    Connection->SendReply(MakeReplyForUser(user)); //тоже может швырнуть, но это не очень важно в моем вопросе
U>  }
catch(Использовать конкретно объект который был отправлен, напреимер oledbexception){
В нём будет максимально подробные данные об исключении
}
U>  catch (const std::exception& e)
U>  {
U>    Connection->SendReplyError(e);
U>  }
U>}
Re: [C++] [:==:] работа с исключениями
От: Sorc17 Россия  
Дата: 12.07.11 06:14
Оценка:
Здравствуйте, uzhas, Вы писали:

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


Тред не читал.

Всю полезную для исправления ошибки инфу я бы кинул в какой-нибудь error.log а пользователю вывел красивое окошко с понятным текстом ошибки и кнопочкой с помощью которой можно анонимно без регистрации отправить информацию об окружении пользователя и содержание error.log разработчикам, при наличии соединения с интернетом.
Для нас [Thompson, Rob Pike, Robert Griesemer] это было просто исследование. Мы собрались вместе и решили, что ненавидим C++ [смех].
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.