まあ今さら感が激しいですが。
枯れ木も山の賑わいというやつかね。
ウインドウのクラス化 再利用のための変遷
基本はSet/GetWindowLong()のGWL_USERDATAでthisをやりとりするパターン。
最初、ウインドウを所持するクラスごとに専用の処理を書いていた。
class Window
{
public:
ATOM RegisterClassEx(
UINT style,int cbClsExtra,int cbWndExtra,HINSTANCE hinst,
HICON hIcon,HCURSOR hCursor,HBRUSH hbrBackground,
LPCTSTR lpszMenuName,LPCTSTR lpszClassName,HICON hIconSm)
{
WNDCLASSEX wcex =
{sizeof(WNDCLASSEX),style,initWindowProc,cbClsExtra,cbWndExtra,hinst,
hIcon,hCursor,hbrBackground,lpszMenuName,lpszClassName,hIconSm};
return ::RegisterClassEx(&wcex);
}
bool CreateWindowEx(
DWORD dwExStyle,LPCTSTR lpClassName,LPCTSTR lpWindowName,DWORD dwStyle,
int x,int y,int nWidth,int nHeight,HWND hWndParent,HMENU hMenu,HINSTANCE hinst)
{
hWnd = ::CreateWindowEx(
dwExStyle,lpClassName,lpWindowName,dwStyle,
x,y,nWidth,nHeight,hWndParent,hMenu,hinst,this);
return hWnd != NULL;
}
private:
//初期化専用
static LRESULT CALLBACK initWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
if (uMsg == WM_CREATE)
{
WindowClass *this_ = (Window*)((LPCREATESTRUCT)lParam)->lpCreateParams;
::SetWindowLongPtr(hwnd,GWL_USERDATA,(LONG_PTR)this_);
::SetWindowLongPtr(hwnd,GWL_WNDPROC,(LONG_PTR)staticWindowProc);
return this_->WindowProc(hwnd,uMsg,wParam,lParam);
}
return ::DefWindowProc(hwnd,uMsg,wParam,lParam);
}
//間接参照
static LRESULT CALLBACK staticWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return ((Window*)::GetWindowLongPtr(hwnd,GWL_USERDATA))->WindowProc(hwnd,uMsg,wParam,lParam);
}
//本命のウインドウプロシージャ
LRESULT WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
HWND hWnd;
};
|
しかし毎回コピペで同じ事を書くのは馬鹿らしいので、共通する処理を基底クラスの非仮想関数とし、
可変処理(ウインドウプロシージャ)を仮想関数として、共通部分を切り出した。
class base_Window
{
public:
base_Window(void) : hWnd(NULL) {}
virtual ~base_Window() {}
ATOM RegisterClassEx(
UINT style,int cbClsExtra,int cbWndExtra,HINSTANCE hinst,
HICON hIcon,HCURSOR hCursor,HBRUSH hbrBackground,
LPCTSTR lpszMenuName,LPCTSTR lpszClassName,HICON hIconSm);
bool CreateWindowEx(
DWORD dwExStyle,LPCTSTR lpClassName,LPCTSTR lpWindowName,DWORD dwStyle,
int x,int y,int nWidth,int nHeight,HWND hWndParent,HMENU hMenu,HINSTANCE hinst);
private:
static LRESULT CALLBACK initWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
static LRESULT CALLBACK staticWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
//本命のウインドウプロシージャ
virtual LRESULT WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam) = 0;
protected:
HWND hWnd;
};
|
とりあえずこれで割と満足していたんだが、ふと思い立った。「この仮想関数は美しくない」
効率云々はもう言わない。しかし毎回クラスに埋め込んだ場合は必要ないってことは、
本質的に必要ではないということで、なんとかして使わないコーディングは出来ないものかと。
で、仮想関数版とは逆の考え方、共通機能を持った基底クラスから様々なクラスに継承するのではなく、
様々なクラスに共通機能を実装させる、と言うことに思い至る。
こいつはtemplateを使えばうまく実装できた。
class base_WindowClass
{
protected:
LRESULT WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
};
template<class base_Class>
class WindowClass : public base_Class
{
public:
WindowClass(void) : base_Class() {}
WindowClass(const base_Class &src) : base_Class(src) {}
ATOM RegisterClassEx(
UINT style,int cbClsExtra,int cbWndExtra,HINSTANCE hinst,
HICON hIcon,HCURSOR hCursor,HBRUSH hbrBackground,
LPCTSTR lpszMenuName,LPCTSTR lpszClassName,HICON hIconSm)
{
WNDCLASSEX wcex =
{sizeof(WNDCLASSEX),style,initWindowProc,cbClsExtra,cbWndExtra,hinst,
hIcon,hCursor,hbrBackground,lpszMenuName,lpszClassName,hIconSm};
return ::RegisterClassEx(&wcex);
}
HWND CreateWindowEx(
DWORD dwExStyle,LPCTSTR lpClassName,LPCTSTR lpWindowName,DWORD dwStyle,
int x,int y,int nWidth,int nHeight,HWND hWndParent,HMENU hMenu,HINSTANCE hinst)
{
return ::CreateWindowEx(
dwExStyle,lpClassName,lpWindowName,dwStyle,
x,y,nWidth,nHeight,hWndParent,hMenu,hinst,this);
}
private:
//初期化専用
static LRESULT CALLBACK initWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
if (uMsg == WM_CREATE)
{
WindowClass *this_ = (WindowClass *)((LPCREATESTRUCT)lParam)->lpCreateParams;
::SetWindowLongPtr(hwnd,GWL_USERDATA,(LONG_PTR)this_);
::SetWindowLongPtr(hwnd,GWL_WNDPROC,(LONG_PTR)staticWindowProc);
return this_->WindowProc(hwnd,uMsg,wParam,lParam);
}
return ::DefWindowProc(hwnd,uMsg,wParam,lParam);
}
//間接参照
static LRESULT CALLBACK staticWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return ((WindowClass *)::GetWindowLongPtr(hwnd,GWL_USERDATA))->WindowProc(hwnd,uMsg,wParam,lParam);
}
};
|
ほとんど1関数だからインライン展開の問題はまず無いだろうけど、
コンストラクタの種類が無いのがちょっと辛い。
メンバ関数テンプレートを並べまくると言う手も無いこともないが。
まあ、基本的にWM_CREATEで初期化するのがWindowsの基本だし。
http://hp.vector.co.jp/authors/VA025927/prog/winclass.html