std::unique_ptrでHDCとHGLRCを扱う。
ゲームフレームワーク的なものを作る。番外編1。
windowsプログラミングにおいて、HDCなどのハンドルの管理は非常に面倒ですよね?
HWND hwnd = ...;
HDC hdc = GetDC( hwnd );
GDIを使った描画など;
ReleaseDC( hwnd, hdc ); //使い終わったらReleaseするのが面倒!
std::unique_ptrを使う。
デバイスコンテキストクラス的なものを作って、デストラクタでReleaseすれば良いわけですが、正直デストラクタも書きたくないのです。
こちらをお手本にして、std::unique_ptrを使ってみましょう。
deleterでWindowDCを解放する。
/** * WindowDCを解放するためのdeleter */ struct dc_deleter { typedef HDC pointer; void operator()( HDC hdc )const { ReleaseDC( hwnd, hdc ); } dc_deleter( HWND hwnd ) : hwnd( hwnd ){} HWND hwnd; };
記述が長くなるのでtypedefしておくと便利。
/** * HDCのunique_ptr */ typedef std::unique_ptr< HDC, dc_deleter > dc_handle;
使い方
{
dc_handle dc( ::GetDC( hwnd ), deleter( hwnd ) );
GDIなどで描画( dc.get(), ... );
}//スコープから抜ける時にdeleterで解放される
Windowクラスにデバイスコンテキスト取得メソッドを追加。
class Window { /*略*/ public: /** * WinodwDC取得 */ dc_handle get_dc()const{ reutrn dc_handle( ::GetDC( hwnd ), deleter( hwnd ) ); } };
使い方
Window* window = ...; //既にウィンドウは生成済み { dc_handle dc( window->get_dc() ); GDIなどで描画( dc.get(), ... ); }//スコープから抜ける時にdeleterで解放される
OpenGLレンダリングコンテキストをstd::unique_ptrで管理する。
上記のウィンドウのデバイスコンテキストと同様に、レンダリングコンテキストもstd::unique_ptrに切り替えてみます。
deleterを作る
/** * レンダリングコンテキストを破棄するためのdeleter */ struct glrc_deleter { typedef HGLRC pointer; void operator()( HGLRC rc )const; { ::wglMakeCurrent( dc, nullptr ); ::wglDeleteContext( rc ); } glrc_deleter( HDC dc ) : dc( dc ){} HDC dc; }; /** * レンダリングコンテキストハンドル */ typedef std::unique_ptr< HGLRC, glrc_deleter > glrc_handle;
OpenGLの初期化
/** * ViewのOpenGL実装 */ class OpenGLView : public View { public: /** * コンストラクタ */ OpenGLView( const String& title, const Sizei& client_size ); private: /** * OpenGLレンダリングコンテキストの生成 */ glrc_handle create_rendering_context( const dc_handle& dc ); private: dc_handle dc; glrc_handle rc; };
/** * コンストラクタ * * @param title ウィンドウのタイトル * @param client_size ウィンドウのクライアントサイズ */ OpenGLView::OpenGLView( const String& title, const Sizei& client_size ) : View( title, client_size ) //Viewの生成 , dc( get_dc() ) //ViewからDCを取得 , rc( create_rendering_context( dc ) ) //ViewのDCを使ってGLRCを生成 { } /** * OpenGLレンダリングコンテキストの生成 * * @param dc デバイスコンテキスト */ glrc_handle OpenGLView::create_rendering_context( const dc_handle& dc ) { //PIXELFORMATDESCRIPTORにピクセルフォーマットを設定し、 //デバイスコンテキストにもっと適合するピクセルフォーマットを選択、設定後 //レンダリングコンテキストを生成する PIXELFORMATDESCRIPTOR pfd = { sizeof( PIXELFORMATDESCRIPTOR ) }; pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW // support window | PFD_SUPPORT_OPENGL // support OpenGL | PFD_DOUBLEBUFFER // double buffered ; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 32; pfd.cDepthBits = 16; pfd.iLayerType = PFD_MAIN_PLANE; int format = ::ChoosePixelFormat( dc.get(), &pfd ); if( format == 0 ) throw std::runtime_error( "ChoosePixelFormat" ); if( !::SetPixelFormat( dc.get(), format, &pfd ) ) throw std::runtime_error( "SetPixelFormat" ); HGLRC rc = ::wglCreateContext( dc.get() ); ::wglMakeCurrent( dc.get(), rc ); return glrc_handle( rc, glrc_deleter( dc.get() ) ); }
ウィンドウの生成からOpenGLの初期化まで非常にシンプルに記述できていると思います。
また、初期化の途中で例外が発生しても、unique_ptrのデストラクタで確実に解放されます。