Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Куда приводит GOTO, по просьбе "телезрителей" 
:(
    Опции темы
Albinos_x
Дата 25.10.2005, 17:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Evil Skynet
****


Профиль
Группа: Комодератор
Сообщений: 3288
Регистрация: 28.5.2004
Где: X-6120400 Y-1 4624650

Репутация: нет
Всего: 108



Вот попросили выложить... выкладываю....

Куда приводит GOTO

С детства нам говорили, что в программах использовать goto нельзя. Это было табу. Долгие годы я не мог его переступить. Некоторые особо рьяные ценители читаемости текста желали также запретить условные переходы, оставив только циклы, но к счастью их призывы до меня не дошли. Однако goto у меня непременно ассоциировался с плохо написаным кодом.
Поработав побольше, я пришел к выводу, что хороший текст должен быть таким:
1. Продолжать работать в любом случае, не падать при несрабатывании каких-либо функций.
2. Отслеживать все ошибки исполнения, по возможности их обходить и записывать толковый файл лога, по которому можно диагностировать проблему.
3. Быть компактным и легким для восприятия. Кроме того, хороший код содержит минимум неявно выполняемых операций.
4. Освобождать все занятые ресурсы и размещенную память в случае неудачи.
Соблюдению этих нехитрых условий зачастую помогает оператор перехода, что я попытаюсь проиллюстрировать ниже. Рассмотрим различные методы стили написания функции, которая заполняет указанный файл символом 'a'.
Итак:
Код

long fill_file( const char *psz )
{
    HANDLE    hfile = ::CreateFile( psz, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0 );
    unsigned    size = ::GetFileSize( hfile, 0 );
    HANDLE    hmap = ::CreateFileMapping( hfile, 0, PAGE_READWRITE, 0, 0, 0 );
    void    *ptr = ::MapViewOfFile( hmap, FILE_MAP_WRITE, 0, 0, 0 );

    memset( ptr, 'a', size );

    ::UnmapViewOfFile( ptr );
    ::CloseHandle( hmap );
    ::CloseHandle( hfile );
    return 0;
}

(Внимание - я привожу не полноценный текст С++, а условный код, опуская неважные с точки зрения стиля программирования параметры функций).
Код прозрачен, однако крайне груб - ошибки не обрабатываются, лог ошибок не ведется.
Проверки и освобождение ресурсов "в лоб"
Код

long fill_file( const char *psz )
{
    HANDLE    hfile = CreateFile( psz, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0 );
    if( hfile == INVALID_HANDLE_VALUE )return 1;
    unsigned    size = ::GetFileSize( hfile, 0 );
    HANDLE    hmap = ::CreateFileMapping( hfile, 0, PAGE_READWRITE, 0, 0, 0 );
    if( hmap == 0 )
    {
        ::CloseHandle( hfile );    
        return 2;
    }
    void    *ptr = ::MapViewOfFile( hmap, FILE_MAP_WRITE, 0, 0, 0 );
    if( !ptr )
    {
        ::CloseHandle( hmap );
        ::CloseHandle( hfile );
        return 3;
    }
    memset( ptr, 'a', size );

    ::UnmapViewOfFile( ptr );
    ::CloseHandle( hmap );
    ::CloseHandle( hfile );
    return 0;
}

Страшно подумать, сколько раз будут дублироваться CloseHandle, как тяжело читать и модифицировать приведенный код, и в скольких местах нужно будет добавлять запись в лог о причине несрабатывания функции.
Что нам принесет C++.
В C++ все делается через классы. Соответственно, перед тем как разработать функцию нужно создать набор классов-оберток для объектов, которые в ней присутствуют:
Код

class handle_object
{
public:
    handle_object()                
    {h = 0;}
public:    
    virtual ~handle_object()    
    {::CloseHandle( h );}
    
    bool is_valid()                
    {return h && h != INVALID_HANDLE_VALUE;}
public:
    HANDLE    h;
};

class file : public handle_object
{
    bool create();
};
    
class file_mapping : public handle_object
{
    bool create( HANDLE hfile );
};

После такой "небольшой" подготовительной работы код придет к виду:
Код

long fill_file( const char *psz )
{
    file    file;
    _file.create( sz_path );
    if( !file.is_valid() )return 1;
    unsigned    size = file.get_size();


    file_mapping    map;
    map.create( file.h );
    if( !map.is_valid() )return 2;

    void    *ptr = map.map_view();
        
    memset( ptr, 'a', size );

    map.unmap_view( ptr );
    return 0;
}

Так уже значительно легче, но крайне напрягает необходимость писать класс на каждый используемый объект. Тут проблем масса - начиная со сложностью передачи экземпляра класса-оболочки в другие функции, и до неочевидности последовательности вызова деструкторов при выходе из функции.
Неочевидный код предполагает ошибки:
1. Первым вызовется деструктор file, а потом map (если экземпляр map создан раньше), что может в некоторых случаях привести к потере ресурсов. (типичный пример - удалить сначала HPEN, выбранный в HDC, а потом сам HDC).
2. Невиртуальность деструктора в handle_object не вызовет ошибки компиляции, однако деструктор не будет вызван при удалении объектов.
Напомню также, что к ведению лога ошибок мы так и не приступили.
Для справедливости замечу, что C++ вместо return в случае несрабатывания функции позволяет использовать оператор throw, однако прозрачность кода при этом становится настолько мала, а код настолько откровенно опасней, что я пожалуй не буду рассматриавть этот вариант.
Хорошее решение:
Вспомним про наш опальный оператор:
Код

long fill_file( const char *psz )
{
    long    error_code = 1;
    void    *ptr = 0;
    HANDLE    hfile = 0, hmap = 0;
    unsigned size;

    hfile = CreateFile( psz, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0 );
    if( !hfile )goto _end;
    
    size = ::GetFileSize( hfile, 0 );
    hmap = ::CreateFileMapping( hfile, 0, PAGE_READWRITE, 0, 0, 0 );
    if( !hmap )goto _end;
    
    ptr = ::MapViewOfFile( hmap, FILE_MAP_WRITE, 0, 0, 0 );
    if( !ptr )goto _end;
    
    memset( ptr, 'a', size );
    error_code = 0;
_end:
    ::UnmapViewOfFile( ptr );
    ::CloseHandle( hmap );
    ::CloseHandle( hfile );
    return error;
}

Жить стало легче. С одной стороны инициализация и освобождение ресурсов прописаны явно (чем достигается прозрачность текста), с другой стороны объем дополнительного кода минимален.
Запись журнала
И наконец, одним росчерком добавим запись в лог всех непредвиденных ситуаций:
Код

long fill_file( const char *psz )
{
    long    error_code = 0;
    void    *ptr = 0;
    HANDLE    hfile = 0, hmap = 0;
    unsigned size;

    error_code = 1;
    hfile = CreateFile( psz, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0 );
    if( !hfile )goto _end;

    error_code = 2;    
    size = ::GetFileSize( hfile, 0 );
    hmap = ::CreateFileMapping( hfile, 0, PAGE_READWRITE, 0, 0, 0 );
    if( !hmap )goto _end;
    
    error_code = 3;
    ptr = ::MapViewOfFile( hmap, FILE_MAP_WRITE, 0, 0, 0 );
    if( !ptr )goto _end;
    
    memset( ptr, 'a', size );
    error_code = 0;
_end:
    if( error_code )_trace_file( 0, "error %d/%08x", error_code, ::GetLastError() );
    ::UnmapViewOfFile( ptr );
    ::CloseHandle( hmap );
    ::CloseHandle( hfile );
    
    return error_code != 0;
}

Код сохранил очевидность и понятность, однако все без исключения ошибки времени исполнения записываются в лог, чтобы потом оказать нам неоценимую помощь при анализе неисправностей у удаленного пользователя.
Eще варианты использования goto
Есть еще одна синтаксическая конструкция, в котором использование goto предпочтительней. Речь пойдет об обработке исключительных ситуаций с возможностью для пользователя повторить попытку. Вот так нам рекомендуют писать блюстители частоты языка.
Код

HANDLE    hfile = INVALID_HANDLE_VALUE;
while( hfile == INVALID_HANDLE_VALUE )
{
    hfile = ::CreateFile( psz, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0 );
    if( hfile != INVALID_HANDLE_VALUE )break;
    DWORD dw = ::GetLastError();
    if( ERROR_DEVICE_NOT_READY  == dw )
    {
        if( MessageBox( "insert disk", MB_RETRYCANCEL ) != IDRETRY )
            break;
    }
}

Цикл на мой взгляд совершенно лишний, тем более что в ветке, когда все в порядке, код в цикле исполняется один раз. На мой взгляд совершенно неверно такие конструкции применять с использованием цикла.
Далее - вариант с goto:
Код

_repeat_open:
    HANDLE    hfile = ::CreateFile( psz, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0 );
    if( hfile == INVALID_HANDLE_VALUE )
    {
        DWORD    dw = GetLastError();
        _trace_file( 0, "file error %08x/%s", GetLastError(), sz_file);
        if( ERROR_DEVICE_NOT_READY  == dw )
        {
            if( MessageBox( "insert disk", MB_RETRYCANCEL ) == IDRETRY )
                goto _repeat_open;
        }
    }

Как видим, все стало очевидней, обработка ошибки легко отделяется от основного текста, а также практически бесплатно получаем полную информацию в логе ошибок.

Андрей Ямов
28.04.2004



--------------------
"Кто владеет информацией, тот владеет миром"    
Уинстон Черчилль
PM MAIL ICQ   Вверх
srd
Дата 25.10.2005, 18:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Нереварин
**


Профиль
Группа: Участник Клуба
Сообщений: 951
Регистрация: 30.8.2003
Где: Владивосток

Репутация: 27
Всего: 49



Пример на Си++ откровенно неудачен.


--------------------
Не смей читать мою подпись!!!
PM MAIL Jabber   Вверх
Void
Дата 25.10.2005, 18:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


λcat.lolcat
****


Профиль
Группа: Участник Клуба
Сообщений: 2206
Регистрация: 16.11.2004
Где: Zürich

Репутация: 40
Всего: 173



Бредовая статья и такие же аргументы.
Оригинал здесь.
Даже в таком простом примере использование RAII понятнее, более сопровождаемо, и оправдывает усилия на написание оберток. Если автор не умеет читать C++ - это его проблемы. "Легкость" логирования в неструктурном коде вообще убила.


--------------------
“Coming back to where you started is not the same as never leaving.” — Terry Pratchett
PM MAIL WWW GTalk   Вверх
DeadSoul
Дата 25.10.2005, 18:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник
Сообщений: 1217
Регистрация: 25.9.2005
Где: Москва

Репутация: 2
Всего: 11



Цитата(Void @ 25.10.2005, 18:50)
Бредовая статья и такие же аргументы.

угу.

Albinos_x,
Цитата(Void @ 25.10.2005, 18:50)
RAII
-"захват ресурса-есть инициализация"?



--------------------
 Если Вы получили ответ на Ваш вопрос, то нажмите на "Вопрос решен". 

Бьем спамеров их же оружием. Пусть весь спам сыпется им
[email protected] 
PM   Вверх
Earnest
Дата 25.10.2005, 19:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 53
Всего: 183



Цитата(Void @ 25.10.2005, 18:50)
Бредовая статья и такие же аргументы.
...
"Легкость" логирования в неструктурном коде вообще убила.

Воистину так.
Что восхищает, так это апломб г-на Ямова.
Можно было бы согласится с автором, если бы речь шла о чистом С без классаов и исключений...

Но "обертки" потрясают до слез, особенно заявление
Цитата
Невиртуальность деструктора в handle_object не вызовет ошибки компиляции, однако деструктор не будет вызван при удалении объектов.

учитывая способ объявления
Код

file_mapping map;


а также
Цитата
...C++ вместо return в случае несрабатывания функции позволяет использовать оператор throw, однако прозрачность кода при этом становится настолько мала, а код настолько откровенно опасней, что я пожалуй не буду рассматриавть этот вариант.




Это сообщение отредактировал(а) Earnest - 25.10.2005, 19:28


--------------------
...
PM   Вверх
Albinos_x
Дата 25.10.2005, 20:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Evil Skynet
****


Профиль
Группа: Комодератор
Сообщений: 3288
Регистрация: 28.5.2004
Где: X-6120400 Y-1 4624650

Репутация: нет
Всего: 108



Цитата(DeadSoul @ 25.10.2005, 18:52)
"захват ресурса-есть инициализация"?

я не автор статьи:
Цитата(Albinos_x @ 25.10.2005, 17:10)
...информацию в логе ошибок.

Андрей Ямов
28.04.2004

попросили выложить почитать, вот я и выложил....поэтому пинать меня не надо, к тому же, если Вы обратили внимание, что моих сообщений в разделе С# только несколько штук, а остальные в Делфи, поэтому код я не разбирал и ещё:
Цитата
(Внимание - я привожу не полноценный текст С++, а условный код,

поэтому не стоит принимать всё так близко к сердцу...

PS: а вообще интересно узнать мнения, т.к. собираюсь углублять свои знания по С#. В Паскале, особенно Делфи про то, что пишет автор, сильно можно не заморачиваться, и он будет всё равно читабельным. А вот про С#, ничего сказать не могу, т.к. знаю его поверхостно smile


--------------------
"Кто владеет информацией, тот владеет миром"    
Уинстон Черчилль
PM MAIL ICQ   Вверх
DeadSoul
Дата 25.10.2005, 21:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник
Сообщений: 1217
Регистрация: 25.9.2005
Где: Москва

Репутация: 2
Всего: 11



Цитата(Albinos_x @ 25.10.2005, 20:48)
Цитата (DeadSoul @ 25.10.2005, 18:52)
"захват ресурса-есть инициализация"?
я не автор статьи:

Это был вопрос Voidу. Но сути это оно, вопрос был правильно ли я понял перевод.



--------------------
 Если Вы получили ответ на Ваш вопрос, то нажмите на "Вопрос решен". 

Бьем спамеров их же оружием. Пусть весь спам сыпется им
[email protected] 
PM   Вверх
Void
Дата 25.10.2005, 21:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


λcat.lolcat
****


Профиль
Группа: Участник Клуба
Сообщений: 2206
Регистрация: 16.11.2004
Где: Zürich

Репутация: 40
Всего: 173



Цитата(DeadSoul @ 25.10.2005, 23:10)
вопрос был правильно ли я понял перевод.

Да.


--------------------
“Coming back to where you started is not the same as never leaving.” — Terry Pratchett
PM MAIL WWW GTalk   Вверх
S.A.P.
Дата 25.10.2005, 21:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник Клуба
Сообщений: 2664
Регистрация: 11.6.2004

Репутация: 9
Всего: 71



Ну вот набросились... Приведите пример как правильно, сравним, а goto - сакс каждый кричать сможет, будучи уверенным на 100% в своей правоте.

ИМХО виной всему сишный WinAPI, а обертки писать задолбаешься, лучше уж MFC тогда.
PM MAIL   Вверх
DeadSoul
Дата 25.10.2005, 21:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник
Сообщений: 1217
Регистрация: 25.9.2005
Где: Москва

Репутация: 2
Всего: 11



Цитата(Perchilla @ 25.10.2005, 21:31)
ИМХО виной всему сишный WinAPI,

Он не только Сишный, мне казалось WinApi универсальная вещь.
Именно это и накладывает свои ограничения


--------------------
 Если Вы получили ответ на Ваш вопрос, то нажмите на "Вопрос решен". 

Бьем спамеров их же оружием. Пусть весь спам сыпется им
[email protected] 
PM   Вверх
Albinos_x
Дата 25.10.2005, 21:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Evil Skynet
****


Профиль
Группа: Комодератор
Сообщений: 3288
Регистрация: 28.5.2004
Где: X-6120400 Y-1 4624650

Репутация: нет
Всего: 108



Цитата(DeadSoul @ 25.10.2005, 21:10)
Это был вопрос Voidу. Но сути это оно, вопрос был правильно ли я понял перевод.

ну...тогда извините smile ....просто перед сообщением стоял мой ник


--------------------
"Кто владеет информацией, тот владеет миром"    
Уинстон Черчилль
PM MAIL ICQ   Вверх
Дрон
Дата 26.10.2005, 23:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java-ненавистник :)
****


Профиль
Группа: Участник Клуба
Сообщений: 3179
Регистрация: 29.12.2002
Где: Санкт-Петербург

Репутация: 10
Всего: 93



ОФФТОП:
Автор статьи, кстати, зарегистрирован на винграде: AndyY. Правда он здесь не появлялся ещё с лета, а так можно было бы поспорить smile

Это сообщение отредактировал(а) Дрон - 26.10.2005, 23:21


--------------------
Да. Именно так.
PM   Вверх
Neitron
Дата 27.10.2005, 09:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 580
Регистрация: 3.10.2005
Где: Москва

Репутация: 2
Всего: 5



Пара-тройка строк о таинствах структурного программирования.

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

Следование - операторы выполняются в порядке очереди, один за другим. Графическое представление:

Код

+-----------+
| оператор1 |
+-----------+
      |
+-----------+
| оператор2 |
+-----------+
      |
+-----------+
| оператор3 |
+-----------+


Ветвление - в зависимости от выполнения условия происходит выполнение операторов по альтернативному пути. Графическое представление:

Код

        +-----------+
        | оператор1 |
        +-----------+
             / \
            /   \
           /     \
          /       \
         /         \
+-----------+    +-----------+ 
| оператор2 |    | оператор3 |
+-----------+    +-----------+


Цикл - операторы выполняются циклически постоянно или до выполнения определённого условия. Графическое представление:

Код

+-----------+
| оператор1 |----+
+-----------+    |
      |          |
+-----------+    |
| оператор2 |    |
+-----------+    |
      |          |
+-----------+    |
| оператор3 |    |
+-----------+    |
      |          |
      +----------+


Обратите внимание, внезапная передача управления "по метке" структурным программированием не предусмотрена в принципе, как излишняя.

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



--------------------
Хороший программист никогда ничего не делает хорошо с первого раза. Он понимает важность патчей.
Ⓘ ⓁⒾⓀⒺ ⓂⓄⓏⒾⓁⓁⒶ
PM MAIL   Вверх
Mayk
Дата 27.10.2005, 17:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


^аВаТаР^ сообщение>>
****


Профиль
Группа: Участник
Сообщений: 2616
Регистрация: 22.5.2005
Где: за границей разум а

Репутация: 45
Всего: 134



Цитата(Neitron @ 27.10.2005, 13:17)
как излишняя.

особенная понимаешь её "излишество", когда приходится выходить break'ом из вложенных циклов.
Особенно когда ты забываешь про то, что находишься во вложенном цикле. Здравствуй, дебагер.


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


Это сообщение отредактировал(а) Mayk - 27.10.2005, 17:15


--------------------
 Здесь был кролик. Но его убили.
Человеки < кроликов, йа считаю.
PM MAIL WWW ICQ   Вверх
maxim1000
Дата 27.10.2005, 17:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 3334
Регистрация: 11.1.2003
Где: Киев

Репутация: 17
Всего: 110



использовать или нет goto - это одно
но приводить аргумент "излишество" в пользу отказа от него - другое
тогда лучше прийти к машине Тьюринга - там нет ничего лишнего
кроме того, сами циклы могут быть реализованы с помощью goto
так что "излишество" - никак не довод...

Это сообщение отредактировал(а) maxim1000 - 27.10.2005, 17:21


--------------------
qqq
PM WWW   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0779 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.