Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2014.03.16;
Скачать: CL | DM;

Вниз

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;
Скачать: CL | DM;

Наверх




Память: 0.55 MB
Время: 0.007 c
15-1380621808
ПЛОВ
2013-10-01 14:03
2014.03.16
Как выбрать элемент перечисляемого типа?


2-1369340105
Ponchik
2013-05-24 00:15
2014.03.16
Сортировка по убыванию StringGrid


2-1369308195
delphistorm
2013-05-23 15:23
2014.03.16
странная проблема с отображение картинки


15-1380637391
xayam
2013-10-01 18:23
2014.03.16
Небольшая задача


15-1380117316
JohnKorsh
2013-09-25 17:55
2014.03.16
Порты COM по BlueTooth