Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2014.03.16;
Скачать: [xml.tar.bz2];

Вниз

C++ и обработка исключений, или за секунду до холивара   Найти похожие ветки 

 
ProgRAMmer Dimonych ©   (2013-09-25 20:31) [0]

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

Что я хочу
Хочу аналог Delphiйского (псевдокодом):

pData = MemAlloc(...);
try
  // Много кода
  Result := pData;
except
  MemFree(pData);
  raise;
end;

Что я могу
pData = MemAlloc(...);
__try
{
  // Много кода
  return pData;
}
__except(MemFree(pData), EXCEPTION_CONTINUE_SEARCH) { return NULL; }


Что мне не нравится
Не нравится необходимость писать return NULL, чтобы не лезло предупреждений компилятора.

Стандартный catch с throw внутри не подходит, т.к. его нельзя использовать в одной процедуре с __finally (а __finally там нужен и уже есть). Внутри нестандартного __except делать throw, судя по всему, нельзя, т.к. throw не умеет работать с системными (не C++) исключениями.

Вопрос
Как гениальный Страуструп и его последователи, молящиеся на стандарт, предлагают записывать такой код?

P.S. Дублирование кода не предлагать!


 
wicked ©   (2013-09-25 20:54) [1]

1. return из внутреннего блока - плохо
лучше заведи переменную result, присваивай ей что нужно, а в конце делай return result - так не будет предупреждений компилятора

2. избавься от finally;
если нужно освобождать память, то лучше завернуть это в какой-либо smart pointer; или напиши свою обертку в стиле RAII
заодно и можно будет использовать нормальные try/catch, а не костыли

а вообще - приведи весь код функции, а не маленький, ничего не значащий кусочек


 
ProgRAMmer Dimonych ©   (2013-09-25 21:13) [2]

> 1. return из внутреннего блока - плохо
> лучше заведи переменную result, присваивай ей что нужно,
> а в конце делай return result - так не будет предупреждений
> компилятора

Предупреждение как раз если блок оставить пустым: о том, что некоторые ветви не возвращают значения.

---

LPTSTR CSomeClass::_LoadFile(LPCTSTR lpszFileName)
{
  LPTSTR pBuf;
  HANDLE hFile = CreateFile(lpszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
  if (INVALID_HANDLE_VALUE == hFile)
    return NULL;

  __try
  {
    DWORD dwSizeLow, dwSizeHigh;
    dwSizeLow = GetFileSize(hFile, &dwSizeHigh);
    if (INVALID_FILE_SIZE == dwSizeLow)
    {
      if (NO_ERROR != GetLastError())
        return NULL;
    }

    HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
    if (0 == hMapping)
      return NULL;

    __try
    {
      LPCSTR pData = (LPCSTR)MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
      if (NULL == pData)
        return NULL;

      __try
      {
        UINT CodePage = CP_ACP;
        // <Много_кода>
        // Определение кодировки с помещением Codepage identifier в CodePage
        // </Много_кода>

        DWORD dwBufSize = MultiByteToWideChar(CodePage, MB_ERR_INVALID_CHARS, pData, dwSizeLow, NULL, 0);
        if (ERROR_NO_UNICODE_TRANSLATION == GetLastError())
          return NULL;

        pBuf = new TCHAR[dwBufSize + 1];
        __try
        {
          MultiByteToWideChar(CodePage, 0, pData, dwSizeLow, pBuf, dwBufSize);
          pBuf[dwBufSize] = "\0";
          return pBuf;
        }
        __except(delete[] pBuf, EXCEPTION_CONTINUE_SEARCH) { return NULL; }
      }
      __finally
      {
        UnmapViewOfFile(pData);
      }
    }
    __finally
    {
      CloseHandle(hMapping);
    }
  }
  __finally
  {
    CloseHandle(hFile);
  }
}


---


> 2. избавься от finally;
> если нужно освобождать память, то лучше завернуть это в
> какой-либо smart pointer; или напиши свою обертку в стиле
> RAII
> заодно и можно будет использовать нормальные try/catch,
> а не костыли

Совсем не в восторге от идеи писать классы вокруг хэндлов: слишком круто для того, чтобы получить один раз получить хэндл и один раз его же освободить. В данном случае они будут костылями ещё покруче, чем __try.


 
Anatoly Podgoretsky ©   (2013-09-25 21:54) [3]

Может до холокоста?


 
Mystic ©   (2013-09-25 22:10) [4]


> Совсем не в восторге от идеи писать классы вокруг хэндлов:
>  слишком круто для того, чтобы получить один раз получить
> хэндл и один раз его же освободить. В данном случае они
> будут костылями ещё покруче, чем __try.


__try это вообще не C++, это Microsoft specific.

А так finally не ввели как раз из-за того, что такое поведение дублирует поведение деструкторов.

Или юзай BOOST_SCOPE_EXIT
http://www.boost.org/doc/libs/1_50_0_beta1/libs/scope_exit/doc/html/BOOST_SCOPE_EXIT.html


 
DVM ©   (2013-09-25 22:15) [5]


> ProgRAMmer Dimonych ©   (25.09.13 21:13) [2]


> Совсем не в восторге от идеи писать классы вокруг хэндлов

не пиши, возьми boost - там уже все написали.


 
ProgRAMmer Dimonych ©   (2013-09-25 22:42) [6]

> [3] Anatoly Podgoretsky ©   (25.09.13 21:54)
> Может до холокоста?

Ну что ж я, холокост от localhost"а не отличу? :)

> [4] Mystic ©   (25.09.13 22:10)
> А так finally не ввели как раз из-за того, что такое поведение дублирует поведение деструкторов.

Да-да, я почитал, что на этот счёт думает Страуструп. Имея некоторый опыт борьбы с C++, не удивился. Имея некоторый опыт вообще — грязно выругался. Это при том, что и раньше знал, что нормального finally там нет.

> [4] Mystic ©   (25.09.13 22:10)
> Или юзай BOOST_SCOPE_EXIT

> [5] DVM ©   (25.09.13 22:15)
> не пиши, возьми boost - там уже все написали.

Не-не-не, коллеги, и не уговаривайте! Для маленькой, но очень гордой программки тащить монстра, для сборки которого у меня не хватит свободного места на жёстком диске, — лучше сразу пристрелите :)


 
Mystic ©   (2013-09-26 11:24) [7]


> Для маленькой, но очень гордой программки тащить монстра,
>  для сборки которого у меня не хватит свободного места на
> жёстком диске, — лучше сразу пристрелите :)


Ну так возьми идею.


 
Anatoly Podgoretsky ©   (2013-09-26 11:48) [8]


> лучше сразу пристрелите

А это идея.


 
jack128_   (2013-09-26 12:35) [9]


> Совсем не в восторге от идеи писать классы вокруг хэндлов:
>  слишком круто для того, чтобы получить один раз получить
> хэндл и один раз его же освободить. В данном случае они
> будут костылями ещё покруче, чем __try.

Это в в дельфи классы/объекты - это некая тяжелая конструкция, несущая нехилый оверхер по памяти и производительности. В С++ - все не так. Учи язык, и не тяни в него свои дельфийские стереотипы.


 
jack128_   (2013-09-26 12:40) [10]

кстати для данной задачи свою RAII обертку не обязательно писать, можно стандартный unique_ptr использовать, подсунув ему свой Deleter


 
Anatoly Podgoretsky ©   (2013-09-26 12:52) [11]


> jack128_   (26.09.13 12:35) [9]

хер говоришь :-)


 
ProgRAMmer Dimonych ©   (2013-09-26 13:38) [12]

> Это в в дельфи классы/объекты - это некая тяжелая конструкция,
> несущая нехилый оверхер по памяти и производительности.
> В С++ - все не так. Учи язык, и не тяни в него свои дельфийские
> стереотипы.

Замечание про C++ way принимается, но http://ru.wikipedia.org/wiki/Бритва_Оккама же. Плодить сущности, которые просто дублируют уже существующие своими Фаберже в профиль…

А вот интересный вопрос: если при выходе из процедуры нужно сделать запись в лог, используя данные из нескольких таких вот RAII-style классов, — чей деструктор будет выполнять эту запись в лог? Класс CWriteToLogForThisAwesomeProcedure?


 
jack128_   (2013-09-26 13:56) [13]


> А вот интересный вопрос: если при выходе из процедуры нужно
> сделать запись в лог, используя данные из нескольких таких
> вот RAII-style классов, — чей деструктор будет выполнять
> эту запись в лог? Класс CWriteToLogForThisAwesomeProcedure?
>

этот конкретный пример задачи приведи. можно в виде дельфийского кода.


> Замечание про C++ way принимается, но http://ru.wikipedia.
> org/wiki/Бритва_Оккама же. Плодить сущности, которые просто
> дублируют уже существующие своими Фаберже в профиль…

это ты про finally ? Абсолютно верно, в с++ он абсолютно лишний, ибо дублирует RAII. Тебе об этом уже говорили.
Вообще RAII обертка на handle"ом - это первое, что должно писаться, если на WinApi взялся. Да и MMF в отдельный класс нужно завернуть.


> хер говоришь :-)

хер - это "херь"??


 
Sergey Masloff   (2013-09-26 16:28) [14]

jack128_   (26.09.13 13:56) [13]

>> хер говоришь :-)

>хер - это "херь"??

а кто сказал
>Это в в дельфи классы/объекты - это некая тяжелая конструкция, несущая нехилый оверхер по памяти

мне кстати тоже понравилось возьму на вооружение
;-)


 
ProgRAMmer Dimonych ©   (2013-09-26 23:08) [15]

> этот конкретный пример задачи приведи. можно в виде дельфийского
> кода.

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

procedure ...
var
  h1, h2: TResource; // Какие-то ресурсы, которые нужно освобождать и которые в C++ положено оборачивать классами
...
begin
 try
    h1 := GetResource(...);
    h2 := GetResource(...);
    if a = b then
    begin
      // Ещё несколько вложенных if
      if ErrorCondition then
      begin
        Result := ERROR_SOMETHING_HAPPENED;
        Exit;
      end;
      // ...
    end;
  finally
    WriteToLog("Procedure exited. Allocated resources contained the following data: ", GetResourceData(h1), GetResourceData(h2));
    FreeResource(h1);
    FreeResource(h2);
  end;
end;


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


> > Замечание про C++ way принимается, но http://ru.wikipedia.
> > org/wiki/Бритва_Оккама же. Плодить сущности, которые просто
>
> > дублируют уже существующие своими Фаберже в профиль…
>
> это ты про finally ? Абсолютно верно, в с++ он абсолютно
> лишний, ибо дублирует RAII. Тебе об этом уже говорили.
> Вообще RAII обертка на handle"ом - это первое, что должно
> писаться, если на WinApi взялся. Да и MMF в отдельный класс
> нужно завернуть.

Это я про те классы, в которые предлагается заворачивать. У меня есть сущность: хэндл. У неё есть определённый интерфейс (функции WinAPI), есть внутренняя реализация. Но мне предлагается её завернуть в ещё одну сущность, со своим интерфейсом, своей реализацией, только для того, чтобы «красиво» освободить ресурс. Причём освободить мне его нужно в одном-единственном месте в коде. Зачем простую операцию, которая, считается, даже зафейлиться не может, превращать в целый метод целого класса?

Внезапно, не зря finally-таки суют в C++: одна простая новая конструкция на уровне компилятора, чтобы лаконично выразить простую логику освобождения ресурса, против десятка классов в коде, пытающихся рвать гланды через прямую кишку, потому что «здесь так заведено». В C#, PHP и других языках (тысячи их!) вообще вопрос освобождения ресурса для программиста не стоит, но даже там finally в наличии, хотя логика для локальных переменных та же: вышел из области видимости — освобождён.


 
jack128_   (2013-09-27 11:45) [16]


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

какой то совсем хреновый пример. Что произойдет, если GetResource или GetResourceData кинет исключение?? AV ?

в плюсах я б написал примерно так


int main() {
// your code goes here
using ResourcePtr = std::unique_ptr<void, void (&) (void*)>;
ResourcePtr h1(GetResource(), FreeResource);
ResourcePtr h2(GetResource(), FreeResource);
auto logger = scope_exit([&]() { // самапальный аналог BOOST_SCOPE_EXIT
 std::cout << "Procedure exited. Allocated resources contained the following data: "
  << GetResourceData(h1.get()) << GetResourceData(h2.get()) << std::endl;
});
// твои if"ы и тд т тп..
return 0;
}


этот код гораздо надежнее твоего, здесь код корректно отработает при любых исключениях


> Причём освободить мне его нужно в одном-единственном месте
> в коде.

сказки не надо рассказывать. если ты полез в WinApi, то хендлы тебе направо и налево придется освобождать.


> Внезапно, не зря finally-таки суют в C++

Только в твоих мечтах. В С++11 такого предложения не было. Сейчас в С++14 обсуждает, опять таки нету предложения.


>  В C#, PHP и других языках (тысячи их!) вообще вопрос освобождения
> ресурса для программиста не стоит

То есть на C#/PHP и других языках в аналогичном коде FreeResource сам собой вызовется?? Срочно пиши статью, премия Тьюринга тебе обеспечена :-D


 
ProgRAMmer Dimonych ©   (2013-09-27 18:12) [17]

> То есть на C#/PHP и других языках в аналогичном коде FreeResource
> сам собой вызовется?? Срочно пиши статью, премия Тьюринга
> тебе обеспечена :-D

Внезапно, в PHP-таки файл закроется автоматом, ибо подсчёт ссылок: http://php.net/manual/ru/language.types.resource.php Стоит полюбопытствовать, прежде чем переходить на личности.


 
Rouse_ ©   (2013-09-27 18:46) [18]


> ProgRAMmer Dimonych ©   (25.09.13 21:13) [2]

Зачем переносить Delphi-style в ся?
Выглядит страшновато...
Во всем приведенном коде все наведенные SEH избыточны...


 
Rouse_ ©   (2013-09-27 18:52) [19]

зы: дополню, в дельфе данный стиль существует из-за того что практически каждый вызов внутрь VCL может привести к вызову raise, а в сях такие ситуации четко обговорены MSDN и стало быть повсеместное использование SEH фреймов, выглядит немного глупо


 
ProgRAMmer Dimonych ©   (2013-09-27 21:58) [20]

> [19] Rouse_ ©   (27.09.13 18:52)

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


 
Rouse_ ©   (2013-09-27 23:28) [21]

значит нужно лишь ее перекрытие


 
|   (2013-09-28 01:49) [22]


> Внезапно, в PHP-таки файл закроется автоматом, ибо подсчёт
> ссылок

почему php не был выбран языком реализации задачи?


 
ProgRAMmer Dimonych ©   (2013-09-28 18:08) [23]

> [22] |   (28.09.13 01:49)

Потому что язык реализации оговорен заказчиком.


 
ProgRAMmer Dimonych ©   (2013-09-28 18:09) [24]

P.S. Ну и к тому же некузяво desktop-софтину писать на PHP.


 
|   (2013-09-29 04:08) [25]


> Потому что язык реализации оговорен заказчиком.

тогда принимай правила игры, или не берись за задачу


 
|   (2013-09-29 04:09) [26]

зы
я уверен на 100%, что заказчик не будет рад ms-like finally



Страницы: 1 вся ветка

Форум: "Прочее";
Текущий архив: 2014.03.16;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.54 MB
Время: 0.003 c
15-1380038174
Игорь Шевченко
2013-09-24 19:56
2014.03.16
Рома aka Kerk, поздравляю со званием!


15-1380313803
Юрий
2013-09-28 00:30
2014.03.16
С днем рождения ! 28 сентября 2013 суббота


15-1380391129
электроник
2013-09-28 21:58
2014.03.16
ps2 keyboard as usb-hid


15-1380126715
ProgRAMmer Dimonych
2013-09-25 20:31
2014.03.16
C++ и обработка исключений, или за секунду до холивара


11-1250348579
Dy1
2009-08-15 19:02
2014.03.16
Obj и IsForm





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский