2013-06-18 6 views
12

OpenGLオブジェクト(テクスチャ、フレームバッファなど)用の簡単なRAIIラッパーを作成したいと思います。すべてglGen*glDelete*の関数が同じシグネチャを共有しています。 :OpenGLオブジェクト用のRAIIラッパー

typedef void (__stdcall *GLGenFunction)(GLsizei, GLuint *); 
typedef void (__stdcall *GLDelFunction)(GLsizei, const GLuint *); 

template <GLGenFunction glGenFunction, GLDelFunction glDelFunction> 
class GLObject 
{ 
    GLuint m_name; 
public: 
    GLObject() 
    { 
     glGenFunction(1, &m_name); 
    } 

    ~GLObject() 
    { 
     glDelFunction(1, &m_name); 
    } 

    GLuint getName() {return m_name;} 
}; 

typedef GLObject<glGenTextures, glDeleteTextures> GLTexture; 

これは、テクスチャの細かい動作しますが、フレームバッファのために失敗:glGenFramebuffersglDeleteFramebuffers機能アドレスはコンパイル時に知られていない、とテンプレート引数として使用することはできません。だから私は、第二版作っ:

class GLObjectBase 
{ 
    GLuint m_name; 
    GLDelFunction m_delFunction; 

public: 
    GLObjectBase(GLGenFunction genFunc, GLDelFunction delFunction) 
     : m_delFunction(delFunction) 
    { 
     genFunc(1, &m_name); 
    } 

    GLuint getName() 
    { 
     return m_name; 
    } 

protected: 
    ~GLObjectBase() 
    { 
     m_delFunction(1, &m_name); 
    } 
}; 

class GLFrameBuffer : public GLObjectBase 
{ 
public: 
    GLFrameBuffer() : GLObjectBase(glGenFramebuffers, glDeleteFramebuffers) {} 
}; 

をしかし、私は実行時に変更されることはありません各インスタンスにおけるデルの関数ポインタを格納する必要があるので、私はそれを好きではありません。

ほとんどのコピー貼り付けクラスを作成せずに、各インスタンスにオブジェクト名だけを格納するラッパークラスを作成するにはどうすればよいですか?

template <int N> 
class GLObject2 
{ 
    GLuint m_name; 
    static GLDelFunction glDelFunction; 
public: 
    GLObject2(GLGenFunction genFunction, GLDelFunction delFunc) 
    { 
     genFunction(1, &m_name); 
     if (glDelFunction == nullptr) 
      glDelFunction = delFunc; 
     ASSERT(glDelFunction == delFunc); 
    } 

    GLuint getName() {return m_name;} 

protected: 
    ~GLObject2() 
    { 
     glDelFunction(1, &m_name); 
    } 
}; 

template <int N> 
GLDelFunction GLObject2<N>::glDelFunction = nullptr; 

class GLTexture: public GLObject2<1> 
{ 
public: 
    GLTexture(): GLObject2<1>(glGenTextures, glDeleteTextures) {} 
}; 

class GLRenderBuffer: public GLObject2<2> 
{ 
public: 
    GLRenderBuffer(): GLObject2<2>(glGenRenderbuffers, glDeleteRenderbuffers) {} 
}; 

は、誰もがよりエレガントな解決策を提案することができます:私はこのような何かを行うことができ

+0

IMHOのOpenGL APIは、この種の一般的なアプローチが非常にうまくいくためには、おそらく十分に統一されていません。異なる世代の機能は、異なるAPI設計手法を使用しています。 – Trillian

+3

