Как сделать это красиво?
От: higohertz  
Дата: 27.06.12 05:18
Оценка:
Даже не знаю, куда запостить — если что поправьте.

Очень часто приходится обрабатывать возвращаемое значение функций идущих подряд. Например:
if( ERROR == func1(p1, p2) )
{
   cout << "ERROR func1()";
   return;
}
if( ERROR == func2(p3, p4) )
{
   cout << "ERROR func2()";
   return;
}
...


Вот вопрос в том как лучше написать подобный код. Те чтобы не сильно напрягало глаз, не плодило лишних строк кода и тп. За все время возникали следующие идеи:
1. #define
#define CHECK_AND_EXIT(func, ok_value, err_str) \
   if( ok_value != func )      \
   {                           \
      cout << err_str;         \
      return;                  \
   }

Далее код превращается в:
CHECK_AND_EXIT( func1(p1, p2), OK, "ERROR func1()" );
CHECK_AND_EXIT( func1(p2, p3), OK, "ERROR func2()" );


2. throw — наверное стандартный подход
try {
  if( ERROR == func1(p1, p2) ) throw "ERROR func1()"
  if( ERROR == func2(p2, p3) ) throw "ERROR func1()"
}
catch(...)
{
   cout << ...;
   return;
}


3. while — аналогично throw
int bOk = true
while(true)
{
  if( ERROR == func1(p1, p2) ) { bOk = false; break; }
  if( ERROR == func2(p3, p4) ) { bOk = false; break; }
  break;
}
if(!bOk)
{
   cout << ...;
   return;
}


Прошу, у кого какие идеи?
Re: Как сделать это красиво?
От: okman Беларусь https://searchinform.ru/
Дата: 27.06.12 05:42
Оценка:
Здравствуйте, higohertz, Вы писали:

H>Очень часто приходится обрабатывать возвращаемое значение функций идущих подряд. Например:


H>...


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

H>плодило лишних строк кода и тп.

H>За все время возникали следующие идеи


H> 1. ...

H> 2. ...
H> 3. ...

H>Прошу, у кого какие идеи?


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

try
{
    foo Foo;
    bar Bar;
    obj Obj;

    Foo.begin();
    Bar.set_foo(Foo);
    Obj.show(Foo, Bar);

    // и т.д.
}

catch (exception const &Exc)
{
    // ...
}
Re: Как сделать это красиво?
От: jazzer Россия Skype: enerjazzer
Дата: 27.06.12 06:19
Оценка:
Здравствуйте, higohertz, Вы писали:

H>Даже не знаю, куда запостить — если что поправьте.

H>Очень часто приходится обрабатывать возвращаемое значение функций идущих подряд. Например:

У тебя примеры разные, так что не очень понятно, какие конкретно требования — нужно ои репортить каждый фейл особым образом и т.д.
Потому что, например, в третьем варианте у тебя общий флаг и общая обработка, а посему достаточно просто написать так:
H>if( ERROR == func1(p1, p2) || ERROR == func2(p3, p4) )
H>{
H>   cout << "ERROR";
H>   return;
H>}
H>
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re: Как сделать это красиво?
От: Karbofos Россия  
Дата: 27.06.12 07:02
Оценка:
Здравствуйте, 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;
}
Re: Как сделать это красиво?
От: femidav  
Дата: 27.06.12 07:10
Оценка: +1
Здравствуйте, higohertz, Вы писали:

H>2. throw — наверное стандартный подход

