ATL には atlwin.h というのがあって、この中にはウインドウ関連の WindowsAPI をラップしてライブラリ化したクラスがあります。
これを使うとウインドウやらダイアログやらコントロールやらを簡単に扱えるようになるのですが、template やらを多用していたりトリッキーなコードを書いていたりするので、結構理解に苦しむ部分があったりします。
まずは全体の構造を理解することだ!ということで、とりあえず atlwin.h 全体の簡単なクラス図を書いてみました。
実際はテンプレートの引数とかあってここまで簡単でも無いのですが、それを含めてもそんなに複雑な構造でも無さそうです。
このクラス群を一つ一つ見ていくことにします。
CWinTraits<> は生成時にウインドウスタイルを設定するためのクラスです。
CWinTraits<> は次のように定義されています。
template 0, DWORD t_dwExStyle = 0>
class CWinTraits
{
public:
static DWORD GetWndStyle(DWORD dwStyle)
{
return dwStyle == 0 ? t_dwStyle : dwStyle;
}
static DWORD GetWndExStyle(DWORD dwExStyle)
{
return dwExStyle == 0 ? t_dwExStyle : dwExStyle;
}
};
typedef CWinTraits> CControlWinTraits;
typedef CWinTraits CFrameWinTraits;
typedef CWinTraits CMDIChildWinTraits;
typedef CWinTraits<0, 0> CNullTraits;
CWinTraits<> は、引数として渡された dwStyle や dwExStyle が 0 だった場合はテンプレート引数の値を返しているだけです。
それから、よくある指定方法のウインドウスタイルは typedef されています。
このように、ただのフラグの集まりだったウインドウスタイルに意味と名前を持たせることが、おそらくは CWinTraits<> の目的の一つだと思います。
このクラスは CWindowImplBaseT<> と CWindowImpl<> で次のように使用されています。
template <class TBase, class TWinTraits>
class ATL_NO_VTABLE CWindowImplBaseT : public CWindowImplRoot< TBase >
{
public:
...
static DWORD GetWndStyle(DWORD dwStyle)
{
return TWinTraits::GetWndStyle(dwStyle);
}
static DWORD GetWndExStyle(DWORD dwExStyle)
{
return TWinTraits::GetWndExStyle(dwExStyle);
}
...
};
template <class T, class TBase, class TWinTraits>
class ATL_NO_VTABLE CWindowImpl : public CWindowImplBaseT< TBase, TWinTraits >
{
public:
HWND Create(HWND hWndParent, _U_RECT rect = NULL, LPCTSTR szWindowName = NULL,
DWORD dwStyle = 0, DWORD dwExStyle = 0,
_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)
{
...
dwStyle = T::GetWndStyle(dwStyle);
dwExStyle = T::GetWndExStyle(dwExStyle);
...
return CWindowImplBaseT< TBase, TWinTraits >::Create(hWndParent, rect, szWindowName,
dwStyle, dwExStyle, MenuOrID, atom, lpCreateParam);
}
};
CWindowImplBaseT::GetWndStyle() は CWinTraits<> に処理を委譲し(TWinTraits は別に CWinTraits<> である必要は無いのだけれど、それ以外を使うメリットは特にないだろう)、CWindowImpl::Create() の中で、T::GetWndStyle(dwStyle) としています。
こうすることで、継承先のクラス(T)が GetWndStyle() を実装しているとそちらが呼び出されて、実装してなかった場合は CWindowImplBaseT::GetWndStyle() が呼び出されます。
多くの場合はわざわざ継承先のクラスが実装したりしないだろうから、もし dwStyle が 0 だった(Create() でスタイルを指定しなかった)場合は、CWinTraits<> の引数の値が返されます。
CWinTraits<> は次のようにして使います。
class CMyFrameWindow : public CWindowImpl
{
...
};
class CMyFrameWindow2 : public CWindowImpl
{
...
};
これで、ウインドウクラスに対してウインドウスタイルが設定出来ました。
また、あるウインドウスタイルに依存したスタイルを書きたい場合は、CWinTraits<> を使うのは不向きです。
例えば、CControlWinTraits というスタイルに PBS_MARQUEE を追加したい場合に、
typedef CWinTraits0> CProgressWinTraits;
と書くと、CControlWinTraits の定義が変わったりすると CProgressWinTraits も変更する必要が出てきます。
それに、これは CControlWinTraits に依存しているということをプログラム上で表現できていないので、あまり良い書き方ではありません。
この場合は CWinTraitsOR<> を使って表現すると便利です。
typedef CWinTraitsOR0, CControlWinTraits> CProgressWinTraits;
これで CControlWinTraits の定義が変わってもこちらは変更する必要が無いし、CControlWinTraits への依存を表すことが出来るようになります。
CWinTraits<>, CWinTraitsOR<> はテンプレートを上手く使っててそれはそれで面白かったりするのですが、技術的な実現方法はどうあれ、一番重要なのは、ウインドウスタイルの組み合わせに意味と名前を付けられるようになったことなのではないだろうか、と勝手に思ってたりしています。