// // Недавно пристально посмотрел на С++ 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(); } // // В чем преимущество перед другими RAII реализациями враперов ресурсов? // 1. Инициализатор не вызывается во время редукции параметров конструктора, // а в самом конструкторе. // Это, например, позволяет реализовать "нормальную" передачу инициализатора, // что дает возможность захвата ресурса в lazy-стиле, до первого вызова operator resource_t(). // Еще это позволяет создавать именованные инициализаторы, тем самым переиспользуя их. // 2. Можно явно передавать какое-либо количество параметров для инициализатора. // Тут, возможно, есть еще второй полезный механизм - std::initializer_list. // 3. Если пункт 2. по каким-то причинам не применим, можно в качестве инициализатора // передавать лямбду, которая замкнет все параметры инициализатора на себя. // 4. Деинициализатор имеет единственный параметр - тип ресурса, // но в случае необходимости также может быть лямбдой, замыкая // на себя дополнительные параметры деинициализации. // 5. Это намного проще в реализации чем 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); // Но неужели это все про naked plain-C ресурсы? А где же примеры с годным С++? // А вот: 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. // То есть возможно с легкостью реализовать что-то подобное, просто начав с декларации // конструктора scoped_resource::scoped_resource(initializer_t, finalizer_t); // и затем наращивать variadic-часть для параметров. // // // Вот как-то так. // © 2013 okertanov@gmail.com //