H>
H>try {
H>  if( ERROR == func1(p1, p2) ) throw "ERROR func1()"
H>

H>Даже не знаю, куда запостить — если что поправьте.
За такое я в детстве убивал из рогатки. Кидаться следует только классами исключений. Далее, если func1 твоя функция, то пусть она и бросает. Если нет — напиши бросающую обертку.

H>Очень часто приходится обрабатывать возвращаемое значение функций идущих подряд. Например:

H>
H>   cout << "ERROR func1()";
H>

Во-первых, cerr, а не cout. Во-вторых, пусть класс исключения делает это сам.
Re: Как сделать это красиво?
От: Centaur Россия  
Дата: 27.06.12 09:42
Оценка:
Здравствуйте, higohertz, Вы писали:

H>Очень часто приходится обрабатывать возвращаемое значение функций идущих подряд. Например:

H>if( ERROR == func1(p1, p2) )
H>{
H>   cout << "ERROR func1()";
H>   return;
H>}
H>if( ERROR == func2(p3, p4) )
H>{
H>   cout << "ERROR func2()";
H>   return;
H>}
H>...


H>Вот вопрос в том как лучше написать подобный код.


То есть на входе у нас есть некий C-style API, который возвращает коды ошибок. И мы хотим этот API использовать из C++-программы.

Я обычно решаю примерно так.

namespace win32
{

class Error : public std::exception
{
public:
    explicit Error(DWORD error_code)
    : error_code_(error_code)
    {}
    DWORD error_code() const { return error_code_; }
    std::string what() const { …format error message… }
private:
    DWORD error_code_;
};

BOOL check(BOOL result)
{
    if (!result) throw Error(GetLastError());
    return result;
}

HANDLE check(HANDLE result)
{
    if (result == INVALID_FILE_HANDLE) throw Error(GetLastError());
    return result;
}

}

…в клиенте…
HANDLE hFile = win32::check(CreateFile(…));

(разумеется, на самом деле win32::check(CreateFile(…)) используется исключительно в конструкторе класса-обёртки File).
Re[2]: Как сделать это красиво?
От: hardcase Пират http://nemerle.org
Дата: 27.06.12 10:02
Оценка: +1
Здравствуйте, Karbofos, Вы писали:

H>>3. while — аналогично throw

K>А вот таким подходом пользуюсь сам, но в несколько видоизмененном варианте:
K>
K>int bOk = false;
K>do
K>{
K>  if( ERROR == func1(p1, p2) ) { break; }
K>  if( ERROR == func2(p3, p4) ) { break; }
K>  bOk = true;
K>} while(false);
K>if(!bOk)
K>{
K>   cout << ...;
K>   return;
K>}
K>


B чем это лучше goto ERROR?
/* иЗвиНите зА неРовнЫй поЧерК */
Re: Как сделать это красиво?
От: minorlogic Украина  
Дата: 27.06.12 10:06
Оценка: +2
Здравствуйте, higohertz, Вы писали:

H>Прошу, у кого какие идеи?


Нормально использовать исключения ?

   func1(p1, p2);
   func2(p3, p4);
... << RSDN@Home 1.2.0 alpha 5 rev. 1539>>
Ищу работу, 3D, SLAM, computer graphics/vision.
Re: Как сделать это красиво?
От: Eye of Hell Россия eyeofhell.habr.ru
Дата: 27.06.12 10:37
Оценка:
Здравствуйте, higohertz, Вы писали:

H>Даже не знаю, куда запостить — если что поправьте.


H>Очень часто приходится обрабатывать возвращаемое значение функций идущих подряд. Например:

H>
H>if( ERROR == func1(p1, p2) )
H>{
H>   cout << "ERROR func1()";
H>   return;
H>}
H>if( ERROR == func2(p3, p4) )
H>{
H>   cout << "ERROR func2()";
H>   return;
H>}
H>...
H>


Стандартное решение (им. Microsoft) — макрос ENSURE:

ENSURE( func1( p1, p2 ) );
ENSURE( func2( p3, p4 ) );


Внутри себя он имеет, в зависимости от контроля ошибок, что-нибудь вроде:

#define ENSURE( e ) if( ! VERIFY( e ) ) { DEBUG_LOG( e ); return FALSE; }


Это единственное разумное решение при контроля ошибок кодами возврата. Для исключений по другому. Более подробно можно почитать тут:
http://habrahabr.ru/post/130611/
Re: Как сделать это красиво?
От: alex_public  
Дата: 27.06.12 13:05
Оценка: +2
Что-то мне кажется автора немного не так поняли. Или же я его не так понял... Но судя по его тексту как раз моё понимание должно быть правильным.

Тут в основном все пишут свои рекомендации на тему "как перевести C-style исключения в C++-style исключения"... Но у автора же был запрос на просто обработку возвращаемых функциями значений с выходом по ошибке. А использование C++ исключений для таких целей — это же бред по всем канонам. Исключения предназначены именно для исключительных ситуаций, а не для обработки одной из веток логики программы. Т.е. возвращения кода ошибки какой-то из функций (например это же может быть проверка на существование какой-то сущности, перед её созданием) вполне может укладываться в логику программы и никаких исключений здесь кидаться не должно.

Т.е. если автор действительно спрашивал именно о красивом оформление ряда вызывов функций, а не о трансляции исключений из С в C++, то никаких советов на тему исключений быть не должно в принципе.

Использование препроцессора в таком виде — это тоже явное зло.

Вариант с break — это по сути просто замена goto, для тех, кого нервирует это слово. )))

