Вот вопрос в том как лучше написать подобный код. Те чтобы не сильно напрягало глаз, не плодило лишних строк кода и тп. За все время возникали следующие идеи: 1. #define
Здравствуйте, higohertz, Вы писали:
H>Очень часто приходится обрабатывать возвращаемое значение функций идущих подряд. Например:
H>...
H>Вот вопрос в том как лучше написать подобный код. Те чтобы не сильно напрягало глаз, не H>плодило лишних строк кода и тп.
H>За все время возникали следующие идеи
H> 1. ... H> 2. ... H> 3. ...
H>Прошу, у кого какие идеи?
4. Вместо функций использовать классы-обертки, кидающие исключения, а перехват и
обработку исключений вынести в одно место:
Здравствуйте, higohertz, Вы писали:
H>Даже не знаю, куда запостить — если что поправьте. H>Очень часто приходится обрабатывать возвращаемое значение функций идущих подряд. Например:
У тебя примеры разные, так что не очень понятно, какие конкретно требования — нужно ои репортить каждый фейл особым образом и т.д.
Потому что, например, в третьем варианте у тебя общий флаг и общая обработка, а посему достаточно просто написать так:
Здравствуйте, higohertz, Вы писали:
H>1. #define
Подобный код я видел в реальных проектах, но мне лично он не нравится т.к.
1. ухудшает читабельность кода — чтобы понять что делает макрос, нужно лезть в его определение
2. если понадобится чуть более сложная реакция на ошибку, то все равно придется писать все явно
H>2. throw — наверное стандартный подход
Вариант, конечно, но я бы так делать не стал, т.к. механизм исключений достаточно медленный (где-то видел статью о его быстродействии, но сейчас уже не помню где). Ну и нужно быть уверенным что эти func своими исключениями не испортят всю идею.
H>3. while — аналогично throw
А вот таким подходом пользуюсь сам, но в несколько видоизмененном варианте:
int bOk = false;
do
{
if( ERROR == func1(p1, p2) ) { break; }
if( ERROR == func2(p3, p4) ) { break; }
bOk = true;
} while(false);
if(!bOk)
{
cout << ...;
return;
}
H>Даже не знаю, куда запостить — если что поправьте.
За такое я в детстве убивал из рогатки. Кидаться следует только классами исключений. Далее, если func1 твоя функция, то пусть она и бросает. Если нет — напиши бросающую обертку.
H>Очень часто приходится обрабатывать возвращаемое значение функций идущих подряд. Например: H>
H> cout << "ERROR func1()";
H>
Во-первых, cerr, а не cout. Во-вторых, пусть класс исключения делает это сам.
Здравствуйте, higohertz, Вы писали:
H>Даже не знаю, куда запостить — если что поправьте.
H>Очень часто приходится обрабатывать возвращаемое значение функций идущих подряд. Например: H>
Внутри себя он имеет, в зависимости от контроля ошибок, что-нибудь вроде:
#define ENSURE( e ) if( ! VERIFY( e ) ) { DEBUG_LOG( e ); return FALSE; }
Это единственное разумное решение при контроля ошибок кодами возврата. Для исключений по другому. Более подробно можно почитать тут: http://habrahabr.ru/post/130611/
Что-то мне кажется автора немного не так поняли. Или же я его не так понял... Но судя по его тексту как раз моё понимание должно быть правильным.
Тут в основном все пишут свои рекомендации на тему "как перевести C-style исключения в C++-style исключения"... Но у автора же был запрос на просто обработку возвращаемых функциями значений с выходом по ошибке. А использование C++ исключений для таких целей — это же бред по всем канонам. Исключения предназначены именно для исключительных ситуаций, а не для обработки одной из веток логики программы. Т.е. возвращения кода ошибки какой-то из функций (например это же может быть проверка на существование какой-то сущности, перед её созданием) вполне может укладываться в логику программы и никаких исключений здесь кидаться не должно.
Т.е. если автор действительно спрашивал именно о красивом оформление ряда вызывов функций, а не о трансляции исключений из С в C++, то никаких советов на тему исключений быть не должно в принципе.
Использование препроцессора в таком виде — это тоже явное зло.
Вариант с break — это по сути просто замена goto, для тех, кого нервирует это слово. )))
В общем мы обычно оставляем подобный код в изначальном виде (как в первом отрывке кода в сообщение) — не так уж и плохо он выглядит на самом деле... Да, тоже задумывались о каком-то более кратком и красивом решение, но так и не придумалось ничего. Кстати, это в случае если на каждую функцию надо делать своё персональное сообщение об ошибке. Если же нет, то есть достаточно простое решение — инвертировать логику. Т.е. можно написать код типа if(OK) if(OK) if (OK) return true;cout << "Error";return false; Правда в таком случае может лесенка отступов возникнуть... Но если глубина не большая, то и не страшно.
Здравствуйте, higohertz,
Тут уже Eye Of Hell дал направление в сторону ENSURE() либо лепи свою реализацию (это же С/С++?) чего-то контрактоподобного/защитнопрограммного.
1. Действительно, while — это заменитель goto. Вспоминаю сейчас, как раньше писал то же самое с помощью goto, завернутыми в define. А потом мне показалось проще и наглядней использовать while:
Тут не сочтите, так как пишу первое, что приходит в голову и сейчас не могу сказать скомпилится или нет, да и синтаксис goto подзабыл — но идея думаю понятна.
2. За ENSURE(...) отдельное спасибо — не знал (или забыл)
3. Вы верно заметили, что проблема появляется, когда необходимо выдавать персональное сообщение об ошибке. Инвертировать логику можно, но код сразу становится похож на лапшу (где то в статье в журнале RSDN вводится такое понятие). Позволю привести порезанный отрывок кода из библиотеки ликтестов (здесь):
if (s!=INVALID_SOCKET)
{
printf("Sending message to %s:%d (UDP).\n"...);
if (sendto(...)!=SOCKET_ERROR)
{
if (com_verbosity_get()) printf("Message sent.\nReceiving answer ...\n");
...
while (totlen<sizeof(buf) && (ret!=SOCKET_ERROR) && (ret!=0))
{
...
if (ret!=SOCKET_ERROR)
if (pending==0)
...
if ((ret!=0) && (ret!=SOCKET_ERROR))
{
...
if (ret==SOCKET_ERROR) com_err_set_nc(&err_inf,"Unable to receive data from %s:%d.\n",ip_str,conf.port);
else totlen+=ret;
}
} else com_err_set_nc(&err_inf,"Unable to get information from socket 0x%X.\n",s);
}
if (ret!=SOCKET_ERROR) res=com_find_pattern_and_print_data(buf,sizeof(buf),&conf,&err_inf);
} else com_err_set(&err_inf,"Unable to send data to %s:%d (UDP).\n",ip_str,conf.port);
closesocket(s);
} else com_err_set(&err_inf,"Unable to create socket.\n");
WSACleanup();
} else com_err_set_sc(&err_inf,err,"Unable to initialize Windows Sockets.\n");
Явно не читабельно, но сразу становится ясно где проблема — это освобождение ресурсов и персональные текста ошибок. Те когда ряд функций занимает ресурсы, подключает библиотеки и тп а потом в середине вываливается и нужно освобождать инициализированное обратно (выделено жирным), а то что инициализировать не успели — то не трогать.
4. Смотря на все это, вероятно, лучшее, что можно было придумать, — это завернуть все в классы-обертки и отдать всю работу деструкторам. Однако — это куча дополнительных строк кода. И по сути тот же
5. Получается, единственного правильного пути снова нет. Где код простой — можно обойтись if-ами (или аналогами типа while, goto), а где посложней — там классы-обертки. Других путей пока не вижу. Исключения вещь хорошая, но раз в С++ не везде его применяют (как в Jave) то и кажется он "не прозрачным" инструментом.
Может кто, посмотрев на приведенный кусок кода, придумает еще что нибудь оригинальное
Здравствуйте, hardcase, Вы писали:
H>B чем это лучше goto ERROR?
С точки зрения кода — ни в чем, ведь это и есть goto, но без goto.
Однако, есть практическая польза — такому коду проще пройти ревью, чем аналогичному варианту с goto.
Здравствуйте, Karbofos, Вы писали:
H>>2. throw — наверное стандартный подход K>Вариант, конечно, но я бы так делать не стал, т.к. механизм исключений достаточно медленный (где-то видел статью о его быстродействии, но сейчас уже не помню где). Ну и нужно быть уверенным что эти func своими исключениями не испортят всю идею.
Это зависит от сценария использования, если ошибки редкое явление, то исключения практически не замедлят работу, если же они вылетают постоянно, то да, можно неплохо просесть в производительности.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Здравствуйте, higohertz, Вы писали:
H>Даже не знаю, куда запостить — если что поправьте.
H>Очень часто приходится обрабатывать возвращаемое значение функций идущих подряд. Например: H>
H>Вот вопрос в том как лучше написать подобный код. Те чтобы не сильно напрягало глаз, не плодило лишних строк кода и тп. За все время возникали следующие идеи: