Давно программирую, видел всякое, ко многим проявлениям программерского дебилизма смог привыкнуть. Но есть образчики кода, которые неизбежно вызывают у меня лютый баттхерт. Например, что-то вот такое:
error_code use_resource(resource_id res_id) { if(auto r = first_operation(res_id); r != error_code::ok) { dispose(res_id); return r; } if(auto r = second_operation(res_id); r != error_code::ok) { dispose(res_id); return r; } ... dispose(res_id); return error_code::ok; } |
Думаю, что несложно догадаться, что именно триггерит: это обилие вызовов dispose.
Я могу простить тот факт, что в use_resource передается голый дескриптор ресурса, а не какая-то RAII-обертка вокруг него.
Ну мало ли, бывает. Может эта функция вообще как extern "C" описана и предназначена для того, чтобы ее вызывали из Си-шного кода. Или же это часть древнего проекта и первоначально use_resource была написана еще в конце 1980-х, а сейчас ее просто дорабатывают не имея возможности поменять все 100500 мест в старой кодовой базе, где она вызывается именно вот так.
Но блин, почему нельзя сделать RAII обертку уже внутри use_resource?
Хотя бы подобным образом:
error_code use_resource(resource_id res_id) { struct resource_disposer { resource_id m_id; resource_disposer(resource_id id) : m_id(id) {} ~resource_disposer() { dispose(m_id); } } disposer(res_id); if(auto r = first_operation(res_id); r != error_code::ok) { return r; } if(auto r = second_operation(res_id); r != error_code::ok) { return r; } ... return error_code::ok; } |
Причем реализация такого `resource_disposer` -- это вообще C++98. Таким подходом можно пользоваться уже больше двадцати пяти(!!!) лет без оглядки на версию компилятора. В современном C++ можно было бы найти еще несколько способов достижения той же самой цели (хотя бы finally из GSL), более лаконичных.
На эту тему подобной "очистки" ресурсов я уже неоднократно писал. Вот, например, от 2015-го года (уже почти десять лет как!!!): раз и два. Но, как я смотрю, время идет, а криворуких программистов недоучек меньше не становится.
И да, я злой, т.к. считаю, что подобный код является признаком профнепригодности, т.к. человек не видит очевидных моментов с дублированием одной и той же функциональности.
Вероятно, C++ программистов нужно начинать учить с идиомы RAII. А уже все остальное -- потом.
Ну а Си-программистов, по аналогии, нужно начинать учить с идиомы goto err (или goto cleanup). Даже не смотря на то, что goto -- это зло. Как и чистый Си, впрочем ;)
На правах саморекламы: изобретаю велосипеды для себя, могу изобретать и для вас.
3 комментария:
> По-моему основная проблема
Это не основная проблема, хотя лучше бы было иметь более говорящее название, да. И, как я говорил, это может быть данность, изменить которую нам не под силу.
не знаю, как с этим сейчас, но раньше (до 11) вроде были ограничения на применение структур внутри функций (как минимум на применение шаблонов)
> вроде были ограничения на применение структур внутри функций (как минимум на применение шаблонов)
Были. Как раз на шаблоны.
Но я специально привел пример, которым пользовался еще во времена C++98.
Вот, можно проверить: https://wandbox.org/permlink/WwiGYTsf4xNjMpsK
Отправить комментарий