В общем мы обычно оставляем подобный код в изначальном виде (как в первом отрывке кода в сообщение) — не так уж и плохо он выглядит на самом деле... Да, тоже задумывались о каком-то более кратком и красивом решение, но так и не придумалось ничего. Кстати, это в случае если на каждую функцию надо делать своё персональное сообщение об ошибке. Если же нет, то есть достаточно простое решение — инвертировать логику. Т.е. можно написать код типа if(OK) if(OK) if (OK) return true;cout << "Error";return false; Правда в таком случае может лесенка отступов возникнуть... Но если глубина не большая, то и не страшно.
Re: Используй гарды.
От: Wolverrum Ниоткуда  
Дата: 27.06.12 15:57
Оценка:
Здравствуйте, higohertz,
Тут уже Eye Of Hell дал направление в сторону ENSURE() либо лепи свою реализацию (это же С/С++?) чего-то контрактоподобного/защитнопрограммного.
Re[2]: Используй гарды.
От: higohertz  
Дата: 27.06.12 16:53
Оценка:
Спасибо всем!

1. Действительно, while — это заменитель goto. Вспоминаю сейчас, как раньше писал то же самое с помощью goto, завернутыми в define. А потом мне показалось проще и наглядней использовать while:

#define MYTRY
#define VERIFY(x) if(!x) goto l_error;
#define MYCATCH l_error:

int main()
{
MYTRY // для симметрии
VERIFY( f1() )
VERIFY( f2() )
MYCATCH
}


Тут не сочтите, так как пишу первое, что приходит в голову и сейчас не могу сказать скомпилится или нет, да и синтаксис 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) то и кажется он "не прозрачным" инструментом.

Может кто, посмотрев на приведенный кусок кода, придумает еще что нибудь оригинальное
Re[3]: Как сделать это красиво?
От: Karbofos Россия  
Дата: 28.06.12 08:16
Оценка:
Здравствуйте, hardcase, Вы писали:

H>B чем это лучше goto ERROR?


С точки зрения кода — ни в чем, ведь это и есть goto, но без goto.
Однако, есть практическая польза — такому коду проще пройти ревью, чем аналогичному варианту с goto.
Re[2]: Как сделать это красиво?
От: Ziaw Россия  
Дата: 28.06.12 10:57
Оценка:
Здравствуйте, femidav, Вы писали:

H>>Очень часто приходится обрабатывать возвращаемое значение функций идущих подряд. Например:

H>>
H>>   cout << "ERROR func1()";
H>>

F>Во-первых, cerr, а не cout. Во-вторых, пусть класс исключения делает это сам.

выводит сообщение в stderr?
Re[2]: Как сделать это красиво?
От: rus blood Россия  
Дата: 29.06.12 14:07
Оценка:
Здравствуйте, Eye of Hell, Вы писали:

