- PVSM.RU - https://www.pvsm.ru -
Недавно пристально посмотрел на С++ Variadic Templates и неожиданно для себя изобрел новый RAII Scoped Resource Manager.
Получилось кратко и эффектно.
Например, с C-style выделением памяти:
// Аллоцируем ресурс в блоке.
{
ha::scoped_resource<void*, size_t> mem(::malloc, 1, ::free);
::memset(mem, 65, 1);
}
При выходе из блока ресурс будет освобожден автоматически.
Или еще вот так можно захватывать владение ресурсом «файловый дескриптор»:
// Захватываем ресурс в блоке.
{
ha::scoped_resource<int> fd(
[&filename]()
{
return ::open(filename.c_str(), O_RDONLY);
},
::close);
assert(fd != -1);
std::vector<char> buff(1024);
ssize_t rc = ::read(fd, &buff[0], 1024);
}
При выходе из блока ресурс будет освобожден автоматически даже после вызова, например, throw std::exception().
Или второй пример можно переписать даже понятней без применения лямбды:
{
ha::scoped_resource<int, char*, int> fd(::open, filename.c_str(), O_RDONLY, ::close);
if (fd == -1)
throw std::runtime_error(std::string("open() failed: ") + ::strerror(errno));
std::vector<char> buff(1024);
ssize_t rc = ::read(fd, &buff[0], 1024);
}
То есть в общем случае имеем темплейтный класс, который инстанциируется типом ресурса, а его конструктор принимает две std::functions:initializer_t и finalizer_t.
Между инициализатором и финализатором следуют параметры для инициализатора, которые являются частью спецификаторов шаблона.
Деструктор просто вызывает финализатор для захваченного ресурса.
Для raw-доступа к ресурсу существует оператор типа ресурса.
{
ha::scoped_resource
<resource_t, param1_t, ...>
resource
(ititializer, param1, ..., finalizer);
resource_t
plain_resource =
resource.operator resource_t();
}
operator resource_t(). Еще это позволяет создавать именованные инициализаторы, тем самым переиспользуя их.std::initializer_list.std::shared_ptr(T* ptr, deleter d).Иногда все же эффективней написать полноценный врапер ресурса.
Создание AVFormatContext контекста:
ha::scoped_resource<ffmpeg::AVFormatContext*> formatctx
(ffmpeg::avformat_alloc_context, ffmpeg::avformat_free_context);
Это есть аналог следующего:
std::shared_ptr<ffmpeg::AVFormatContext> formatctx =
std::shared_ptr<ffmpeg::AVFormatContext>
(ffmpeg::avformat_alloc_context(), ffmpeg::avformat_free_context);
Или вот еще, тут применяется составной деинициализатор:
ha::scoped_resource<ffmpeg::AVCodecContext*> codecctx(
ffmpeg::avcodec_alloc_context,
[](ffmpeg::AVCodecContext* c)
{
ffmpeg::avcodec_close(c),
ffmpeg::av_free(c);
});
А этот пример интересен тем, что происходит захват ресурса, который не нужно освобождать:
ha::scoped_resource<ffmpeg::AVCodec*, ffmpeg::AVCodecID> codec(
ffmpeg::avcodec_find_decoder,
codecctx->codec_id,
[](__attribute__((unused)) ffmpeg::AVCodec* c)
{
});
И наконец самый простой oneliner:
ha::scoped_resource<ffmpeg::AVFrame*> frame(ffmpeg::avcodec_alloc_frame, ffmpeg::av_free);
Который аналог следующего:
std::shared_ptr<ffmpeg::AVFrame> frame =
std::shared_ptr<ffmpeg::AVFrame>(ffmpeg::avcodec_alloc_frame(), ffmpeg::av_free);
ha::mutex mutex;
ha::scoped_resource<ha::mutex*, ha::mutex*> scoped_lock(
[](ha::mutex* m) -> ha::mutex*
{
return m->lock(), m;
},
&mutex,
[](ha::mutex* m) -> void
{
m->unlock();
}
);
Реализация класса scoped_resource настолько проста и элегантна, что даже чем-то напомнила мне идею Y-combinator [1]'a.
То есть возможно с легкостью реализовать что-то подобное, просто начав с декларации конструктора scoped_resource::scoped_resource(initializer_t, finalizer_t); и затем наращивать variadic-часть для параметров.
Вот как-то так. 
Автор: oletorr
Источник [2]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/c-3/29454
Ссылки в тексте:
[1] Y-combinator: http://en.wikipedia.org/wiki/Fixed-point_combinator#Y_combinator
[2] Источник: http://habrahabr.ru/post/172817/
Нажмите здесь для печати.