@Trillian:ナンセンス。 12のOpenGLオブジェクトタイプのうち、[3つしか非標準の作成/破壊メダドロジーを使用しています](http://www.opengl.org/wiki/OpenGL_Object#Nonstandard_objects)。これは他の理由では悪い考えですが、そうではありません。 –

+1

@NicolBolas:好奇心から:このアイデアは何が悪いのですか?そして、あなたはこの悪い決定の一部に答えますか、それともあなたはそれをお勧めしますか? – Skalli

答えて

15

本当に、これはCのプログラマのように考えています。 C++を使用しているので、C++プログラマーのやり方で解決してください。特性クラスでは:適切なC++特性クラスと同様に

struct VertexArrayObjectTraits 
{ 
    typedef GLuint value_type; 
    static value_type Create(); 
    static void Destroy(value_type); 
}; 

、私たちは、それぞれのオブジェクトは、それが自分のvalue_typeだ宣言しています。これにより、sync objectsのようにGLuintを使用しないOpenGLオブジェクトに適応させることができます(Create/Destroyインタフェースはそれには関係ないので、おそらく気にしないはずです)。

OpenGLオブジェクトの種類ごとに1つの特性クラスを記述します。 CreateDestroy関数は、C APIに適切にコールを転送します。

template<typename T> 
class OpenGLObject 
{ 
public: 
    OpenGLObject() : m_obj(T::Create()) {} 
    ~OpenGLObject() {T::Destroy(m_obj);} 

    operator typename T::value_type() {return m_obj;} 

private: 
    typename T::value_type m_obj; 
}; 

OpenGLObject<VertexArrayObjectTraits>VAOを開催する:あなたが必要とするすべてのものをインターフェース周りRAII-ラッパーである、ことを行った後

+4

私は急いでこの答えを受け入れたとマークしましたが、それを少し考えた後、私はあまり好きではありません。私は、オブジェクトを構築して破壊するために使用される関数だけが異なる、コピー・ペースト・クラスの半ダースを作成することができました。あなたの提案は、構造的にはやや優れていますが、コピー・ペーストされた現在の特性 - クラスとオブジェクト・クラスの同じ量を私に残します。 – n0rd

+3

@ n0rd:同じ数のクラスはありますが、クラスは比較的簡単で、20ishのラインを除いてそれぞれ5本のEASYラインです。それはまだ複雑さにおいて大きな勝利です。 –

+0

Naiveの実装は、オブジェクト名、1行のコンストラクタ、1行のデストラクタ、1行のゲッタを保持するための変数よりはるかに大きくも複雑でもありません。 traitsクラス: 'value_type' typedef、' Create'関数、 'Destroy'関数と比較してください。 – n0rd

9

なぜホイールを改造するのですか?あなただけの特徴を記述する必要があるので、すでに必要な機能を提供しますstd::unique_ptrを使用してきちんとした解決策は、あります(!):

template<void (*func)(GLuint)> 
struct gl_object_deleter { 
    struct pointer { // I wish we could inherit from GLuint... 
     GLuint x; 
     pointer(std::nullptr_t = nullptr) : x(0) {} 
     pointer(GLuint x) : x(x) {} 
     operator GLuint() const { return x; } 
     friend bool operator == (pointer x, pointer y) { return x.x == y.x; } 
     friend bool operator != (pointer x, pointer y) { return x.x != y.x; } 
    }; 
    void operator()(GLuint p) const { func(p); } 
}; 

void delete_texture(GLuint p) { glDeleteTextures(1, &p); } 
void delete_shader(GLuint p) { glDeleteShader(p); } 
// ... 
typedef std::unique_ptr<void, gl_object_deleter<delete_texture>> GLtexture; 
typedef std::unique_ptr<void, gl_object_deleter<delete_shader>> GLshader; 
// ... 

Create*機能はあなた不便である彼らの引数を介して、配列を返すほとんどオブジェクトを1つずつ割り当てます。単一のインスタンスの作成ルーチンのセットを定義することが可能である:

GLuint glCreateTextureSN(GLenum target) { GLuint ret; glCreateTextures(target, 1, &ret); return ret; } 
GLuint glCreateBufferSN() { GLuint ret; glCreateBuffers(1, &ret); return ret; } 
// ... 

いくつかのOpenGLの機能、glCreateShaderを直接使用することができますように。次のように今、私たちはそれを使用することができます。

GLtexture tex(glCreateTextureSN(GL_TEXTURE_2D)); 
glTextureParameteri(tex.get(), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 
// ... 
GLtexture tex2 = std::move(tex); // we can move 
tex2.reset(); // delete 
return tex2; // return... 

一つの欠点は、明示的にget()呼び出す必要がありますので、あなたは、GLuintへの暗黙的なキャストを定義することができないということです。しかし、2番目の考えでは、偶発的なキャストを防ぐことはGLuintに悪いことではありません。

+2

OpenGLのようなオブジェクトは、shared_ptrの使い方が本当に良い場合の古典的な例の1つです。テクスチャやものは通常、レンダラのさまざまな場所で共有されます。誰もそれを必要としなくなったときにだけ削除するのは、shared_ptrとまったく同じです。 – TravisG

+0

@ TravisG: 'unique_ptr'と比較した' shared_ptr'の問題は、 'get()'関数が常にポインタを返すことです。結局、 'GLuint'をヒープに割り当てたり、明示的な' reinterpret_cast (tex.get()) 'を書くことになります。たぶんあなたは何らかの種類のラッパーを書くことができます。 – ybungalobill

+1

JohannesDの投稿(このスレッドから)(http://stackoverflow.com/a/6272139)を見てください。 GLuint型は、unique_ptrカスタム記憶域タイプのNullablePointer要件を満たしていない可能性があります。 – Bovinedragon

関連する問題