2015-09-24 1 views
5

私は "Acquire/Release" RAIIクラス(現時点では各種類のリソースごとに1つずつ)を1つのテンプレートクラスに置き換えようとしています。取得の一般的な形式は、Acquire()、Acquire(p1)、Acquire(p1、p2)などのいくつかのタイプがあります。Releaseについても同様です。しかし、リソースがパラメータで取得された場合、それらは同じパラメータで解放する必要があります。バリエーションテンプレートを使用したアクイジション/リリース用のRAIIパターン

私は多次元テンプレートでこれを行うことができ、引数をタプルに格納すると思います。私はもちろん構文に落ちた。誰も助けることができますか?

#include <tuple> 

template<class T, typename... Args> 
class Raii 
{ 
public: 

    Raii(T * s, Args&& ... a) : subect(s), arguments(a) 
    { 
     subject->Acquire(arguments); 
    } 

    ~Raii() 
    { 
     subject->Release(arguments); 
    } 

private: 

    T subject; 
    std::tuple<Args...> arguments; 
}; 

class Framebuffer 
{ 
public: 

    void Acquire() {} 
    void Release() {} 
}; 

class Sampler 
{ 
public: 

    void Acquire(int channel) {} 
    void Release(int channel) {} 
}; 

class Buffer 
{ 
public: 

    void Acquire(int target, int location) {} 
    void Release(int target, int location) {} 
}; 

int main(void) 
{ 
    Framebuffer f; 
    Sampler s; 
    Buffer b; 

    auto b1 = Raii(&f); 
    { 
     auto b2 = Raii(&s, 10); 
     { 
      auto b3 = Raii(&b, 10, 20); 
      { 

      } 
     } 
    } 
    return 0; 
} 
+0

可能重複します(http:// stackoverflowの.com/questions/6486432/variadic-template-templates-and-perfect-forwarding) –

+0

これは完璧なフォワーディングに触れていますが、これはそのスレッドで説明されているものよりはるかに単純で実用的な例です。だから、私は少なくともそれが働くまでは閉じないように投票します。 – Robinson

答えて

3

ポインタと値の不一致のようなわずかな違いは別として、テンプレート引数なしでRaiiを参照することはできず、引数パラメータpack/tupleを展開しないことが主な問題です。ここで

はあなたが完璧な転送のいくつかの余分なsprinklingsで改善できる作業バージョンであるなど

template<class T, typename... Args> 
class Raii 
{ 
public: 

    Raii(T & s, Args&& ... a) : subject(s), arguments(a...) 
    { 
     //expand a 
     subject.Acquire(a...); 
    } 

    //helper to expand tuple 
    //requires std::index_sequence and friends from C++14 
    //if you are limited to C++11, you can find implementations online 
    template <std::size_t... Idx> 
    void release(std::index_sequence<Idx...>) 
    { 
     subject.Release(std::get<Idx>(arguments)...); 
    } 

    ~Raii() 
    { 
     //yay, index trick 
     release(std::index_sequence_for<Args...>{}); 
    } 

private: 

    T &subject; 
    std::tuple<Args...> arguments; 
}; 

//helper function so that we can deduce the Raii template args 
template <class T, typename... Args> 
Raii<T,Args...> make_raii (T & t, Args&&... args) { 
    return {t, std::forward<Args>(args)...}; 
} 

// Framebuffer etc. 

int main() 
{ 
    Framebuffer f; 
    Sampler s; 
    Buffer b; 

    //use make_raii instead of Raii 
    auto b1 = make_raii(f); 
    { 
     auto b2 = make_raii(s, 10); 
     { 
      auto b3 = make_raii(b, 10, 20); 
      { 

      } 
     } 
    } 
} 

Live Demo

[可変引数テンプレートテンプレートと完璧な転送]の
+0

興味深いことに、私は 'index_sequence'がC++ 14になったことを知りませんでした。いい答えだ。 –

+0

はいポインタ/値の問題を修正しました。残念ながら私はC++ 11を持っていないのでindex_sequenceはありません。私はgithubでC++ 11の実装を見つけました。だから、それを使うかもしれません。 https://gist.github.com/doicanhden/7249956、この実装は実際に私に "致命的なエラーC1001:コンパイラで内部エラーが発生しました"(Visual Studio 2013)です。 – Robinson

+0

@Robinson Yay、コンパイラのバグ。 Clangの[うまくいく](http://coliru.stacked-crooked.com/a/c95f162cd89dbfd7)のようです。私はVS2013互換のものが見つかるかどうか確認します。 – TartanLlama

1

私は魔法のようになりますかなり確信している:

subject->Acquire(a...); 

aはテンプレートパックですので、あなたが呼び出しのためにそれを拡張する必要があります。

タプルを可変呼び出しに拡張する場合は、integer_sequence expansionが必要です。

+0

ああ! 「クラスの使用にはテンプレート引数リストが必要です」(ただし、私はRaiiオブジェクトをインスタンス化しています)です。なぜテンプレート引数リストをコンストラクタパラメータから推測できないのですか? – Robinson

+0

@RobinsonはC++です。ヘルパー関数を使用する( 'make_raii')。これは 'make_tuple'や' make_pair'などで起こる同じ問題です。 –