EOH>
EOH>ENSURE( func1( p1, p2 ) );
EOH>ENSURE( func2( p3, p4 ) );
EOH>


EOH>
EOH>#define ENSURE( e ) if( ! VERIFY( e ) ) { DEBUG_LOG( e ); return FALSE; }
EOH>


Вопрос — сколько раз будет вызвана функция func1, если VERIFY вернет FALSE ?
Имею скафандр — готов путешествовать!
Re[3]: Как сделать это красиво?
От: Eye of Hell Россия eyeofhell.habr.ru
Дата: 29.06.12 14:28
Оценка:
RB>Вопрос — сколько раз будет вызвана функция func1, если VERIFY вернет FALSE ?

Я похож на человека, который кормит троллей?
Re[2]: Как сделать это красиво?
От: Ops Россия  
Дата: 29.06.12 16:13
Оценка:
Здравствуйте, Karbofos, Вы писали:

H>>2. throw — наверное стандартный подход

K>Вариант, конечно, но я бы так делать не стал, т.к. механизм исключений достаточно медленный (где-то видел статью о его быстродействии, но сейчас уже не помню где). Ну и нужно быть уверенным что эти func своими исключениями не испортят всю идею.

Это зависит от сценария использования, если ошибки редкое явление, то исключения практически не замедлят работу, если же они вылетают постоянно, то да, можно неплохо просесть в производительности.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Re: Как сделать это красиво?
От: kov_serg Россия  
Дата: 04.07.12 10:15
Оценка:
Здравствуйте, higohertz, Вы писали:

H>Даже не знаю, куда запостить — если что поправьте.


H>Очень часто приходится обрабатывать возвращаемое значение функций идущих подряд. Например:

H>
H>if( ERROR == func1(p1, p2) )
H>{
H>   cout << "ERROR func1()";
H>   return;
H>}
H>if( ERROR == func2(p3, p4) )
H>{
H>   cout << "ERROR func2()";
H>   return;
H>}
H>...
H>


H>Вот вопрос в том как лучше написать подобный код. Те чтобы не сильно напрягало глаз, не плодило лишних строк кода и тп. За все время возникали следующие идеи:


Что мешает делать так?
void throw_error(const char* panic_message) { thorw panic_message; }
void func1_or_throw(int p1,int p2) { if (ERROR==func1(p1,p2)) throw_error("func1"); }
void func2_or_throw(int p1,int p2) { if (ERROR==func2(p1,p2)) throw_error("func2"); }
...

func1_or_throw(p1,p2);
func2_or_throw(p3,p4);
...
Re[2]: Как сделать это красиво?
От: kov_serg Россия  
Дата: 04.07.12 10:25
Оценка:
Здравствуйте, kov_serg, Вы писали:

Или вот такой дурацкий если лень писать обёртки.
struct Must {
  const char* id;
  Must(const char* id) : id(id) {}
  int  operator= (int res) {
    if (res==ERROR) throw_error(id);
    return res;
  }
};

#define MUST(id) Must(id)=
//#define MUST(id)

...
MUST("func1") func1(p1,p2);
int r2= MUST("func2") func2(p3,p4);
Re: Как сделать это красиво?
От: b-3 Россия  
Дата: 04.07.12 17:56
Оценка: 3 (1)
Здравствуйте, higohertz, Вы писали:

H>Даже не знаю, куда запостить — если что поправьте.

H>Прошу, у кого какие идеи?

Сишная классика.
int rc = func1(p1, p2);

if (!rc) 
{
  rc = func2(p3, p4);
}

if (!rc) 
{
  rc = func3(p5, p6);
}

return rc;


GCC насколько я помню такое эффективно оптимизирует, так что получается тот же return.
Да, и строку с сообщением об ошибке в код хардкодить не стоит.
Забанен с формулировкой "клинический дисидент".
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.