RAII + C ++可変長テンプレート= win

私は最近、C ++ Variadic Templatesを詳しく調べ、予想外に新しいRAII Scoped Resource Managerを発明しました。

簡単かつ効果的に判明しました。



たとえば、Cスタイルのメモリ割り当ての場合:

//    . { 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()



するなどの呼び出しの後でも、リソースは自動的に解放されます。



または、2番目の例は、ラムダを使用せずにさらに明確に書き換えることができます。

 { 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); }
      
      







つまり、一般的な場合、リソースタイプによってインスタンス化されるテンプレートクラスがあり、そのコンストラクターは2つのstd::functions



initializer_t finalizer_t



受け入れます。



イニシャライザとファイナライザの間には、テンプレート修飾子の一部であるイニシャライザのパラメータがあります。



デストラクタは、キャプチャされたリソースのファイナライザを呼び出すだけです。



リソースへの未加工アクセスの場合、リソースタイプ演算子が存在します。

 { ha::scoped_resource <resource_t, param1_t, ...> resource (ititializer, param1, ..., finalizer); resource_t plain_resource = resource.operator resource_t(); }
      
      







リソースラッパーの他のRAII実装よりも優れている点は何ですか?


  1. 初期化子は、コンストラクターパラメーターの削減中に呼び出されるのではなく、コンストラクター自体で呼び出されます。 これにより、たとえば、「通常の」初期化転送を実装できます。これにより、 operator resource_t()



    最初の呼び出しの前に、遅延スタイルでリソースをキャプチャできるようになります。 これにより、名前付き初期化子を作成して再利用することもできます。
  2. イニシャライザーに任意の数のパラメーターを明示的に渡すことができます。 ここで、おそらく、別の2番目の便利なメカニズムstd::initializer_list



  3. ポイント2.が何らかの理由で適用できない場合、ラムダを初期化子として渡すことができます。これにより、初期化子のすべてのパラメータが自身に閉じられます。
  4. deinitializerには単一のパラメーター(リソースのタイプ)がありますが、必要に応じてラムダにすることもでき、それ自体で追加の初期化解除パラメーターをロックします。
  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) { });
      
      







そして最後に、最も簡単なワンライナー:

 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);
      
      





しかし、それは本当にむき出しのプレーンCリソースについてのすべてですか? また、有効な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);



の宣言から開始するだけで、このようなものを簡単に実装できscoped_resource::scoped_resource(initializer_t, finalizer_t);



そして、パラメーターの可変部分を構築します。



わかりましたが、とにかく実装はどこですか?
 template <typename T, typename... A> class scoped_resource { public: typedef std::function<T (A...)> initializer_t; typedef std::function<void(T)> finalizer_t; typedef T resource_t; scoped_resource(initializer_t finit, A... args, finalizer_t final) : finit_(finit), final_(final), resource_(finit_(args...)) { }; ~scoped_resource() { final_(resource_); } template <typename Y> Y get() const { return static_cast<Y>(resource_); } T get() const { return resource_; } operator T() const { return get(); } // No copy, no move scoped_resource(const scoped_resource&) = delete; scoped_resource(scoped_resource&&) = delete; scoped_resource& operator=(const scoped_resource&) = delete; scoped_resource& operator=(scoped_resource&&) = delete; private: const initializer_t finit_; const finalizer_t final_; T resource_; };
      
      







そのようなもの。 画像






All Articles