Defer в Си

Мне очень нравится конструкция defer в «Гоу» — очень удобно для управления ресурсами, можно рядом с получением какого-либо ресурса сразу прописать и его освобождение, так сложнее забыть освободить выделенное.

Оказывается в прошлом году её предложили добавить и в Си (чему я очень рад, потому что в последнее время больше всего, пожалуй, пишу именно на нём), а в 22-й версии «силанга» уже даже есть реализация, можно попробовать.

Код на C с использованием _Defer для автоматического закрытия файла /dev/random и вывода одного случайного байта через fgetc.

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

Kirill Myshkin № 1
дык макросами же давно делали дефёр, не?
Евгений Степанищев автор № 2
Да? Как? Через __cleanup__?
Kirill Myshkin № 3
Евгений Степанищев автор № 4
Занятный приём, я бы не додумался, но выглядит громозко и возможности другие. Один в другой не вложишь (для освобождения нескольких ресурсов), goto и return всё сломает, наверное и ещё ограничения есть

Я вообще думал, что нечто похожее можно через __cleanup__ и фейковую переменную сделать, будет работать лучше этого хака с циклом.
Kirill Myshkin № 5
все что я знаю, что чувакам (как этому, который дебагер пишет), которые избегают всего кроме Си хватает

типа им эти ограничения меньшее зло, чем то что тянут с собой остальные решения (другой язык и пр.)
Евгений Степанищев автор № 6
Ну, если нет другого выбора, то и это хлеб) но лучше будет, когда в языке стандартная конструкция появится, которая будет вести себя понятно во всех ситуациях.
Александр Игумнов № 7
Интересно, как они его реализуют. Первой пришла в голову идея размещения на стеке таблицы ссылок на внутренние безымянные функции, связанная с блоком. Тогда каждый _Defer преобразуется в функцию. Таблица заполняется в процессе выполнения данными вида: адрес функции, парам1, парам2…, где парам — локальные переменные, используемые внутри _Defer. При выходе из блока пробегаемся вверх по стеку и вызываем call'ами эти безымянные функции используя параметры, которые уже лежат здесь же на стеке. Когда я увидел пример 16, где в цикле вызывается printf каждый раз с новым параметром, я задумался. Параметры используемые внутри _Defer надо хранить, и в цикле можно нечаянно учинить переполнение стека. Зато для отладки можно вставить внутрь цикла _Defer, в котором используется интересующая переменная, а потом разглядывая в отладчике стек, можно посмотреть динамику её сохранения (шучу).
Евгений Степанищев автор № 8
Интересно, как они его реализуют.

Так уже реализовали, я об этом и пишу. Можно скачать clang 22 и посмотреть.

Александр Игумнов № 9
Из документации неправильно понял поведение defer внутри цикла. Думал, что все блок defer будет выполняться после завершения всего цикла, и потому начал придумывать стек вызовов.

В реальности всё проще — компилятор статически ищет все точки выхода из блока, такие как return и break и вставляет код defer перед ними.

Забавно, что clang даже с -O0 может объединять несколько if() return; в один сложный if и единственный return.