Как стать автором
Обновить

Комментарии 39

RAII неплохая идиома, но у нее весьма конкретная сфера применения, и за приведенный код я бы отрывал ноги.
Комментарий не с целью критики статьи, просто хочу пресечь злоупотребления идиомой.
Написали бы, почему оторвали ноги. С++ такая штука, что одни пользуются половиной языка и оторвут ноги за другую половину, а другая половина — наоборот
Во-первых, этот код не компилируется в VC2012.
Во-вторых, использование fopen() deprecated.
В-третьих, если я, например, буду искать в этом коде трудноуловимый баг, то я хочу, чтобы каждая функция стояла на отдельной строчке, чтобы я мог поставить брейкпоинт — то есть вместо file_(fopen()) я хочу всегда видеть { file_ = fopen();… }
В-четвертых, пусть код немного другой, исключение происходит после некоторого первого конструктора, но перед инициализацией переменной file_. В деструкторе вызывается fclose(0xCCCCCCCC); и имеем повторное исключение в обработчике.
Думаю ваши претензии совершенно не обоснованны. Здесь показана идея, а не законченный рабочий код. Поэтому и воспринимать его нужно соответственно. Вы же не берете код из книжки Саттера и не суете его в свой продукт один в один? Вот и здесь так же.
RAII неплохая идиома, но у нее весьма конкретная сфера применения, и за приведенный код я бы отрывал ноги.
А я бы отрывал ноги за попытку рекурсии в комментариях.
а зачем отрывать ноги, если код и комментарии пишут обычно руками?
Чтобы не убежал, а сидел переписывал.
Открываете окошко Disassmebly и ставить брейкпоинты на те функции, которые вас интересует. Да и из гуи это вроде бы неплохо делается — брейкпоинты на строку разворачиваются на кучу отдельных одним кликом мышки (возможно, там не очень понятно только какой брейкпоинт к чему относится, поэтому проще на call поставить бряк). В чем проблема-то?
Проблема, видимо, в том, что я трачу свое время на отладку чужого кода.
1. Значит VS2012 недостаточно поддерживает C++11. Или в чём проблема?
2. И на что его заменить? Я вообще что-то не вижу в какой редакции стандарта (Си или C++) её объявили deprecated?
3. Это Ваши личные предпочтения, не более. Если функция без кода, то всё равное не получите ничего более внятного, чем возвращённое значение (через дизассемблер всё равно можно добраться). Если есть код, то можно поставить бряк на первую строчку этой функции.
4. При правильной реализации после выполнения первого конструктора все поля должны быть инициализированы. В том числе file_. Хотя бы NULL'ом. Тогда в деструкторе добавляется один if и всё.
2. Гугл мне подсказал, что это богомерзкий Майкрософт так определил.

Пишу программ мало, исключительно чтобы раз что-то посчитала. Набрел на эту фичу поэтому случайно, но по-моему, она очень хороша. 0x3f00, зачем отрывать ноги за код, который понятен, работает надежно и удовлетворяет свежим стандартам? Я понимаю, за неоптимальность… Но за чуждую философию — это косность и зашоренность :)
И действительно, кто такой этот богомерзкий Майкрософт и какое отношение он имеет к языкам программирования и инструментам разработки.
Понимаете, есть стандарт языка. И если есть желание писать кроссплатформенный код, то лучше следовать стандарту, а не использовать расширения от MS. Расширения GCC ещё понимаю, поскольку им можно получить код под Windows, OSX, Linux, FreeBSD, etc.
Да успокойтесь вы :)
Может человек сидит в своем загончике «Windows+x86+M$VS» и ему большего не нужно. Правда, непонятно почему он считает себя вправе давать остальным вредные советы, да еще в такой категоричной форме. Но оставим это на его совести.
а по-моему, это обычный тролль, которому скучно
Они очень многие POSIX функции из стандарта объявили почему-то deprecated, интересно, почему? Приходится дефайн _POSIX_ подавать студии чтобы заткнулась и не возникала.
2. Да, я так и подумал. Но fopen_s только их фича, не более того. Более того, в чём смысл большинства этих «безопасных» не вполне понимаю. Говорит мне cl.exe, что strcpy опасна используйте strcpy_s. Зачем, если есть вполне стандартная strncpy?
Ну, дело не только в буферах: тут приводят выдержки из святого писания. Говорится еще и о каких-то правах доступа к файлу. Не вчитывался.
When creating a file, the fopen_s and freopen_s functions improve security by protecting the file from unauthorized access by setting its file protection and opening the file with exclusive access.
И вправду, зачем эта приблуда, если пишешь не под Windows? Или не только под него (кроссплатформенное приложение).

0x3f00, да я не отрицаю вклада Майкрософта, только они тянут одеяло на себя постоянно, монополизировали рынок. Не единственное, но одно из самых их важных достижений, приносящее бабки, имхо, то, что специфический добротный софт есть только под их ОС. Тот софт, который стал стандартом де факто. Типа AutoCAD, что-то для программирования контроллеров, офисные приложения и подобное. Слава богам, весь нормальный научный софт уже достаточно давно отлично работает под Linux. Дело в том, что уже наверно большинство ученых за рубежом используют Mac или Linux. Да и сервера часто делают на на Windows же. Так что не нужно продвигать выдумки этих капиталистов как последнюю истину :)
Ну strcpy_s хороша как минимум тем, что у нее есть шаблонная перегрузка, которая сама опредлеяет размер статически размещенных буферов, так что ее вызов выглядит ровно так же, как и вызов просто strcpy. Кроме того, если среди n байтов в исходной строке при вызове strncpy не окажется нуля, то целевой буфер его не получит, и это чревато боком.
Гм, не знал о таком поведении strncpy(). И вообще считал её наиболее безопасным вариантом, избегая «чистого» strcpy() везде где возможно.
Ок, а как еще по другому можно? Естественно если указать num меньше чем реальная длина строки src, то нуля в конце dest мы не получим. А с какой стати функция его должна ставить и самое главное куда? При условии что программист может забыть выделить под dest место равное длина строки + длина символа завершения.

strcpy_s тоже может повредить кучу, если недостаточно выделить места под dest
strcpy_s автоматически определяет размер статического массива, если передается статический массив в качестве назначения, то есть она имеет соответствующую специализацию шаблона. Написано что всегда ставит null в конце строки, значит в dst копируется из src sizeof(dst)-1 элементов и затем в dst добавляется в конце null.

Круто.

А вот интересно, что будет если скормить strcpy_s вот это:
char dst[1]; // Статический массив из одного элемента — не строка ибо не так :char dst[] = «a»;!!!
strcpy_s(dst, «A»);
В dst будет только null terminated символ?
Ну в принципе безопасно, но тоже не однозначно.

Ну лан пофиг strcpy и иже с ними пришли из древнего C.

Для наибольшей безопасности, если она так нужна, пользуйтесь string'ом и его перегруженными операторами, insert'ом, replace'ом и т.д.

Я вообще не понимаю, почему бы Микрософту не сделать свой API совместимым с STL.
Наверно потому, что нужна поддержка C и таких программистов очень много. Сейчас кстати C и C++ развиваются разными путями. Например в C есть ключевое слово restrict. В C++ его нет и не предвидится.
Страуструп здесь маху дал. Надо бы было поддержку C11 включить в стандарт C++11 или хотя бы в C++14 ну или в C++17
Иначе мы получим несовместимость, некроссплатформенность, кучу геммора при попытке скомпилить современный Си код вместе с C++ кодом в одной единице трансляции и два разных языка, которые в принципе могли бы быть одним единственным, как было в 80 — 90 годах
Жесть, одним словом (((
По поводу пункта «В-третьих»: для начала, брейкпойнты не нужны. Как и вообще отладчики.

К тому же, предложенный вами стиль заведомо хуже. Если поле — тоже объект, то в таком случае он создастся конструктором по умолчанию, и только потом выполнится оператор присваивания. А в некоторых случаях (я знаю три) такое присваивание вообще невозможно.
1. Это означает, что я не смогу показать очень красивый, элегантный и полностью безопасный по отношению к исключениям код, стабильность которого, тем не менее, будет зависеть от фазы луны и случайного содержимого стека.

Обычно это происходит так. Приходит багрепорт: «У половины наших клиентов с вин2003 программа крэшится». Я лично с удовольствием бы писал много нового кода на С++11, однако приходится браться за эту ошибку. Просто потому что у меня больше опыта в отладке подобных проблем. Я вообще три вещи делаю очень хорошо: пишу код на С++, отлаживаю код на С++ и пишу очень красивые configure.ac. Если кто не верит, может зайти в профиль и посчитать мой возраст.

И поскольку я не собираюсь менять род деятельности и кровно заинтересован в экономии своего времени, то настаиваю на своей мысли: RAII не следует использовать для fopen() и подобного.
Если Вы 80-ого года рождения (всего-то 32), то у Вас длинее? Вам не нужно — не используйте, но не называйте это говном. А так и говорите: " в моей практике применение RAII затруднительно потому-то и потому". Каждый сделает тогда вывод сам.
Применение RAII в моей практике вовсе не затруднительно. Однако опыт показывает, что между «тяжелым» конструктором (RAII) и «легким» (с отложенной инициализацией) лучше выбрать второй.
Спасибо, нам очень важно ваше мнение. До свидания.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Я понимаю теоретиков от программирования, который тащатся, написав такой «красивый» код, но блин!
1. Как сюда добавить какую-нибудь проверку, обработку ошибок?
2. Как сюда добавить логирование?
3. Как такое отлаживать?
4. Нафига писать код открытия файла, который нужно вдумчиво читать и пытаться понять, вместо банального линейного кода, который каждый первокурсник поймёт?
НЛО прилетело и опубликовало эту надпись здесь
Это сугубо практичный подход.
Есть проект, у него есть требования на логирование, обработку ошибок и прочие соглашения.
Что бы этот код не дублировать везде где используются внешние ресурсы (а мы помним поему дублирование кода плохо да?).
Логирование добавляется на 1-2-3 — делаешь приватные статические методы _fopen/_fclose — в них добавляешь логирование и все утилитарную логику. Остальной код класса работает с этими методами. Вопрос снят?
Подобный код обычно отлаживать не надо — тк требования к нему хорошо формализовану в документации по используемому системном API, что позволяет легко написать юнит тесты на его 99% покрытие.
Сорри что немного не по теме, но в чем смысл писать typedef _после_ изпользования синонима в коде?
Хм, вроде оба typedef'а объявлены до использования? В конструкторе typedef'ы не фигурируют, а вызывается конструктор объекта
Видимо я не совсем корретно выразился.
Фигурирует file и его конструирование, с передачей deleter 2м параметром.
НЛО прилетело и опубликовало эту надпись здесь
Да, спасибо, в этом случае действительно все typedef относятся только в деталям реализции и не «светят» в паблик методах класса.
Хей, да это прям как двухфазное конструирование в Symbian C++, тока из коробки и без лишнего кода.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории