Ручное управление ресурсами в низкоуровневом си-подобном коде на C++ — довольно хлопотное дельце. Создание достойных RAII-врапперов для каждого используемого сишного API не всегда практично, а использование подходов с goto cleanup или множеством вложенных if (success) вредит читаемости кода.
Макрос defer, вдохновленный Go, как никогда кстати! Использовать его просто:
void* p = malloc(0x1000);
defer [&] { free(p); };
Отложенная лямбда будет выполнена при выходе из области видимости, независимо от того, будет ли выполнен return, брошено исключение (если разрешено), или даже выполнен goto наружу.
Реализация макроса лаконична и полагается на базовые возможности C++17 (Clang 5+, GCC 7+, MSVC 2017+):
#ifndef defer
template <typename T>
struct Deferrer
{
T f;
Deferrer(T f) : f(f) { };
Deferrer(const Deferrer&) = delete;
~Deferrer() { f(); }
};
#define TOKEN_CONCAT_NX(a, b) a ## b
#define TOKEN_CONCAT(a, b) TOKEN_CONCAT_NX(a, b)
#define defer Deferrer TOKEN_CONCAT(__deferred, __COUNTER__) =
#endif
Данный макрос по-настоящему zero-cost и не зависит от рантайма C или стандартной библиотеки, поэтому его можно использовать даже в разработке под ядро ОС.
Читать далее →